@panneau/ui 5.0.0-alpha.4 → 5.0.0-alpha.6

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 (121) hide show
  1. package/README.md +55 -2
  2. package/dist/command-palette/CommandPalette.css +299 -0
  3. package/dist/command-palette/CommandPalette.d.ts +37 -0
  4. package/dist/command-palette/CommandPalette.d.ts.map +1 -0
  5. package/dist/command-palette/CommandPalette.js +194 -0
  6. package/dist/command-palette/CommandPalette.js.map +1 -0
  7. package/dist/command-palette/CommandPalette.module.js +62 -0
  8. package/dist/command-palette/CommandPalette.module.js.map +1 -0
  9. package/dist/command-palette/match.d.ts +39 -0
  10. package/dist/command-palette/match.d.ts.map +1 -0
  11. package/dist/command-palette/match.js +44 -0
  12. package/dist/command-palette/match.js.map +1 -0
  13. package/dist/command-palette/tokens.d.ts +3 -0
  14. package/dist/command-palette/tokens.d.ts.map +1 -0
  15. package/dist/command-palette/tokens.js +23 -0
  16. package/dist/command-palette/tokens.js.map +1 -0
  17. package/dist/command-palette/useCommandShortcut.d.ts +12 -0
  18. package/dist/command-palette/useCommandShortcut.d.ts.map +1 -0
  19. package/dist/command-palette/useCommandShortcut.js +29 -0
  20. package/dist/command-palette/useCommandShortcut.js.map +1 -0
  21. package/dist/date-picker/DatePicker.css +40 -0
  22. package/dist/date-picker/DatePicker.d.ts +2 -1
  23. package/dist/date-picker/DatePicker.d.ts.map +1 -1
  24. package/dist/date-picker/DatePicker.js +54 -8
  25. package/dist/date-picker/DatePicker.js.map +1 -1
  26. package/dist/date-picker/DatePicker.module.js +5 -1
  27. package/dist/date-picker/DatePicker.module.js.map +1 -1
  28. package/dist/dialog/Dialog.css +36 -2
  29. package/dist/dialog/Dialog.d.ts +15 -3
  30. package/dist/dialog/Dialog.d.ts.map +1 -1
  31. package/dist/dialog/Dialog.js +22 -14
  32. package/dist/dialog/Dialog.js.map +1 -1
  33. package/dist/dialog/Dialog.module.js +8 -2
  34. package/dist/dialog/Dialog.module.js.map +1 -1
  35. package/dist/dialog/tokens.d.ts +1 -1
  36. package/dist/dialog/tokens.d.ts.map +1 -1
  37. package/dist/dialog/tokens.js +2 -0
  38. package/dist/dialog/tokens.js.map +1 -1
  39. package/dist/field/Field.css +9 -0
  40. package/dist/field/Field.module.js.map +1 -1
  41. package/dist/index.d.ts +9 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +10 -3
  44. package/dist/menu/Menu.css +84 -0
  45. package/dist/menu/Menu.d.ts +33 -6
  46. package/dist/menu/Menu.d.ts.map +1 -1
  47. package/dist/menu/Menu.js +110 -25
  48. package/dist/menu/Menu.js.map +1 -1
  49. package/dist/menu/Menu.module.js +22 -0
  50. package/dist/menu/Menu.module.js.map +1 -0
  51. package/dist/menu/tokens.d.ts +3 -0
  52. package/dist/menu/tokens.d.ts.map +1 -0
  53. package/dist/menu/tokens.js +31 -0
  54. package/dist/menu/tokens.js.map +1 -0
  55. package/dist/number-input/NumberInput.css +18 -0
  56. package/dist/number-input/NumberInput.js +1 -1
  57. package/dist/number-input/NumberInput.js.map +1 -1
  58. package/dist/number-input/NumberInput.module.js +3 -1
  59. package/dist/number-input/NumberInput.module.js.map +1 -1
  60. package/dist/pill-trigger/PillTrigger.css +104 -0
  61. package/dist/pill-trigger/PillTrigger.d.ts +20 -0
  62. package/dist/pill-trigger/PillTrigger.d.ts.map +1 -0
  63. package/dist/pill-trigger/PillTrigger.js +38 -0
  64. package/dist/pill-trigger/PillTrigger.js.map +1 -0
  65. package/dist/pill-trigger/PillTrigger.module.js +18 -0
  66. package/dist/pill-trigger/PillTrigger.module.js.map +1 -0
  67. package/dist/pill-trigger/tokens.d.ts +3 -0
  68. package/dist/pill-trigger/tokens.d.ts.map +1 -0
  69. package/dist/pill-trigger/tokens.js +37 -0
  70. package/dist/pill-trigger/tokens.js.map +1 -0
  71. package/dist/radio-group/RadioGroup.css +90 -0
  72. package/dist/radio-group/RadioGroup.d.ts +22 -3
  73. package/dist/radio-group/RadioGroup.d.ts.map +1 -1
  74. package/dist/radio-group/RadioGroup.js +42 -21
  75. package/dist/radio-group/RadioGroup.js.map +1 -1
  76. package/dist/radio-group/RadioGroup.module.js +12 -2
  77. package/dist/radio-group/RadioGroup.module.js.map +1 -1
  78. package/dist/radio-group/tokens.d.ts +2 -2
  79. package/dist/radio-group/tokens.d.ts.map +1 -1
  80. package/dist/radio-group/tokens.js +22 -2
  81. package/dist/radio-group/tokens.js.map +1 -1
  82. package/dist/segmented-control/SegmentedControl.css +83 -0
  83. package/dist/segmented-control/SegmentedControl.d.ts +35 -0
  84. package/dist/segmented-control/SegmentedControl.d.ts.map +1 -0
  85. package/dist/segmented-control/SegmentedControl.js +44 -0
  86. package/dist/segmented-control/SegmentedControl.js.map +1 -0
  87. package/dist/segmented-control/SegmentedControl.module.js +16 -0
  88. package/dist/segmented-control/SegmentedControl.module.js.map +1 -0
  89. package/dist/segmented-control/tokens.d.ts +3 -0
  90. package/dist/segmented-control/tokens.d.ts.map +1 -0
  91. package/dist/segmented-control/tokens.js +29 -0
  92. package/dist/segmented-control/tokens.js.map +1 -0
  93. package/dist/select/ListBox.css +23 -1
  94. package/dist/select/ListBox.module.js +2 -2
  95. package/dist/select/ListBox.module.js.map +1 -1
  96. package/dist/select/listBoxTokens.d.ts +1 -1
  97. package/dist/select/listBoxTokens.d.ts.map +1 -1
  98. package/dist/select/listBoxTokens.js +2 -0
  99. package/dist/select/listBoxTokens.js.map +1 -1
  100. package/dist/slider/Slider.css +84 -0
  101. package/dist/slider/Slider.d.ts +20 -0
  102. package/dist/slider/Slider.d.ts.map +1 -0
  103. package/dist/slider/Slider.js +70 -0
  104. package/dist/slider/Slider.js.map +1 -0
  105. package/dist/slider/Slider.module.js +20 -0
  106. package/dist/slider/Slider.module.js.map +1 -0
  107. package/dist/tags-input/TagsInput.css +60 -0
  108. package/dist/tags-input/TagsInput.d.ts +32 -0
  109. package/dist/tags-input/TagsInput.d.ts.map +1 -0
  110. package/dist/tags-input/TagsInput.js +160 -0
  111. package/dist/tags-input/TagsInput.js.map +1 -0
  112. package/dist/tags-input/TagsInput.module.js +20 -0
  113. package/dist/tags-input/TagsInput.module.js.map +1 -0
  114. package/dist/text-input/TextInput.d.ts.map +1 -1
  115. package/dist/text-input/TextInput.js +2 -0
  116. package/dist/text-input/TextInput.js.map +1 -1
  117. package/dist/theme/components.d.ts +7 -3
  118. package/dist/theme/components.d.ts.map +1 -1
  119. package/dist/theme/components.js +8 -0
  120. package/dist/theme/components.js.map +1 -1
  121. package/package.json +3 -3
