kiso 0.1.0.pre → 0.2.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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -2
  3. data/README.md +67 -27
  4. data/Rakefile +8 -0
  5. data/app/assets/tailwind/kiso/checkbox.css +18 -0
  6. data/app/assets/tailwind/kiso/color-mode.css +9 -0
  7. data/app/assets/tailwind/kiso/dashboard.css +194 -0
  8. data/app/assets/tailwind/kiso/engine.css +117 -0
  9. data/app/assets/tailwind/kiso/input-otp.css +10 -0
  10. data/app/assets/tailwind/kiso/radio-group.css +17 -0
  11. data/app/helpers/kiso/component_helper.rb +46 -27
  12. data/app/helpers/kiso/icon_helper.rb +53 -9
  13. data/app/helpers/kiso/theme_helper.rb +38 -0
  14. data/app/javascript/controllers/kiso/combobox_controller.js +616 -0
  15. data/app/javascript/controllers/kiso/command_controller.js +184 -0
  16. data/app/javascript/controllers/kiso/command_dialog_controller.js +104 -0
  17. data/app/javascript/controllers/kiso/dropdown_menu_controller.js +684 -0
  18. data/app/javascript/controllers/kiso/index.d.ts +12 -0
  19. data/app/javascript/controllers/kiso/index.js +42 -0
  20. data/app/javascript/controllers/kiso/input_otp_controller.js +195 -0
  21. data/app/javascript/controllers/kiso/popover_controller.js +254 -0
  22. data/app/javascript/controllers/kiso/select_controller.js +307 -0
  23. data/app/javascript/controllers/kiso/sidebar_controller.js +84 -0
  24. data/app/javascript/controllers/kiso/theme_controller.js +89 -0
  25. data/app/javascript/controllers/kiso/toggle_controller.js +24 -0
  26. data/app/javascript/controllers/kiso/toggle_group_controller.js +128 -0
  27. data/app/javascript/kiso/utils/focusable.js +8 -0
  28. data/app/javascript/kiso/utils/highlight.js +43 -0
  29. data/app/javascript/kiso/utils/positioning.js +86 -0
  30. data/app/javascript/kiso/vendor/floating-ui-core.js +1 -0
  31. data/app/javascript/kiso/vendor/floating-ui-dom.js +1 -0
  32. data/app/views/kiso/components/_alert.html.erb +1 -1
  33. data/app/views/kiso/components/_avatar.html.erb +23 -0
  34. data/app/views/kiso/components/_badge.html.erb +1 -1
  35. data/app/views/kiso/components/_breadcrumb.html.erb +8 -0
  36. data/app/views/kiso/components/_button.html.erb +1 -1
  37. data/app/views/kiso/components/_card.html.erb +1 -1
  38. data/app/views/kiso/components/_checkbox.html.erb +7 -0
  39. data/app/views/kiso/components/_color_mode_button.html.erb +14 -0
  40. data/app/views/kiso/components/_color_mode_select.html.erb +24 -0
  41. data/app/views/kiso/components/_combobox.html.erb +12 -0
  42. data/app/views/kiso/components/_command.html.erb +7 -0
  43. data/app/views/kiso/components/_dashboard_group.html.erb +14 -0
  44. data/app/views/kiso/components/_dashboard_navbar.html.erb +7 -0
  45. data/app/views/kiso/components/_dashboard_panel.html.erb +7 -0
  46. data/app/views/kiso/components/_dashboard_sidebar.html.erb +11 -0
  47. data/app/views/kiso/components/_dashboard_toolbar.html.erb +7 -0
  48. data/app/views/kiso/components/_dropdown_menu.html.erb +7 -0
  49. data/app/views/kiso/components/{_empty_state.html.erb → _empty.html.erb} +2 -2
  50. data/app/views/kiso/components/_field.html.erb +12 -0
  51. data/app/views/kiso/components/_field_group.html.erb +7 -0
  52. data/app/views/kiso/components/_field_set.html.erb +7 -0
  53. data/app/views/kiso/components/_input.html.erb +8 -0
  54. data/app/views/kiso/components/_input_group.html.erb +8 -0
  55. data/app/views/kiso/components/_input_otp.html.erb +22 -0
  56. data/app/views/kiso/components/_kbd.html.erb +7 -0
  57. data/app/views/kiso/components/_label.html.erb +5 -0
  58. data/app/views/kiso/components/_nav.html.erb +7 -0
  59. data/app/views/kiso/components/_pagination.html.erb +9 -0
  60. data/app/views/kiso/components/_popover.html.erb +8 -0
  61. data/app/views/kiso/components/_radio_group.html.erb +8 -0
  62. data/app/views/kiso/components/_select.html.erb +8 -0
  63. data/app/views/kiso/components/_select_native.html.erb +16 -0
  64. data/app/views/kiso/components/_separator.html.erb +1 -1
  65. data/app/views/kiso/components/_stats_card.html.erb +1 -1
  66. data/app/views/kiso/components/_stats_grid.html.erb +1 -1
  67. data/app/views/kiso/components/_switch.html.erb +10 -0
  68. data/app/views/kiso/components/_table.html.erb +2 -1
  69. data/app/views/kiso/components/_textarea.html.erb +9 -0
  70. data/app/views/kiso/components/_toggle.html.erb +12 -0
  71. data/app/views/kiso/components/_toggle_group.html.erb +12 -0
  72. data/app/views/kiso/components/alert/_description.html.erb +1 -1
  73. data/app/views/kiso/components/alert/_title.html.erb +1 -1
  74. data/app/views/kiso/components/avatar/_badge.html.erb +7 -0
  75. data/app/views/kiso/components/avatar/_fallback.html.erb +7 -0
  76. data/app/views/kiso/components/avatar/_group.html.erb +7 -0
  77. data/app/views/kiso/components/avatar/_group_count.html.erb +7 -0
  78. data/app/views/kiso/components/avatar/_image.html.erb +6 -0
  79. data/app/views/kiso/components/breadcrumb/_ellipsis.html.erb +10 -0
  80. data/app/views/kiso/components/breadcrumb/_item.html.erb +7 -0
  81. data/app/views/kiso/components/breadcrumb/_link.html.erb +7 -0
  82. data/app/views/kiso/components/breadcrumb/_list.html.erb +7 -0
  83. data/app/views/kiso/components/breadcrumb/_page.html.erb +9 -0
  84. data/app/views/kiso/components/breadcrumb/_separator.html.erb +9 -0
  85. data/app/views/kiso/components/card/_action.html.erb +7 -0
  86. data/app/views/kiso/components/card/_content.html.erb +1 -1
  87. data/app/views/kiso/components/card/_description.html.erb +1 -1
  88. data/app/views/kiso/components/card/_footer.html.erb +1 -1
  89. data/app/views/kiso/components/card/_header.html.erb +1 -1
  90. data/app/views/kiso/components/card/_title.html.erb +1 -1
  91. data/app/views/kiso/components/combobox/_chip.html.erb +19 -0
  92. data/app/views/kiso/components/combobox/_chips.html.erb +20 -0
  93. data/app/views/kiso/components/combobox/_chips_input.html.erb +10 -0
  94. data/app/views/kiso/components/combobox/_content.html.erb +9 -0
  95. data/app/views/kiso/components/combobox/_empty.html.erb +9 -0
  96. data/app/views/kiso/components/combobox/_group.html.erb +8 -0
  97. data/app/views/kiso/components/combobox/_input.html.erb +23 -0
  98. data/app/views/kiso/components/combobox/_item.html.erb +19 -0
  99. data/app/views/kiso/components/combobox/_label.html.erb +7 -0
  100. data/app/views/kiso/components/combobox/_list.html.erb +10 -0
  101. data/app/views/kiso/components/combobox/_separator.html.erb +6 -0
  102. data/app/views/kiso/components/command/_dialog.html.erb +11 -0
  103. data/app/views/kiso/components/command/_empty.html.erb +9 -0
  104. data/app/views/kiso/components/command/_group.html.erb +14 -0
  105. data/app/views/kiso/components/command/_input.html.erb +16 -0
  106. data/app/views/kiso/components/command/_item.html.erb +13 -0
  107. data/app/views/kiso/components/command/_list.html.erb +10 -0
  108. data/app/views/kiso/components/command/_separator.html.erb +7 -0
  109. data/app/views/kiso/components/command/_shortcut.html.erb +7 -0
  110. data/app/views/kiso/components/dashboard_navbar/_toggle.html.erb +11 -0
  111. data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +12 -0
  112. data/app/views/kiso/components/dashboard_sidebar/_footer.html.erb +7 -0
  113. data/app/views/kiso/components/dashboard_sidebar/_header.html.erb +7 -0
  114. data/app/views/kiso/components/dashboard_sidebar/_toggle.html.erb +11 -0
  115. data/app/views/kiso/components/dashboard_toolbar/_left.html.erb +7 -0
  116. data/app/views/kiso/components/dashboard_toolbar/_right.html.erb +7 -0
  117. data/app/views/kiso/components/dropdown_menu/_checkbox_item.html.erb +18 -0
  118. data/app/views/kiso/components/dropdown_menu/_content.html.erb +10 -0
  119. data/app/views/kiso/components/dropdown_menu/_group.html.erb +8 -0
  120. data/app/views/kiso/components/dropdown_menu/_item.html.erb +15 -0
  121. data/app/views/kiso/components/dropdown_menu/_label.html.erb +8 -0
  122. data/app/views/kiso/components/dropdown_menu/_radio_group.html.erb +10 -0
  123. data/app/views/kiso/components/dropdown_menu/_radio_item.html.erb +19 -0
  124. data/app/views/kiso/components/dropdown_menu/_separator.html.erb +6 -0
  125. data/app/views/kiso/components/dropdown_menu/_shortcut.html.erb +7 -0
  126. data/app/views/kiso/components/dropdown_menu/_sub.html.erb +8 -0
  127. data/app/views/kiso/components/dropdown_menu/_sub_content.html.erb +10 -0
  128. data/app/views/kiso/components/dropdown_menu/_sub_trigger.html.erb +12 -0
  129. data/app/views/kiso/components/dropdown_menu/_trigger.html.erb +9 -0
  130. data/app/views/kiso/components/empty/_content.html.erb +7 -0
  131. data/app/views/kiso/components/empty/_description.html.erb +7 -0
  132. data/app/views/kiso/components/empty/_header.html.erb +7 -0
  133. data/app/views/kiso/components/empty/_media.html.erb +7 -0
  134. data/app/views/kiso/components/empty/_title.html.erb +7 -0
  135. data/app/views/kiso/components/field/_content.html.erb +7 -0
  136. data/app/views/kiso/components/field/_description.html.erb +7 -0
  137. data/app/views/kiso/components/field/_error.html.erb +22 -0
  138. data/app/views/kiso/components/field/_label.html.erb +5 -0
  139. data/app/views/kiso/components/field/_separator.html.erb +15 -0
  140. data/app/views/kiso/components/field/_title.html.erb +7 -0
  141. data/app/views/kiso/components/field_set/_legend.html.erb +9 -0
  142. data/app/views/kiso/components/input_group/_addon.html.erb +7 -0
  143. data/app/views/kiso/components/input_otp/_group.html.erb +7 -0
  144. data/app/views/kiso/components/input_otp/_separator.html.erb +8 -0
  145. data/app/views/kiso/components/input_otp/_slot.html.erb +11 -0
  146. data/app/views/kiso/components/kbd/_group.html.erb +7 -0
  147. data/app/views/kiso/components/nav/_item.html.erb +15 -0
  148. data/app/views/kiso/components/nav/_section.html.erb +37 -0
  149. data/app/views/kiso/components/nav/_section_title.html.erb +7 -0
  150. data/app/views/kiso/components/pagination/_content.html.erb +7 -0
  151. data/app/views/kiso/components/pagination/_ellipsis.html.erb +9 -0
  152. data/app/views/kiso/components/pagination/_item.html.erb +7 -0
  153. data/app/views/kiso/components/pagination/_link.html.erb +9 -0
  154. data/app/views/kiso/components/pagination/_next.html.erb +12 -0
  155. data/app/views/kiso/components/pagination/_previous.html.erb +12 -0
  156. data/app/views/kiso/components/popover/_anchor.html.erb +8 -0
  157. data/app/views/kiso/components/popover/_content.html.erb +11 -0
  158. data/app/views/kiso/components/popover/_description.html.erb +7 -0
  159. data/app/views/kiso/components/popover/_header.html.erb +7 -0
  160. data/app/views/kiso/components/popover/_title.html.erb +7 -0
  161. data/app/views/kiso/components/popover/_trigger.html.erb +9 -0
  162. data/app/views/kiso/components/radio_group/_item.html.erb +6 -0
  163. data/app/views/kiso/components/select/_content.html.erb +10 -0
  164. data/app/views/kiso/components/select/_group.html.erb +8 -0
  165. data/app/views/kiso/components/select/_item.html.erb +19 -0
  166. data/app/views/kiso/components/select/_label.html.erb +7 -0
  167. data/app/views/kiso/components/select/_separator.html.erb +6 -0
  168. data/app/views/kiso/components/select/_trigger.html.erb +13 -0
  169. data/app/views/kiso/components/select/_value.html.erb +11 -0
  170. data/app/views/kiso/components/stats_card/_description.html.erb +1 -1
  171. data/app/views/kiso/components/stats_card/_header.html.erb +1 -1
  172. data/app/views/kiso/components/stats_card/_label.html.erb +1 -1
  173. data/app/views/kiso/components/stats_card/_value.html.erb +1 -1
  174. data/app/views/kiso/components/table/_body.html.erb +1 -1
  175. data/app/views/kiso/components/table/_caption.html.erb +1 -1
  176. data/app/views/kiso/components/table/_cell.html.erb +1 -1
  177. data/app/views/kiso/components/table/_footer.html.erb +1 -1
  178. data/app/views/kiso/components/table/_head.html.erb +1 -1
  179. data/app/views/kiso/components/table/_header.html.erb +1 -1
  180. data/app/views/kiso/components/table/_row.html.erb +1 -1
  181. data/app/views/kiso/components/toggle_group/_item.html.erb +13 -0
  182. data/config/deploy.docs.yml +31 -0
  183. data/config/deploy.yml +34 -0
  184. data/config/importmap.rb +10 -0
  185. data/lib/kiso/cli/base.rb +15 -0
  186. data/lib/kiso/cli/icons.rb +2 -1
  187. data/lib/kiso/cli/main.rb +6 -0
  188. data/lib/kiso/cli/make.rb +22 -12
  189. data/lib/kiso/configuration.rb +54 -0
  190. data/lib/kiso/engine.rb +36 -1
  191. data/lib/kiso/theme_overrides.rb +130 -0
  192. data/lib/kiso/themes/alert.rb +16 -1
  193. data/lib/kiso/themes/avatar.rb +53 -0
  194. data/lib/kiso/themes/badge.rb +15 -5
  195. data/lib/kiso/themes/breadcrumb.rb +44 -0
  196. data/lib/kiso/themes/button.rb +15 -2
  197. data/lib/kiso/themes/card.rb +18 -2
  198. data/lib/kiso/themes/checkbox.rb +33 -0
  199. data/lib/kiso/themes/color_mode_button.rb +15 -0
  200. data/lib/kiso/themes/color_mode_select.rb +7 -0
  201. data/lib/kiso/themes/combobox.rb +97 -0
  202. data/lib/kiso/themes/command.rb +79 -0
  203. data/lib/kiso/themes/dashboard.rb +51 -0
  204. data/lib/kiso/themes/dropdown_menu.rb +108 -0
  205. data/lib/kiso/themes/empty.rb +54 -0
  206. data/lib/kiso/themes/field.rb +76 -0
  207. data/lib/kiso/themes/field_group.rb +15 -0
  208. data/lib/kiso/themes/field_set.rb +32 -0
  209. data/lib/kiso/themes/input.rb +33 -0
  210. data/lib/kiso/themes/input_group.rb +39 -0
  211. data/lib/kiso/themes/input_otp.rb +46 -0
  212. data/lib/kiso/themes/kbd.rb +31 -0
  213. data/lib/kiso/themes/label.rb +16 -0
  214. data/lib/kiso/themes/nav.rb +27 -0
  215. data/lib/kiso/themes/pagination.rb +73 -0
  216. data/lib/kiso/themes/popover.rb +32 -0
  217. data/lib/kiso/themes/radio_group.rb +43 -0
  218. data/lib/kiso/themes/select.rb +78 -0
  219. data/lib/kiso/themes/select_native.rb +49 -0
  220. data/lib/kiso/themes/separator.rb +8 -2
  221. data/lib/kiso/themes/shared.rb +51 -0
  222. data/lib/kiso/themes/stats_card.rb +26 -14
  223. data/lib/kiso/themes/switch.rb +56 -0
  224. data/lib/kiso/themes/table.rb +18 -15
  225. data/lib/kiso/themes/textarea.rb +33 -0
  226. data/lib/kiso/themes/toggle.rb +71 -0
  227. data/lib/kiso/themes/toggle_group.rb +13 -0
  228. data/lib/kiso/version.rb +4 -1
  229. data/lib/kiso.rb +70 -2
  230. metadata +183 -22
  231. data/app/views/kiso/components/empty_state/_content.html.erb +0 -7
  232. data/app/views/kiso/components/empty_state/_description.html.erb +0 -7
  233. data/app/views/kiso/components/empty_state/_header.html.erb +0 -7
  234. data/app/views/kiso/components/empty_state/_media.html.erb +0 -7
  235. data/app/views/kiso/components/empty_state/_title.html.erb +0 -7
  236. data/lib/kiso/themes/empty_state.rb +0 -42
