jsl-ui 9.28.7 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/docs/AutoSuggestComponent.md +17 -0
  2. package/docs/ButtonComponent.md +14 -0
  3. package/docs/CheckBoxComponent.md +12 -0
  4. package/docs/DateComponent.md +12 -0
  5. package/docs/DateRangeComponent.md +13 -0
  6. package/docs/DateTimeComponent.md +13 -0
  7. package/docs/DialogComponent.md +15 -0
  8. package/docs/ExpanderComponent.md +13 -0
  9. package/docs/FormComponent.md +15 -0
  10. package/docs/FormFieldComponent.md +15 -0
  11. package/docs/GridComponent.md +19 -0
  12. package/docs/InputComponent.md +15 -0
  13. package/docs/IntervalComponent.md +13 -0
  14. package/docs/LoadingComponent.md +12 -0
  15. package/docs/NavigationComponent.md +17 -0
  16. package/docs/Notification.md +14 -0
  17. package/docs/NotificationComponent.md +13 -0
  18. package/docs/NumberComponent.md +12 -0
  19. package/docs/OverlayComponent.md +12 -0
  20. package/docs/ProgressComponent.md +12 -0
  21. package/docs/README.md +24 -0
  22. package/docs/RibbonMenuComponent.md +14 -0
  23. package/docs/SelectComponent.md +15 -0
  24. package/docs/SplitterComponent.md +14 -0
  25. package/docs/StackPanelComponent.md +13 -0
  26. package/docs/TabsComponent.md +16 -0
  27. package/docs/TimeComponent.md +13 -0
  28. package/docs/TreeViewComponent.md +16 -0
  29. package/examples/showcase/checks-and-expanders.ts +163 -0
  30. package/examples/showcase/cultures.ts +39 -0
  31. package/examples/showcase/dates.ts +182 -0
  32. package/examples/showcase/dialogs.ts +184 -0
  33. package/examples/showcase/feedback-and-navigation.ts +174 -0
  34. package/examples/showcase/fields.ts +178 -0
  35. package/examples/showcase/fonts/Jost-Variable.ttf +0 -0
  36. package/examples/showcase/fonts/OFL-Jost.txt +93 -0
  37. package/examples/showcase/grids.ts +150 -0
  38. package/examples/showcase/index.html +60 -0
  39. package/examples/showcase/layout-and-navigation.ts +47 -0
  40. package/examples/showcase/nivona.css +98 -0
  41. package/examples/showcase/selects.ts +158 -0
  42. package/examples/showcase/showcase.css +231 -0
  43. package/examples/showcase/showcase.ts +214 -0
  44. package/examples/showcase/tabs.ts +161 -0
  45. package/examples/showcase/themes.css +83 -0
  46. package/examples/showcase/themes.ts +54 -0
  47. package/lib/components/IntervalComponent.d.ts +5 -0
  48. package/lib/components/IntervalComponent.js +29 -13
  49. package/lib/components/auto-suggest/AutoSuggestComponent.d.ts +11 -9
  50. package/lib/components/auto-suggest/AutoSuggestComponent.js +25 -38
  51. package/lib/components/button/ButtonComponent.d.ts +30 -1
  52. package/lib/components/button/ButtonComponent.js +85 -4
  53. package/lib/components/checkbox/CheckBoxComponent.d.ts +11 -1
  54. package/lib/components/checkbox/CheckBoxComponent.js +66 -56
  55. package/lib/components/checkbox/CheckBoxStyle.scss +72 -85
  56. package/lib/components/date/DateComponent.d.ts +10 -0
  57. package/lib/components/date/DateComponent.js +92 -44
  58. package/lib/components/date-range/DateRangeComponent.d.ts +14 -3
  59. package/lib/components/date-range/DateRangeComponent.js +96 -86
  60. package/lib/components/date-range/DateRangeStyle.scss +35 -11
  61. package/lib/components/date-time/DateTimeComponent.d.ts +8 -0
  62. package/lib/components/date-time/DateTimeComponent.js +64 -23
  63. package/lib/components/date-time/DateTimeStyle.scss +10 -7
  64. package/lib/components/dialog/DialogComponent.d.ts +21 -7
  65. package/lib/components/dialog/DialogComponent.js +179 -122
  66. package/lib/components/expander/ExpanderComponent.d.ts +15 -1
  67. package/lib/components/expander/ExpanderComponent.js +85 -64
  68. package/lib/components/expander/ExpanderStyle.scss +61 -47
  69. package/lib/components/form-field/FormFieldComponent.d.ts +45 -15
  70. package/lib/components/form-field/FormFieldComponent.js +249 -96
  71. package/lib/components/form-field/FormFieldStyle.scss +36 -87
  72. package/lib/components/form-field/create-control.d.ts +7 -0
  73. package/lib/components/form-field/create-control.js +48 -0
  74. package/lib/components/input/InputComponent.d.ts +18 -1
  75. package/lib/components/input/InputComponent.js +68 -93
  76. package/lib/components/loading/LoadingComponent.d.ts +9 -2
  77. package/lib/components/loading/LoadingComponent.js +13 -12
  78. package/lib/components/loading/LoadingStyle.css +19 -29
  79. package/lib/components/navigation/NavigationComponent.d.ts +34 -10
  80. package/lib/components/navigation/NavigationComponent.js +261 -33
  81. package/lib/components/navigation/NavigationStyle.scss +62 -226
  82. package/lib/components/notification/Notification.d.ts +37 -4
  83. package/lib/components/notification/Notification.js +160 -41
  84. package/lib/components/notification/Notification.scss +103 -19
  85. package/lib/components/number/NumberComponent.d.ts +16 -2
  86. package/lib/components/number/NumberComponent.js +54 -53
  87. package/lib/components/overlay/OverlayComponent.d.ts +13 -7
  88. package/lib/components/overlay/OverlayComponent.js +46 -114
  89. package/lib/components/overlay/overlay.css +18 -57
  90. package/lib/components/progress/ProgressComponent.d.ts +1 -0
  91. package/lib/components/progress/ProgressComponent.js +6 -6
  92. package/lib/components/progress/ProgressStyle.scss +5 -15
  93. package/lib/components/ribbon-menu/RibbonMenuComponent.js +2 -2
  94. package/lib/components/ribbon-menu/RibbonMenuStyle.scss +6 -76
  95. package/lib/components/select/SelectComponent.d.ts +33 -3
  96. package/lib/components/select/SelectComponent.js +312 -278
  97. package/lib/components/select/SelectDDAreaComponent.d.ts +13 -7
  98. package/lib/components/select/SelectDDAreaComponent.js +215 -256
  99. package/lib/components/select/SelectStyle.scss +204 -186
  100. package/lib/components/slider/nouislider.css +40 -54
  101. package/lib/components/stack-panel/SplitterComponent.d.ts +19 -3
  102. package/lib/components/stack-panel/SplitterComponent.js +94 -111
  103. package/lib/components/stack-panel/SplitterStyle.scss +9 -45
  104. package/lib/components/stack-panel/StackPanelComponent.js +3 -3
  105. package/lib/components/stack-panel/StackPanelStyle.scss +10 -52
  106. package/lib/components/tabs/TabsComponent.d.ts +17 -1
  107. package/lib/components/tabs/TabsComponent.js +247 -123
  108. package/lib/components/tabs/TabsStyle.scss +138 -75
  109. package/lib/components/time/TimeComponent.d.ts +5 -0
  110. package/lib/components/time/TimeComponent.js +61 -69
  111. package/lib/components/time/TimeComponent.scss +4 -4
  112. package/lib/components/tree-view/TreeViewComponent.d.ts +10 -4
  113. package/lib/components/tree-view/TreeViewComponent.js +65 -32
  114. package/lib/components/tree-view/TreeViewStyle.scss +12 -96
  115. package/lib/core/date.d.ts +2 -0
  116. package/lib/core/date.js +6 -0
  117. package/lib/core/jq.js +11 -8
  118. package/lib/grid/slick-default-theme.css +107 -31
  119. package/lib/grid/slick.creator.js +23 -5
  120. package/lib/grid/slick.formatters.js +4 -3
  121. package/lib/grid/slick.grid.css +38 -26
  122. package/lib/grid/slick.grid.d.ts +1 -0
  123. package/lib/grid/slick.grid.js +2 -3
  124. package/lib/grid.css +3413 -454
  125. package/lib/index.css +2500 -1031
  126. package/lib/index.css.map +1 -1
  127. package/lib/index.d.ts +1 -0
  128. package/lib/index.js +1 -1
  129. package/lib/index.js.map +1 -1
  130. package/lib/interaction/moveable.d.ts +1 -0
  131. package/lib/interaction/moveable.js +8 -0
  132. package/lib/interaction/resizable.d.ts +1 -0
  133. package/lib/interaction/resizable.js +9 -0
  134. package/lib/ui/auto.suggest.css +31 -14
  135. package/lib/ui/auto.suggest.d.ts +1 -0
  136. package/lib/ui/auto.suggest.js +88 -18
  137. package/lib/ui/base.css +2 -48
  138. package/lib/ui/button.css +134 -0
  139. package/lib/ui/date-input.css +11 -0
  140. package/lib/ui/date.picker/css/tiny-date-picker.css +124 -325
  141. package/lib/ui/date.picker/date-picker-options.js +4 -3
  142. package/lib/ui/date.picker/lib/date-manip.js +9 -0
  143. package/lib/ui/date.picker/mode/base-mode.d.ts +2 -1
  144. package/lib/ui/date.picker/mode/base-mode.js +112 -22
  145. package/lib/ui/date.picker/mode/dropdown-mode.d.ts +2 -4
  146. package/lib/ui/date.picker/mode/dropdown-mode.js +29 -42
  147. package/lib/ui/date.picker/mode/index.d.ts +2 -1
  148. package/lib/ui/date.picker/mode/modal-mode.d.ts +2 -1
  149. package/lib/ui/date.picker/mode/modal-mode.js +4 -2
  150. package/lib/ui/date.picker/mode/permanent-mode.d.ts +2 -1
  151. package/lib/ui/date.picker/views/day-picker.js +24 -15
  152. package/lib/ui/date.picker/views/month-picker.js +1 -1
  153. package/lib/ui/date.picker/views/year-picker.js +3 -2
  154. package/lib/ui/date.picker.d.ts +2 -1
  155. package/lib/ui/dialog.css +144 -108
  156. package/lib/ui/dialog.d.ts +19 -1
  157. package/lib/ui/dialog.js +178 -60
  158. package/lib/ui/input.css +35 -0
  159. package/lib/ui/number.input.js +50 -37
  160. package/lib/ui/theme.css +28 -0
  161. package/package.json +12 -8
  162. package/lib/components/drop-down/DropDownComponent.d.ts +0 -27
  163. package/lib/components/drop-down/DropDownComponent.js +0 -60
  164. package/lib/components/expander/OldExpanderStyle.scss +0 -37
  165. package/lib/ui/drop.down.d.ts +0 -52
  166. package/lib/ui/drop.down.js +0 -2112
