ui-svelte 0.2.11 → 0.2.12

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 (204) hide show
  1. package/dist/charts/ArcChart.svelte +9 -13
  2. package/dist/charts/ArcChart.svelte.d.ts +3 -3
  3. package/dist/charts/AreaChart.svelte +347 -118
  4. package/dist/charts/AreaChart.svelte.d.ts +33 -4
  5. package/dist/charts/BarChart.svelte +288 -66
  6. package/dist/charts/BarChart.svelte.d.ts +26 -1
  7. package/dist/charts/Candlestick.svelte +53 -50
  8. package/dist/charts/Candlestick.svelte.d.ts +8 -8
  9. package/dist/charts/LineChart.svelte +391 -91
  10. package/dist/charts/LineChart.svelte.d.ts +26 -3
  11. package/dist/charts/PieChart.svelte +333 -92
  12. package/dist/charts/PieChart.svelte.d.ts +33 -5
  13. package/dist/charts/css/arc-chart.css +3 -3
  14. package/dist/charts/css/area-chart.css +127 -29
  15. package/dist/charts/css/bar-chart.css +114 -8
  16. package/dist/charts/css/candlestick.css +2 -0
  17. package/dist/charts/css/line-chart.css +111 -13
  18. package/dist/charts/css/pie-chart.css +92 -20
  19. package/dist/control/Audio.svelte +86 -44
  20. package/dist/control/Audio.svelte.d.ts +4 -1
  21. package/dist/control/Button.svelte +18 -27
  22. package/dist/control/Button.svelte.d.ts +3 -2
  23. package/dist/control/IconButton.svelte +17 -27
  24. package/dist/control/IconButton.svelte.d.ts +3 -3
  25. package/dist/control/Image.svelte +123 -0
  26. package/dist/control/Image.svelte.d.ts +13 -0
  27. package/dist/control/Record.svelte +144 -98
  28. package/dist/control/Record.svelte.d.ts +2 -1
  29. package/dist/control/ToggleGroup.svelte +22 -8
  30. package/dist/control/ToggleGroup.svelte.d.ts +2 -1
  31. package/dist/control/ToggleTheme.svelte +13 -11
  32. package/dist/control/ToggleTheme.svelte.d.ts +3 -2
  33. package/dist/control/Video.svelte +55 -29
  34. package/dist/control/Video.svelte.d.ts +1 -0
  35. package/dist/control/css/btn.css +200 -152
  36. package/dist/control/css/image.css +56 -0
  37. package/dist/control/css/media.css +95 -30
  38. package/dist/control/css/toggle-group.css +269 -84
  39. package/dist/control/css/video.css +1 -14
  40. package/dist/css/animations.css +5 -9
  41. package/dist/css/base.css +13 -347
  42. package/dist/css/decorations.css +402 -0
  43. package/dist/css/rich-text.css +485 -0
  44. package/dist/css/transitions.css +158 -0
  45. package/dist/css/typography.css +291 -0
  46. package/dist/display/Accordion.svelte +28 -4
  47. package/dist/display/Accordion.svelte.d.ts +2 -1
  48. package/dist/display/Alert.svelte +32 -12
  49. package/dist/display/Alert.svelte.d.ts +2 -3
  50. package/dist/display/Avatar.svelte +23 -18
  51. package/dist/display/Avatar.svelte.d.ts +4 -1
  52. package/dist/display/AvatarGroup.svelte +20 -18
  53. package/dist/display/AvatarGroup.svelte.d.ts +6 -3
  54. package/dist/display/Badge.svelte +11 -4
  55. package/dist/display/Badge.svelte.d.ts +2 -1
  56. package/dist/display/Card.svelte +15 -14
  57. package/dist/display/Card.svelte.d.ts +2 -3
  58. package/dist/display/Carousel.svelte +130 -99
  59. package/dist/display/Carousel.svelte.d.ts +6 -4
  60. package/dist/display/ChatBox.svelte +245 -106
  61. package/dist/display/ChatBox.svelte.d.ts +32 -5
  62. package/dist/display/Chip.svelte +31 -17
  63. package/dist/display/Chip.svelte.d.ts +3 -2
  64. package/dist/display/Code.svelte +6 -3
  65. package/dist/display/Code.svelte.d.ts +1 -0
  66. package/dist/display/Collapsible.svelte +30 -4
  67. package/dist/display/Collapsible.svelte.d.ts +2 -1
  68. package/dist/display/Empty.svelte +37 -3
  69. package/dist/display/Empty.svelte.d.ts +3 -0
  70. package/dist/display/Item.svelte +30 -11
  71. package/dist/display/Item.svelte.d.ts +2 -2
  72. package/dist/display/Map.svelte +488 -0
  73. package/dist/display/Map.svelte.d.ts +44 -0
  74. package/dist/display/Section.svelte +14 -12
  75. package/dist/display/Section.svelte.d.ts +2 -3
  76. package/dist/display/Skeleton.svelte +32 -0
  77. package/dist/display/Skeleton.svelte.d.ts +10 -0
  78. package/dist/display/Table.svelte +94 -132
  79. package/dist/display/Table.svelte.d.ts +10 -1
  80. package/dist/display/css/accordion.css +349 -52
  81. package/dist/display/css/alert.css +18 -25
  82. package/dist/display/css/avatar-group.css +38 -75
  83. package/dist/display/css/avatar.css +139 -121
  84. package/dist/display/css/badge.css +50 -27
  85. package/dist/display/css/card.css +51 -71
  86. package/dist/display/css/carousel.css +25 -5
  87. package/dist/display/css/chat-box.css +158 -26
  88. package/dist/display/css/chip.css +142 -68
  89. package/dist/display/css/code.css +2 -6
  90. package/dist/display/css/collapsible.css +349 -45
  91. package/dist/display/css/divider.css +8 -6
  92. package/dist/display/css/empty.css +7 -0
  93. package/dist/display/css/item.css +311 -89
  94. package/dist/display/css/map.css +164 -0
  95. package/dist/display/css/section.css +78 -33
  96. package/dist/display/css/skeleton.css +58 -0
  97. package/dist/display/css/table.css +320 -189
  98. package/dist/form/Checkbox.svelte +11 -5
  99. package/dist/form/Checkbox.svelte.d.ts +2 -1
  100. package/dist/form/ColorField.svelte +543 -0
  101. package/dist/form/ColorField.svelte.d.ts +29 -0
  102. package/dist/form/ComboBox.svelte +24 -9
  103. package/dist/form/ComboBox.svelte.d.ts +2 -2
  104. package/dist/form/CsvField.svelte +62 -136
  105. package/dist/form/CsvField.svelte.d.ts +2 -2
  106. package/dist/form/DateField.svelte +33 -15
  107. package/dist/form/DateField.svelte.d.ts +2 -1
  108. package/dist/form/DateRange.svelte +436 -0
  109. package/dist/form/DateRange.svelte.d.ts +24 -0
  110. package/dist/form/DragDrop.svelte +348 -0
  111. package/dist/form/DragDrop.svelte.d.ts +32 -0
  112. package/dist/form/Dropzone.svelte +28 -8
  113. package/dist/form/Dropzone.svelte.d.ts +2 -2
  114. package/dist/form/Editor.svelte +626 -0
  115. package/dist/form/Editor.svelte.d.ts +50 -0
  116. package/dist/form/ImageCropper.svelte +291 -61
  117. package/dist/form/ImageCropper.svelte.d.ts +15 -1
  118. package/dist/form/{PasswordStrength.svelte → PasswordField.svelte} +58 -24
  119. package/dist/form/{PasswordStrength.svelte.d.ts → PasswordField.svelte.d.ts} +6 -5
  120. package/dist/form/PhoneField.svelte +26 -14
  121. package/dist/form/PhoneField.svelte.d.ts +4 -3
  122. package/dist/form/PinField.svelte +39 -31
  123. package/dist/form/PinField.svelte.d.ts +3 -3
  124. package/dist/form/RadioGroup.svelte +4 -4
  125. package/dist/form/RadioGroup.svelte.d.ts +1 -1
  126. package/dist/form/Select.svelte +20 -19
  127. package/dist/form/Select.svelte.d.ts +2 -2
  128. package/dist/form/Slider.svelte +4 -2
  129. package/dist/form/Slider.svelte.d.ts +1 -0
  130. package/dist/form/TextField.svelte +16 -7
  131. package/dist/form/TextField.svelte.d.ts +2 -2
  132. package/dist/form/Textarea.svelte +15 -6
  133. package/dist/form/Textarea.svelte.d.ts +2 -2
  134. package/dist/form/Toggle.svelte +1 -1
  135. package/dist/form/css/checkbox.css +18 -2
  136. package/dist/form/css/color-field.css +141 -0
  137. package/dist/form/css/control.css +193 -82
  138. package/dist/form/css/csv-field.css +226 -0
  139. package/dist/form/css/date-range.css +122 -0
  140. package/dist/form/css/date.css +24 -2
  141. package/dist/form/css/drag-drop.css +271 -0
  142. package/dist/form/css/dropzone.css +153 -34
  143. package/dist/form/css/editor.css +367 -0
  144. package/dist/form/css/field.css +4 -0
  145. package/dist/form/css/image-cropper.css +223 -22
  146. package/dist/form/css/radio-group.css +1 -1
  147. package/dist/form/css/select.css +2 -2
  148. package/dist/form/css/slider.css +1 -0
  149. package/dist/form/css/textarea.css +178 -75
  150. package/dist/form/css/toggle.css +3 -3
  151. package/dist/hooks/use-table.svelte.d.ts +1 -0
  152. package/dist/hooks/use-table.svelte.js +6 -0
  153. package/dist/icons/index.d.ts +30 -2
  154. package/dist/icons/index.js +32 -4
  155. package/dist/index.css +16 -1
  156. package/dist/index.d.ts +12 -4
  157. package/dist/index.js +11 -3
  158. package/dist/layout/AppBar.svelte +22 -14
  159. package/dist/layout/AppBar.svelte.d.ts +2 -1
  160. package/dist/layout/Footer.svelte +19 -11
  161. package/dist/layout/Footer.svelte.d.ts +2 -1
  162. package/dist/layout/Provider.svelte +27 -4
  163. package/dist/layout/Provider.svelte.d.ts +3 -1
  164. package/dist/layout/css/app-bar.css +63 -66
  165. package/dist/layout/css/footer.css +62 -65
  166. package/dist/navigation/BottomNav.svelte +41 -13
  167. package/dist/navigation/FooterGroup.svelte +1 -1
  168. package/dist/navigation/NavMenu.svelte +47 -23
  169. package/dist/navigation/NavMenu.svelte.d.ts +29 -0
  170. package/dist/navigation/Pagination.svelte +158 -0
  171. package/dist/navigation/Pagination.svelte.d.ts +18 -0
  172. package/dist/navigation/SideNav.svelte +30 -25
  173. package/dist/navigation/SideNav.svelte.d.ts +2 -3
  174. package/dist/navigation/Tabs.svelte +17 -7
  175. package/dist/navigation/Tabs.svelte.d.ts +2 -2
  176. package/dist/navigation/css/bottom-nav.css +279 -257
  177. package/dist/navigation/css/footer-group.css +1 -1
  178. package/dist/navigation/css/footer-nav.css +1 -1
  179. package/dist/navigation/css/nav-menu.css +332 -106
  180. package/dist/navigation/css/pagination.css +74 -0
  181. package/dist/navigation/css/side-nav.css +515 -75
  182. package/dist/navigation/css/tabs.css +246 -52
  183. package/dist/overlay/Command.svelte +340 -0
  184. package/dist/overlay/Command.svelte.d.ts +24 -25
  185. package/dist/overlay/Drawer.svelte +49 -21
  186. package/dist/overlay/Drawer.svelte.d.ts +2 -2
  187. package/dist/overlay/Dropdown.svelte +3 -3
  188. package/dist/overlay/Modal.svelte +51 -16
  189. package/dist/overlay/Modal.svelte.d.ts +3 -3
  190. package/dist/overlay/Toast.svelte +41 -17
  191. package/dist/overlay/Toast.svelte.d.ts +1 -1
  192. package/dist/overlay/Tooltip.svelte +40 -26
  193. package/dist/overlay/Tooltip.svelte.d.ts +2 -2
  194. package/dist/overlay/css/command.css +80 -0
  195. package/dist/overlay/css/drawer.css +63 -24
  196. package/dist/overlay/css/dropdown.css +1 -1
  197. package/dist/overlay/css/hovercard.css +1 -1
  198. package/dist/overlay/css/modal.css +27 -27
  199. package/dist/overlay/css/toast.css +17 -29
  200. package/dist/overlay/css/tooltip.css +83 -66
  201. package/dist/stores/theme.svelte.js +26 -1
  202. package/dist/stores/toast.svelte.d.ts +4 -4
  203. package/dist/stores/toast.svelte.js +2 -2
  204. package/package.json +1 -1