@@ -0,0 +1,184 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { highlightItem, wrapIndex } from "kiso-ui/utils/highlight"
3
+
4
+ /**
5
+ * Command palette with search filtering, keyboard navigation, and item selection.
6
+ * Filters items by text content, hides empty groups, and dispatches a "select"
7
+ * event when an item is chosen.
8
+ *
9
+ * @example
10
+ * <div data-controller="kiso--command" data-slot="command">
11
+ * <input data-kiso--command-target="input"
12
+ * data-action="input->kiso--command#filter keydown->kiso--command#inputKeydown">
13
+ * <div data-kiso--command-target="list" role="listbox">
14
+ * <div data-kiso--command-target="empty" hidden>No results found.</div>
15
+ * <div data-kiso--command-target="group" role="group">
16
+ * <div data-kiso--command-target="item" data-value="calendar"
17
+ * data-action="click->kiso--command#selectItem" role="option">
18
+ * Calendar
19
+ * </div>
20
+ * </div>
21
+ * </div>
22
+ * </div>
23
+ *
24
+ * @property {HTMLInputElement} inputTarget - The search input field
25
+ * @property {HTMLElement} listTarget - Scrollable results container
26
+ * @property {HTMLElement} emptyTarget - "No results found" message
27
+ * @property {HTMLElement[]} groupTargets - Command groups
28
+ * @property {HTMLElement[]} itemTargets - Selectable command items
29
+ *
30
+ * @fires kiso--command:select - When an item is selected.
31
+ * Detail: `{ value: string, item: HTMLElement }`.
32
+ */
33
+ export default class extends Controller {
34
+ static targets = ["input", "list", "empty", "group", "item"]
35
+
36
+ connect() {
37
+ this._selectedIndex = -1
38
+
39
+ // Initialize: show all items, select first
40
+ this._updateVisibility()
41
+ }
42
+
43
+ /** Filters visible items based on the current input value. */
44
+ filter() {
45
+ this._updateVisibility()
46
+ }
47
+
48
+ /**
49
+ * Handles keyboard navigation on the input: ArrowDown/Up to move selection,
50
+ * Enter to select, Escape to bubble up for dialog to handle.
51
+ *
52
+ * @param {KeyboardEvent} event
53
+ */
54
+ inputKeydown(event) {
55
+ switch (event.key) {
56
+ case "ArrowDown":
57
+ event.preventDefault()
58
+ this._moveSelection(1)
59
+ break
60
+ case "ArrowUp":
61
+ event.preventDefault()
62
+ this._moveSelection(-1)
63
+ break
64
+ case "Enter":
65
+ event.preventDefault()
66
+ this._selectCurrent()
67
+ break
68
+ case "Escape":
69
+ event.preventDefault()
70
+ // Let it bubble up for dialog to handle
71
+ this.element.dispatchEvent(new CustomEvent("command:escape", { bubbles: true }))
72
+ break
73
+ case "Home":
74
+ event.preventDefault()
75
+ this._selectedIndex = 0
76
+ highlightItem(this.itemTargets, this._visibleEnabledItems, 0, { attr: "data-selected" })
77
+ break
78
+ case "End":
79
+ event.preventDefault()
80
+ {
81
+ const items = this._visibleEnabledItems
82
+ this._selectedIndex = items.length - 1
83
+ highlightItem(this.itemTargets, items, this._selectedIndex, { attr: "data-selected" })
84
+ }
85
+ break
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Selects an item when clicked.
91
+ *
92
+ * @param {Event} event - Click event from an item element
93
+ */
94
+ selectItem(event) {
95
+ const item = event.currentTarget
96
+ if (item.dataset.disabled === "true") return
97
+
98
+ const value = item.dataset.value
99
+ this.dispatch("select", { detail: { value, item } })
100
+ }
101
+
102
+ // --- Private ---
103
+
104
+ /**
105
+ * Filters items by the input query, hides empty groups, shows/hides
106
+ * the empty state, and resets selection to the first visible enabled item.
107
+ *
108
+ * @private
109
+ */
110
+ _updateVisibility() {
111
+ const query = this.hasInputTarget ? this.inputTarget.value.trim().toLowerCase() : ""
112
+
113
+ let visibleCount = 0
114
+ const enabledItems = []
115
+
116
+ this.itemTargets.forEach((item) => {
117
+ const text = item.textContent.trim().toLowerCase()
118
+ const matches = !query || text.includes(query)
119
+
120
+ item.hidden = !matches
121
+ if (matches) {
122
+ visibleCount++
123
+ if (item.dataset.disabled !== "true") {
124
+ enabledItems.push(item)
125
+ }
126
+ }
127
+ })
128
+
129
+ // Hide groups with no visible items
130
+ if (this.hasGroupTarget) {
131
+ this.groupTargets.forEach((group) => {
132
+ const visibleItems = group.querySelectorAll("[data-slot='command-item']:not([hidden])")
133
+ group.hidden = visibleItems.length === 0
134
+ })
135
+ }
136
+
137
+ // Show/hide empty state
138
+ if (this.hasEmptyTarget) {
139
+ this.emptyTarget.hidden = visibleCount > 0
140
+ }
141
+
142
+ // Reset selection to first visible enabled item
143
+ this._selectedIndex = enabledItems.length > 0 ? 0 : -1
144
+ highlightItem(this.itemTargets, enabledItems, this._selectedIndex, { attr: "data-selected" })
145
+ }
146
+
147
+ /**
148
+ * Moves the selection highlight by a given direction, wrapping at boundaries.
149
+ *
150
+ * @param {number} direction - +1 for next, -1 for previous
151
+ * @private
152
+ */
153
+ _moveSelection(direction) {
154
+ const items = this._visibleEnabledItems
155
+ if (items.length === 0) return
156
+
157
+ this._selectedIndex = wrapIndex(this._selectedIndex, direction, items.length)
158
+ highlightItem(this.itemTargets, items, this._selectedIndex, { attr: "data-selected" })
159
+ }
160
+
161
+ /**
162
+ * Dispatches a "select" event for the currently highlighted item.
163
+ *
164
+ * @private
165
+ */
166
+ _selectCurrent() {
167
+ const items = this._visibleEnabledItems
168
+ if (this._selectedIndex >= 0 && this._selectedIndex < items.length) {
169
+ const item = items[this._selectedIndex]
170
+ const value = item.dataset.value
171
+ this.dispatch("select", { detail: { value, item } })
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Returns visible, non-disabled items.
177
+ *
178
+ * @returns {HTMLElement[]}
179
+ * @private
180
+ */
181
+ get _visibleEnabledItems() {
182
+ return this.itemTargets.filter((item) => !item.hidden && item.dataset.disabled !== "true")
183
+ }
184
+ }
@@ -0,0 +1,104 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ /**
4
+ * Command dialog controller. Opens with a configurable keyboard shortcut
5
+ * (default: Cmd/Ctrl+K) and closes with Escape. Wraps the native `<dialog>` element.
6
+ *
7
+ * @example
8
+ * <dialog data-controller="kiso--command-dialog"
9
+ * data-kiso--command-dialog-shortcut-value="k"
10
+ * data-slot="command-dialog">
11
+ * <div>
12
+ * <div data-controller="kiso--command">...</div>
13
+ * </div>
14
+ * </dialog>
15
+ *
16
+ * @property {string} shortcutValue - The key paired with Cmd/Ctrl to open the dialog (default: "k")
17
+ */
18
+ export default class extends Controller {
19
+ static values = {
20
+ shortcut: { type: String, default: "k" },
21
+ }
22
+
23
+ connect() {
24
+ this._handleKeydown = this._handleKeydown.bind(this)
25
+ this._handleCommandEscape = this._handleCommandEscape.bind(this)
26
+ this._handleBackdropClick = this._handleBackdropClick.bind(this)
27
+ document.addEventListener("keydown", this._handleKeydown)
28
+ this.element.addEventListener("command:escape", this._handleCommandEscape)
29
+ this.element.addEventListener("click", this._handleBackdropClick)
30
+ }
31
+
32
+ disconnect() {
33
+ document.removeEventListener("keydown", this._handleKeydown)
34
+ this.element.removeEventListener("command:escape", this._handleCommandEscape)
35
+ this.element.removeEventListener("click", this._handleBackdropClick)
36
+ }
37
+
38
+ /**
39
+ * Opens the dialog as a modal, focuses the command input,
40
+ * clears its value, and resets the filter.
41
+ */
42
+ open() {
43
+ this.element.showModal()
44
+ // Focus the input inside the command palette
45
+ const input = this.element.querySelector("[data-slot='command-input']")
46
+ if (input) {
47
+ input.focus()
48
+ input.value = ""
49
+ // Trigger filter to reset visibility
50
+ input.dispatchEvent(new Event("input", { bubbles: true }))
51
+ }
52
+ }
53
+
54
+ /** Closes the dialog. */
55
+ close() {
56
+ this.element.close()
57
+ }
58
+
59
+ /**
60
+ * Handles the keyboard shortcut (Cmd/Ctrl + shortcutValue) to toggle
61
+ * the dialog, and Escape to close it.
62
+ *
63
+ * @param {KeyboardEvent} event
64
+ * @private
65
+ */
66
+ _handleKeydown(event) {
67
+ if (event.key === this.shortcutValue && (event.metaKey || event.ctrlKey)) {
68
+ event.preventDefault()
69
+ if (this.element.open) {
70
+ this.close()
71
+ } else {
72
+ this.open()
73
+ }
74
+ }
75
+
76
+ // Close on Escape when dialog is open
77
+ if (event.key === "Escape" && this.element.open) {
78
+ event.preventDefault()
79
+ this.close()
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Handles the custom "command:escape" event bubbled up from the
85
+ * command controller's input.
86
+ *
87
+ * @private
88
+ */
89
+ _handleCommandEscape() {
90
+ this.close()
91
+ }
92
+
93
+ /**
94
+ * Closes the dialog when clicking the backdrop (the `<dialog>` element itself).
95
+ *
96
+ * @param {MouseEvent} event
97
+ * @private
98
+ */
99
+ _handleBackdropClick(event) {
100
+ if (event.target === this.element) {
101
+ this.close()
102
+ }
103
+ }
104
+ }