{"id":22120750,"url":"https://github.com/grz0zrg/wui","last_synced_at":"2025-07-10T17:04:12.791Z","repository":{"id":31367478,"uuid":"34930435","full_name":"grz0zrg/wui","owner":"grz0zrg","description":"Collection of GUI widgets for the web","archived":false,"fork":false,"pushed_at":"2021-10-17T13:54:53.000Z","size":2535,"stargazers_count":48,"open_issues_count":0,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-02T07:51:13.867Z","etag":null,"topics":["circular-menu","customizable","dialog","dropdown","gui","javascript","lightweight","midi","pie-menu","rangeslider","slider","tabs","toolbar","ui-components","vanilla","webmidi","widget"],"latest_commit_sha":null,"homepage":"https://grz0zrg.github.io/wui-demo/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grz0zrg.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}},"created_at":"2015-05-02T00:38:48.000Z","updated_at":"2025-03-31T10:43:52.000Z","dependencies_parsed_at":"2022-08-24T13:10:34.816Z","dependency_job_id":null,"html_url":"https://github.com/grz0zrg/wui","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/grz0zrg/wui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grz0zrg%2Fwui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grz0zrg%2Fwui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grz0zrg%2Fwui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grz0zrg%2Fwui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grz0zrg","download_url":"https://codeload.github.com/grz0zrg/wui/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grz0zrg%2Fwui/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264613659,"owners_count":23637415,"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":["circular-menu","customizable","dialog","dropdown","gui","javascript","lightweight","midi","pie-menu","rangeslider","slider","tabs","toolbar","ui-components","vanilla","webmidi","widget"],"created_at":"2024-12-01T14:29:46.813Z","updated_at":"2025-07-10T17:04:12.768Z","avatar_url":"https://github.com/grz0zrg.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WUI #\n\nCollection of **easy to use**, **independent** and **lightweight** (*~5.6kb css*, *~15.9kb js* gzipped) **vanilla** GUI widgets for the web.\n\n**Require no dependencies, all widgets can be used on their own.**\n\nThere is a dark and a bright theme, the **dark theme is the default theme** and is more polished than the bright one.\n\nThe main advantages compared to other libraries are:\n\n* Widgets can be used on their own (no dependencies)\n* Input and Slider widgets can be MIDI controlled with two mode (absolute, relative) and can be user-configurable\n* MIDI learn can be used as a widget of its own through the `midi_square_only` option of input / slider widget\n* Dialogs can be detached (and everything related to WUI will still work)\n* Circular menu aka [pie menu](https://en.wikipedia.org/wiki/Pie_menu) aka radial menu :)\n* Quick forms creation with standard HTML5 forms elements and WUI elements (WIP/Experimental)\n* Events are optimized (meaning that there is few listeners for each widgets)\n* Easily customizable/hackable\n* Lightweight\n* ...\n\nGreat for single page apps (audio apps, games etc.), experiments and the like.\n\n#### Demo\n- [Demo](https://grz0zrg.github.io/wui-demo/)\n- [Demo repository](https://github.com/grz0zrg/wui-demo)\n\n#### Links\n- [Documentation](#doc)\n- [Compatibility](#compat)\n- [License](#license)\n\n\u003ca name=\"doc\"\u003e\u003c/a\u003e\n# Documentation #\n\n##### Usage\n\nThere is a minified and gzipped up to date ready-to-use js file in the **_dist_** folder, to use the entire library, just include it in your HTML file\n\nThere is also a minified and gzipped up to date ready-to-use css file for each themes in the **_dist/[theme_name]_** folder, to use it, just include it in your HTML file\n\n```html\n\u003clink rel=\"stylesheet\" type=\"text/css\" href=\"wui.min.css\"/\u003e\n\u003cscript type=\"text/javascript\" src=\"wui.min.js\"\u003e\u003c/script\u003e\n```\n\nIf you need a single (or more) widget, you can find minified files of each widget in the **_dist/widgets_** folder\n\n#### Building with [Node](https://nodejs.org/) and [Grunt](http://gruntjs.com/)\n\n\n```\nnpm install\ngrunt dist\n```\n\n### Introduction\n\n\n*   [WUI](#wui_main)\n*   [WUI_Tabs](#tabs)\n*   [WUI_Dialog](#dialog)\n*   [WUI_DropDown](#dropdown)\n*   [WUI_ToolBar](#toolbar)\n*   [WUI_Form](#form)\n*   [WUI_Input](#input)\n*   [WUI_RangeSlider](#rangeslider)\n*   [WUI_CircularMenu](#circularmenu)\n\nThe WUI API is simple, all widgets have a method **_\"create\"_** which accept a DOM element (which should contain an identifier) or a DOM element identifier as first argument which is used as a bind target and an option object as a second argument to customize it, WUI_ToolBar, WUI_Form and WUI_DropDown has a third argument which is used to specify the items.\n\nAll **_\"create\"_** methods return a reference of the widget which can be used later to do stuff with the widget like destroying them, the reference is a string (it is the dialog element id).\n\nAll widgets also have a method **_\"destroy\"_**.\n\nWUI_RangeSlider widget have a method **_\"getParameters\"_** and **_\"setParameters\" which can be used to save/retrieve the widget parameters.\n\nHTML elements with a specific layout are required to use some widgets (like tabs, see the documentation)\n\nA bit of style hacking may be necessary if you want widgets to suit your need or your taste, you can build themes easily with bits of CSS, the demo page can be helpful.\n\n### Hacking\n\n\nWUI is itself a big hack so hacking it is just the right way to use it! :)\n\nExtending widgets to suit your needs or taste is very easy, widgets code are separated into three sections, **_private fields_**, **_private functions_** and **_public functions_**.\n\nIf you want to add an option to a widget, you add a field into the **_known_options** object in the **_private fields_** section, this is where all the options known by the widget are stored with their default value, the option behavior can then be implemented in the **_create_** function (or used later since it is also stored in the widget specific storage), you access to the option value in the **_create_** function by using the local **_opts_** object.\n\nIf you want to add/change advanced behaviors, you can create or modify private or public functions, the object **_widget_list** which is available in the scope of most widgets serve as a generic storage for all widgets, all created widgets are stored here with their ID as key (**_widget_list[\"my_widget_id\"]** to access to a specific widget storage), there is at least an **_opts_** field for each widgets which can be used to retrieve/override the widget options.\n\nThemes are done by cloning existing `.css` files and modifying them, to do it properly you can clone a theme `wui/css/themes/[theme_name] folder`, rename it, add the corresponding build line into Gruntfile.js (see **_files_** object), build and your minified/gzipped theme will be available in the **_dist_** folder.\n\n### Reporting\n\nBy default, console.log reporting is disabled, to turn it on introduce the global variable `WUI_Reporting` this will enable output to the console which may be useful to lookup issues.\n\n```javascript\nwindow.WUI_Reporting = true;\n```\n\n\u003ca name=\"wui_main\"\u003e\u003c/a\u003e\n\n### WUI ###\n\n\nNot really a widget but a collection of tools, can be helpful if you want to add draggable/resizable functionality to an element or apply fade in/out.\n\nFade in/Fade out method default to 500ms if no duration is provided\n\n*Methods*:\n\n\u003e*   draggable(element, on_drag_cb, virtual, target_element)\n\u003e*   undraggable(element)\n\u003e*   lockDraggable(element, axis)\n\u003e*   fadeIn(element, duration_ms)\n\u003e*   fadeOut(element, duration_ms, fade_finish_cb, hide_when_fade_finish)\n\n*Example*:\n\n```javascript\n// make the element draggable\n// a callback for the second argument can be specified to gather the new position, like: function (element, x, y) {  }\n// third argument is optional, is a boolean and can be true to make the draggable functionality \"virtual\" which mean it will NOT modify the element position but still call the callback, it is left to the user to do any appropriate changes, this may be useful to add resizable functionality easily by using it with \"lockDraggable\", this default to false if not specified\n// fourth argument is optional and is used to bind multiple draggable hooks to control a specific DOM target\n// Note: If you want to remove the element while it is draggable, make it undraggable first by calling the appropriate function (undraggable), this is because WUI keep an internal reference of the element and it may cause memory leaks later, calling undraggable will make the reference go away\nWUI.draggable(my_element, function (element, x, y) {  }, false, target_element);\n\n// make a WUI draggable element undraggable and remove the internal reference\nWUI.undraggable(my_element);\n\n// lock draggable element to a specific axis ('x' or 'y', to clear the lock, just call it without specifying axis or specify whatever)\nWUI.lockDraggable(my_element, 'x');\n\n// apply a 500ms fade in effect to an element\nWUI.fadeIn(my_element, 500);\n\n// apply a 500ms fade out effect to an element, when finished output \"finished\" to the browser console and hide the element (display: none)\nWUI.fadeOut(my_element, 500, function () { console.log(\"finished!\"); }, true);\n```\n\n\n\n\u003ca name=\"tabs\"\u003e\u003c/a\u003e\n### Tabs ###\n\nNote : Must be called after WUI_Dialog.create if tabs are within a dialog and if the dialog is detachable.\n\n*Methods*:\n\n\u003e*   create(id, options)\n\u003e*   destroy(wui_tabs)\n\u003e*   getContentElement(wui_tabs, tab_index)\n\u003e*   getTabName(wui_tabs, tab_index)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_tabs\"\u003e\n\t\u003cdiv\u003e\n\t\t\u003c!-- a list of tabs name --\u003e\n\t\t\u003cdiv\u003eTab 1\u003c/div\u003e\n        \u003cdiv\u003eTab 2\u003c/div\u003e\n\t\u003c/div\u003e\n\n\t\u003cdiv\u003e\n\t\t\u003c!-- a list of tabs content --\u003e\n\t    \u003cdiv\u003e\n\t\t\tTab 1 content\n\t    \u003c/div\u003e\n\n\t    \u003cdiv\u003e\n\t        Tab 2 content\n\t    \u003c/div\u003e\n\t\u003c/div\u003e\n\u003c/div\u003e\n```\n\n```javascript\nWUI_Tabs.create(\"my_tabs\", {\n\t// function called when a tab is clicked (tab index will be passed as argument)\n\ton_tab_click: tab_clicked,\n\n    // style value for the content height\n    height: \"calc(100% - 32px)\" // this is the default value\n});\n```\n\n\n\n\u003ca name=\"dialog\"\u003e\u003c/a\u003e\n### Dialog/Panel ###\n\n\nDialogs can be draggable, closable, minimizable, resizable, detachable, modal and act as panels, they also go in front of others when you move them.\n\nOne of the coolest and rather unique feature of the dialog widget is the ability to be detached from the window it is on and act as a proper window without breaking the content (including events), this may be very useful, the user can detach any dialogs and move them on other screens etc.\n\nAll WUI widgets work very well with the detachable feature, what you change in the detachable dialog will be changed in the 'original' dialog, this should be the same dialog after all, for example, if you toggle a WUI_ToolBar button in the detached dialog and close it, when you open the dialog again (detached or not) the button will be toggled, the only thing which is not synced is the size of the detached dialog and its position.\n\n\n\u003e**Notes**:\n\u003e\n\u003eOn iPad, the detach feature will work but Safari will open the dialog as a new tab.\n\u003e\n\u003e\n\u003eThe detach feature keep track of events by overriding `addEventListener`, it will only keep track of  events that are added on an element inside the dialog which mean that the calls to `addEventListener` must be made after the element is attached to a node inside the dialog, in order to work correctly the WUI_Dialog/WUI library should be loaded before you or other libs add events.\n\u003e\n\u003e\n\u003eWhen a dialog is detached, it will add back event listeners added with `addEventListener` only (and also inline events), if you attach events to elements in the dialog content using `elem.onclick` etc, the event will not be added back, also since the dialog content will be in another window/document, events attached to the initial window or document and acting on the dialog content will not work, because the dialog is now in another window, you will have to take care of attaching to/using `element.ownerDocument` or `element.parentWindow` instead of `document` or `window`.\n\u003e\n\u003e\n\u003eDialogs `zIndex` is between 100 and 101.\n\u003e\n\u003eWhen a dialog is detached any CSS (must have `rel=\"stylesheet\" type=\"text/css\"` attributes) / Style defined in the `head` element will be attached back.\n\n*Methods*:\n\n\u003e*   create(id, options)\n\u003e*   destroy(wui_dialog)\n\u003e*   open(wui_dialog, detach)\n\u003e*   centerAll()\n\u003e*   close(wui_dialog, propagate)\n\u003e*   closeAll(propagate)\n\u003e*   focus(wui_dialog)\n\u003e*   setStatusBarContent(wui_dialog, html_content)\n\u003e*   setTitle(wui_dialog, html_content)\n\u003e*   getTitle(wui_dialog)\n\u003e*   getDetachedDialog(wui_dialog)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_dialog\"\u003e\n\t\u003cdiv\u003e\n\t\tthe dialog content\n\t\u003c/div\u003e\n\u003c/div\u003e\n```\n\n```javascript\nvar my_dialog = WUI_Dialog.create(\"my_dialog\", {\n\ttitle: \"dialog title\",\n\n    width: \"20%\",\n    height: \"50%\",\n\n    // 'left', 'center', 'right', default to 'left'\n    halign: \"left\",\n    // 'top', 'center', 'bottom', default to 'top'\n    valign: \"center\",\n\n    // wether the dialog is opened or not after creation\n    open: true,\n\n    // wether the dialog is minimized or not after creation\n    minimized: false,\n    \n    // function called when the dialog has been opened\n    on_open: null,\n\n    // function called when the dialog has been closed\n    on_close: null,\n\n    // function called before the dialog is detached\n    on_pre_detach: function () {\n\n    },\n\n    // function called when the dialog is detached, the new `window` object is passed as argument\n    on_detach: function (new_window) {\n        // you can modify the detachable window there, example:\n        new_window.document.title = \"My detached window\"; // replace the detached dialog title\n    },\n\n    // function called when the dialog is resized, `new_width` and `new_height` is the new dimension of the dialog content\n    on_resize: function (new_width, new_height) {\n\n    },\n\n    // you can add header buttons easily by using this (such as a help \"?\" button)\n    header_btn: [\n        {\n            title: \"Help\",\n            on_click: function () {\n                // code called when the header button is clicked, redirecting to a documentation for example\n            },\n            class_name: \"\" // class name to use (you will generally need it if you want to show an icon like \"?\", use the background-image property to do so)\n        }\n    ],\n\n    modal: false,\n\n    // wether the dialog have a status bar\n    status_bar: true,\n\n    // HTML content of the status bar\n    status_bar_content: \"status bar content\",\n\n    closable: false,\n    draggable: true,\n    minimizable: true,\n\n    resizable: true,\n\n    detachable: true,\n\n    // the minimun width/height the dialog can be when resized (min_width accept a value or \"title\")\n    min_width: \"title\",\n    min_height: 64,\n\n    // option to keep the align when resized, example: if the dialog is centered in the window, the dialog will always be in center when it is resized\n    keep_align_when_resized: true,\n\n    // can be used to position the dialog, default to 0\n    top: 0,\n    left: 0\n});\n```\n\nClosed and want to open it again?\n\n```javascript\n// second argument is optional (default to false) and tell wether the dialog is opened in its own window\nWUI_Dialog.open(my_dialog, false);\n```\n\n\nOpen and want to close it programmatically?\n\n```javascript\nWUI_Dialog.close(my_dialog, true); // last argument is optional (default to false) mean the on_close function will be called\n```\n\n\nFocusing a specific dialog\n\n```javascript\nWUI_Dialog.focus(my_dialog);\n```\n\n\nWant to change the status bar content?\n\n```javascript\nWUI_Dialog.setStatusBarContent(my_dialog, \"My new status bar content\");\n```\n\nWant to change the title bar content ?\n\n```javascript\nWUI_Dialog.setTitle(my_dialog, \"My new title\");\n```\n\nNeed access to the detached dialog window object?\n\n```javascript\nvar detached_dialog_window = WUI_Dialog.getDetachedDialog(my_dialog);\n```\n\n\u003ca name=\"dropdown\"\u003e\u003c/a\u003e\n### DropDown ###\n\n\n\u003eA simple and automatically opening/closing dropdown.\n\n*Method*:\n\n\u003e*   create(id, options, entry_name_array)\n\u003e*   destroy(wui_dropdown)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_dropdown\"\u003e\u003c/div\u003e\n```\n\n```javascript\nWUI_DropDown.create(\"my_dropdown\", {\n\t\twidth: \"100px\",\n\t    height: \"24px\",\n\n\t    // the space between the floating list of items and the dropdown \"button\"\n\t    vspacing: 4,\n\n\t    // time before the floating list close\n\t    ms_before_hiding: 1000,\n\n\t    // default item (id) to be selected after creation\n\t    selected_id: 0,\n\n\t    vertical: false,\n\n\t    // function called when an item is selected, the item index is passed as argument\n\t    on_item_selected: item_selected\n    },\n    // a list of items\n    [\"First item\", \"Second item\", \"Third item\"]\n);\n```\n\n\u003ca name=\"toolbar\"\u003e\u003c/a\u003e\n### ToolBar ###\n\n\nThe toolbar can be horizontal or vertical, have groups and minimizable groups, have three type of buttons, simple, toggle and dropdown (useful to make menu bar), a set of toggle buttons can be linked (grouped), buttons can be an icon, a text or both.\n\n*Method*:\n\n\u003e*   create(id, options, tools)\n\u003e*   destroy(wui_toolbar)\n\u003e*   hideGroup(wui_toolbar, group_index)\n\u003e*   showGroup(wui_toolbar, group_index)\n\u003e*   toggle(wui_toolbar, tool_id, propagate)\n\u003e*   getItemElement(wui_toolbar, tool_id)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_toolbar\"\u003e\u003c/div\u003e\n```\n\n```javascript\nvar my_toolbar = WUI_ToolBar.create(\"my_toolbar\", {\n    // display the minimize icon for each groups\n    allow_groups_minimize: true,\n    \n    // show groups title (key) below or above the toolbar buttons (also work with vertical toolbar)\n    show_groups_title: false,\n    \n    // groups title orientation (either \"s\"/bottom or \"n\"/top)\n    groups_title_orientation: \"s\",\n\n    // this will modify the size of the button\n    item_width: 32,\n    item_height: 32,\n\n    // this set the spacing between buttons, aka margin to left/right or top/bottom\n    item_hmargin: 0,\n    item_vmargin: 0,\n\n    icon_width: 32,\n    icon_height: 32,\n\n    vertical: false\n  },\n  {  \n\t// properties added to this object are recognized automatically as new groups by the widget\n    my_first_group_of_tools: [\n      {\n        // id attribute\n        id: \"my_pencil_tool\",\n\n        // CSS class name\n        icon: \"pencil-icon\",\n\n        // CSS class name when toggled, default to the value of icon if not present\n        toggled_icon: \"pencil-icon\",\n\n        // button text\n        text: \"\",\n\n        // can be \"toggle\", \"dropdown\" or nothing for a simple button\n        type: \"toggle\",\n\n        // a toggling group id, another button with the same toggling group id will be linked to this one and will be off when this one will be switched on\n        toggle_group: 0,\n\n        // toggle state of the button after creation, only if it is a toggle button\n        toggle_state: true,\n\n        // function called when a button is clicked, if the item is of type \"toggle\", an object containing a field \"id\" (id of the toolbar tool), \"type\" (will contain \"toggle\") and \"state\" (0 or 1) will be passed as argument\n        on_click: toolbar_item_toggle,\n\n        // function called when a button is right clicked, valable for all items\n        on_rclick: null,\n\n        // tooltip\n        tooltip: \"Toggle me!\",\n\n        // tooltip when toggled, default to the value of tooltip if not present\n        tooltip_toggled: \"Toggle me!\"\n      }\n    ],\n\n    my_second_group_of_tools:    [\n      { icon: \"undo-icon\", on_click: toolbar_item_click, tooltip: \"Click me!\" },\n      { icon: \"redo-icon\", on_click: toolbar_item_click, tooltip: \"Click me!\" }\n    ],\n\n    // a typical menu with a list of clickable items\n    my_menu: [\n      {\n        text: \"File\",\n        on_click: toolbar_item_click,\n        tooltip: \"Click me!\",\n        type: \"dropdown\",\n\n        // define where the list of items will appear around the item when it will be opened\n        // can be \"s\", \"sw\", \"se\", \"nw\", \"ne\", anything else will default to \"n\" (north)\n        orientation: \"n\",\n\n        // define the width of the list items\n        // can be a CSS width (auto, 64px etc) or \"tb_item\" so the list items have the same width as the toolbar item button\n        dropdown_items_width: \"tb_item\",\n\n        // items of the dropdown are defined here\n        items: [\n          {\n            title: \"New\",\n\n            // function called when the item is clicked\n            on_click: my_new_file\n          },\n\n          {\n            title: \"Open\",\n\n            // function called when the item is clicked\n            on_click: my_open_file\n          }\n        ]\n      }\n    ]\n  });\n```\n\nYou can toggle a specific button programmatically with:\n\n```javascript\nWUI_ToolBar.toggle(my_toolbar, 0, true); // the last argument is optional and mean that the toggle event will call the onClick function\n```\n\n\nYou can integrate [Font Awesome](http://fontawesome.io/) easily with the CSS icon class name attribute, for example for a CSS icon class named `app-home-icon`:\n\n```css\n.wui-toolbar-item.app-home-icon:before {\n    font-size: 24px; /*you may need to change that*/\n    content: \"\\f015\";\n    font-family: FontAwesome;\n    margin-left: 4px; /*you may need to change that if font-size change*/\n    margin-top: 4px;\n    position: absolute;\n}\n```\n\nIf you find this too cumbersome to setup, you can render Font Awesome icons to images with this tool: [fa2png](http://fa2png.io/)\n\n\u003ca name=\"form\"\u003e\u003c/a\u003e\n### Form ###\n\nThe form widget is a powerful feature allowing to design complete 'settings' panel and things requiring form elements with coherent styling, quickly, easily with integration of some high level functionalities (such as hide/show items group), it accept standard HTML5 form elements and WUI_Input, WUI_RangeSlider, WUI_DropDown.\n\nThe form widget has two important methods `getParameters` and `setParameters` which allow to respectively serialize and deserialize entire forms data (and subsequently trigger `change` event if needed) as JSON, **a form item must have an unique name to be serializable**.\n\nSee the demo page for a detailed example of configuration options.\n\nNote : Work in progress / experimental feature; may be broken\n\n*Methods*:\n\n\u003e* create(id, options, items)\n\u003e* destroy(wui_form)\n\u003e* getParameters(wui_form)\n\u003e* setParameters(wui_form, parameters, trigger_on_change_boolean)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_form\"\u003e\u003c/div\u003e\n\u003c!--\n\tYou can also use \u003cform id=\"my_form\"\u003e\u003c/form\u003e for standard HTML forms\n--\u003e\n```\n\n```javascript\nWUI_Form.create(\"my_form\", {\n\twidth: \"auto\"\n}, \n{\n\t// properties added to this object are recognized automatically as new FIELDSET by the widget, the property name is used for the fieldset legend\n\t\"Form : First group\": [\n\t\t// list of form items\n\t\t{\n\t\t\t// a WUI_DropDown\n\t\t\ttype: \"WUI_DropDown\",\n\t\t\t// WUI_DropDown options\n\t\t\topts: {\n\t\t\t\twidth: \"100px\",\n\t\t\t\tvspacing: 4,\n\t\t\t\tms_before_hiding: 1000,\n\t\t\t\tselected_id: 0,\n\t\t\t\tvertical: false,\n\t\t\t\ton_item_selected: function () {}\n\t\t\t},\n\t\t\t// WUI_DropDown items\n\t\t\titems: [\"First item\", \"Second item\", \"Third item\"]\n\t\t},\n\t\t{\n\t\t\t// a standard \u003cselect\u003e with legend\n\t\t\ttype: \"select\",\n            label: \"My choice\",\n            wrap: true, // wrap it in a div (for line-break)\n            attr: {\n            \t// you can put any DOM attributes here to apply customizations\n        \t},\n        \tvalue: \"\",\n        \t\n        \t// specific datalist/select attributes\n        \toptions: [\n                {\n        \t\t\t// required attributes\n\t\t\t\t\tname: \"option1\",\n        \t\t\t// optional attributes\n        \t\t\tdisabled: false,\n        \t\t\tselected: false,\n        \t\t\tvalue: \"option1\",\n        \n                    attr: {\n                        // you can put any DOM attributes here to apply customizations to the \u003coption\u003e element\n                    }\n                },\n                {\n\t\t\t\t\tname: \"option2\"\n                },\n        \t\t\"option3\" // also work (but not customizable)\n    \t\t]\n\t\t}\n\t]\n});\n```\n\n\u003ca name=\"input\"\u003e\u003c/a\u003e\n### Input ###\n\n\nWUI does not support an input widget out of the box but there is a shortcut \"WUI_Input\" which is just a RangeSlider in disguise, you must set the \"bar\" option to false and you get an input and it share the same properties like MIDI support and so on, see RangeSlider widget for options and methods.\n\n\u003ca name=\"rangeslider\"\u003e\u003c/a\u003e\n### RangeSlider ###\n\n\nRange slider widget can be horizontal or vertical, can be user configurable (step, min, max etc options can be set by the user as he want it), have a negative/positive range, the value can be changed with the mouse wheel, by moving the hook point by dragging, by a MIDI controller or by clicking on the slider bar, a double click on the slider will reset the value to its default value, the value also appear as an input which perform automatically all sanity check and will indicate if the value is correct or not (red)\n\nThere is one special option to hide all the widget and only show the MIDI learn square, this allow to enable MIDI learn capabilities to any custom widgets or use it as standalone.\n\nOne of the coolest (and maybe 'unique') feature of the WUI slider widget is the ability to be controlled entirely from MIDI controllers with absolute and relative mode, it is as easy as calling a function when a MIDIMessage is received and all MIDI enabled sliders will be usable with any MIDI interfaces.\n0\nTo assign a MIDI controller, the widget implement a sort of MIDI learn function, you click on a square on MIDI enabled sliders and, when a MIDI data is received, the controller is automatically assigned to that widget and you can start to control it from your MIDI interface...\n\nThe MIDI learn square tooltip show the device / controller / MIDI mode when it is assigned to a device.\n\nThe \"rel\" MIDI mode allow infinite values, it work well with \"endless\" rotary controls (also called encoders).\nThe \"abs\" MIDI mode is based on the \"min\" and \"max\" property, it act as a percentage of the range, if you want to match MIDI spec, just assign \"0\" and \"127\" for \"min\" and \"max\" option.\n\nDefault mode is \"abs\". When \"min\" and \"max\" property are not defined and the range cannot be computed the widget automatically fallback to \"rel\" MIDI mode.\n\nOnly MIDI input is supported at the moment but it should not be hard to add MIDI output later on.\n\n*Method*:\n\n\u003e*   create(id, options)\n\u003e*   destroy(wui_rangeslider)\n\u003e*   getParameters(wui_rangeslider)\n\u003e*   setParameters(wui_rangeslider, parameters, trigger_on_change_boolean)\n\u003e*   setValue(wui_rangeslider, value, trigger_on_change_boolean)\n\u003e*   submitMIDIMessage(midi_event)\n\n*Example*:\n\n```html\n\u003cdiv id=\"my_range_slider\"\u003e\u003c/div\u003e\n```\n\n```javascript\nWUI_RangeSlider.create(\"my_range_slider\", {\n    // width/height of the slider, if you make a vertical slider, you should swap theses values\n    width: 300,\n    height: 8,\n\n    // the value range, -100 \u003c-\u003e 100, optional\n    min: -100,\n    max: 100,\n\n    // standard increment when dragging NOTE : can be \"any\" if you need to accept both decimals and integers for the validation (\"step\" is just the bare \"step\" input attribute value actually!)\n    step: 1,\n\n    // on mouse wheel increment\n    scroll_step: 2,\n\n    vertical: false,\n\n    // max number of decimals to display for decimal values (default to 4)\n    decimals: 4,\n\n    // the widget default value (used for \"reset to default\" behaviors)\n    default_value: 0,\n\n    // the widget current value\n    value: 0.5,\n\n    // this will place the title on top and the value at the bottom, good for vertical sliders\n    title_on_top: true,\n\n    title: \"my range slider\",\n\n    // enable the slider to be controllable by MIDI\n    // can be a boolean (or anything) if you do not need to configure it, otherwise an object with a fiel \"type\" with value \"rel\" or \"abs\", by default the control type is set to \"abs\"\n    midi: false,\n\n    // only show the MIDI learn square (nothing else!) this may be usefull if you don't need the widget at all but just want to use the MIDI learn feature with some kind of custom widget or alone\n    midi_square_only: false,\n\n    // used to line up multiple sliders perfectly\n    title_min_width: 150,\n    value_min_width: 48,\n\n    // allow the user to configure the parameters of the slider, a settings button will appear around the slider and when it is clicked, a box with input fields will allow the user to change range and step values\n    // this can be usefull if an \"unlimited\" slider is needed or simply to allow the step to be changed by the user\n    // Note: if you want a few parameters to be configurable, just remove those you don't want in that object\n    configurable: {\n    \t// this allow the \"min\" parameter of the slider to be configurable\n    \t// \"min\" and \"max\" is the allowed range of the created input field, it is used to set some boundaries to what range of values the user can set\n    \t// it is also possible to leave the object empty (like \"max\" below) to allow unlimited sliders (any values for min/max will be then allowed, allowing the user to extend the range as he like)\n    \tmin: { min: -100, max: 100 },\n    \tmax: { },\n    \tstep: { min: 0.1, max: 2 },\n    \tscroll_step: { max: 5 }\n    },\n\n    // function to call when the slider value change with the value passed as argument\n    on_change: slider_change\n  });\n```\n\n\nMIDI usage example :\n\n```javascript\n// setup Web MIDI so we can control the MIDI enabled sliders with any connected MIDI controllers\nif (navigator.requestMIDIAccess) {\n    navigator.requestMIDIAccess().then(\n            function (m) {\n                m.inputs.forEach(\n                    function (midi_input) {\n                        midi_input.onmidimessage = function (midi_message) {\n                            WUI_RangeSlider.submitMIDIMessage(midi_message);\n                        };\n                    }\n                );\n        });\n}\n```\n\n\n\nSet value programmaticaly (last argument is optional and can be used to trigger the change function by setting it to true) :\n\n```javascript\nWUI_RangeSlider.setValue(my_slider, my_value, trigger_on_change);\n```\n\n\n\nExport the widget parameters :\n\n```javascript\nvar parameters = WUI_RangeSlider.getParameters(my_slider);\n// parameters contain opts (containing all options fields), endless, midi and value field\n```\n\n\n\nImport parameters (last argument is optional and can be used to trigger the change function by setting it to true) :\n\n```javascript\nWUI_RangeSlider.setParameters(my_slider, parameters);\n```\n\n\n\n\u003ca name=\"circularmenu\"\u003e\u003c/a\u003e\n### CircularMenu ###\n\n\nShow a menu with items arranged in a circle/ellipse.\n\nThis widget `create` function does not return anything and the widget do not have a destroy method, it is automatically destroyed by the library, thus you need to call `create` each time you want it to appear somewhere.\n\nThe menu can be shown around an element or a position, if an element is specified in the options, `x y` properties will be ignored, `x y` or `element` need to be set in order to use this widget, all other properties are optional.\n\nYou can use the \"angle\" option (degree) to offset the items position by an amount.\n\nItems are simple round buttons with or without HTML content, an icon class name can be specified, a tooltip and a callback.\n\n*Method*:\n\n\u003e*   create(options, items)\n\n*Example*:\n\n```javascript\nWUI_CircularMenu.create({\n    x: 64,\n    y: 64,\n\n    element: null, // if an element is specified, the menu will be shown around it\n\n    angle: 90, // offset the items positions by 90°\n\n    // radius\n    rx: 64,\n    ry: 64,\n\n    // dimension of the items\n    item_width:  32,\n    item_height: 32,\n\n    // if you want to display the widget at a position x, y in another window, you may need to specify the target window here\n    window: null\n  },\n  [\n    { content: \"\u003cstrong\u003e1\u003c/strong\u003e\", tooltip: \"first button with textual (HTML) content\",  on_click: function () { }, on_right_click: function () { }, on_middle_click: function () { } },\n    { icon: \"css-icon-class\", tooltip: \"second button\", on_click: function () { }, on_right_click: function () { }, on_middle_click: function () { } },\n    { icon: \"css-icon-class\", tooltip: \"third button\",  on_click: function () { }, on_right_click: function () { }, on_middle_click: function () { } },\n    { icon: \"css-icon-class\", tooltip: \"fourth button\", on_click: function () { }, on_right_click: function () { }, on_middle_click: function () { } },\n  ]);\n```\n\n\n\u003ca name=\"compat\"\u003e\u003c/a\u003e\n# Compatibility #\n\n\nNot well tested but should work in all modern browsers supporting **_ECMAScript 5_** and **_CSS3_**.\n\nIt was not built to target mobile devices, but it still support touch events and should work well on iPad and the like.\n\nTested and work ok with IE 11, Opera 12, Chrome (30, 35, 40, 53), Firefox (31, 37, 49) and Safari (6, 7, 8).\n\nMostly work (problems with the ToolBar and Dialog) under IE 10 but i do not support it.\n\nThe Web MIDI API should be supported by the browser if you want to use the MIDI features.\n\n\u003ca name=\"license\"\u003e\u003c/a\u003e\n# License #\n\n\n[Revised BSD](https://github.com/grz0zrg/wui/blob/master/LICENSE)\n\nThis was made for an [audio app.](https://github.com/grz0zrg/fsynth) and the UI of a wargame engine.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrz0zrg%2Fwui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrz0zrg%2Fwui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrz0zrg%2Fwui/lists"}