kiso 0.5.2.pre → 0.6.0.pre

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/tailwind/kiso/button.css +12 -3
  3. data/app/assets/tailwind/kiso/checkbox.css +13 -2
  4. data/app/assets/tailwind/kiso/color-mode.css +15 -3
  5. data/app/assets/tailwind/kiso/dashboard.css +97 -44
  6. data/app/assets/tailwind/kiso/dialog.css +39 -5
  7. data/app/assets/tailwind/kiso/engine.css +117 -34
  8. data/app/assets/tailwind/kiso/input-otp.css +24 -4
  9. data/app/assets/tailwind/kiso/palettes/blue.css +14 -5
  10. data/app/assets/tailwind/kiso/palettes/green.css +9 -5
  11. data/app/assets/tailwind/kiso/palettes/orange.css +9 -5
  12. data/app/assets/tailwind/kiso/palettes/violet.css +9 -5
  13. data/app/assets/tailwind/kiso/palettes/zinc.css +11 -7
  14. data/app/assets/tailwind/kiso/radio-group.css +11 -4
  15. data/app/assets/tailwind/kiso/slider.css +25 -6
  16. data/app/assets/tailwind/kiso/tooltip.css +37 -11
  17. data/app/helpers/kiso/app_component_helper.rb +83 -34
  18. data/app/helpers/kiso/component_helper.rb +227 -70
  19. data/app/helpers/kiso/icon_helper.rb +101 -39
  20. data/app/helpers/kiso/theme_helper.rb +50 -9
  21. data/app/helpers/kiso/ui_context_helper.rb +87 -35
  22. data/app/javascript/controllers/kiso/combobox_controller.js +10 -2
  23. data/app/javascript/controllers/kiso/command_controller.js +2 -0
  24. data/app/javascript/controllers/kiso/command_dialog_controller.js +4 -0
  25. data/app/javascript/controllers/kiso/dialog_controller.js +6 -1
  26. data/app/javascript/controllers/kiso/dialog_trigger_controller.js +1 -1
  27. data/app/javascript/controllers/kiso/dropdown_menu_controller.js +23 -5
  28. data/app/javascript/controllers/kiso/index.js +25 -0
  29. data/app/javascript/controllers/kiso/input_otp_controller.js +5 -3
  30. data/app/javascript/controllers/kiso/popover_controller.js +18 -4
  31. data/app/javascript/controllers/kiso/select_controller.js +10 -2
  32. data/app/javascript/controllers/kiso/sidebar_controller.js +26 -4
  33. data/app/javascript/controllers/kiso/slider_controller.js +3 -3
  34. data/app/javascript/controllers/kiso/theme_controller.js +2 -1
  35. data/app/javascript/controllers/kiso/toggle_controller.js +2 -0
  36. data/app/javascript/controllers/kiso/toggle_group_controller.js +3 -0
  37. data/app/javascript/controllers/kiso/tooltip_controller.js +3 -0
  38. data/app/javascript/kiso/utils/focusable.js +14 -0
  39. data/app/javascript/kiso/utils/highlight.js +15 -1
  40. data/app/views/kiso/components/_alert.html.erb +2 -0
  41. data/app/views/kiso/components/_alert_dialog.html.erb +5 -2
  42. data/app/views/kiso/components/_app.html.erb +2 -0
  43. data/app/views/kiso/components/_aspect_ratio.html.erb +1 -0
  44. data/app/views/kiso/components/_avatar.html.erb +6 -2
  45. data/app/views/kiso/components/_button.html.erb +3 -0
  46. data/app/views/kiso/components/_checkbox.html.erb +1 -0
  47. data/app/views/kiso/components/_color_mode_button.html.erb +2 -0
  48. data/app/views/kiso/components/_color_mode_select.html.erb +2 -0
  49. data/app/views/kiso/components/_combobox.html.erb +3 -0
  50. data/app/views/kiso/components/_command.html.erb +2 -0
  51. data/app/views/kiso/components/_dashboard_group.html.erb +4 -0
  52. data/app/views/kiso/components/_dashboard_navbar.html.erb +2 -0
  53. data/app/views/kiso/components/_dashboard_panel.html.erb +1 -0
  54. data/app/views/kiso/components/_dashboard_sidebar.html.erb +2 -0
  55. data/app/views/kiso/components/_dashboard_toolbar.html.erb +2 -0
  56. data/app/views/kiso/components/_dialog.html.erb +3 -0
  57. data/app/views/kiso/components/_dropdown_menu.html.erb +2 -0
  58. data/app/views/kiso/components/_empty.html.erb +2 -0
  59. data/app/views/kiso/components/_field.html.erb +2 -0
  60. data/app/views/kiso/components/_field_group.html.erb +1 -0
  61. data/app/views/kiso/components/_field_set.html.erb +1 -0
  62. data/app/views/kiso/components/_input_group.html.erb +1 -0
  63. data/app/views/kiso/components/_input_otp.html.erb +3 -0
  64. data/app/views/kiso/components/_nav.html.erb +2 -0
  65. data/app/views/kiso/components/_page_card.html.erb +3 -0
  66. data/app/views/kiso/components/_page_header.html.erb +3 -0
  67. data/app/views/kiso/components/_page_section.html.erb +2 -0
  68. data/app/views/kiso/components/_pagination.html.erb +2 -0
  69. data/app/views/kiso/components/_popover.html.erb +3 -0
  70. data/app/views/kiso/components/_select.html.erb +3 -0
  71. data/app/views/kiso/components/_select_native.html.erb +2 -0
  72. data/app/views/kiso/components/_separator.html.erb +2 -0
  73. data/app/views/kiso/components/_skeleton.html.erb +1 -0
  74. data/app/views/kiso/components/_slider.html.erb +4 -0
  75. data/app/views/kiso/components/_spinner.html.erb +2 -0
  76. data/app/views/kiso/components/_stats_card.html.erb +2 -0
  77. data/app/views/kiso/components/_stats_grid.html.erb +1 -0
  78. data/app/views/kiso/components/_switch.html.erb +2 -0
  79. data/app/views/kiso/components/_table.html.erb +2 -0
  80. data/app/views/kiso/components/_textarea.html.erb +3 -0
  81. data/app/views/kiso/components/_toggle.html.erb +2 -0
  82. data/app/views/kiso/components/_toggle_group.html.erb +2 -0
  83. data/app/views/kiso/components/_tooltip.html.erb +3 -0
  84. data/app/views/kiso/components/alert_dialog/_action.html.erb +1 -0
  85. data/app/views/kiso/components/alert_dialog/_cancel.html.erb +1 -0
  86. data/app/views/kiso/components/alert_dialog/_description.html.erb +1 -0
  87. data/app/views/kiso/components/alert_dialog/_title.html.erb +1 -0
  88. data/app/views/kiso/components/avatar/_image.html.erb +1 -0
  89. data/app/views/kiso/components/breadcrumb/_separator.html.erb +3 -0
  90. data/app/views/kiso/components/combobox/_chips.html.erb +3 -0
  91. data/app/views/kiso/components/command/_dialog.html.erb +2 -0
  92. data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +2 -0
  93. data/app/views/kiso/components/dialog/_close.html.erb +1 -0
  94. data/app/views/kiso/components/field/_error.html.erb +4 -0
  95. data/app/views/kiso/components/field/_label.html.erb +2 -0
  96. data/app/views/kiso/components/field/_separator.html.erb +3 -0
  97. data/app/views/kiso/components/input_otp/_separator.html.erb +2 -0
  98. data/app/views/kiso/components/input_otp/_slot.html.erb +2 -0
  99. data/app/views/kiso/components/nav/_section.html.erb +4 -0
  100. data/app/views/kiso/components/tooltip/_content.html.erb +2 -0
  101. data/lib/generators/kiso/install/USAGE +23 -0
  102. data/lib/generators/kiso/install/install_generator.rb +91 -0
  103. data/lib/generators/kiso/install/templates/design_system.md.tt +190 -0
  104. data/lib/generators/kiso/install/templates/initializer.rb.tt +40 -0
  105. data/lib/kiso/cli/make.rb +6 -3
  106. data/lib/kiso/cli.rb +10 -0
  107. data/lib/kiso/color_utils.rb +31 -8
  108. data/lib/kiso/configuration.rb +11 -0
  109. data/lib/kiso/engine.rb +9 -2
  110. data/lib/kiso/propshaft_tailwind_stub_filter.rb +9 -2
  111. data/lib/kiso/themes/avatar.rb +40 -6
  112. data/lib/kiso/themes/badge.rb +5 -1
  113. data/lib/kiso/themes/color_mode_button.rb +11 -0
  114. data/lib/kiso/themes/color_mode_select.rb +7 -0
  115. data/lib/kiso/themes/dashboard.rb +28 -0
  116. data/lib/kiso/themes/dropdown_menu.rb +2 -2
  117. data/lib/kiso/themes/input_otp.rb +6 -3
  118. data/lib/kiso/themes/nav.rb +17 -0
  119. data/lib/kiso/themes/pagination.rb +9 -4
  120. data/lib/kiso/themes/shared.rb +27 -7
  121. data/lib/kiso/version.rb +5 -2
  122. metadata +5 -1