@@ -19,95 +19,289 @@
19
19
  @apply flex-col;
20
20
  }
21
21
  }
22
+
22
23
  .tabs-list {
23
24
  @apply flex w-auto max-w-min overflow-x-auto scrollbar-hide gap-2 p-1 relative select-none;
25
+
26
+ .tab {
27
+ @apply flex flex-nowrap cursor-pointer px-4 py-1 relative transition-colors;
28
+ .tabs-icon {
29
+ @apply mr-2 shrink-0 h-6 w-auto;
30
+ }
31
+ .tabs-label {
32
+ @apply whitespace-nowrap;
33
+ }
34
+ }
35
+ }
36
+
37
+ .tabs-list.is-solid {
38
+ @apply rounded-xl py-1;
39
+ .tab {
40
+ @apply rounded-lg;
41
+ }
24
42
  &.is-primary {
25
- @apply bg-primary/10 text-on-surface rounded-xl py-1;
26
- .tab {
27
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative rounded-lg transition-colors;
28
- &.on-active {
29
- @apply bg-primary text-on-primary;
30
- }
43
+ @apply bg-primary/10 text-on-surface;
44
+ .tab.on-active {
45
+ @apply bg-primary text-on-primary;
31
46
  }
32
47
  }
33
48
  &.is-secondary {
34
- @apply bg-secondary/10 text-on-surface rounded-xl py-1;
35
- .tab {
36
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative rounded-lg transition-colors;
37
- &.on-active {
38
- @apply bg-secondary text-on-secondary;
39
- }
49
+ @apply bg-secondary/10 text-on-surface;
50
+ .tab.on-active {
51
+ @apply bg-secondary text-on-secondary;
40
52
  }
41
53
  }
42
54
  &.is-muted {
43
- @apply bg-muted text-on-muted rounded-xl py-1;
44
- .tab {
45
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative rounded-lg transition-colors;
46
- &.on-active {
47
- @apply bg-on-muted text-muted;
48
- }
55
+ @apply bg-muted text-on-muted;
56
+ .tab.on-active {
57
+ @apply bg-on-muted text-muted;
49
58
  }
50
59
  }
51
- &.is-outline {
52
- @apply border border-muted rounded-xl;
53
- .tab {
54
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative rounded-lg transition-colors;
55
- &.on-active {
56
- @apply bg-primary text-on-primary;
57
- }
60
+ &.is-success {
61
+ @apply bg-success/10 text-on-surface;
62
+ .tab.on-active {
63
+ @apply bg-success text-on-success;
58
64
  }
59
65
  }
60
- &.is-line {
61
- @apply gap-4;
62
- .tab {
63
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative;
64
- &.on-active {
65
- @apply text-primary border-b-2 border-primary;
66
- }
66
+ &.is-info {
67
+ @apply bg-info/10 text-on-surface;
68
+ .tab.on-active {
69
+ @apply bg-info text-on-info;
67
70
  }
68
71
  }
69
- &.is-ghost {
70
- .tab {
71
- @apply flex flex-nowrap cursor-pointer px-4 py-1 relative;
72
- &.on-active {
73
- @apply text-primary;
74
- }
72
+ &.is-warning {
73
+ @apply bg-warning/10 text-on-surface;
74
+ .tab.on-active {
75
+ @apply bg-warning text-on-warning;
75
76
  }
76
77
  }
78
+ &.is-danger {
79
+ @apply bg-danger/10 text-on-surface;
80
+ .tab.on-active {
81
+ @apply bg-danger text-on-danger;
82
+ }
83
+ }
84
+ }
85
+
86
+ .tabs-list.is-outline {
87
+ @apply border border-muted rounded-xl;
77
88
  .tab {
78
- .tabs-icon {
79
- @apply mr-2 shrink-0 h-6 w-auto;
89
+ @apply rounded-lg;
90
+ }
91
+ &.is-primary {
92
+ .tab.on-active {
93
+ @apply bg-primary text-on-primary;
80
94
  }
81
- .tabs-label {
82
- @apply whitespace-nowrap;
95
+ }
96
+ &.is-secondary {
97
+ .tab.on-active {
98
+ @apply bg-secondary text-on-secondary;
99
+ }
100
+ }
101
+ &.is-muted {
102
+ .tab.on-active {
103
+ @apply bg-on-muted text-muted;
104
+ }
105
+ }
106
+ &.is-success {
107
+ .tab.on-active {
108
+ @apply bg-success text-on-success;
109
+ }
110
+ }
111
+ &.is-info {
112
+ .tab.on-active {
113
+ @apply bg-info text-on-info;
114
+ }
115
+ }
116
+ &.is-warning {
117
+ .tab.on-active {
118
+ @apply bg-warning text-on-warning;
119
+ }
120
+ }
121
+ &.is-danger {
122
+ .tab.on-active {
123
+ @apply bg-danger text-on-danger;
83
124
  }
84
125
  }
85
126
  }
86
- &.is-top,
87
- &.is-bottom {
88
- .tabs-list {
89
- &.is-pill:not(.is-line) {
90
- @apply rounded-full;
91
- .tab {
92
- @apply rounded-full;
93
- }
127
+
128
+ .tabs-list.is-line {
129
+ @apply gap-4;
130
+ &.is-primary {
131
+ .tab.on-active {
132
+ @apply text-primary border-b-2 border-primary;
133
+ }
134
+ }
135
+ &.is-secondary {
136
+ .tab.on-active {
137
+ @apply text-secondary border-b-2 border-secondary;
138
+ }
139
+ }
140
+ &.is-muted {
141
+ .tab.on-active {
142
+ @apply text-on-muted border-b-2 border-on-muted;
143
+ }
144
+ }
145
+ &.is-success {
146
+ .tab.on-active {
147
+ @apply text-success border-b-2 border-success;
148
+ }
149
+ }
150
+ &.is-info {
151
+ .tab.on-active {
152
+ @apply text-info border-b-2 border-info;
153
+ }
154
+ }
155
+ &.is-warning {
156
+ .tab.on-active {
157
+ @apply text-warning border-b-2 border-warning;
158
+ }
159
+ }
160
+ &.is-danger {
161
+ .tab.on-active {
162
+ @apply text-danger border-b-2 border-danger;
163
+ }
164
+ }
165
+ }
166
+
167
+ .tabs-list.is-ghost {
168
+ &.is-primary {
169
+ .tab.on-active {
170
+ @apply text-primary;
171
+ }
172
+ }
173
+ &.is-secondary {
174
+ .tab.on-active {
175
+ @apply text-secondary;
176
+ }
177
+ }
178
+ &.is-muted {
179
+ .tab.on-active {
180
+ @apply text-on-muted;
181
+ }
182
+ }
183
+ &.is-success {
184
+ .tab.on-active {
185
+ @apply text-success;
186
+ }
187
+ }
188
+ &.is-info {
189
+ .tab.on-active {
190
+ @apply text-info;
191
+ }
192
+ }
193
+ &.is-warning {
194
+ .tab.on-active {
195
+ @apply text-warning;
196
+ }
197
+ }
198
+ &.is-danger {
199
+ .tab.on-active {
200
+ @apply text-danger;
201
+ }
202
+ }
203
+ }
204
+
205
+ .tabs-list.is-pills {
206
+ @apply rounded-full py-1;
207
+ .tab {
208
+ @apply rounded-full;
209
+ }
210
+ &.is-primary {
211
+ @apply bg-primary/10 text-on-surface;
212
+ .tab.on-active {
213
+ @apply bg-primary text-on-primary;
214
+ }
215
+ }
216
+ &.is-secondary {
217
+ @apply bg-secondary/10 text-on-surface;
218
+ .tab.on-active {
219
+ @apply bg-secondary text-on-secondary;
220
+ }
221
+ }
222
+ &.is-muted {
223
+ @apply bg-muted text-on-muted;
224
+ .tab.on-active {
225
+ @apply bg-on-muted text-muted;
226
+ }
227
+ }
228
+ &.is-success {
229
+ @apply bg-success/10 text-on-surface;
230
+ .tab.on-active {
231
+ @apply bg-success text-on-success;
232
+ }
233
+ }
234
+ &.is-info {
235
+ @apply bg-info/10 text-on-surface;
236
+ .tab.on-active {
237
+ @apply bg-info text-on-info;
238
+ }
239
+ }
240
+ &.is-warning {
241
+ @apply bg-warning/10 text-on-surface;
242
+ .tab.on-active {
243
+ @apply bg-warning text-on-warning;
244
+ }
245
+ }
246
+ &.is-danger {
247
+ @apply bg-danger/10 text-on-surface;
248
+ .tab.on-active {
249
+ @apply bg-danger text-on-danger;
94
250
  }
95
251
  }
96
252
  }
253
+
97
254
  &.is-start {
98
255
  .tabs-list.is-line {
99
- .tab.on-active {
256
+ &.is-primary .tab.on-active {
100
257
  @apply border-b-0 border-l-2 border-primary;
101
258
  }
259
+ &.is-secondary .tab.on-active {
260
+ @apply border-b-0 border-l-2 border-secondary;
261
+ }
262
+ &.is-muted .tab.on-active {
263
+ @apply border-b-0 border-l-2 border-on-muted;
264
+ }
265
+ &.is-success .tab.on-active {
266
+ @apply border-b-0 border-l-2 border-success;
267
+ }
268
+ &.is-info .tab.on-active {
269
+ @apply border-b-0 border-l-2 border-info;
270
+ }
271
+ &.is-warning .tab.on-active {
272
+ @apply border-b-0 border-l-2 border-warning;
273
+ }
274
+ &.is-danger .tab.on-active {
275
+ @apply border-b-0 border-l-2 border-danger;
276
+ }
102
277
  }
103
278
  }
104
279
  &.is-end {
105
280
  .tabs-list.is-line {
106
- .tab.on-active {
281
+ &.is-primary .tab.on-active {
107
282
  @apply border-b-0 border-r-2 border-primary;
108
283
  }
284
+ &.is-secondary .tab.on-active {
285
+ @apply border-b-0 border-r-2 border-secondary;
286
+ }
287
+ &.is-muted .tab.on-active {
288
+ @apply border-b-0 border-r-2 border-on-muted;
289
+ }
290
+ &.is-success .tab.on-active {
291
+ @apply border-b-0 border-r-2 border-success;
292
+ }
293
+ &.is-info .tab.on-active {
294
+ @apply border-b-0 border-r-2 border-info;
295
+ }
296
+ &.is-warning .tab.on-active {
297
+ @apply border-b-0 border-r-2 border-warning;
298
+ }
299
+ &.is-danger .tab.on-active {
300
+ @apply border-b-0 border-r-2 border-danger;
301
+ }
109
302
  }
110
303
  }
304
+
111
305
  .tabs-wrapper {
112
306
  @apply flex flex-1 overflow-hidden relative;
113
307
  .tabs-content {
@@ -0,0 +1,340 @@
1
+ <script lang="ts">
2
+ import type { SearchState, SearchOption } from '../hooks/use-search.svelte.js';
3
+ import { Search24RegularIcon, DotsMoveIcon } from '../icons/index.js';
4
+ import { Icon, Item, type IconData } from '../index.js';
5
+ import { cn } from '../utils/class-names.js';
6
+ import { popover } from '../utils/popover.js';
7
+ import { tick } from 'svelte';
8
+ import { fade, scale } from 'svelte/transition';
9
+
10
+ type CommandGroup = {
11
+ label: string;
12
+ options: SearchOption[];
13
+ };
14
+
15
+ type Props = {
16
+ search: SearchState;
17
+ open?: boolean;
18
+ placeholder?: string;
19
+ emptyText?: string;
20
+ loadingText?: string;
21
+ loadingMoreText?: string;
22
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
23
+ disableOverlayClose?: boolean;
24
+ disableGlobalShortcut?: boolean;
25
+ shortcut?: string;
26
+ showFooter?: boolean;
27
+ groups?: CommandGroup[];
28
+ onselect?: (item: SearchOption) => void;
29
+ onclose?: () => void;
30
+ class?: string;
31
+ };
32
+
33
+ let {
34
+ search,
35
+ open = $bindable(false),
36
+ placeholder = 'Search...',
37
+ emptyText = 'No results found',
38
+ loadingText = 'Loading...',
39
+ loadingMoreText = 'Loading more...',
40
+ color = 'muted',
41
+ disableOverlayClose = false,
42
+ disableGlobalShortcut = false,
43
+ shortcut = 'k',
44
+ showFooter = true,
45
+ groups,
46
+ onselect,
47
+ onclose,
48
+ class: className
49
+ }: Props = $props();
50
+
51
+ let openContent = $state(false);
52
+ let optionsEl = $state<HTMLElement>();
53
+ let searchInputEl = $state<HTMLInputElement>();
54
+ let focusedIndex = $state(-1);
55
+ let hasSearched = $state(false);
56
+
57
+ // Flatten options for keyboard navigation
58
+ const flatOptions = $derived(() => {
59
+ if (groups && groups.length > 0) {
60
+ return groups.flatMap((g) => g.options);
61
+ }
62
+ return search.options;
63
+ });
64
+
65
+ const scrollToItem = (index: number) => {
66
+ if (!optionsEl) return;
67
+ const items = Array.from(optionsEl.querySelectorAll('.item'));
68
+ const target = items[index] as HTMLElement;
69
+ if (!target) return;
70
+
71
+ target.scrollIntoView({ block: 'nearest' });
72
+ };
73
+
74
+ const handleKeyDown = (event: KeyboardEvent) => {
75
+ const items = flatOptions();
76
+ if (!items.length && event.key !== 'Escape') return;
77
+
78
+ switch (event.key) {
79
+ case 'ArrowDown':
80
+ event.preventDefault();
81
+ focusedIndex = (focusedIndex + 1) % items.length;
82
+ scrollToItem(focusedIndex);
83
+ break;
84
+ case 'ArrowUp':
85
+ event.preventDefault();
86
+ focusedIndex = (focusedIndex - 1 + items.length) % items.length;
87
+ scrollToItem(focusedIndex);
88
+ break;
89
+ case 'Enter': {
90
+ event.preventDefault();
91
+ if (focusedIndex >= 0 && focusedIndex < items.length) {
92
+ handleSelect(items[focusedIndex]);
93
+ }
94
+ break;
95
+ }
96
+ case 'Escape':
97
+ event.preventDefault();
98
+ handleClose();
99
+ break;
100
+ }
101
+ };
102
+
103
+ const handleGlobalKeyDown = (event: KeyboardEvent) => {
104
+ if (disableGlobalShortcut) return;
105
+
106
+ const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
107
+ const modifier = isMac ? event.metaKey : event.ctrlKey;
108
+
109
+ if (modifier && event.key.toLowerCase() === shortcut.toLowerCase()) {
110
+ event.preventDefault();
111
+ open = !open;
112
+ }
113
+ };
114
+
115
+ const handleScroll = async () => {
116
+ if (!optionsEl || !search.hasMore || search.isLoadingMore) return;
117
+
118
+ const { scrollTop, scrollHeight, clientHeight } = optionsEl;
119
+ const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;
120
+
121
+ if (scrollPercentage >= 0.8) {
122
+ await search.loadMore();
123
+ }
124
+ };
125
+
126
+ const handleSelect = (item: SearchOption) => {
127
+ if (item.href) {
128
+ open = false;
129
+ search.setSearch('');
130
+ } else {
131
+ onselect?.(item);
132
+ open = false;
133
+ search.setSearch('');
134
+ }
135
+ };
136
+
137
+ const handleOverlayClick = () => {
138
+ if (!disableOverlayClose) {
139
+ handleClose();
140
+ }
141
+ };
142
+
143
+ const handleClose = () => {
144
+ open = false;
145
+ focusedIndex = -1;
146
+ hasSearched = false;
147
+ search.setSearch('');
148
+ onclose?.();
149
+ };
150
+
151
+ $effect(() => {
152
+ if (search.search && search.search.length > 0) {
153
+ hasSearched = true;
154
+ }
155
+ });
156
+
157
+ $effect(() => {
158
+ if (open) {
159
+ setTimeout(() => {
160
+ openContent = true;
161
+ }, 20);
162
+ document.addEventListener('keydown', handleKeyDown);
163
+ } else {
164
+ openContent = false;
165
+ document.removeEventListener('keydown', handleKeyDown);
166
+ }
167
+
168
+ return () => {
169
+ document.removeEventListener('keydown', handleKeyDown);
170
+ };
171
+ });
172
+
173
+ $effect(() => {
174
+ if (openContent && searchInputEl) {
175
+ tick().then(() => {
176
+ searchInputEl?.focus();
177
+ focusedIndex = -1;
178
+ hasSearched = false;
179
+ });
180
+ }
181
+ });
182
+
183
+ $effect(() => {
184
+ if (!disableGlobalShortcut) {
185
+ document.addEventListener('keydown', handleGlobalKeyDown);
186
+ }
187
+
188
+ return () => {
189
+ document.removeEventListener('keydown', handleGlobalKeyDown);
190
+ };
191
+ });
192
+
193
+ const isMac =
194
+ typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
195
+ const modifierKey = isMac ? '⌘' : 'Ctrl';
196
+ </script>
197
+
198
+ {#if open}
199
+ <div transition:fade class="command-dialog" use:popover>
200
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
201
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
202
+ <div class="command-overlay" onclick={handleOverlayClick}></div>
203
+ {#if openContent}
204
+ <div in:scale={{ duration: 100 }} class={cn('command', className)}>
205
+ <div class="command-search">
206
+ <Icon icon={Search24RegularIcon} class="command-search-icon" />
207
+ <input
208
+ bind:this={searchInputEl}
209
+ type="text"
210
+ class="command-search-input"
211
+ {placeholder}
212
+ bind:value={search.search}
213
+ />
214
+ <div class="command-kbd">
215
+ <kbd>Esc</kbd>
216
+ <span>to close</span>
217
+ </div>
218
+ </div>
219
+
220
+ <div class="command-results" bind:this={optionsEl} onscroll={handleScroll}>
221
+ {#if search.isLoading}
222
+ <div class="command-loading">
223
+ <Icon icon={DotsMoveIcon} class="command-loading-spinner" />
224
+ <span>{loadingText}</span>
225
+ </div>
226
+ {:else if flatOptions().length === 0 && hasSearched}
227
+ <div class="command-empty">{emptyText}</div>
228
+ {:else if groups && groups.length > 0}
229
+ {#each groups as group}
230
+ {#if group.options.length > 0}
231
+ <div class="command-group">
232
+ <div class="command-group-label">{group.label}</div>
233
+ {#each group.options as item}
234
+ {@const globalIndex = flatOptions().indexOf(item)}
235
+ {#if item.href}
236
+ <Item
237
+ id={item.id}
238
+ label={item.label}
239
+ icon={item.icon as IconData}
240
+ src={item.src}
241
+ description={item.description}
242
+ href={item.href as string}
243
+ isFocused={focusedIndex === globalIndex}
244
+ isDisabled={item.disabled}
245
+ {color}
246
+ size="sm"
247
+ isCompact
248
+ onclick={() => handleSelect(item)}
249
+ />
250
+ {:else}
251
+ <Item
252
+ id={item.id}
253
+ label={item.label}
254
+ icon={item.icon as IconData}
255
+ src={item.src}
256
+ description={item.description}
257
+ isFocused={focusedIndex === globalIndex}
258
+ isDisabled={item.disabled}
259
+ {color}
260
+ size="sm"
261
+ isCompact
262
+ onclick={() => handleSelect(item)}
263
+ />
264
+ {/if}
265
+ {/each}
266
+ </div>
267
+ {/if}
268
+ {/each}
269
+
270
+ {#if search.isLoadingMore}
271
+ <div class="command-loading">
272
+ <Icon icon={DotsMoveIcon} class="command-loading-spinner" />
273
+ <span>{loadingMoreText}</span>
274
+ </div>
275
+ {/if}
276
+ {:else if search.options.length > 0}
277
+ {#each search.options as item, index}
278
+ {#if item.href}
279
+ <Item
280
+ id={item.id}
281
+ label={item.label}
282
+ icon={item.icon as IconData}
283
+ src={item.src}
284
+ description={item.description}
285
+ href={item.href as string}
286
+ isFocused={focusedIndex === index}
287
+ isDisabled={item.disabled}
288
+ {color}
289
+ size="sm"
290
+ isCompact
291
+ onclick={() => handleSelect(item)}
292
+ />
293
+ {:else}
294
+ <Item
295
+ id={item.id}
296
+ label={item.label}
297
+ icon={item.icon as IconData}
298
+ src={item.src}
299
+ description={item.description}
300
+ isFocused={focusedIndex === index}
301
+ isDisabled={item.disabled}
302
+ {color}
303
+ size="sm"
304
+ isCompact
305
+ onclick={() => handleSelect(item)}
306
+ />
307
+ {/if}
308
+ {/each}
309
+
310
+ {#if search.isLoadingMore}
311
+ <div class="command-loading">
312
+ <Icon icon={DotsMoveIcon} class="command-loading-spinner" />
313
+ <span>{loadingMoreText}</span>
314
+ </div>
315
+ {/if}
316
+ {/if}
317
+ </div>
318
+
319
+ {#if showFooter}
320
+ <div class="command-footer">
321
+ <div class="command-shortcut">
322
+ <div class="command-shortcut-item">
323
+ <kbd>↑</kbd><kbd>↓</kbd>
324
+ <span>Navigate</span>
325
+ </div>
326
+ <div class="command-shortcut-item">
327
+ <kbd>↵</kbd>
328
+ <span>Select</span>
329
+ </div>
330
+ </div>
331
+ <div class="command-shortcut-item">
332
+ <kbd>{modifierKey}</kbd><kbd>{shortcut.toUpperCase()}</kbd>
333
+ <span>Toggle</span>
334
+ </div>
335
+ </div>
336
+ {/if}
337
+ </div>
338
+ {/if}
339
+ </div>
340
+ {/if}