@@ -0,0 +1,17 @@
1
+ # AutoSuggestComponent
2
+
3
+ ```ts
4
+ import { AutoSuggestComponent } from "jsl-ui";
5
+
6
+ const cities = ["Vienna", "London", "Berlin"];
7
+ const city = new AutoSuggestComponent({
8
+ ariaLabel: "City", minChars: 1,
9
+ source: (term, suggest) => {
10
+ suggest(cities.filter(name => name.toLowerCase().includes(term.toLowerCase())));
11
+ }
12
+ });
13
+ ```
14
+
15
+ - `source` must call `suggest(items)`, including after an asynchronous lookup; returning an array or promise alone does not populate results.
16
+ - Values are text. Custom `renderItem` returns HTML, so escape any untrusted item text.
17
+ - `getRef()` is available only after mounting an enabled, editable control.
@@ -0,0 +1,14 @@
1
+ # ButtonComponent
2
+
3
+ ```ts
4
+ import { ButtonComponent } from "jsl-ui";
5
+
6
+ const bookmark = new ButtonComponent({
7
+ content: "Bookmark", type: "button", toggle: true,
8
+ onToggle: pressed => { /* persist selection */ }
9
+ });
10
+ ```
11
+
12
+ - Set `type: "button"` for actions inside forms; omitting it retains native submit behavior.
13
+ - `pressed` is the initial state. Afterwards, use `bookmark.pressed`; assigning it does not raise callbacks.
14
+ - Icon strings are literal text, not HTML or icon-library class names. Supply a VNode/component for markup; name icon-only buttons with `ariaLabel` or hidden `content`.
@@ -0,0 +1,12 @@
1
+ # CheckBoxComponent
2
+
3
+ ```ts
4
+ import { CheckBoxComponent } from "jsl-ui";
5
+
6
+ const archived = new CheckBoxComponent({
7
+ label: "Include archived projects", value: false
8
+ });
9
+ ```
10
+
11
+ - `indeterminate` is a separate visual state, not a third value; `getValue()` remains boolean.
12
+ - `setValue()` updates the binding without raising change callbacks.
@@ -0,0 +1,12 @@
1
+ # DateComponent
2
+
3
+ ```ts
4
+ import { DateComponent } from "jsl-ui";
5
+
6
+ const due = new DateComponent({
7
+ value: new Date(2026, 9, 23), ariaLabel: "Due date"
8
+ });
9
+ ```
10
+
11
+ - Values and bindings use `Date` objects; display and parsing follow the culture helper. Supply both `format` and `parse` when overriding that convention.
12
+ - `getValue()` returns text; use `isValid()` and `getValueAsDate()` when reading edited dates. `setValue()` does not raise change callbacks.
@@ -0,0 +1,13 @@
1
+ # DateRangeComponent
2
+
3
+ ```ts
4
+ import { DateRangeComponent } from "jsl-ui";
5
+
6
+ const range = new DateRangeComponent({
7
+ min: new Date(2026, 9, 1), max: new Date(2026, 9, 31), increment: "days",
8
+ minValue: new Date(2026, 9, 5), maxValue: new Date(2026, 9, 20)
9
+ });
10
+ ```
11
+
12
+ - Only `increment: "days"` is implemented, despite the broader type. Dates are normalized to local calendar days; bounds must be valid and ascending.
13
+ - `min` / `max` are bounds; `minValue` / `maxValue` are the initial selection. Read the selection through `getMin()` / `getMax()` or `onChange`.
@@ -0,0 +1,13 @@
1
+ # DateTimeComponent
2
+
3
+ ```ts
4
+ import { DateTimeComponent } from "jsl-ui";
5
+
6
+ const appointment = new DateTimeComponent({
7
+ value: new Date(2026, 9, 23, 9, 30),
8
+ dateAriaLabel: "Appointment date", timeAriaLabel: "Appointment time"
9
+ });
10
+ ```
11
+
12
+ - Bind the composite control to one `Date`; it combines the date and time inputs. `getValueAsDate()` returns `null` for invalid input; `getValue()` returns text.
13
+ - `setValue()` updates both inputs without raising change callbacks. The control edits local date/time, without timezone conversion.
@@ -0,0 +1,15 @@
1
+ # DialogComponent
2
+
3
+ ```ts
4
+ import { DialogComponent } from "jsl-ui";
5
+
6
+ const dialog = DialogComponent.show({
7
+ header: "Confirm changes", content: "Save this version?",
8
+ footer: { tag: "div", children: [
9
+ DialogComponent.Cancel(), DialogComponent.Ok(() => { /* save changes */ })
10
+ ] }
11
+ });
12
+ ```
13
+
14
+ - `show()` mounts and cleans up its own host. To close programmatically, use `dialog.getRef().close()`.
15
+ - Footer helper buttons close the dialog unless their callback returns `false` (also after awaiting a promise). `onClose` can veto any close by returning `false`.
@@ -0,0 +1,13 @@
1
+ # ExpanderComponent
2
+
3
+ ```ts
4
+ import { ExpanderComponent, InputComponent } from "jsl-ui";
5
+
6
+ const advanced = new ExpanderComponent({
7
+ header: "Advanced options", expanded: false,
8
+ body: new InputComponent({ ariaLabel: "Reference", placeholder: "Reference" })
9
+ });
10
+ ```
11
+
12
+ - Collapsing hides the body but keeps it mounted.
13
+ - Return `false` from `onToggle` to veto a user toggle. `setExpanded()` changes state without calling `onToggle`.
@@ -0,0 +1,15 @@
1
+ # FormComponent
2
+
3
+ ```ts
4
+ import { FormComponent, FormFieldComponent, InputComponent } from "jsl-ui";
5
+
6
+ const form = new FormComponent([
7
+ new FormFieldComponent({
8
+ label: "Project", required: true,
9
+ control: new InputComponent({ id: "project-name" })
10
+ })
11
+ ]);
12
+ ```
13
+
14
+ - `form.validate()` calls validators on direct children only. Keep fields as direct children if relying on this method.
15
+ - This is a native form; it does not intercept submission. Validate in your submit/action handler; use `type: "button"` for buttons that should not submit.
@@ -0,0 +1,15 @@
1
+ # FormFieldComponent
2
+
3
+ ```ts
4
+ import { FormFieldComponent, InputComponent } from "jsl-ui";
5
+
6
+ const field = new FormFieldComponent({
7
+ label: "Project", required: true,
8
+ control: new InputComponent({ id: "project-name", value: "Website" })
9
+ });
10
+ ```
11
+
12
+ - Put editing, binding, and formatting on the child control. The field supplies its label, layout, and validation feedback.
13
+ - Call `validate()` when submitting; `required` does not submit or validate the surrounding form automatically.
14
+ - `getValue()` / `setValue()` delegate to supplied controls. Match the child's value contract; the legacy `type` factory may use strings instead.
15
+ - For custom markup with several inputs, set `controlId` to identify the label target.
@@ -0,0 +1,19 @@
1
+ # GridComponent
2
+
3
+ ```ts
4
+ import { GridComponent, NumericFormatter } from "jsl-ui";
5
+
6
+ const grid = new GridComponent(
7
+ [{ id: 1, amount: 1250.5 }],
8
+ [{ id: "amount", name: "Amount", width: 140,
9
+ formatter: NumericFormatter, headerCssClass: "right" }],
10
+ { primaryKey: "id" }, "300px"
11
+ );
12
+ grid.initEvents(g => { /* subscribe to grid events here */ });
13
+ ```
14
+
15
+ - Use unique, stable primary keys. Mount before awaiting `getRef()`; register initialization callbacks with `initEvents()`.
16
+ - Keep component instances stable across parent renders so unrelated UI updates do not recreate the grid.
17
+ - Preserve `content-box` on cells and headers. Selection/formatter classes must not change padding or border widths.
18
+ - NumericFormatter aligns cells; header alignment is explicit via `headerCssClass: "right"` or `"center"`.
19
+ - Register dependent calculations with `addFormula()`, then call `triggerAllFormulas()` for initial values. See [the showcase](../examples/showcase/grids.ts) for formulas, totals, frozen columns, and row ordering.
@@ -0,0 +1,15 @@
1
+ # InputComponent
2
+
3
+ ```ts
4
+ import { InputComponent } from "jsl-ui";
5
+
6
+ const model = { name: "Website" };
7
+ const input = new InputComponent({
8
+ bindTo: model, property: "name", bindMode: "both",
9
+ bindOnInput: true, ariaLabel: "Project name"
10
+ });
11
+ ```
12
+
13
+ - Binding defaults to `"toModel"`. Use `"both"` to also read external model changes on render; `bindOnInput` writes while typing rather than waiting for change.
14
+ - `setValue()` updates the binding without raising change callbacks.
15
+ - Use [FormFieldComponent](FormFieldComponent.md) for a visible label. Keep the default control styling unless the host deliberately supplies its own via `unstyled`.
@@ -0,0 +1,13 @@
1
+ # IntervalComponent
2
+
3
+ ```ts
4
+ import { IntervalComponent } from "jsl-ui";
5
+
6
+ const state = { ticks: 0 };
7
+ const timer = new IntervalComponent(() => { state.ticks++; }, {
8
+ interval: 30000, stopOnError: true
9
+ });
10
+ ```
11
+
12
+ - Mount the component to start it; the callback runs immediately, then repeats after the interval in milliseconds. Async callbacks settle before the next delay begins.
13
+ - The owning view refreshes after callbacks unless `noRefresh` is set. Unmounting clears the pending timeout; cancellation of in-flight asynchronous work belongs to your callback.
@@ -0,0 +1,12 @@
1
+ # LoadingComponent
2
+
3
+ ```ts
4
+ import { LoadingComponent } from "jsl-ui";
5
+
6
+ const loading = new LoadingComponent({
7
+ sizeInRem: 1.25, label: "Loading report", showLabel: true
8
+ });
9
+ ```
10
+
11
+ - Specify size for compact layouts; the legacy default is `6rem`.
12
+ - The label remains accessible when visually hidden. The indicator does not disable controls or manage the surrounding content's `aria-busy` state.
@@ -0,0 +1,17 @@
1
+ # NavigationComponent
2
+
3
+ ```ts
4
+ import { NavigationComponent } from "jsl-ui";
5
+
6
+ const navigation = new NavigationComponent({
7
+ title: "Workspace", position: "default",
8
+ data: [
9
+ { content: "Projects", link: "#projects" },
10
+ { content: "Settings", children: [{ content: "Profile", link: "#profile" }] }
11
+ ]
12
+ });
13
+ ```
14
+
15
+ - Position defaults to `"fixed"`; use `"default"` or `"static"` to participate in the page layout.
16
+ - Items with `link` use native anchors; callback-only items use buttons. Children provide submenus.
17
+ - Active links follow the current location. For a custom router, update `activeLink` in the options and refresh the view.
@@ -0,0 +1,14 @@
1
+ # Notification
2
+
3
+ ```ts
4
+ import { Notification } from "jsl-ui";
5
+
6
+ const id = Notification.show({
7
+ title: "Saved", content: "Your changes have been saved.",
8
+ variant: "success", timeOut: 4000
9
+ });
10
+ ```
11
+
12
+ - `show()` owns the floating stack. Use `clearNotification(id)` for targeted dismissal or `clearAll()` for the whole stack.
13
+ - `timeOut` is milliseconds; omitted or zero means persistent. Timed notifications show a countdown that pauses on hover/focus.
14
+ - Clicking the body dismisses by default. Set `closeOnClick: false` when the content contains actions.
@@ -0,0 +1,13 @@
1
+ # NotificationComponent
2
+
3
+ ```ts
4
+ import { NotificationComponent } from "jsl-ui";
5
+
6
+ const notice = new NotificationComponent({
7
+ title: "Review needed", content: "Check the project settings.",
8
+ variant: "warning", closeOnClick: false
9
+ });
10
+ ```
11
+
12
+ - Mount this inline like other components. `dismiss()` hides it; the parent owns removal.
13
+ - Automatic dismissal and countdowns require the host provided by [Notification.show()](Notification.md); `timeOut` alone does not start a timer on an inline component.
@@ -0,0 +1,12 @@
1
+ # NumberComponent
2
+
3
+ ```ts
4
+ import { NumberComponent } from "jsl-ui";
5
+
6
+ const amount = new NumberComponent({
7
+ value: 1250.5, format: "#,0.00", ariaLabel: "Amount"
8
+ });
9
+ ```
10
+
11
+ - Format masks use invariant comma/dot syntax; displayed separators and parsing follow the culture helper.
12
+ - `getValue()` returns formatted text; use `getValueAsNumber()` for calculations. `setValue()` takes a number and does not raise change callbacks.
@@ -0,0 +1,12 @@
1
+ # OverlayComponent
2
+
3
+ ```ts
4
+ import { OverlayComponent } from "jsl-ui";
5
+
6
+ const panel = OverlayComponent.show({
7
+ ariaLabel: "Settings", content: "Application settings", size: "26rem"
8
+ });
9
+ ```
10
+
11
+ - `show()` owns mounting and cleanup; `panel.close()` closes it. The overlay is modal and defaults to a left-side panel.
12
+ - Escape and backdrop clicks close by default. Return `false` from `onClose` to veto closing.
@@ -0,0 +1,12 @@
1
+ # ProgressComponent
2
+
3
+ ```ts
4
+ import { ProgressComponent } from "jsl-ui";
5
+
6
+ const upload = new ProgressComponent(45, {
7
+ ariaLabel: "Upload progress", showPercent: true
8
+ });
9
+ ```
10
+
11
+ - Progress uses `0..100`, not `0..1`.
12
+ - `setProgress()` changes the value without refreshing. When updating outside renderer-managed events, refresh the owning `jsl-render` view yourself.
package/docs/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # jsl-ui: agent guide
2
+
3
+ - Import from the package root, e.g. `import { GridComponent, NumericFormatter } from "jsl-ui"`. Mount components with `jsl-render`; the static `show()` helpers mount themselves.
4
+ - Use a browser bundler that handles CSS/SCSS imports. Components import their own styles.
5
+ - Theme through `--jsl-color-*` variables on the application or a container; avoid overriding internal markup.
6
+ - [Showcase sources](../examples/showcase/) are included as usage references.
7
+ - Consult the shipped `lib/**/*.d.ts` files for options; these guides cover usage decisions rather than duplicate interfaces.
8
+
9
+ Guides for the public components exported by `jsl-ui`:
10
+
11
+ - [ButtonComponent](ButtonComponent.md)
12
+ - [InputComponent](InputComponent.md), [NumberComponent](NumberComponent.md), [AutoSuggestComponent](AutoSuggestComponent.md)
13
+ - [CheckBoxComponent](CheckBoxComponent.md), [SelectComponent](SelectComponent.md)
14
+ - [DateComponent](DateComponent.md), [TimeComponent](TimeComponent.md), [DateTimeComponent](DateTimeComponent.md), [DateRangeComponent](DateRangeComponent.md)
15
+ - [FormFieldComponent](FormFieldComponent.md)
16
+ - [FormComponent](FormComponent.md)
17
+ - [TabsComponent](TabsComponent.md), [ExpanderComponent](ExpanderComponent.md)
18
+ - [StackPanelComponent](StackPanelComponent.md), [SplitterComponent](SplitterComponent.md)
19
+ - [TreeViewComponent](TreeViewComponent.md), [NavigationComponent](NavigationComponent.md), [RibbonMenuComponent](RibbonMenuComponent.md)
20
+ - [DialogComponent](DialogComponent.md), [OverlayComponent](OverlayComponent.md)
21
+ - [Notification](Notification.md), [NotificationComponent](NotificationComponent.md)
22
+ - [ProgressComponent](ProgressComponent.md), [LoadingComponent](LoadingComponent.md)
23
+ - [GridComponent](GridComponent.md)
24
+ - [IntervalComponent](IntervalComponent.md)
@@ -0,0 +1,14 @@
1
+ # RibbonMenuComponent
2
+
3
+ ```ts
4
+ import { ButtonComponent, RibbonMenuComponent } from "jsl-ui";
5
+
6
+ const ribbon = new RibbonMenuComponent([
7
+ { title: "File", children: [
8
+ new ButtonComponent({ content: "Open", type: "button" }),
9
+ new ButtonComponent({ content: "Save", type: "button" })
10
+ ] }
11
+ ]);
12
+ ```
13
+
14
+ - The ribbon groups controls; commands, toggle state, and enabled state belong to those child controls. Use `type: "button"` for commands inside forms.
@@ -0,0 +1,15 @@
1
+ # SelectComponent
2
+
3
+ ```ts
4
+ import { SelectComponent } from "jsl-ui";
5
+
6
+ const teams = new SelectComponent({
7
+ ariaLabel: "Teams", multiple: true,
8
+ data: [{ value: "design", text: "Design" }, { value: "engineering", text: "Engineering" }],
9
+ value: ["design"]
10
+ });
11
+ ```
12
+
13
+ - `value` / `getValue()` use keys from `keyColumn` (default `"value"`); `getSelected()` returns the matching data objects. Multiple selection uses arrays; single selection uses a scalar.
14
+ - Omit `multiple` and use `value: "design"` for single selection with the same data.
15
+ - `setValue()` takes keys and updates the binding without raising change callbacks. The component owns its dropdown; do not create `SelectDDAreaComponent` separately.
@@ -0,0 +1,14 @@
1
+ # SplitterComponent
2
+
3
+ ```ts
4
+ import { SplitterComponent, StackPanelComponent } from "jsl-ui";
5
+
6
+ const layout = new StackPanelComponent([
7
+ { tag: "div", content: "Sidebar" },
8
+ new SplitterComponent({ minSizesInPX: [120, 180], ariaLabel: "Resize sidebar" }),
9
+ { tag: "div", content: "Main content" }
10
+ ], { sizes: ["40%", undefined, "*"], style: "height:20rem" });
11
+ ```
12
+
13
+ - Place directly between two panels in a `StackPanelComponent`; orientation comes from the stack. Leave its entry in `sizes` undefined.
14
+ - Min/max arrays refer to the preceding and following panel. `onResizeDone` receives the preceding panel's size, including height for a vertical stack.
@@ -0,0 +1,13 @@
1
+ # StackPanelComponent
2
+
3
+ ```ts
4
+ import { StackPanelComponent } from "jsl-ui";
5
+
6
+ const layout = new StackPanelComponent([
7
+ { tag: "div", content: "Sidebar" },
8
+ { tag: "div", content: "Main content" }
9
+ ], { sizes: ["12rem", "*"] });
10
+ ```
11
+
12
+ - Children get layout wrappers; `"*"` fills remaining space. Size indices match the children array, including any [SplitterComponent](SplitterComponent.md).
13
+ - `responsive: true` stacks horizontal panels and hides splitters on narrow screens. Give vertical layouts a definite height when distributing available height.
@@ -0,0 +1,16 @@
1
+ # TabsComponent
2
+
3
+ ```ts
4
+ import { TabsComponent } from "jsl-ui";
5
+
6
+ const tabs = new TabsComponent({
7
+ preserveContent: true,
8
+ tabs: [
9
+ { header: "Overview", content: "Project overview" },
10
+ { header: "Details", onCreate: () => "Project details" }
11
+ ]
12
+ });
13
+ ```
14
+
15
+ - `onCreate` lazily supplies content on first activation. `preserveContent: true` keeps visited panels mounted; by default inactive panels are removed.
16
+ - `onSelect` and `onClosing` can return `false` to veto user actions. `SetSelectedTab(index)` changes selection without calling `onSelect`.
@@ -0,0 +1,13 @@
1
+ # TimeComponent
2
+
3
+ ```ts
4
+ import { TimeComponent } from "jsl-ui";
5
+
6
+ const time = new TimeComponent({
7
+ value: new Date(2026, 9, 23, 9, 30), ariaLabel: "Start time"
8
+ });
9
+ const start = time.getValueAsDate(new Date(2026, 9, 23));
10
+ ```
11
+
12
+ - `getValueAsDate(datePart)` combines the edited time with the supplied calendar date; invalid input returns `null`. `getValue()` returns text.
13
+ - Use `inputMode: "HourMinuteSecond"` to include seconds. `setValue()` takes a `Date` without raising change callbacks.
@@ -0,0 +1,16 @@
1
+ # TreeViewComponent
2
+
3
+ ```ts
4
+ import { TreeViewComponent } from "jsl-ui";
5
+
6
+ const tree = new TreeViewComponent({
7
+ ariaLabel: "Project files",
8
+ data: { id: "project", content: "Project", children: [
9
+ { id: "notes", content: "Notes" },
10
+ { id: "assets", content: "Assets" }
11
+ ] }
12
+ });
13
+ ```
14
+
15
+ - `data` is one root node, not an array; node IDs must be unique within the tree.
16
+ - Branch activation selects the node and changes its `collapsed` property in the supplied data. Keep that data stable to preserve expansion state.
@@ -0,0 +1,163 @@
1
+ import { IJSLComponent, IJSLVNode } from "jsl-render";
2
+ import { CheckBoxComponent } from "../../components/checkbox/CheckBoxComponent";
3
+ import { ExpanderComponent } from "../../components/expander/ExpanderComponent";
4
+ import { ButtonComponent } from "../../components/button/ButtonComponent";
5
+
6
+ type Content = IJSLVNode | IJSLComponent;
7
+ const text = (tag: string, content: string, className?: string): IJSLVNode => ({ tag, content, attr: { class: className } });
8
+ const group = (children: Content[], className?: string): IJSLVNode => ({ tag: "div", attr: { class: className }, children });
9
+ const status = (content: string): IJSLVNode => ({ tag: "p", content, attr: { class: "sample-status", role: "status" } });
10
+ const action = (content: string, click: () => void) => new ButtonComponent({ content, click, type: "button", outlined: true, size: "small" });
11
+ const copy = (title: string, content: string) => group([text("h4", title), text("p", content)], "expander-example-copy");
12
+ function card(id: string, number: string, title: string, description: string, children: Content[]): IJSLVNode {
13
+ return { tag: "article", attr: { id, class: "sample-card" }, children: [
14
+ group([text("span", number, "sample-number"), text("h3", title)], "card-heading"),
15
+ text("p", description, "card-description"), ...children
16
+ ] };
17
+ }
18
+ function section(id: string, number: string, title: string, description: string, preview: Content, cards: IJSLVNode[], usage: Content[]): IJSLVNode {
19
+ return { tag: "section", attr: { id, class: "component-section", "aria-labelledby": id + "-title" }, children: [
20
+ group([
21
+ group([text("p", "THE ESSENTIALS / " + number, "eyebrow"),
22
+ { tag: "h2", content: title, attr: { id: id + "-title", class: "section-title" } },
23
+ text("p", description, "hero-description")], "hero-copy"),
24
+ group([text("span", "INTERACTIVE PREVIEW", "preview-caption"), preview], "hero-preview checks-expander-preview")
25
+ ], "hero"),
26
+ group(cards, "sample-grid checks-expander-grid"),
27
+ { tag: "details", attr: { class: "usage" }, children: [text("summary", title + " usage & compatibility"), ...usage] }
28
+ ] };
29
+ }
30
+
31
+ const basicStatus = status("Email updates are off.");
32
+ const partialStatus = status("1 of 3 channels selected.");
33
+ const channels = ["Product news", "Release notes", "Community events"].map((label, index) => new CheckBoxComponent({
34
+ id: "checkbox-channel-" + index, label, value: index === 0,
35
+ onChange: () => syncChannels()
36
+ }));
37
+ const allChannels = new CheckBoxComponent({
38
+ id: "checkbox-all", label: "All updates", indeterminate: true,
39
+ onChange: checked => { channels.forEach(channel => channel.setValue(checked)); syncChannels(); }
40
+ });
41
+ function syncChannels() {
42
+ const count = channels.filter(channel => channel.getValue()).length;
43
+ allChannels.setValue(count === channels.length);
44
+ allChannels.indeterminate = count > 0 && count < channels.length;
45
+ partialStatus.content = count + " of " + channels.length + " channels selected.";
46
+ }
47
+ const model = { digest: 1 };
48
+ const bindingStatus = status("Model value: 1");
49
+ const boundCheck = new CheckBoxComponent({
50
+ id: "checkbox-bound", label: "Send a weekly digest", bindTo: model, property: "digest", bindMode: "both",
51
+ converter: { toUI: value => value === 1, toData: checked => checked ? 1 : 0 },
52
+ onChange: () => { bindingStatus.content = "Model value: " + model.digest; }
53
+ });
54
+
55
+ export const checkboxShowcase = section("checkboxes", "09", "Checkboxes",
56
+ "Small choices, clearly marked. Comfortable labels and a visible place for focus.",
57
+ group([
58
+ new CheckBoxComponent({ label: "Keep me informed", value: true }),
59
+ new CheckBoxComponent({ label: "Share usage insights" })
60
+ ], "checkbox-stack"), [
61
+ card("checkbox-basic", "01", "One clear choice", "Click the box or its label. Tab focuses the input; Space changes its state.", [
62
+ new CheckBoxComponent({ id: "checkbox-basic-input", label: "Email me product updates", onChange: checked => {
63
+ basicStatus.content = checked ? "Email updates are on." : "Email updates are off.";
64
+ } }), basicStatus,
65
+ text("pre", '{ label: "Email me product updates", onChange: checked => { ... } }', "sample-code")
66
+ ]),
67
+ card("checkbox-states", "02", "A little more context", "Supporting text explains a choice. Disabled controls keep their labels and current state.", [
68
+ group([
69
+ new CheckBoxComponent({ id: "checkbox-description", label: "Remember this workspace", description: "Open this workspace automatically the next time you sign in.", value: true }),
70
+ new CheckBoxComponent({ id: "checkbox-disabled", label: "Managed by your administrator", enabled: false }),
71
+ new CheckBoxComponent({ id: "checkbox-disabled-checked", label: "Security notifications", value: true, enabled: false })
72
+ ], "checkbox-stack")
73
+ ]),
74
+ card("checkbox-partial", "03", "Some, all, or none", "A mixed parent summarizes its children. Activating it selects or clears the whole group.", [
75
+ group([allChannels, group(channels, "checkbox-child-list")], "checkbox-stack"), partialStatus,
76
+ text("pre", "checkbox.indeterminate = true;\ncheckbox.setValue(true); // Silent checked update", "sample-code")
77
+ ]),
78
+ card("checkbox-binding", "04", "Connected to your model", "A converter maps checked state to a numeric value. Two-way binding keeps the display in sync.", [
79
+ boundCheck, bindingStatus,
80
+ group([action("Toggle from the model", () => {
81
+ model.digest = model.digest === 1 ? 0 : 1;
82
+ bindingStatus.content = "Model value: " + model.digest;
83
+ })], "sample-row")
84
+ ])
85
+ ], [
86
+ text("pre", 'new CheckBoxComponent({\n label: "Send a weekly digest",\n value: true,\n bindTo: model, property: "digest", bindMode: "both",\n onChange: checked => console.log(checked)\n});', "sample-code"),
87
+ text("p", "Existing value, label, enabled, class, style, id, binding, converter, onChange, getValue(), and setValue() APIs are preserved. class still applies to the input and style to its wrapper. setValue() works before mounting, updates writable bindings, and does not raise onChange."),
88
+ text("p", "The optional description is associated with the input as supporting text. Use ariaLabel when the visible label is empty. indeterminate sets an initial mixed state; the indeterminate property can update it later. The next user activation clears mixed state and raises the usual boolean change event.")
89
+ ]);
90
+
91
+ const basicExpanderStatus = status("Details are expanded.");
92
+ const preservedStatus = status("Collapse and reopen to keep your draft.");
93
+ let allowCollapse = false;
94
+ const guardedStatus = status("Closing is currently blocked.");
95
+ const headerStatus = status("The header action has not been used.");
96
+ const nested = new ExpanderComponent({
97
+ header: "Workspace settings",
98
+ body: group([
99
+ new ExpanderComponent({ header: "Notifications", expanded: false, body: group([
100
+ new CheckBoxComponent({ label: "Email notifications", value: true }),
101
+ new CheckBoxComponent({ label: "Desktop notifications" })
102
+ ], "checkbox-stack") }),
103
+ new ExpanderComponent({ header: "Privacy", expanded: false, body: copy("Your preferences", "Choose what you share and how your workspace appears to others.") })
104
+ ], "expander-stack")
105
+ });
106
+ const customIcons = new ExpanderComponent({
107
+ header: "A different indicator", body: copy("Make it your own", "Supply custom expandedIcon and collapsedIcon nodes to match your application."),
108
+ expanded: false, expandedIcon: text("span", "−", "expander-custom-icon"), collapsedIcon: text("span", "+", "expander-custom-icon")
109
+ });
110
+
111
+ export const expanderShowcase = section("expanders", "10", "Expanders",
112
+ "Keep the overview clear. Open the details when you need them.",
113
+ new ExpanderComponent({ header: "Project details", body: text("p", "A little more context, close at hand.", "expander-preview-copy") }), [
114
+ card("expander-basic", "01", "Details on demand", "The whole header toggles the section. Its native button also responds to Enter and Space.", [
115
+ new ExpanderComponent({
116
+ header: "About this project", body: copy("A place to get started", "Keep the essentials visible and tuck supporting information into a section that is easy to open."),
117
+ onToggle: expanded => { basicExpanderStatus.content = expanded ? "Details are expanded." : "Details are collapsed."; }
118
+ }), basicExpanderStatus
119
+ ]),
120
+ card("expander-preserved", "02", "Keep unfinished work", "Collapsing hides the body while keeping its inputs and components mounted.", [
121
+ new ExpanderComponent({
122
+ header: "Project draft",
123
+ body: group([
124
+ { tag: "label", content: "Project name", attr: { for: "expander-draft" } },
125
+ { tag: "input", attr: { id: "expander-draft", class: "tabs-draft-input", placeholder: "Type a draft, then collapse this section" } },
126
+ new CheckBoxComponent({ id: "expander-draft-checkbox", label: "Share with my team" })
127
+ ], "expander-example-copy"),
128
+ onToggle: expanded => { preservedStatus.content = expanded ? "Your draft is still here." : "Draft hidden and preserved."; }
129
+ }), preservedStatus
130
+ ]),
131
+ card("expander-header", "03", "Room for a header action", "Use isToggleOnHeader: false to toggle only with the indicator and keep header actions independent.", [
132
+ new ExpanderComponent({
133
+ isToggleOnHeader: false,
134
+ header: group([text("span", "Team members"), action("Invite", () => { headerStatus.content = "Invite action used. Expansion is unchanged."; })], "expander-rich-header"),
135
+ body: copy("Work together", "Invite people to the workspace and keep everyone in the same place."),
136
+ ariaLabel: "Team members"
137
+ }), headerStatus
138
+ ]),
139
+ card("expander-guarded", "04", "Keep important details open", "Return false from onToggle to veto a change. Allow closing with the checkbox below.", [
140
+ new ExpanderComponent({
141
+ header: "Unsaved changes",
142
+ body: copy("A draft needs your attention", "This section stays open until you allow it to close."),
143
+ onToggle: expanding => {
144
+ if (!expanding && !allowCollapse) { guardedStatus.content = "Still open. Allow closing to continue."; return false; }
145
+ guardedStatus.content = expanding ? "Section opened." : "Section closed.";
146
+ }
147
+ }),
148
+ group([new CheckBoxComponent({ id: "expander-allow-close", label: "Allow closing", onChange: value => { allowCollapse = value; } })], "sample-row"),
149
+ guardedStatus
150
+ ]),
151
+ card("expander-nested", "05", "One level at a time", "Nested sections stay independent. Opening Notifications or Privacy keeps the parent in place.", [nested]),
152
+ card("expander-options", "06", "A familiar shape, your details", "Custom indicators and disabled toggles use the same surface and theme.", [
153
+ group([
154
+ customIcons,
155
+ new ExpanderComponent({ header: "Unavailable section", body: "This section is not available yet.", expanded: false, enabled: false })
156
+ ], "expander-stack")
157
+ ])
158
+ ], [
159
+ text("pre", 'new ExpanderComponent({\n header: "Project details", body: content, expanded: true,\n onToggle: expanding => { /* Return false to veto. */ }\n});\n\nexpander.setExpanded(false); // Silent update\nexpander.toggle(); // Consults onToggle', "sample-code"),
160
+ text("p", "Both the options constructor and new ExpanderComponent(header, body, expanded) remain supported. expanded still defaults to true. Existing icons, iconSize, isToggleOnHeader, class, style, getIcon(), onToggle, and State.expanded remain available. Optional id, ariaLabel, and enabled add labeling and disabled behavior."),
161
+ text("p", "isExpanded() reads the current state. setExpanded() changes it silently; toggle() follows the same callback and veto rules as user activation. State and panel IDs survive parent renders that create a new ExpanderComponent instance. Collapsing preserves the body and returns focus to the toggle if focus was inside it."),
162
+ text("p", "Custom headers can contain independent buttons, links, and form inputs. Those controls do not toggle the section. The body is a named region connected to its button. Reduced-motion preferences disable the short reveal animation.")
163
+ ]);