@@ -20,7 +20,8 @@ import { startPositioning } from "kiso-ui/utils/positioning"
20
20
  *
21
21
  * @property {HTMLElement} triggerTarget - Button that opens/closes the popover
22
22
  * @property {HTMLElement} contentTarget - The floating panel
23
- * @property {HTMLElement} [anchorTarget] - Optional alternate positioning reference
23
+ * @property {HTMLElement} [anchorTarget] - Optional alternate positioning reference element.
24
+ * When present, the popover is positioned relative to this element instead of the trigger.
24
25
  */
25
26
  export default class extends Controller {
26
27
  static targets = ["trigger", "content", "anchor"]
@@ -164,7 +165,12 @@ export default class extends Controller {
164
165
  }
165
166
  }
166
167
 
167
- /** @private */
168
+ /**
169
+ * Maps `data-align` attribute values to Floating UI placement strings.
170
+ *
171
+ * @type {Record<string, string>}
172
+ * @private
173
+ */
168
174
  static _alignToPlacement = {
169
175
  start: "bottom-start",
170
176
  center: "bottom",
@@ -240,13 +246,21 @@ export default class extends Controller {
240
246
  }
241
247
  }
242
248
 
243
- /** @private */
249
+ /**
250
+ * Attaches global listeners for outside-click and keyboard navigation.
251
+ *
252
+ * @private
253
+ */
244
254
  _addGlobalListeners() {
245
255
  document.addEventListener("click", this._handleOutsideClick, true)
246
256
  document.addEventListener("keydown", this._handleKeydown)
247
257
  }
