{"id":15538462,"url":"https://github.com/manatlan/htagui","last_synced_at":"2025-07-13T08:39:50.373Z","repository":{"id":232641477,"uuid":"784738307","full_name":"manatlan/htagui","owner":"manatlan","description":"Htag.ui ... Basic widgets for htag apps","archived":false,"fork":false,"pushed_at":"2025-04-05T08:23:50.000Z","size":380,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-21T03:41:47.309Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manatlan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-04-10T13:08:55.000Z","updated_at":"2025-04-05T08:23:02.000Z","dependencies_parsed_at":"2024-05-01T18:32:00.640Z","dependency_job_id":"f5a220a4-88e1-4fc1-875b-29cc543706e3","html_url":"https://github.com/manatlan/htagui","commit_stats":{"total_commits":124,"total_committers":1,"mean_commits":124.0,"dds":0.0,"last_synced_commit":"f6ee62dcf96cdc6002a1e4a305f42084001a481d"},"previous_names":["manatlan/htagui"],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/manatlan/htagui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manatlan%2Fhtagui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manatlan%2Fhtagui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manatlan%2Fhtagui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manatlan%2Fhtagui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manatlan","download_url":"https://codeload.github.com/manatlan/htagui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manatlan%2Fhtagui/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265111362,"owners_count":23713022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T12:04:13.552Z","updated_at":"2025-07-13T08:39:50.317Z","avatar_url":"https://github.com/manatlan.png","language":"Python","readme":"# htagUI\n\nThis is a (basic) UI toolkit for [htag](https://github.com/manatlan/htag) apps. Contrario to [htbulma](https://github.com/manatlan/htbulma), this one is a minimal toolkit, providing only useful and solid widgets, and will be maintained (you can use it ;-)).\n\n\u003cimg src=\"https://manatlan.github.io/htag/htag.png\" width=\"100\" height=\"100\"\u003e\n\n[![Test](https://github.com/manatlan/htagui/actions/workflows/on_commit_do_all_unittests.yml/badge.svg)](https://github.com/manatlan/htagui/actions/workflows/on_commit_do_all_unittests.yml)\n\n\u003ca href=\"https://pypi.org/project/htagui/\"\u003e\n    \u003cimg src=\"https://badge.fury.io/py/htagui.svg?x\" alt=\"Package version\"\u003e\n\u003c/a\u003e\n\n[here is a minimal example in the pyscript/runner](https://raw.githack.com/manatlan/htag/main/examples/pyscript_htagui.html)\n\n**roadmap**\n\n - provide a darker theme css\n - test test \u0026 test, to be rock solid\n - be available in htag4brython too, with the same apis.\n - perhaps provide version using \"shoelace web components\", or \"simple bulma styles\" (like htbulma) ... but the basics version (current one) will always be available, with its minimal footprint (js/css dependancies in mind)\n\n\n**INSTALL**\n```bash\npython3 -m pip install -U htagui\n```\n**note**: it will install htag and htagui, and provide the `ui` in the htag namespace (`htag.ui`)\n\nA hello world could be :\n\n```python\nfrom htag import Tag, Runner, ui\n\nclass MyApp(ui.App):\n    def init(self):\n        self \u003c= ui.Button(\"test\", _onclick=lambda ev: self.ui.alert( \"hello world\" ) )\n\nif __name__ == \"__main__\":\n    Runner(MyApp).run()\n```\n\n\n\nIt provides some (ready-to-use) Htag Objects, and some utilities methods.\n\n\n## Object App\n\nThis is a surcharge of Tag.body( ... ) which auto provide an `ui` property on the instance, to interact with `Dialog` features\n\nIn place of:\n```python\nclass MyApp(Tag.body):\n    def init(self):\n        self.ui = ui.Dialog(self)   # \u003c- it will do that, automatically\n        self \u003c= ui.Button(\"test\", _onclick=lambda ev: self.ui.alert( \"hello\" ) )\n```\n\nyou can do :\n```python\nclass MyApp(ui.App):\n    def init(self):\n        self \u003c= ui.Button(\"test\", _onclick=lambda ev: self.ui.alert( \"hello\" ) )\n```\n\n## Object Button\n\nA simple surcharge of Tag.button(...), to define a css class \n\n```python\nimport htagui as ui\nself \u003c= ui.Button(\"my button\",_class=\"myclass\", _onclick = myevent )\n```\n\n## Object Input\n\nA simple surcharge of Tag.input(...), to define a css class \n\n\n```python\nimport htagui as ui\nself \u003c= ui.Input(_value=\"my value\", _name=\"myfield\", _class=\"myclass\", _required=True )\n```\n\nAvailables managed `type`s (setted with `_type`) params:\n * `text` : the default one (if omitted): an input text\n * `checkbox` : a checkbox\n * `radio` : a radio button (non sense : use ui.Radios or uiIRadios !)\n * `range` : a slider/range\n\nFor `text`: a special field \"_label\" will set the html \"placeholder\" attribut.\n\n## Object Textarea\n\nA simple surcharge of Tag.textarea(...), to define a css class \n\n\n```python\nimport htagui as ui\nself \u003c= ui.Textarea(\"my value\", _name=\"myfield\", _class=\"myclass\", _required=True )\n```\n\nA special field \"_label\" will set the html \"placeholder\" attribut.\n\n## Object Spinner\n\nA spinner object.\n\n```python\nimport htagui as ui\nself \u003c= ui.Spinner()\n```\n\n## Objets \"I-fields\"\n\nTheses are interactive/reactive fields, which are automatically synced between client and server side, thru a 'value' property\n\n- IText .. an input of type text\n- IPassword .. an input of type password\n- ITextarea ... a textarea \n- IBool ...  an input of type checkbox\n- IRange ...  an input of type range\n- ISelect ... a multichoice select/option\n- IRadios ... a multichoice radio buttons\n\n## Object Select\n\nAn htag class to help to create \"select/option\" html tags, using a dict of {value:title, ...}.\n\n```python\nimport htagui as ui\nself \u003c= ui.Select( dict(a=\"A\",b=\"B\"), _value=\"a\", _name=\"myfield\" )\n```\n\n## Object Radios\n\nAn htag class to help to create \"radio button choices\" html tags, using a dict of {value:title, ...}.\n\n```python\nimport htagui as ui\nself \u003c= ui.Radios( dict(a=\"A\",b=\"B\"), _value=\"a\", _name=\"myfield\" )\n```\n\n## Object Menu\n\nAn htag class to help to create a (first-level) menu and menu items, using a dict of {title:callback,...}\n\n```python\nimport htagui as ui\nux = ui.Dialog(self)\nentries={\n    \"menu1\": lambda: ux.notify(\"menu1\"),\n    \"menu2\": lambda: ux.notify(\"menu2\"),\n    \"menu3\": lambda: ux.notify(\"menu3\"),\n}  \nself \u003c= ui.Menu( entries )\n```\n\n\n## Object Form\n\nA simple surcharge of Tag.form(...) where you can define a callback to call a method wich will receive a python \"dict\" of all named inputs defined in the form.\n\n```python\nimport htagui as ui\nux = ui.Dialog( self )\nform = ui.Form( onsubmit=lambda dico: ux.notify(str(dico)) )\nform \u003c= ui.Input(_name=\"mystring\",_placeholder=\"input something\")\nform \u003c= ui.Button(\"ok\")\nself \u003c= form\n```\n\n## Object FileUpload\n\nA simple surcharge of Tag.input( _type='file'...) which call a method to upload the selected file.\n\n```python\nimport htagui as ui\n\ndef uploaded(name:str, content:bytes):\n    print(name,content)\n\nself \u003c= ui.FileUpload( uploaded , _multiple=True)\n```\n\n## Object Tabs\n\nAn htag class to easily create tabs structure. And provides somes attributs/methods to interact with it.\n\n```python\nimport htagui as ui\ntab1 = Tag.div(\"content1\",name=\"tab1\") # tab object needs a `name` property !\ntab2 = Tag.div(\"content2\",name=\"tab2\")\nt = ui.Tabs( tab1, tab2 )\nself += t\n```\n\n### method t.add_tab( obj )\n\nA method to add dynamically a tab instance, which is automatically selected.\n(note that the tab object needs a `name` property !)\n\n### property t.selected\n\nDynamic property to retrieve or select the current selected tab.\n\n### event \"onchange\"\n\nEvent which is called when selected index changes.\n\n## Object Dialog\n\nExpose \"Dialog boxes\" with methods on the instance.\nNote that, there can be only one dialog at a time (except toast notification)\n\n```python\nimport htagui as ui\ndialog = ui.Dialog( self )\n```\n\n### method dialog.alert(obj, size:float=None)\n\n(like js window.alert(...)) Display a modal dialog box containing the object 'obj' (obj must be str'able)\n\nsize is a float to force the percent of width on the dialog box. If None, it will use the default from the ui used.\n\n### method dialog.confirm(obj, cbresponse=lambda bool)\n\n(like js window.confirm(...)) Display a modal dialog box containing the object 'obj' (obj must be str'able), and let the user click on Yes|No buttons, which will call the cbresponse callback with True or False ...\n\n### method dialog.prompt(title, value=None, cbresponse=lambda val)\n\n(like js window.prompt(...)) Display a modal dialog letting the user edit the `value` in an Input box, with a `title` (title must be str'able). When the user click the OK button the value is sent in the callback cbresponse. (clicking the cancel button does nothing, except close the dialog)\n\n\n\n### method dialog.notify(obj, time=2000)\n\nDisplay a toast message (notification), in the right-bottom ... during 2000 ms.\n(currently toast messages are not stacked)\n\n### method dialog.pop(obj, xy:tuple)\n\nDisplay an object, at coords (x,y).\n\nex \"create a popmenu\", using \"Menu object\"\n```python\nimport htagui as ui\ndialog = ui.Dialog(self)\nentries={\n    \"menu1\": lambda: dialog.notify(\"menu1\"),\n    \"menu2\": lambda: dialog.notify(\"menu2\"),\n    \"menu3\": lambda: dialog.notify(\"menu3\"),\n}  \nself \u003c= ui.Button(\"pop menu\", _onclick=lambda ev: dialog.pop( ui.Menu(entries) ,(ev.clientX,ev.clientY)) )\n```\nnote: it ensures that the object is fully visible in the inner window.\n\n### method dialog.drawer(obj, mode=\"left\")\n\nDisplay a drawer, in the left-side. mode can be left,right,top,bottom.\n\n### method dialog.page(obj=None)\n\nDisplay a full page 'obj', or remove page if obj is None. (note that ui.close() does not close the page) \n\n### method dialog.block(obj=None)\n\nDisplay a modal dialog box containing the object 'obj'. But the dialog is not closable, so be sure to provide a way to close it.\n\n### method dialog.close()\n\nClose programatically, the current ui dialog.\n\n### method dialog.clipboard_copy(txt:str)\n\nCopy the txt in the clipboard\n\n### method dialog.clipboard_paste( lambda content )\n\nCall the callback with the text content from the clipboard. (user may need to authorize the interaction from the navigator)\n\n### method dialog.download( name:str, content:bytes ):\n\nForce the navigator to download a file named 'name', with the content bytes 'content' into the browser.\n\n## Object HSplit \u0026 VSplit\n\nA Tag object to use \"SplitJS\"\n\n```python\nimport htagui as ui\nsplit = ui.HSplit( Tag.div(1), Tag.div(2), sizes=[60,40], minSize=100, _style=\"border:2px solid red;height:100px\" )\nself \u003c= split\n```\n\nmethods:\nsplit.setSizes( [50,50] )\nsplit.onchange = lambda object: print( object.sizes )   # event\n\n\n## Object Sortable\n\nA component to let the user reorder items using drag'n'drop.\n\n```python\nll=[Tag.div(f\"Item {i} (drag me)\",value=i) for i in range(10)]\n        \ndef onchange(o:ui.Sortable):\n    print( str([i.value for i in o.values]) )\n\nself \u003c= ui.Sortable(ll,onchange=onchange)\n```\n\n## Object VScroll\n\nA component to help you to create an infinite scroller.\n\n```python\ndef feed():\n    yield [Tag.div(i) for i in range(20) ]\n\nself \u003c= Tag.div(_style=\"height:200px; border:1px solid red;\" ) \u003c= ui.VScroll( feed )\n```\n\nThis component should be embbeded in an element which have a constraint on its height. The given callback should yield Tag(s) (or `None` to finnish the endless scroll)\n\n## Object VScrollPager\n\nA component (which inherit from VScroll) to present a finnished list of items in an \"on-demand\" way (create items on-demand), can contain a big amount of data.\n\n```python\n\nll=[lambda i=i: MyObject(i) for i in range(1,200_000_000)]\nself \u003c= Tag.div(_style=\"height:200px; border:1px solid red;\" ) \u003c= ui.VScrollPager(ll,preload=50,moreload=10,endzone=50)\n\n```\nThis component should be embbeded in an element which have a constraint on its height. `preload` is the number of items which will be created (it should overflow on height). `moreload` is the number of items which will be loaded when scrolling is in the `endzone` pixels at the end.\n\nNote that the list is a list of lambda (`List[Callable[[], Tag]]`) to create the rendering on-demand.\n\n\n## Object View\n\nA view component which handle the browser navigation mechanism (go back)\n\n**IMPORTANT**:\n\n - this component is here for tests only (it may disappear if real problems)\n - FOR POWER USERS : may be used as first class, and you need to understand how htag works, and how the browser history works ! (if the object is deleted -\u003e nothing work)\n\nTODO: make a better good example !\n\n```python\n\ndefault_view=Tag.div(\"Default view\")\n\nv = ui.View( default_view, _style=\"border:1px solid red;width:100%;height:400px\" )\n\nself \u003c= ui.Button(\"p1\", _onclick = lambda ev: v.go( Tag.div(\"p1\") ))\nself \u003c= ui.Button(\"p2\", _onclick = lambda ev: v.go( Tag.div(\"p2\") ))\nself \u003c= v\n```\n## Object Grid\n\nsignature : Grid(format,vformat=\"auto\",gap=\"1px\",**a)\n\nA simple grid container\n\nexample:\n```python\n\ng = Grid(\"1fr 1fr 2fr\")\ng \u003c= Tag.div( \"2colums\",_style=\"grid-column:1 / 3\")    \ng \u003c= Tag.div( \"1column\")  \n\n```\n\n\n## utilities methods\n\n### hflex \u0026 vflex\n\nMethods to create an HBox or VBox htag class (flexbox horizontal or vertical, with nowrap mode)\n\n```python\nimport htagui as ui\nHBox = ui.hflex(50, 50)  # create a hbox of 2 elements of 50% each\nself \u003c= HBox( Tag.div(1), Tag.div(2) )\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanatlan%2Fhtagui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanatlan%2Fhtagui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanatlan%2Fhtagui/lists"}