package/README.md CHANGED
@@ -290,11 +290,64 @@ The theme model is described in [decision 0024](../../docs/decisions/0024-theme-
290
290
 
291
291
  ## Primitives
292
292
 
293
- `Heading`, `Text`, `Prose`, `Stack`, `Inline`, `Grid`, `Container`, `Divider`, `Button`, `IconButton`, `TextInput`, `TextArea`, `NumberInput`, `Select` (single or multiple), `ComboBox`, `Checkbox`, `Switch`, `RadioGroup`, `DatePicker`, `TimeInput`, `Fieldset`, `Dialog`, `Popover`, `Menu`, `Tabs`, `Tooltip`, toasts (`useToast()`), `Alert`, `Badge`, `Spinner`, `Card`, `Disclosure` and `Icon`.
293
+ `Heading`, `Text`, `Prose`, `Stack`, `Inline`, `Grid`, `Container`, `Divider`, `Button`, `IconButton`, `TextInput`, `TextArea`, `NumberInput`, `Slider`, `TagsInput`, `Select` (single or multiple), `ComboBox`, `Checkbox`, `Switch`, `RadioGroup`, `SegmentedControl`, `DatePicker`, `TimeInput`, `Fieldset`, `Dialog`, `Popover`, `Menu`, `PillTrigger`, `Tabs`, `Tooltip`, toasts (`useToast()`), `Alert`, `Badge`, `Spinner`, `Card`, `Disclosure` and `Icon`.
294
294
 
295
295
  - Every component accepts `className`, and `xxxClassName` props for its inner elements (`labelClassName`, `inputClassName`…).
296
296
  - Labelled controls take `label`, `description` and `errors` (a list of messages: any message makes the control invalid and describes it).
297
- - They work on phones: larger touch targets on touch screens, dialogs as bottom sheets, scrolling tabs.
297
+ - They work on phones: larger touch targets on touch screens, dialogs as bottom sheets (or the whole screen, with `fullScreenOnPhone`), scrolling tabs.
298
+
299
+ ### Rich choices
300
+
301
+ Picking one item among a few rich ones, such as an account (an avatar, a name, a handle), a plan or a template, takes a menu or radio cards.
302
+
303
+ - **`Menu`** groups its items in `MenuSection`s, whose `title` is a small muted eyebrow that labels the group, and takes a `footer`: a short note under the items, after a line, which describes the menu and can't be focused. With `selectionMode` (`single` or `multiple`) and `selectedKeys`, its items are radio or checkbox items, and the selected ones have a check at their end and a sunken background.
304
+ - **`MenuItem`** takes a leading `media` (an avatar or an image) instead of an `icon`: larger when the item has a `description`, and clipped to a circle by default (`--panneau-menu-media-radius`).
305
+ - **`PillTrigger`** is a compact rounded button that shows the current value, with an optional leading `media` and a chevron, for a `MenuTrigger` or a `PopoverTrigger`. Its accessible name is its value: give it an `aria-label` that contains the value when the value alone doesn't say what it picks.
306
+
307
+ ```tsx
308
+ <MenuTrigger>
309
+ <PillTrigger size="sm" media={<Avatar user={account} />}>
310
+ {account.name}
311
+ </PillTrigger>
312
+ <Menu
313
+ aria-label="Accounts"
314
+ selectionMode="single"
315
+ selectedKeys={[account.id]}
316
+ onSelectionChange={(keys) => keys !== 'all' && onChange([...keys][0])}
317
+ footer="Only this step changes."
318
+ >
319
+ <MenuSection title="Publish on">
320
+ {accounts.map((item) => (
321
+ <MenuItem
322
+ key={item.id}
323
+ id={item.id}
324
+ media={<Avatar user={item} />}
325
+ description={item.handle}
326
+ >
327
+ {item.name}
328
+ </MenuItem>
329
+ ))}
330
+ </MenuSection>
331
+ </Menu>
332
+ </MenuTrigger>
333
+ ```
334
+
335
+ - **`RadioGroup`** with `appearance="cards"` shows its options as bordered cards in a grid of `columns` (2 by default): each card has the radio, the option's `media`, its label and its description, and the selected card has a stronger border. The grid is a size container of its own, and becomes a single column when it is narrower than 28rem. Keyboard and focus work as in a list of radios.
336
+
337
+ ```tsx
338
+ <RadioGroup
339
+ label="Publish on"
340
+ appearance="cards"
341
+ options={accounts.map((item) => ({
342
+ value: item.id,
343
+ label: item.name,
344
+ description: item.handle,
345
+ media: <Avatar user={item} />,
346
+ }))}
347
+ value={accountId}
348
+ onChange={setAccountId}
349
+ />
350
+ ```
298
351
 
299
352
  ## Icons
300
353
 
@@ -0,0 +1,299 @@
1
+ /* A dialog near the top of the screen: the search field over the results, and a footer with the
2
+ * count and the keys. The whole screen on a phone. */
3
+
4
+ .panneau-command-palette-overlay {
5
+ position: fixed;
6
+ inset: 0;
7
+ z-index: var(--panneau-z-overlay);
8
+ display: flex;
9
+ align-items: flex-start;
10
+ justify-content: center;
11
+ padding: var(--panneau-command-palette-offset-top, 12dvh) var(--panneau-space-4)
12
+ var(--panneau-space-4);
13
+ background-color: var(--panneau-color-backdrop);
14
+ }
15
+
16
+ .panneau-command-palette-overlay[data-entering] {
17
+ animation: panneau-command-palette-fade var(--panneau-duration) ease-out;
18
+ }
19
+
20
+ .panneau-command-palette-modal {
21
+ display: flex;
22
+ width: 100%;
23
+ max-width: var(--panneau-command-palette-width, 40rem);
24
+ outline: none;
25
+ }
26
+
27
+ .panneau-command-palette-modal[data-entering] {
28
+ animation: panneau-command-palette-rise var(--panneau-duration) ease-out;
29
+ }
30
+
31
+ .panneau-command-palette-dialog {
32
+ display: flex;
33
+ flex-direction: column;
34
+ width: 100%;
35
+ overflow: hidden;
36
+ color: var(--panneau-color-text);
37
+ background-color: var(--panneau-command-palette-bg, var(--panneau-color-surface-raised));
38
+ border: var(--panneau-border-width) solid var(--panneau-color-border);
39
+ border-radius: var(--panneau-command-palette-radius, var(--panneau-radius-lg));
40
+ box-shadow: var(--panneau-shadow-lg);
41
+ outline: none;
42
+ }
43
+
44
+ .panneau-command-palette-field {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: var(--panneau-space-2);
48
+ padding: var(--panneau-space-3) var(--panneau-space-4);
49
+ border-bottom: var(--panneau-border-width) solid var(--panneau-color-border);
50
+ }
51
+
52
+ .panneau-command-palette-search {
53
+ display: flex;
54
+ flex: 1 1 auto;
55
+ align-items: center;
56
+ gap: var(--panneau-space-2);
57
+ min-width: 0;
58
+ }
59
+
60
+ .panneau-command-palette-close {
61
+ display: none;
62
+ }
63
+
64
+ .panneau-command-palette-field-icon {
65
+ flex: none;
66
+ color: var(--panneau-color-text-muted);
67
+ }
68
+
69
+ /* What the search is narrowed to, before the field. */
70
+ .panneau-command-palette-scope {
71
+ display: inline-flex;
72
+ flex: none;
73
+ align-items: center;
74
+ gap: var(--panneau-space-1);
75
+ max-width: 50%;
76
+ padding: 0 var(--panneau-space-1) 0 var(--panneau-space-2);
77
+ border-radius: var(--panneau-radius-full);
78
+ background-color: var(--panneau-color-surface-sunken);
79
+ font-size: var(--panneau-font-size-sm);
80
+ font-weight: var(--panneau-font-weight-medium);
81
+ }
82
+
83
+ .panneau-command-palette-scope-label {
84
+ overflow: hidden;
85
+ text-overflow: ellipsis;
86
+ white-space: nowrap;
87
+ }
88
+
89
+ .panneau-command-palette-scope-remove {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ padding: var(--panneau-space-1);
94
+ border: 0;
95
+ border-radius: var(--panneau-radius-full);
96
+ background: none;
97
+ color: var(--panneau-color-text-muted);
98
+ cursor: pointer;
99
+ outline: none;
100
+ }
101
+
102
+ .panneau-command-palette-scope-remove[data-hovered] {
103
+ color: var(--panneau-color-text);
104
+ }
105
+
106
+ .panneau-command-palette-scope-remove[data-focus-visible] {
107
+ outline: var(--panneau-focus-width) solid var(--panneau-color-focus);
108
+ }
109
+
110
+ .panneau-command-palette-input {
111
+ flex: 1 1 auto;
112
+ min-width: 0;
113
+ padding: var(--panneau-space-1) 0;
114
+ border: 0;
115
+ background: none;
116
+ color: inherit;
117
+ font: inherit;
118
+ font-size: var(--panneau-font-size-lg);
119
+ outline: none;
120
+ }
121
+
122
+ .panneau-command-palette-input::placeholder {
123
+ color: var(--panneau-color-text-muted);
124
+ }
125
+
126
+ /* The browser's own clear button: Escape clears the field. */
127
+ .panneau-command-palette-input::-webkit-search-cancel-button {
128
+ appearance: none;
129
+ }
130
+
131
+ .panneau-command-palette-list {
132
+ display: flex;
133
+ flex-direction: column;
134
+ gap: var(--panneau-space-2);
135
+ max-height: var(--panneau-command-palette-list-max-height, 60dvh);
136
+ padding: var(--panneau-space-2);
137
+ overflow-y: auto;
138
+ outline: none;
139
+ }
140
+
141
+ .panneau-command-palette-group {
142
+ display: flex;
143
+ flex-direction: column;
144
+ }
145
+
146
+ .panneau-command-palette-group-label {
147
+ padding: var(--panneau-space-1) var(--panneau-space-2);
148
+ color: var(--panneau-color-text-muted);
149
+ font-size: var(--panneau-font-size-xs);
150
+ font-weight: var(--panneau-font-weight-medium);
151
+ letter-spacing: 0.04em;
152
+ text-transform: uppercase;
153
+ }
154
+
155
+ .panneau-command-palette-item {
156
+ display: flex;
157
+ align-items: center;
158
+ gap: var(--panneau-space-3);
159
+ min-height: var(--panneau-control-height);
160
+ padding: var(--panneau-space-1) var(--panneau-space-2);
161
+ border-radius: var(--panneau-command-palette-item-radius, var(--panneau-radius-md));
162
+ color: var(--panneau-color-text);
163
+ text-decoration: none;
164
+ cursor: pointer;
165
+ outline: none;
166
+ }
167
+
168
+ .panneau-command-palette-item[data-focused],
169
+ .panneau-command-palette-item[data-hovered] {
170
+ background-color: var(--panneau-command-palette-item-active-bg, var(--panneau-color-hover));
171
+ }
172
+
173
+ .panneau-command-palette-item[data-focus-visible] {
174
+ box-shadow: inset 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);
175
+ }
176
+
177
+ .panneau-command-palette-icon,
178
+ .panneau-command-palette-image {
179
+ display: inline-grid;
180
+ flex: none;
181
+ place-items: center;
182
+ width: 2rem;
183
+ height: 2rem;
184
+ border-radius: var(--panneau-radius-sm);
185
+ color: var(--panneau-color-text-muted);
186
+ background-color: var(--panneau-color-surface-sunken);
187
+ object-fit: cover;
188
+ }
189
+
190
+ .panneau-command-palette-text {
191
+ display: flex;
192
+ flex-direction: column;
193
+ min-width: 0;
194
+ }
195
+
196
+ .panneau-command-palette-label,
197
+ .panneau-command-palette-description {
198
+ overflow: hidden;
199
+ text-overflow: ellipsis;
200
+ white-space: nowrap;
201
+ }
202
+
203
+ .panneau-command-palette-description {
204
+ color: var(--panneau-color-text-muted);
205
+ font-size: var(--panneau-font-size-sm);
206
+ }
207
+
208
+ .panneau-command-palette-empty {
209
+ margin: 0;
210
+ padding: var(--panneau-space-6) var(--panneau-space-2);
211
+ color: var(--panneau-color-text-muted);
212
+ text-align: center;
213
+ }
214
+
215
+ .panneau-command-palette-footer {
216
+ display: flex;
217
+ flex-wrap: wrap;
218
+ align-items: center;
219
+ justify-content: space-between;
220
+ gap: var(--panneau-space-2);
221
+ padding: var(--panneau-space-2) var(--panneau-space-4);
222
+ border-top: var(--panneau-border-width) solid var(--panneau-color-border);
223
+ color: var(--panneau-color-text-muted);
224
+ font-size: var(--panneau-font-size-xs);
225
+ }
226
+
227
+ .panneau-command-palette-hints {
228
+ display: flex;
229
+ gap: var(--panneau-space-3);
230
+ }
231
+
232
+ .panneau-command-palette-hint {
233
+ display: inline-flex;
234
+ align-items: center;
235
+ gap: var(--panneau-space-1);
236
+ }
237
+
238
+ .panneau-command-palette-key {
239
+ display: inline-grid;
240
+ place-items: center;
241
+ min-width: 1.25rem;
242
+ height: 1.25rem;
243
+ padding: 0 var(--panneau-space-1);
244
+ border: var(--panneau-border-width) solid var(--panneau-color-border);
245
+ border-radius: var(--panneau-radius-sm);
246
+ background: var(--panneau-color-surface);
247
+ font-family: inherit;
248
+ font-size: var(--panneau-font-size-xs);
249
+ }
250
+
251
+ @keyframes panneau-command-palette-fade {
252
+ from {
253
+ opacity: 0;
254
+ }
255
+ }
256
+
257
+ @keyframes panneau-command-palette-rise {
258
+ from {
259
+ transform: translateY(-0.5rem);
260
+ opacity: 0;
261
+ }
262
+ }
263
+
264
+ /* A phone: the whole screen, without the keys, which a touch screen has no use for. */
265
+ @container panneau-root (max-width: 40rem) {
266
+ .panneau-command-palette-overlay {
267
+ padding: 0;
268
+ }
269
+
270
+ .panneau-command-palette-modal {
271
+ max-width: none;
272
+ height: 100dvh;
273
+ }
274
+
275
+ .panneau-command-palette-dialog {
276
+ border: 0;
277
+ border-radius: 0;
278
+ }
279
+
280
+ .panneau-command-palette-list {
281
+ flex: 1 1 auto;
282
+ max-height: none;
283
+ }
284
+
285
+ .panneau-command-palette-hints {
286
+ display: none;
287
+ }
288
+
289
+ .panneau-command-palette-close {
290
+ display: inline-flex;
291
+ }
292
+ }
293
+
294
+ @media (prefers-reduced-motion: reduce) {
295
+ .panneau-command-palette-overlay[data-entering],
296
+ .panneau-command-palette-modal[data-entering] {
297
+ animation: none;
298
+ }
299
+ }
@@ -0,0 +1,37 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type CommandPaletteGroup } from './match';
3
+ export interface CommandPaletteProps {
4
+ isOpen: boolean;
5
+ onOpenChange: (isOpen: boolean) => void;
6
+ /**
7
+ * The groups of items. The palette matches them with the query itself, except groups that
8
+ * are already `matched`, such as results of a server, which come after the others.
9
+ */
10
+ groups: readonly CommandPaletteGroup[];
11
+ /** The query, controlled: to search a server with it. Empty each time the palette opens. */
12
+ inputValue?: string;
13
+ onInputChange?: (value: string) => void;
14
+ /** Accessible name of the palette and its field. Defaults to "Search". */
15
+ 'aria-label'?: string;
16
+ placeholder?: string;
17
+ /** A line under the results, and in place of them when there are none: loading or failed
18
+ * results of a server, a query too short to search. */
19
+ status?: ReactNode;
20
+ /**
21
+ * What the search is narrowed to, such as one resource, shown as a chip before the field.
22
+ * Backspace in the empty field, or the chip's button, calls `onScopeRemove`.
23
+ */
24
+ scope?: string | null;
25
+ onScopeRemove?: () => void;
26
+ className?: string;
27
+ }
28
+ /**
29
+ * A command palette: a search field over groups of items to go to or run, in a dialog near the top
30
+ * of the screen. Typing matches the items without case or accents, every word having to match,
31
+ * the best matches first. Arrow keys move through the items, Enter opens one and closes the
32
+ * palette, Escape clears the field, then closes it. A `scope` narrows what it searches, as a chip
33
+ * before the field. It knows no data or router: links go through
34
+ * the app's `RouterProvider`. `useCommandShortcut()` opens it with ⌘K or Ctrl+K.
35
+ */
36
+ export declare function CommandPalette({ isOpen, onOpenChange, groups, inputValue: controlledValue, onInputChange, 'aria-label': ariaLabel, placeholder, status, scope, onScopeRemove, className, }: CommandPaletteProps): import("react").JSX.Element | null;
37
+ //# sourceMappingURL=CommandPalette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.d.ts","sourceRoot":"","sources":["../../src/command-palette/CommandPalette.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAuBjD,OAAO,EAAE,KAAK,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvC,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;2DACuD;IACvD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,YAAY,EACZ,MAAM,EACN,UAAU,EAAE,eAAe,EAC3B,aAAa,EACb,YAAY,EAAE,SAAS,EACvB,WAAW,EACX,MAAM,EACN,KAAY,EACZ,aAAa,EACb,SAAS,GACZ,EAAE,mBAAmB,sCA2KrB"}
@@ -0,0 +1,194 @@
1
+ import { cx } from "../utils/cx.js";
2
+ import { Icon } from "../icon/Icon.js";
3
+ import { messages } from "../intl/messages.js";
4
+ import { usePanneauIntl } from "../intl/usePanneauIntl.js";
5
+ import { usePortalContainer } from "../root/PanneauRoot.js";
6
+ import { IconButton } from "../icon-button/IconButton.js";
7
+ import CommandPalette_module_default from "./CommandPalette.module.js";
8
+ import { matchCommands } from "./match.js";
9
+ import { ArrowDownUp, CornerDownLeft, Search, X } from "lucide-react";
10
+ import { Autocomplete, Button, Dialog, Header, Input, Menu, MenuItem, MenuSection, Modal, ModalOverlay, SearchField, Text } from "react-aria-components";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { useState } from "react";
13
+ //#region src/command-palette/CommandPalette.tsx
14
+ /**
15
+ * A command palette: a search field over groups of items to go to or run, in a dialog near the top
16
+ * of the screen. Typing matches the items without case or accents, every word having to match,
17
+ * the best matches first. Arrow keys move through the items, Enter opens one and closes the
18
+ * palette, Escape clears the field, then closes it. A `scope` narrows what it searches, as a chip
19
+ * before the field. It knows no data or router: links go through
20
+ * the app's `RouterProvider`. `useCommandShortcut()` opens it with ⌘K or Ctrl+K.
21
+ */
22
+ function CommandPalette({ isOpen, onOpenChange, groups, inputValue: controlledValue, onInputChange, "aria-label": ariaLabel, placeholder, status, scope = null, onScopeRemove, className }) {
23
+ const intl = usePanneauIntl();
24
+ const container = usePortalContainer();
25
+ const [uncontrolledValue, setUncontrolledValue] = useState("");
26
+ const value = controlledValue ?? uncontrolledValue;
27
+ const setValue = (next) => {
28
+ setUncontrolledValue(next);
29
+ onInputChange?.(next);
30
+ };
31
+ const label = ariaLabel ?? intl.formatMessage(messages.commandPalette);
32
+ const visible = matchCommands(groups, value, intl.locale);
33
+ const count = visible.reduce((total, group) => total + group.items.length, 0);
34
+ const changeOpen = (open) => {
35
+ if (!open) setValue("");
36
+ onOpenChange(open);
37
+ };
38
+ if (container === null) return null;
39
+ return /* @__PURE__ */ jsx(ModalOverlay, {
40
+ isOpen,
41
+ onOpenChange: changeOpen,
42
+ isDismissable: true,
43
+ className: CommandPalette_module_default.overlay,
44
+ UNSTABLE_portalContainer: container,
45
+ children: /* @__PURE__ */ jsx(Modal, {
46
+ className: CommandPalette_module_default.modal,
47
+ children: /* @__PURE__ */ jsxs(Dialog, {
48
+ "aria-label": label,
49
+ className: cx("panneau-command-palette", CommandPalette_module_default.dialog, className),
50
+ children: [/* @__PURE__ */ jsxs(Autocomplete, {
51
+ inputValue: value,
52
+ onInputChange: setValue,
53
+ children: [/* @__PURE__ */ jsxs("div", {
54
+ className: CommandPalette_module_default.field,
55
+ children: [/* @__PURE__ */ jsxs(SearchField, {
56
+ "aria-label": scope !== null ? `${label}: ${scope}` : label,
57
+ autoFocus: true,
58
+ className: CommandPalette_module_default.search,
59
+ children: [
60
+ /* @__PURE__ */ jsx(Icon, {
61
+ icon: Search,
62
+ size: "md",
63
+ className: CommandPalette_module_default.fieldIcon
64
+ }),
65
+ scope !== null ? /* @__PURE__ */ jsxs("span", {
66
+ className: CommandPalette_module_default.scope,
67
+ children: [/* @__PURE__ */ jsx("span", {
68
+ className: CommandPalette_module_default.scopeLabel,
69
+ children: scope
70
+ }), /* @__PURE__ */ jsx(Button, {
71
+ "aria-label": intl.formatMessage(messages.commandRemoveScope, { scope }),
72
+ className: CommandPalette_module_default.scopeRemove,
73
+ onPress: onScopeRemove,
74
+ children: /* @__PURE__ */ jsx(Icon, {
75
+ icon: X,
76
+ size: "xs"
77
+ })
78
+ })]
79
+ }) : null,
80
+ /* @__PURE__ */ jsx(Input, {
81
+ placeholder: placeholder ?? label,
82
+ className: CommandPalette_module_default.input,
83
+ onKeyDown: (event) => {
84
+ if (event.key === "Backspace" && value === "" && scope !== null) {
85
+ event.preventDefault();
86
+ onScopeRemove?.();
87
+ }
88
+ }
89
+ })
90
+ ]
91
+ }), /* @__PURE__ */ jsx(IconButton, {
92
+ icon: X,
93
+ label: intl.formatMessage(messages.close),
94
+ size: "sm",
95
+ showTooltip: false,
96
+ className: CommandPalette_module_default.close,
97
+ onPress: () => changeOpen(false)
98
+ })]
99
+ }), /* @__PURE__ */ jsx(Menu, {
100
+ "aria-label": label,
101
+ className: CommandPalette_module_default.list,
102
+ renderEmptyState: () => /* @__PURE__ */ jsx("p", {
103
+ className: CommandPalette_module_default.empty,
104
+ children: status ?? intl.formatMessage(messages.commandResults, { count: 0 })
105
+ }),
106
+ children: visible.map((group_0) => /* @__PURE__ */ jsxs(MenuSection, {
107
+ id: group_0.id,
108
+ className: CommandPalette_module_default.group,
109
+ children: [/* @__PURE__ */ jsx(Header, {
110
+ className: CommandPalette_module_default.groupLabel,
111
+ children: group_0.label
112
+ }), group_0.items.map((item) => /* @__PURE__ */ jsxs(MenuItem, {
113
+ id: `${group_0.id}:${item.id}`,
114
+ textValue: item.label,
115
+ href: item.href,
116
+ onAction: () => {
117
+ item.onAction?.();
118
+ if (item.keepsOpen !== true) changeOpen(false);
119
+ },
120
+ className: CommandPalette_module_default.item,
121
+ children: [item.imageUrl !== void 0 ? /* @__PURE__ */ jsx("img", {
122
+ src: item.imageUrl,
123
+ alt: "",
124
+ className: CommandPalette_module_default.image
125
+ }) : /* @__PURE__ */ jsx("span", {
126
+ className: CommandPalette_module_default.icon,
127
+ "aria-hidden": "true",
128
+ children: item.icon !== void 0 ? /* @__PURE__ */ jsx(Icon, {
129
+ icon: item.icon,
130
+ size: "md"
131
+ }) : null
132
+ }), /* @__PURE__ */ jsxs("span", {
133
+ className: CommandPalette_module_default.text,
134
+ children: [/* @__PURE__ */ jsx(Text, {
135
+ slot: "label",
136
+ className: CommandPalette_module_default.label,
137
+ children: item.label
138
+ }), item.description !== void 0 ? /* @__PURE__ */ jsx(Text, {
139
+ slot: "description",
140
+ className: CommandPalette_module_default.description,
141
+ children: item.description
142
+ }) : null]
143
+ })]
144
+ }, item.id))]
145
+ }, group_0.id))
146
+ })]
147
+ }), /* @__PURE__ */ jsxs("div", {
148
+ className: CommandPalette_module_default.footer,
149
+ children: [/* @__PURE__ */ jsx("span", {
150
+ className: CommandPalette_module_default.status,
151
+ role: "status",
152
+ children: status ?? intl.formatMessage(messages.commandResults, { count })
153
+ }), /* @__PURE__ */ jsxs("span", {
154
+ className: CommandPalette_module_default.hints,
155
+ "aria-hidden": "true",
156
+ children: [
157
+ /* @__PURE__ */ jsxs("span", {
158
+ className: CommandPalette_module_default.hint,
159
+ children: [/* @__PURE__ */ jsx("kbd", {
160
+ className: CommandPalette_module_default.key,
161
+ children: /* @__PURE__ */ jsx(Icon, {
162
+ icon: ArrowDownUp,
163
+ size: "xs"
164
+ })
165
+ }), intl.formatMessage(messages.commandHintMove)]
166
+ }),
167
+ /* @__PURE__ */ jsxs("span", {
168
+ className: CommandPalette_module_default.hint,
169
+ children: [/* @__PURE__ */ jsx("kbd", {
170
+ className: CommandPalette_module_default.key,
171
+ children: /* @__PURE__ */ jsx(Icon, {
172
+ icon: CornerDownLeft,
173
+ size: "xs"
174
+ })
175
+ }), intl.formatMessage(messages.commandHintOpen)]
176
+ }),
177
+ /* @__PURE__ */ jsxs("span", {
178
+ className: CommandPalette_module_default.hint,
179
+ children: [/* @__PURE__ */ jsx("kbd", {
180
+ className: CommandPalette_module_default.key,
181
+ children: "esc"
182
+ }), intl.formatMessage(messages.commandHintClose)]
183
+ })
184
+ ]
185
+ })]
186
+ })]
187
+ })
188
+ })
189
+ });
190
+ }
191
+ //#endregion
192
+ export { CommandPalette };
193
+
194
+ //# sourceMappingURL=CommandPalette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.js","names":["group","styles"],"sources":["../../src/command-palette/CommandPalette.tsx"],"sourcesContent":["import { ArrowDownUp, CornerDownLeft, Search, X } from 'lucide-react';\nimport { type ReactNode, useState } from 'react';\nimport {\n Autocomplete,\n Button,\n Dialog,\n Header,\n Input,\n Menu,\n MenuItem,\n MenuSection,\n Modal,\n ModalOverlay,\n SearchField,\n Text,\n} from 'react-aria-components';\n\nimport { IconButton } from '../icon-button/IconButton';\nimport { Icon } from '../icon/Icon';\nimport { messages } from '../intl/messages';\nimport { usePanneauIntl } from '../intl/usePanneauIntl';\nimport { usePortalContainer } from '../root/PanneauRoot';\nimport { cx } from '../utils/cx';\nimport styles from './CommandPalette.module.css';\nimport { type CommandPaletteGroup, matchCommands } from './match';\n\nexport interface CommandPaletteProps {\n isOpen: boolean;\n onOpenChange: (isOpen: boolean) => void;\n /**\n * The groups of items. The palette matches them with the query itself, except groups that\n * are already `matched`, such as results of a server, which come after the others.\n */\n groups: readonly CommandPaletteGroup[];\n /** The query, controlled: to search a server with it. Empty each time the palette opens. */\n inputValue?: string;\n onInputChange?: (value: string) => void;\n /** Accessible name of the palette and its field. Defaults to \"Search\". */\n 'aria-label'?: string;\n placeholder?: string;\n /** A line under the results, and in place of them when there are none: loading or failed\n * results of a server, a query too short to search. */\n status?: ReactNode;\n /**\n * What the search is narrowed to, such as one resource, shown as a chip before the field.\n * Backspace in the empty field, or the chip's button, calls `onScopeRemove`.\n */\n scope?: string | null;\n onScopeRemove?: () => void;\n className?: string;\n}\n\n/**\n * A command palette: a search field over groups of items to go to or run, in a dialog near the top\n * of the screen. Typing matches the items without case or accents, every word having to match,\n * the best matches first. Arrow keys move through the items, Enter opens one and closes the\n * palette, Escape clears the field, then closes it. A `scope` narrows what it searches, as a chip\n * before the field. It knows no data or router: links go through\n * the app's `RouterProvider`. `useCommandShortcut()` opens it with ⌘K or Ctrl+K.\n */\nexport function CommandPalette({\n isOpen,\n onOpenChange,\n groups,\n inputValue: controlledValue,\n onInputChange,\n 'aria-label': ariaLabel,\n placeholder,\n status,\n scope = null,\n onScopeRemove,\n className,\n}: CommandPaletteProps) {\n const intl = usePanneauIntl();\n const container = usePortalContainer();\n const [uncontrolledValue, setUncontrolledValue] = useState('');\n const value = controlledValue ?? uncontrolledValue;\n const setValue = (next: string) => {\n setUncontrolledValue(next);\n onInputChange?.(next);\n };\n const label = ariaLabel ?? intl.formatMessage(messages.commandPalette);\n const visible = matchCommands(groups, value, intl.locale);\n const count = visible.reduce((total, group) => total + group.items.length, 0);\n\n const changeOpen = (open: boolean) => {\n if (!open) {\n setValue('');\n }\n onOpenChange(open);\n };\n\n if (container === null) {\n return null;\n }\n return (\n <ModalOverlay\n isOpen={isOpen}\n onOpenChange={changeOpen}\n isDismissable\n className={styles.overlay}\n UNSTABLE_portalContainer={container}\n >\n <Modal className={styles.modal}>\n <Dialog\n aria-label={label}\n className={cx('panneau-command-palette', styles.dialog, className)}\n >\n <Autocomplete inputValue={value} onInputChange={setValue}>\n <div className={styles.field}>\n <SearchField\n aria-label={scope !== null ? `${label}: ${scope}` : label}\n autoFocus\n className={styles.search}\n >\n <Icon icon={Search} size=\"md\" className={styles.fieldIcon} />\n {scope !== null ? (\n <span className={styles.scope}>\n <span className={styles.scopeLabel}>{scope}</span>\n <Button\n aria-label={intl.formatMessage(\n messages.commandRemoveScope,\n { scope },\n )}\n className={styles.scopeRemove}\n onPress={onScopeRemove}\n >\n <Icon icon={X} size=\"xs\" />\n </Button>\n </span>\n ) : null}\n <Input\n placeholder={placeholder ?? label}\n className={styles.input}\n onKeyDown={(event) => {\n if (\n event.key === 'Backspace' &&\n value === '' &&\n scope !== null\n ) {\n event.preventDefault();\n onScopeRemove?.();\n }\n }}\n />\n </SearchField>\n {/* Touch screens have no Escape key. */}\n <IconButton\n icon={X}\n label={intl.formatMessage(messages.close)}\n size=\"sm\"\n showTooltip={false}\n className={styles.close}\n onPress={() => changeOpen(false)}\n />\n </div>\n <Menu\n aria-label={label}\n className={styles.list}\n // Without results, the status says why: loading, failed, too short…\n renderEmptyState={() => (\n <p className={styles.empty}>\n {status ??\n intl.formatMessage(messages.commandResults, { count: 0 })}\n </p>\n )}\n >\n {visible.map((group) => (\n <MenuSection key={group.id} id={group.id} className={styles.group}>\n <Header className={styles.groupLabel}>{group.label}</Header>\n {group.items.map((item) => (\n <MenuItem\n key={item.id}\n id={`${group.id}:${item.id}`}\n textValue={item.label}\n href={item.href}\n onAction={() => {\n item.onAction?.();\n if (item.keepsOpen !== true) {\n changeOpen(false);\n }\n }}\n className={styles.item}\n >\n {item.imageUrl !== undefined ? (\n <img\n src={item.imageUrl}\n alt=\"\"\n className={styles.image}\n />\n ) : (\n <span className={styles.icon} aria-hidden=\"true\">\n {item.icon !== undefined ? (\n <Icon icon={item.icon} size=\"md\" />\n ) : null}\n </span>\n )}\n <span className={styles.text}>\n <Text slot=\"label\" className={styles.label}>\n {item.label}\n </Text>\n {item.description !== undefined ? (\n <Text\n slot=\"description\"\n className={styles.description}\n >\n {item.description}\n </Text>\n ) : null}\n </span>\n </MenuItem>\n ))}\n </MenuSection>\n ))}\n </Menu>\n </Autocomplete>\n <div className={styles.footer}>\n <span className={styles.status} role=\"status\">\n {status ?? intl.formatMessage(messages.commandResults, { count })}\n </span>\n <span className={styles.hints} aria-hidden=\"true\">\n <span className={styles.hint}>\n <kbd className={styles.key}>\n <Icon icon={ArrowDownUp} size=\"xs\" />\n </kbd>\n {intl.formatMessage(messages.commandHintMove)}\n </span>\n <span className={styles.hint}>\n <kbd className={styles.key}>\n <Icon icon={CornerDownLeft} size=\"xs\" />\n </kbd>\n {intl.formatMessage(messages.commandHintOpen)}\n </span>\n <span className={styles.hint}>\n <kbd className={styles.key}>esc</kbd>\n {intl.formatMessage(messages.commandHintClose)}\n </span>\n </span>\n </div>\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n"],"mappings":"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"}