248
258
 
249
- /** @private */
259
+ /**
260
+ * Removes global listeners for outside-click and keyboard navigation.
261
+ *
262
+ * @private
263
+ */
250
264
  _removeGlobalListeners() {
251
265
  document.removeEventListener("click", this._handleOutsideClick, true)
252
266
  document.removeEventListener("keydown", this._handleKeydown)
@@ -293,13 +293,21 @@ export default class extends Controller {
293
293
  }
294
294
  }
295
295
 
296
- /** @private */
296
+ /**
297
+ * Attaches global listeners for outside-click and keyboard navigation.
298
+ *
299
+ * @private
300
+ */
297
301
  _addGlobalListeners() {
298
302
  document.addEventListener("click", this._handleOutsideClick, true)
299
303
  document.addEventListener("keydown", this._handleKeydown)
300
304
  }
301
305
 
302
- /** @private */
306
+ /**
307
+ * Removes global listeners for outside-click and keyboard navigation.
308
+ *
309
+ * @private
310
+ */
303
311
  _removeGlobalListeners() {
304
312
  document.removeEventListener("click", this._handleOutsideClick, true)
305
313
  document.removeEventListener("keydown", this._handleKeydown)
@@ -9,6 +9,9 @@ import { Controller } from "@hotwired/stimulus"
9
9
  * boolean attribute and persists the preference to a cookie for
10
10
  * FOUC-free server-side restoration on the next page load.
11
11
  *
12
+ * On mobile, the sidebar auto-closes when a nav item link is clicked
13
+ * so users don't have to manually dismiss it after every navigation.
14
+ *
12
15
  * Register as `kiso--sidebar` (the engine index does this automatically).
13
16
  *
14
17
  * @example
@@ -35,12 +38,31 @@ import { Controller } from "@hotwired/stimulus"
35
38
  * aria-hidden="true"></div>
36
39
  * </div>
37
40
  *
38
- * @property {Element[]} triggerTargets - Toggle/collapse buttons that control the sidebar
39
- * @property {Element} scrimTarget - The mobile overlay scrim
41
+ * @property {HTMLElement[]} triggerTargets - Toggle/collapse buttons that control the sidebar
42
+ * @property {HTMLElement} scrimTarget - The mobile overlay scrim element
40
43
  */
41
44
  export default class extends Controller {
42
45
  static targets = ["trigger", "scrim"]
43
46
 
47
+ /**
48
+ * Sets up event delegation for auto-closing the sidebar on mobile
49
+ * when a nav item link is clicked.
50
+ */
51
+ connect() {
52
+ this._handleNavClick = (event) => {
53
+ // Matches both: nav-item that IS a link (current Kiso markup: <a data-slot="nav-item">)
54
+ // and a link nested inside a nav-item (defensive for host app customization)
55
+ if (event.target.closest('[data-slot="nav-item"][href], [data-slot="nav-item"] a')) {
56
+ this.closeOnMobile()
57
+ }
58
+ }
59
+ this.element.addEventListener("click", this._handleNavClick)
60
+ }
61
+
62
+ disconnect() {
63
+ this.element.removeEventListener("click", this._handleNavClick)
64
+ }
65
+
44
66
  /**
45
67
  * Toggles the sidebar open/closed state.
46
68
  *
@@ -56,8 +78,8 @@ export default class extends Controller {
56
78
  /**
57
79
  * Closes the sidebar on mobile viewports only.
58
80
  *
59
- * Connected to the scrim click event. Tapping the overlay outside
60
- * the mobile sidebar dismisses it without affecting desktop layout.
81
+ * Connected to the scrim click event and nav item click delegation.
82
+ * Dismisses the sidebar without affecting desktop layout.
61
83
  */
62
84
  closeOnMobile() {
63
85
  if (matchMedia("(max-width: 767px)").matches) {
@@ -30,9 +30,9 @@ import { Controller } from "@hotwired/stimulus"
30
30
  * @property {HTMLElement} trackTarget - The background track element
31
31
  * @property {HTMLElement} rangeTarget - The filled range portion
32
32
  * @property {HTMLElement} thumbTarget - The draggable thumb handle
33
- * @property {Number} minValue - Minimum slider value
34
- * @property {Number} maxValue - Maximum slider value
35
- * @property {Number} stepValue - Step increment
33
+ * @property {number} minValue - Minimum slider value (default: 0)
34
+ * @property {number} maxValue - Maximum slider value (default: 100)
35
+ * @property {number} stepValue - Step increment (default: 1)
36
36
  *
37
37
  * @fires kiso--slider:change - When the slider value changes.
38
38
  * Detail: `{ value: number }`
@@ -25,7 +25,8 @@ import { Controller } from "@hotwired/stimulus"
25
25
  * <!-- kui(:select) with light/dark/system items -->
26
26
  * </div>
27
27
  *
28
- * @fires kiso:theme-change on document.documentElement when theme changes
28
+ * @fires kiso:theme-change - Dispatched on `document.documentElement` when the theme changes.
29
+ * Detail: `{ theme: "light" | "dark" | "system" }`.
29
30
  */
30
31
  export default class extends Controller {
31
32
  /**
@@ -4,6 +4,8 @@ import { Controller } from "@hotwired/stimulus"
4
4
  * Manages pressed state for a standalone toggle button.
5
5
  * Toggles `data-state` between "on" and "off" and updates `aria-pressed`.
6
6
  *
7
+ * For grouped toggles, use the `kiso--toggle-group` controller instead.
8
+ *
7
9
  * @example
8
10
  * <button data-controller="kiso--toggle"
9
11
  * data-action="click->kiso--toggle#toggle"
@@ -77,6 +77,7 @@ export default class extends Controller {
77
77
  * Wraps around at boundaries.
78
78
  *
79
79
  * @param {KeyboardEvent} event
80
+ * @private
80
81
  */
81
82
  #handleKeydown = (event) => {
82
83
  const items = this.itemTargets.filter((item) => !item.disabled)
@@ -113,6 +114,8 @@ export default class extends Controller {
113
114
  * Dispatches a "change" event with the currently selected value(s).
114
115
  * Single mode emits `{ value: string | null }`,
115
116
  * multiple mode emits `{ value: string[] }`.
117
+ *
118
+ * @private
116
119
  */
117
120
  #dispatchChange() {
118
121
  const selectedValues = this.itemTargets
@@ -30,6 +30,9 @@ import { startPositioning } from "kiso-ui/utils/positioning"
30
30
  * @property {HTMLElement} triggerTarget - Element that activates the tooltip on hover/focus
31
31
  * @property {HTMLElement} contentTarget - The floating tooltip panel (popover)
32
32
  * @property {HTMLElement} arrowTarget - Arrow element positioned by Floating UI
33
+ * @property {string} sideValue - Which side to place the tooltip: "top", "bottom", "left", or "right" (default: "top")
34
+ * @property {string} alignValue - Alignment along the side: "start", "center", or "end" (default: "center")
35
+ * @property {number} delayValue - Delay in milliseconds before showing the tooltip (default: 0)
33
36
  *
34
37
  * @fires kiso--tooltip:show - When the tooltip becomes visible
35
38
  * @fires kiso--tooltip:hide - When the tooltip is hidden
@@ -1,8 +1,22 @@
1
+ /**
2
+ * Shared focusable element utilities.
3
+ * Provides a CSS selector for querying focusable elements in the DOM.
4
+ *
5
+ * Used by popover, dropdown_menu, and other controllers that need
6
+ * focus trapping or focus management.
7
+ *
8
+ * @module utils/focusable
9
+ */
10
+
1
11
  /**
2
12
  * CSS selector for all natively focusable elements that are not disabled
3
13
  * or explicitly removed from the tab order.
4
14
  *
5
15
  * @type {string}
16
+ *
17
+ * @example
18
+ * const focusable = container.querySelectorAll(FOCUSABLE_SELECTOR)
19
+ * focusable[0]?.focus()
6
20
  */
7
21
  export const FOCUSABLE_SELECTOR =
8
22
  'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
@@ -10,11 +10,19 @@
10
10
  * clearItems, then sets it on the item at `index` within `items` and
11
11
  * scrolls it into view.
12
12
  *
13
- * @param {HTMLElement[]} clearItems - Items to remove the attribute from
13
+ * @param {HTMLElement[]} clearItems - Items to remove the attribute from (may differ from `items` to clear a broader set)
14
14
  * @param {HTMLElement[]} items - Items to index into for highlighting
15
15
  * @param {number} index - Index to highlight, or -1 to clear only
16
16
  * @param {Object} [options]
17
17
  * @param {string} [options.attr="data-highlighted"] - The attribute to toggle
18
+ *
19
+ * @example
20
+ * // Highlight the third item, clearing all item targets first
21
+ * highlightItem(this.itemTargets, this._visibleEnabledItems, 2)
22
+ *
23
+ * @example
24
+ * // Use a custom attribute for command palette selection
25
+ * highlightItem(this.itemTargets, enabledItems, 0, { attr: "data-selected" })
18
26
  */
19
27
  export function highlightItem(clearItems, items, index, { attr = "data-highlighted" } = {}) {
20
28
  clearItems.forEach((item) => item.removeAttribute(attr))
@@ -32,6 +40,12 @@ export function highlightItem(clearItems, items, index, { attr = "data-highlight
32
40
  * @param {number} direction - +1 for next, -1 for previous
33
41
  * @param {number} length - Total number of items
34
42
  * @returns {number} The wrapped index, or -1 if length is 0
43
+ *
44
+ * @example
45
+ * wrapIndex(2, 1, 5) // => 3
46
+ * wrapIndex(4, 1, 5) // => 0 (wraps to start)
47
+ * wrapIndex(0, -1, 5) // => 4 (wraps to end)
48
+ * wrapIndex(0, 1, 0) // => -1 (empty list)
35
49
  */
36
50
  export function wrapIndex(current, direction, length) {
37
51
  if (length === 0) return -1
@@ -1,4 +1,6 @@
1
1
  <%# locals: (icon: nil, color: :primary, variant: :soft, close: false, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Dismissible alert banner with optional icon. Composes with alert/title, alert/description,
3
+ and alert/actions sub-parts. The close: prop adds a Stimulus-powered dismiss button. %>
2
4
  <%= content_tag :div,
3
5
  role: :alert,
4
6
  class: Kiso::Themes::Alert.render(color: color, variant: variant, class: css_classes),
@@ -1,9 +1,12 @@
1
1
  <%# locals: (open: false, size: :default, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Non-dismissible confirmation dialog using native <dialog> with showModal().
3
+ Requires role="alertdialog" for screen readers. Pass id: to enable automatic
4
+ aria-labelledby/aria-describedby linking with title and description sub-parts. %>
2
5
  <%
3
6
  # Store the dialog id on the view context so child sub-part partials
4
7
  # (_title, _description) can auto-generate matching aria-labelledby /
5
- # aria-describedby id attributes without requiring the caller to pass
6
- # the id through each nested kui() call.
8
+ # aria-describedby ids without requiring the caller to thread the id
9
+ # through each nested kui() call.
7
10
  @_kiso_alert_dialog_id = component_options[:id]
8
11
  aria = {}
9
12
  if @_kiso_alert_dialog_id
@@ -1,4 +1,6 @@
1
1
  <%# locals: (center: false, css_classes: "", **component_options) %>
2
+ <%# Top-level app layout wrapper. Composes with Header, Main, Footer sub-components.
3
+ Set center: true to constrain content width. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::App.render(center: center, class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "app"),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (ratio: 16.0/9, css_classes: "", **component_options) %>
2
+ <%# Constrains child content to a fixed aspect ratio via inline style. %>
2
3
  <%= content_tag :div,
3
4
  style: "aspect-ratio: #{ratio}",
4
5
  class: Kiso::Themes::AspectRatio.render(class: css_classes),
@@ -1,10 +1,14 @@
1
- <%# locals: (src: nil, alt: "", text: nil, size: :md, color: nil, ui: {}, css_classes: "", **component_options) %>
2
- <% text_color = Kiso::ColorUtils.contrast_text_color(color) if color %>
1
+ <%# locals: (src: nil, alt: "", text: nil, size: :md, color: nil, contrast_threshold: nil, ui: {}, css_classes: "", **component_options) %>
2
+ <%# User avatar with image, text fallback, and optional status badge. Supports
3
+ arbitrary background colors via color: with automatic contrast text selection.
4
+ Yield a block to fully override internal structure, or use props for defaults. %>
5
+ <% text_color = Kiso::ColorUtils.contrast_text_color(color, threshold: contrast_threshold) if color %>
3
6
  <%= content_tag :span,
4
7
  class: Kiso::Themes::Avatar.render(size: size, class: css_classes),
5
8
  style: (color ? "background-color: #{color}; color: #{text_color};" : nil),
6
9
  data: kiso_prepare_options(component_options, slot: "avatar", size: size),
7
10
  **component_options do %>
11
+ <%# Block override: if caller yields content, render it instead of the default image+fallback. %>
8
12
  <% content = capture { yield }.presence %>
9
13
  <% if content %>
10
14
  <%= content %>
@@ -1,6 +1,9 @@
1
1
  <%# locals: (color: :primary, variant: :solid, size: :md, block: false,
2
2
  type: :button, href: nil, method: nil, disabled: false,
3
3
  form: {}, css_classes: "", **component_options) %>
4
+ <%# Polymorphic button that renders as <button>, <a>, or button_to depending on props.
5
+ With href: renders an anchor tag. With href: + method: (non-GET) renders a Rails
6
+ button_to form for safe non-GET navigation. Without href: renders a plain <button>. %>
4
7
  <%
5
8
  css = Kiso::Themes::Button.render(
6
9
  color: color, variant: variant, size: size, block: block, class: css_classes)
@@ -1,4 +1,5 @@
1
1
  <%# locals: (color: :primary, checked: false, css_classes: "", **component_options) %>
2
+ <%# Native <input type="checkbox"> styled with accent-color theming. %>
2
3
  <% component_options[:type] = :checkbox
3
4
  component_options[:checked] = true if checked %>
4
5
  <%= tag.input(
@@ -1,4 +1,6 @@
1
1
  <%# locals: (size: :md, css_classes: "", **component_options) %>
2
+ <%# Light/dark mode toggle button. Swaps sun/moon icons via CSS visibility
3
+ controlled by the .dark class on <html>. Uses the kiso--theme Stimulus controller. %>
2
4
  <%= content_tag :button,
3
5
  class: Kiso::Themes::ColorModeButton.render(size: size, class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "color-mode-button",
@@ -1,4 +1,6 @@
1
1
  <%# locals: (size: :md, css_classes: "", **component_options) %>
2
+ <%# Color mode dropdown with Light/Dark/System options. Composes a Select component
3
+ and dispatches kiso--select:change events to the kiso--theme controller. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::ColorModeSelect.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "color-mode-select",
@@ -1,4 +1,7 @@
1
1
  <%# locals: (name: nil, multiple: false, css_classes: "", **component_options) %>
2
+ <%# Autocomplete combobox with keyboard navigation and optional multi-select.
3
+ Composes with combobox/input, combobox/content, combobox/item sub-parts.
4
+ Pass name: to include a hidden input for form submission. %>
2
5
  <%= content_tag :div,
3
6
  class: Kiso::Themes::Combobox.render(class: css_classes),
4
7
  data: kiso_prepare_options(component_options, slot: "combobox",
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Command palette with search filtering and keyboard navigation.
3
+ Composes with command/input, command/list, command/group, command/item sub-parts. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::Command.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "command", controller: "kiso--command"),
@@ -1,4 +1,8 @@
1
1
  <%# locals: (sidebar_open: nil, layout: :sidebar, css_classes: "", **component_options) %>
2
+ <%# Root wrapper for the dashboard layout system. Reads sidebar state from cookies
3
+ by default, or accepts an explicit sidebar_open: override. Renders a scrim
4
+ overlay for mobile sidebar dismissal. %>
5
+ <%# Default to cookie-based sidebar state; explicit prop overrides. %>
2
6
  <% sidebar_open = cookies[:sidebar_open] != "false" if sidebar_open.nil? %>
3
7
  <%= content_tag :div,
4
8
  class: Kiso::Themes::DashboardGroup.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Top navigation bar for the dashboard layout. Typically contains sidebar toggle
3
+ buttons, breadcrumbs, and user actions. %>
2
4
  <%= content_tag :header,
3
5
  class: Kiso::Themes::DashboardNavbar.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "dashboard-navbar"),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Main content area of the dashboard layout, adjacent to the sidebar. %>
2
3
  <%= content_tag :main,
3
4
  class: Kiso::Themes::DashboardPanel.render(class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "dashboard-panel"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Dashboard sidebar <aside> with a fixed id for aria-controls references.
3
+ The inner wrapper div provides the scrollable content area. %>
2
4
  <%= content_tag :aside,
3
5
  id: "dashboard-sidebar",
4
6
  class: Kiso::Themes::DashboardSidebar.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Secondary toolbar below the navbar. Composes with dashboard_toolbar/left
3
+ and dashboard_toolbar/right for split layouts. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::DashboardToolbar.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "dashboard-toolbar"),
@@ -1,4 +1,7 @@
1
1
  <%# locals: (open: false, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Modal dialog using native <dialog> with showModal(). Dismissable via Escape
3
+ key or backdrop click (unlike AlertDialog). Composes with dialog/header,
4
+ dialog/title, dialog/description, dialog/body, dialog/footer, dialog/close. %>
2
5
  <%= content_tag :dialog,
3
6
  class: Kiso::Themes::Dialog.render(class: css_classes),
4
7
  data: kiso_prepare_options(component_options, slot: "dialog",
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Dropdown menu with keyboard navigation, checkbox/radio items, and nested submenus.
3
+ Composes with dropdown_menu/trigger, dropdown_menu/content, dropdown_menu/item, etc. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::DropdownMenu.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "dropdown-menu", controller: "kiso--dropdown-menu"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Empty state placeholder. Composes with empty/media, empty/header, empty/title,
3
+ empty/description, empty/content, and empty/actions sub-parts. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::Empty.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "empty"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (orientation: :vertical, invalid: false, disabled: false, css_classes: "", **component_options) %>
2
+ <%# Form field wrapper that groups a label, input, description, and error message.
3
+ Sets data-invalid and data-disabled attributes for CSS-driven state styling. %>
2
4
  <%= content_tag :div,
3
5
  role: :group,
4
6
  class: Kiso::Themes::Field.render(orientation: orientation, class: css_classes),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Vertical stack of Field components with consistent spacing. %>
2
3
  <%= content_tag :div,
3
4
  class: Kiso::Themes::FieldGroup.render(class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "field-group"),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Native <fieldset> for grouping related form fields. Use with field_set/legend. %>
2
3
  <%= content_tag :fieldset,
3
4
  class: Kiso::Themes::FieldSet.render(class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "field-set"),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Groups an Input with leading/trailing addons (icons, text, buttons). %>
2
3
  <%= content_tag :div,
3
4
  role: :group,
4
5
  class: Kiso::Themes::InputGroup.render(class: css_classes),
@@ -1,4 +1,7 @@
1
1
  <%# locals: (length: 6, name: nil, id: nil, value: nil, pattern: "\\d", disabled: false, autocomplete: "one-time-code", aria_label: nil, css_classes: "", **component_options) %>
2
+ <%# One-time password input with individual character slots. A hidden real input
3
+ captures keystrokes while visible slot divs display each character. Compose
4
+ with input_otp/group and input_otp/slot sub-parts for the visual layout. %>
2
5
  <%= content_tag :div,
3
6
  class: Kiso::Themes::InputOtp.render(class: css_classes),
4
7
  data: kiso_prepare_options(component_options, slot: "input-otp",
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Sidebar navigation container. Composes with nav/section, nav/section_title,
3
+ and nav/item sub-parts. Typically placed inside a dashboard_sidebar. %>
2
4
  <%= content_tag :nav,
3
5
  class: Kiso::Themes::Nav.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "nav"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (variant: :outline, icon: nil, title: nil, description: nil, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Page-level card with optional icon, title, and description props.
3
+ Yield a block to fully override the internal structure. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::PageCard.render(variant: variant, class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "page-card"),
@@ -9,6 +11,7 @@
9
11
  <%= content_tag :div,
10
12
  class: Kiso::Themes::PageCardWrapper.render(class: ui[:wrapper]),
11
13
  data: { slot: "page-card-wrapper" } do %>
14
+ <%# Block override: yield replaces default icon/title/description layout. %>
12
15
  <% content = capture { yield }.presence %>
13
16
  <% if content %>
14
17
  <%= content %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (headline: nil, title: nil, description: nil, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Page header with optional headline, title, and description props.
3
+ Yield a block to fully override the internal structure (e.g., to add action buttons). %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::PageHeader.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "page-header"),
@@ -6,6 +8,7 @@
6
8
  <%= content_tag :div,
7
9
  class: Kiso::Themes::PageHeaderWrapper.render(class: ui[:wrapper]),
8
10
  data: { slot: "page-header-wrapper" } do %>
11
+ <%# Block override: yield replaces default headline/title/description layout. %>
9
12
  <% content = capture { yield }.presence %>
10
13
  <% if content %>
11
14
  <%= content %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (orientation: :horizontal, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Page content section with horizontal or vertical orientation. Composes with
3
+ page_section/header, page_section/body, page_section/title, page_section/description. %>
2
4
  <%= content_tag :section,
3
5
  class: Kiso::Themes::PageSection.render(orientation: orientation, class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "page-section"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Pagination navigation. Composes with pagination/content, pagination/item,
3
+ pagination/link, pagination/previous, pagination/next, pagination/ellipsis. %>
2
4
  <%= content_tag :nav,
3
5
  role: "navigation",
4
6
  aria: {label: t("kiso.pagination.label")},
@@ -1,4 +1,7 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Floating popover positioned via Floating UI. Uses position: relative as the
3
+ anchor reference for absolute positioning of the content panel.
4
+ Composes with popover/trigger, popover/anchor, and popover/content sub-parts. %>
2
5
  <%= content_tag :div,
3
6
  class: css_classes.presence,
4
7
  data: kiso_prepare_options(component_options, slot: "popover", controller: "kiso--popover"),
@@ -1,4 +1,7 @@
1
1
  <%# locals: (name: nil, css_classes: "", **component_options) %>
2
+ <%# Custom select with keyboard navigation and Stimulus-driven open/close.
3
+ Pass name: to include a hidden input for form submission. For a native
4
+ <select>, use select_native instead. %>
2
5
  <%= content_tag :div,
3
6
  class: Kiso::Themes::Select.render(class: css_classes),
4
7
  data: kiso_prepare_options(component_options, slot: "select", controller: "kiso--select"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (variant: :outline, size: :md, disabled: false, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Native <select> element with a styled chevron icon overlay. Yield <option>
3
+ elements as the block content. For a custom dropdown, use select instead. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::SelectNativeWrapper.render(class: ui[:wrapper]),
4
6
  data: {slot: "select-native-wrapper"} do %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (orientation: :horizontal, decorative: true, css_classes: "", **component_options) %>
2
+ <%# Visual divider line. Set decorative: false to expose a semantic separator
3
+ role to assistive technology. %>
2
4
  <%= tag.div(
3
5
  role: (decorative ? "none" : "separator"),
4
6
  aria: (decorative ? {} : { orientation: orientation }),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Animated placeholder for content that is loading. Style dimensions via css_classes:. %>
2
3
  <%= tag.div(
3
4
  class: Kiso::Themes::Skeleton.render(class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "skeleton"),
@@ -1,5 +1,9 @@
1
1
  <%# locals: (min: 0, max: 100, step: 1, value: nil, name: nil, id: nil, disabled: false, size: :md, aria_label: nil, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Range slider with a hidden native <input type="range"> for form submission and an
3
+ accessible ARIA slider thumb for keyboard/pointer interaction. The Stimulus controller
4
+ syncs the visual position with the hidden input value. %>
2
5
  <% slider_value = value || min %>
6
+ <%# Calculate initial thumb/range position as a percentage for inline styles. %>
3
7
  <% percent = ((slider_value.to_f - min) / (max - min) * 100).clamp(0, 100) %>
4
8
  <%= content_tag :div,
5
9
  class: Kiso::Themes::Slider.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (label: nil, css_classes: "", **component_options) %>
2
+ <%# Animated loading spinner rendered via kiso_component_icon. Uses role="status"
3
+ for screen reader announcements. %>
2
4
  <%= kiso_component_icon(:spinner,
3
5
  role: "status",
4
6
  aria: { label: label || t("kiso.spinner.loading") },
@@ -1,4 +1,6 @@
1
1
  <%# locals: (variant: :outline, css_classes: "", **component_options) %>
2
+ <%# Metric display card. Composes with stats_card/header, stats_card/label,
3
+ stats_card/value, and stats_card/description sub-parts. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::StatsCard.render(variant: variant, class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "stats-card"),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (columns: 4, css_classes: "", **component_options) %>
2
+ <%# Responsive grid layout for stats_card components. %>
2
3
  <%= content_tag :div,
3
4
  class: Kiso::Themes::StatsGrid.render(columns: columns, class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "stats-grid"),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (color: :primary, size: :md, checked: false, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Toggle switch built on a hidden checkbox with role="switch". The <label> wraps
3
+ the hidden input and a visible thumb span, using peer-checked: for state styling. %>
2
4
  <% component_options[:type] = :checkbox
3
5
  component_options[:role] = :switch
4
6
  component_options[:checked] = true if checked %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Data table wrapped in a horizontally-scrollable container. Composes with table/header,
3
+ table/body, table/row, table/head, table/cell, table/footer, and table/caption. %>
2
4
  <div class="relative w-full overflow-x-auto">
3
5
  <%= content_tag :table,
4
6
  class: Kiso::Themes::Table.render(class: css_classes),