@noxlovette/material 0.8.3 → 0.9.1

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 (53) hide show
  1. package/claude-skill/material-design/SKILL.md +1 -1
  2. package/claude-skill/material-design/references/component-patterns.md +36 -1
  3. package/dist/animation/presence.svelte.js +37 -1
  4. package/dist/animation/sharedAxisTransition.d.ts +2 -1
  5. package/dist/animation/sharedAxisTransition.js +19 -1
  6. package/dist/components/buttons/FAB.svelte +4 -1
  7. package/dist/components/buttons/theme.d.ts +6 -3
  8. package/dist/components/buttons/theme.js +3 -2
  9. package/dist/components/containers/context-menu/theme.js +4 -4
  10. package/dist/components/containers/list/theme.js +1 -1
  11. package/dist/components/containers/menu/Menu.svelte +2 -5
  12. package/dist/components/containers/menu/theme.js +4 -2
  13. package/dist/components/containers/pane/theme.js +1 -1
  14. package/dist/components/date/theme.js +1 -1
  15. package/dist/components/forms/command/Command.mdx +90 -0
  16. package/dist/components/forms/command/Command.stories.svelte +69 -10
  17. package/dist/components/forms/command/Command.stories.svelte.d.ts +2 -17
  18. package/dist/components/forms/command/CommandDialog.svelte +58 -0
  19. package/dist/components/forms/command/CommandDialog.svelte.d.ts +11 -0
  20. package/dist/components/forms/command/CommandItem.svelte +48 -5
  21. package/dist/components/forms/command/CommandItem.svelte.d.ts +4 -0
  22. package/dist/components/forms/command/index.d.ts +1 -0
  23. package/dist/components/forms/command/index.js +1 -0
  24. package/dist/components/forms/command/theme.d.ts +371 -6
  25. package/dist/components/forms/command/theme.js +32 -1
  26. package/dist/components/forms/command/types.d.ts +27 -1
  27. package/dist/components/forms/search/Search.mdx +9 -0
  28. package/dist/components/forms/search/Search.stories.svelte +14 -2
  29. package/dist/components/forms/search/Search.svelte +41 -9
  30. package/dist/components/forms/search/Search.svelte.d.ts +3 -0
  31. package/dist/components/forms/search/SearchView.svelte +57 -6
  32. package/dist/components/forms/search/SearchView.svelte.d.ts +4 -2
  33. package/dist/components/forms/search/theme.js +4 -2
  34. package/dist/components/forms/search/types.d.ts +14 -1
  35. package/dist/components/forms/slider/theme.js +1 -1
  36. package/dist/components/nav/appbar/AppBar.stories.svelte +30 -2
  37. package/dist/components/nav/appbar/AppBar.svelte +40 -8
  38. package/dist/components/nav/appbar/theme.js +1 -1
  39. package/dist/components/nav/appbar/types.d.ts +14 -0
  40. package/dist/components/nav/navbar/theme.js +1 -1
  41. package/dist/components/nav/rail/Rail.mdx +6 -0
  42. package/dist/components/nav/rail/Rail.svelte +55 -1
  43. package/dist/components/nav/rail/Rail.svelte.d.ts +3 -0
  44. package/dist/components/nav/rail/theme.js +1 -1
  45. package/dist/components/nav/rail/types.d.ts +7 -0
  46. package/dist/components/toolbar/theme.js +4 -1
  47. package/dist/styles/motion.css +26 -0
  48. package/dist/utils/Layer.svelte +3 -1
  49. package/dist/utils/index.d.ts +1 -0
  50. package/dist/utils/index.js +1 -0
  51. package/dist/utils/shortcut.d.ts +26 -0
  52. package/dist/utils/shortcut.js +84 -0
  53. package/package.json +1 -1
@@ -1,14 +1,57 @@
1
+ <!--
2
+ @component
3
+ A command in a `Command` or `CommandDialog`: an M3 list item. The children are its headline; add a
4
+ `leading` icon, `supporting` text, and a `shortcut` (shown, not bound) or other `trailingText`.
5
+ -->
1
6
  <script lang="ts">
2
7
  import { Command } from 'bits-ui';
3
8
  import clsx from 'clsx';
4
- import { command } from './theme.js';
9
+ import { Layer, isApplePlatform, shortcutLabel } from '../../../utils/index.js';
10
+ import { commandItem } from './theme.js';
5
11
  import type { CommandItemProps } from './types.js';
6
12
 
7
- let { children, class: className, value, ...restProps }: CommandItemProps = $props();
13
+ let {
14
+ children,
15
+ leading,
16
+ supporting,
17
+ trailingText,
18
+ shortcut,
19
+ disabled = false,
20
+ class: className,
21
+ value,
22
+ ...restProps
23
+ }: CommandItemProps = $props();
8
24
 
9
- const { item } = command();
25
+ // `Mod` resolves on the client, so SSR and the first render agree.
26
+ let apple = $state(false);
27
+ $effect(() => {
28
+ apple = isApplePlatform();
29
+ });
30
+
31
+ const trailing = $derived(shortcut ? shortcutLabel(shortcut, apple) : trailingText);
32
+ const cls = $derived(commandItem({ lines: supporting ? 2 : 1, disabled }));
10
33
  </script>
11
34
 
12
- <Command.Item {...restProps} {value} class={item({ class: clsx(className) })}>
13
- {@render children()}
35
+ <Command.Item {...restProps} {value} {disabled}>
36
+ {#snippet child({ props })}
37
+ <div {...props} class={cls.base({ class: clsx(className) })}>
38
+ {#if !disabled}
39
+ <Layer />
40
+ {/if}
41
+ {#if leading}
42
+ <div class={cls.leading()} aria-hidden="true">{@render leading()}</div>
43
+ {/if}
44
+ <div class={cls.body()}>
45
+ <span class={cls.headline()}>{@render children()}</span>
46
+ {#if supporting}
47
+ <span class={cls.supporting()}>{supporting}</span>
48
+ {/if}
49
+ </div>
50
+ {#if trailing}
51
+ <div class={cls.trailing()}>
52
+ <span class={cls.trailingText()}>{trailing}</span>
53
+ </div>
54
+ {/if}
55
+ </div>
56
+ {/snippet}
14
57
  </Command.Item>
@@ -1,4 +1,8 @@
1
1
  import type { CommandItemProps } from './types.js';
2
+ /**
3
+ * A command in a `Command` or `CommandDialog`: an M3 list item. The children are its headline; add a
4
+ * `leading` icon, `supporting` text, and a `shortcut` (shown, not bound) or other `trailingText`.
5
+ */
2
6
  declare const CommandItem: import("svelte").Component<CommandItemProps, {}, "">;
3
7
  type CommandItem = ReturnType<typeof CommandItem>;
4
8
  export default CommandItem;
@@ -1,6 +1,7 @@
1
1
  export * from './theme.js';
2
2
  export * from './types.js';
3
3
  export { default as Command } from './Command.svelte';
4
+ export { default as CommandDialog } from './CommandDialog.svelte';
4
5
  export { default as CommandEmpty } from './CommandEmpty.svelte';
5
6
  export { default as CommandGroup } from './CommandGroup.svelte';
6
7
  export { default as CommandInput } from './CommandInput.svelte';
@@ -4,6 +4,7 @@ export * from './theme.js';
4
4
  export * from './types.js';
5
5
  // default exports for components in this folder
6
6
  export { default as Command } from './Command.svelte';
7
+ export { default as CommandDialog } from './CommandDialog.svelte';
7
8
  export { default as CommandEmpty } from './CommandEmpty.svelte';
8
9
  export { default as CommandGroup } from './CommandGroup.svelte';
9
10
  export { default as CommandInput } from './CommandInput.svelte';
@@ -3,7 +3,6 @@ export type CommandVariants = VariantProps<typeof command>;
3
3
  export declare const command: import("tailwind-variants").TVReturnType<{
4
4
  [key: string]: {
5
5
  [key: string]: import("tailwind-variants").ClassValue | {
6
- item?: import("tailwind-variants").ClassValue;
7
6
  base?: import("tailwind-variants").ClassValue;
8
7
  input?: import("tailwind-variants").ClassValue;
9
8
  group?: import("tailwind-variants").ClassValue;
@@ -18,7 +17,6 @@ export declare const command: import("tailwind-variants").TVReturnType<{
18
17
  } | {
19
18
  [x: string]: {
20
19
  [x: string]: import("tailwind-variants").ClassValue | {
21
- item?: import("tailwind-variants").ClassValue;
22
20
  base?: import("tailwind-variants").ClassValue;
23
21
  input?: import("tailwind-variants").ClassValue;
24
22
  group?: import("tailwind-variants").ClassValue;
@@ -39,12 +37,10 @@ export declare const command: import("tailwind-variants").TVReturnType<{
39
37
  empty: string;
40
38
  group: string;
41
39
  separator: string;
42
- item: string;
43
40
  itemIcon: string;
44
41
  }, undefined, {
45
42
  [key: string]: {
46
43
  [key: string]: import("tailwind-variants").ClassValue | {
47
- item?: import("tailwind-variants").ClassValue;
48
44
  base?: import("tailwind-variants").ClassValue;
49
45
  input?: import("tailwind-variants").ClassValue;
50
46
  group?: import("tailwind-variants").ClassValue;
@@ -65,7 +61,6 @@ export declare const command: import("tailwind-variants").TVReturnType<{
65
61
  empty: string;
66
62
  group: string;
67
63
  separator: string;
68
- item: string;
69
64
  itemIcon: string;
70
65
  }, import("tailwind-variants").TVReturnTypeLike<unknown, {
71
66
  base: string;
@@ -76,6 +71,376 @@ export declare const command: import("tailwind-variants").TVReturnType<{
76
71
  empty: string;
77
72
  group: string;
78
73
  separator: string;
79
- item: string;
80
74
  itemIcon: string;
81
75
  }>>;
76
+ export declare const commandItem: import("tailwind-variants").TVReturnType<{
77
+ variant: {
78
+ standard: {
79
+ base: string;
80
+ };
81
+ };
82
+ }, {
83
+ base: string[];
84
+ }, undefined, {
85
+ lines: {
86
+ 1: {
87
+ base: string;
88
+ };
89
+ 2: {
90
+ base: string;
91
+ };
92
+ 3: {
93
+ base: string;
94
+ leading: string;
95
+ trailing: string;
96
+ };
97
+ };
98
+ variant: {
99
+ standard: {
100
+ base: string;
101
+ nested: string;
102
+ };
103
+ segmented: {
104
+ base: string;
105
+ nested: string;
106
+ };
107
+ };
108
+ interactive: {
109
+ true: {
110
+ base: string[];
111
+ };
112
+ false: "";
113
+ };
114
+ selected: {
115
+ true: {
116
+ base: string;
117
+ leading: string;
118
+ overline: string;
119
+ headline: string;
120
+ supporting: string;
121
+ trailing: string;
122
+ trailingText: string;
123
+ };
124
+ false: "";
125
+ };
126
+ disabled: {
127
+ true: {
128
+ base: string;
129
+ leading: string;
130
+ overline: string;
131
+ headline: string;
132
+ supporting: string;
133
+ trailing: string;
134
+ trailingText: string;
135
+ };
136
+ false: "";
137
+ };
138
+ expanded: {
139
+ true: {
140
+ base: string;
141
+ expandIcon: string;
142
+ region: string;
143
+ nested: string;
144
+ };
145
+ false: {
146
+ expandIcon: string;
147
+ region: string;
148
+ };
149
+ };
150
+ }, {
151
+ base: string[];
152
+ selection: string;
153
+ leading: string;
154
+ body: string;
155
+ overline: string;
156
+ headline: string;
157
+ supporting: string;
158
+ trailing: string;
159
+ trailingText: string;
160
+ expandIcon: string;
161
+ group: string;
162
+ region: string;
163
+ regionInner: string;
164
+ nested: string;
165
+ }, import("tailwind-variants").TVReturnType<{
166
+ lines: {
167
+ 1: {
168
+ base: string;
169
+ };
170
+ 2: {
171
+ base: string;
172
+ };
173
+ 3: {
174
+ base: string;
175
+ leading: string;
176
+ trailing: string;
177
+ };
178
+ };
179
+ variant: {
180
+ standard: {
181
+ base: string;
182
+ nested: string;
183
+ };
184
+ segmented: {
185
+ base: string;
186
+ nested: string;
187
+ };
188
+ };
189
+ interactive: {
190
+ true: {
191
+ base: string[];
192
+ };
193
+ false: "";
194
+ };
195
+ selected: {
196
+ true: {
197
+ base: string;
198
+ leading: string;
199
+ overline: string;
200
+ headline: string;
201
+ supporting: string;
202
+ trailing: string;
203
+ trailingText: string;
204
+ };
205
+ false: "";
206
+ };
207
+ disabled: {
208
+ true: {
209
+ base: string;
210
+ leading: string;
211
+ overline: string;
212
+ headline: string;
213
+ supporting: string;
214
+ trailing: string;
215
+ trailingText: string;
216
+ };
217
+ false: "";
218
+ };
219
+ expanded: {
220
+ true: {
221
+ base: string;
222
+ expandIcon: string;
223
+ region: string;
224
+ nested: string;
225
+ };
226
+ false: {
227
+ expandIcon: string;
228
+ region: string;
229
+ };
230
+ };
231
+ }, {
232
+ base: string[];
233
+ selection: string;
234
+ leading: string;
235
+ body: string;
236
+ overline: string;
237
+ headline: string;
238
+ supporting: string;
239
+ trailing: string;
240
+ trailingText: string;
241
+ expandIcon: string;
242
+ group: string;
243
+ region: string;
244
+ regionInner: string;
245
+ nested: string;
246
+ }, undefined, {
247
+ lines: {
248
+ 1: {
249
+ base: string;
250
+ };
251
+ 2: {
252
+ base: string;
253
+ };
254
+ 3: {
255
+ base: string;
256
+ leading: string;
257
+ trailing: string;
258
+ };
259
+ };
260
+ variant: {
261
+ standard: {
262
+ base: string;
263
+ nested: string;
264
+ };
265
+ segmented: {
266
+ base: string;
267
+ nested: string;
268
+ };
269
+ };
270
+ interactive: {
271
+ true: {
272
+ base: string[];
273
+ };
274
+ false: "";
275
+ };
276
+ selected: {
277
+ true: {
278
+ base: string;
279
+ leading: string;
280
+ overline: string;
281
+ headline: string;
282
+ supporting: string;
283
+ trailing: string;
284
+ trailingText: string;
285
+ };
286
+ false: "";
287
+ };
288
+ disabled: {
289
+ true: {
290
+ base: string;
291
+ leading: string;
292
+ overline: string;
293
+ headline: string;
294
+ supporting: string;
295
+ trailing: string;
296
+ trailingText: string;
297
+ };
298
+ false: "";
299
+ };
300
+ expanded: {
301
+ true: {
302
+ base: string;
303
+ expandIcon: string;
304
+ region: string;
305
+ nested: string;
306
+ };
307
+ false: {
308
+ expandIcon: string;
309
+ region: string;
310
+ };
311
+ };
312
+ }, {
313
+ base: string[];
314
+ selection: string;
315
+ leading: string;
316
+ body: string;
317
+ overline: string;
318
+ headline: string;
319
+ supporting: string;
320
+ trailing: string;
321
+ trailingText: string;
322
+ expandIcon: string;
323
+ group: string;
324
+ region: string;
325
+ regionInner: string;
326
+ nested: string;
327
+ }, import("tailwind-variants").TVReturnTypeLike<{
328
+ lines: {
329
+ 1: {
330
+ base: string;
331
+ };
332
+ 2: {
333
+ base: string;
334
+ };
335
+ 3: {
336
+ base: string;
337
+ leading: string;
338
+ trailing: string;
339
+ };
340
+ };
341
+ variant: {
342
+ standard: {
343
+ base: string;
344
+ nested: string;
345
+ };
346
+ segmented: {
347
+ base: string;
348
+ nested: string;
349
+ };
350
+ };
351
+ interactive: {
352
+ true: {
353
+ base: string[];
354
+ };
355
+ false: "";
356
+ };
357
+ selected: {
358
+ true: {
359
+ base: string;
360
+ leading: string;
361
+ overline: string;
362
+ headline: string;
363
+ supporting: string;
364
+ trailing: string;
365
+ trailingText: string;
366
+ };
367
+ false: "";
368
+ };
369
+ disabled: {
370
+ true: {
371
+ base: string;
372
+ leading: string;
373
+ overline: string;
374
+ headline: string;
375
+ supporting: string;
376
+ trailing: string;
377
+ trailingText: string;
378
+ };
379
+ false: "";
380
+ };
381
+ expanded: {
382
+ true: {
383
+ base: string;
384
+ expandIcon: string;
385
+ region: string;
386
+ nested: string;
387
+ };
388
+ false: {
389
+ expandIcon: string;
390
+ region: string;
391
+ };
392
+ };
393
+ }, {
394
+ base: string[];
395
+ selection: string;
396
+ leading: string;
397
+ body: string;
398
+ overline: string;
399
+ headline: string;
400
+ supporting: string;
401
+ trailing: string;
402
+ trailingText: string;
403
+ expandIcon: string;
404
+ group: string;
405
+ region: string;
406
+ regionInner: string;
407
+ nested: string;
408
+ }>>>;
409
+ export type CommandItemVariants = VariantProps<typeof commandItem>;
410
+ export declare const commandDialog: import("tailwind-variants").TVReturnType<{
411
+ [key: string]: {
412
+ [key: string]: import("tailwind-variants").ClassValue | {
413
+ content?: import("tailwind-variants").ClassValue;
414
+ command?: import("tailwind-variants").ClassValue;
415
+ scrim?: import("tailwind-variants").ClassValue;
416
+ };
417
+ };
418
+ } | {
419
+ [x: string]: {
420
+ [x: string]: import("tailwind-variants").ClassValue | {
421
+ content?: import("tailwind-variants").ClassValue;
422
+ command?: import("tailwind-variants").ClassValue;
423
+ scrim?: import("tailwind-variants").ClassValue;
424
+ };
425
+ };
426
+ } | {}, {
427
+ scrim: string;
428
+ content: string;
429
+ command: string;
430
+ }, undefined, {
431
+ [key: string]: {
432
+ [key: string]: import("tailwind-variants").ClassValue | {
433
+ content?: import("tailwind-variants").ClassValue;
434
+ command?: import("tailwind-variants").ClassValue;
435
+ scrim?: import("tailwind-variants").ClassValue;
436
+ };
437
+ };
438
+ } | {}, {
439
+ scrim: string;
440
+ content: string;
441
+ command: string;
442
+ }, import("tailwind-variants").TVReturnTypeLike<unknown, {
443
+ scrim: string;
444
+ content: string;
445
+ command: string;
446
+ }>>;
@@ -1,4 +1,5 @@
1
1
  import { tv } from '../../../utils/tv.js';
2
+ import { listItem } from '../../containers/list/theme.js';
2
3
  export const command = tv({
3
4
  slots: {
4
5
  base: 'flex h-full w-full flex-col overflow-hidden rounded-2xl bg-md-sys-color-surface-container-high text-md-sys-color-on-surface shadow-elevation-3 ring-1 ring-md-sys-color-outline/20',
@@ -9,7 +10,37 @@ export const command = tv({
9
10
  empty: 'py-spacing-300 text-center md-sys-typescale-body-medium text-md-sys-color-on-surface-variant',
10
11
  group: 'overflow-hidden p-spacing-50 text-md-sys-color-on-surface-variant [&_[data-command-group-heading]]:px-spacing-150 [&_[data-command-group-heading]]:py-spacing-100 [&_[data-command-group-heading]]:md-sys-typescale-label-medium [&_[data-command-group-heading]]:text-md-sys-color-on-surface-variant',
11
12
  separator: '-mx-spacing-50 h-px bg-md-sys-color-outline-variant',
12
- item: 'relative flex cursor-default select-none items-center rounded-lg px-spacing-150 py-spacing-150 md-sys-typescale-body-medium outline-none data-[selected=true]:bg-md-sys-color-secondary-container data-[selected=true]:text-md-sys-color-on-secondary-container data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 transition-colors md-sys-motion-fast-effects',
13
13
  itemIcon: 'mr-spacing-150 h-spacing-250 w-spacing-250 shrink-0'
14
14
  }
15
15
  });
16
+ /*
17
+ Palette items are M3 list items (ListItem's anatomy, shape and type), sitting on the palette's
18
+ container colour. Focus stays in the input, so the item the arrow keys or pointer are on
19
+ (bits-ui's `data-selected`, an empty attribute) takes the focused item's state: a 10% on-surface
20
+ fill and the 12dp hover shape, the way menus show their highlighted item. A fill, not Layer's
21
+ tint: Layer's styles are unlayered component CSS, which beats any Tailwind utility.
22
+ */
23
+ export const commandItem = tv({
24
+ extend: listItem,
25
+ slots: {
26
+ base: [
27
+ 'cursor-default select-none outline-none',
28
+ 'data-selected:[--li-shape:0.75rem] data-selected:bg-md-sys-color-on-surface/10'
29
+ ]
30
+ },
31
+ // The list's own surface fill is a variant, so it's replaced here, not in the base.
32
+ variants: { variant: { standard: { base: 'bg-transparent' } } },
33
+ defaultVariants: { interactive: true, lines: 1 }
34
+ });
35
+ /*
36
+ The ⌘K palette. M3 has no command palette; it borrows the docked search view's width (360–720dp)
37
+ and a modal dialog's scrim, extra-large shape and motion, and sits in the top third of the
38
+ window so the list has room to grow downwards.
39
+ */
40
+ export const commandDialog = tv({
41
+ slots: {
42
+ scrim: 'fixed inset-spacing-0 z-layer-modal bg-md-sys-color-scrim/32',
43
+ content: 'fixed inset-x-spacing-200 top-spacing-200 md:top-spacing-900 z-layer-modal mx-auto max-w-(--md-comp-search-view-contained-docked-container-width-maximum) outline-none',
44
+ command: 'h-auto rounded-xl'
45
+ }
46
+ });
@@ -1,3 +1,4 @@
1
+ import type { Shortcut } from '../../../utils/index.js';
1
2
  import type { Command } from 'bits-ui';
2
3
  import type { Snippet } from 'svelte';
3
4
  import type { CommandVariants } from './theme.js';
@@ -13,9 +14,21 @@ export interface CommandListProps extends Command.ListProps {
13
14
  class?: string;
14
15
  children: Snippet;
15
16
  }
16
- export interface CommandItemProps extends Command.ItemProps {
17
+ export interface CommandItemProps extends Omit<Command.ItemProps, 'child' | 'children'> {
17
18
  class?: string;
19
+ /** The headline. */
18
20
  children: Snippet;
21
+ /** A leading icon, e.g. `<Icon name="settings" />`. */
22
+ leading?: Snippet;
23
+ /** A second line under the headline. */
24
+ supporting?: string;
25
+ /** Trailing label, e.g. a count. `shortcut` replaces it. */
26
+ trailingText?: string;
27
+ /**
28
+ * A keyboard shortcut to show at the end, written like `'Mod+,'` and labelled for the platform
29
+ * (`⌘,` on a Mac, `Ctrl+,` elsewhere). Only shown: bind the key yourself.
30
+ */
31
+ shortcut?: Shortcut;
19
32
  }
20
33
  export interface CommandGroupProps extends Command.GroupProps {
21
34
  class?: string;
@@ -29,3 +42,16 @@ export interface CommandEmptyProps extends Command.EmptyProps {
29
42
  export interface CommandSeparatorProps extends Command.SeparatorProps {
30
43
  class?: string;
31
44
  }
45
+ export interface CommandDialogProps extends CommandProps {
46
+ /** Whether the palette is open. */
47
+ open?: boolean;
48
+ /**
49
+ * Page-wide keys that open and close the palette. `Mod` is ⌘ on Apple platforms, Ctrl
50
+ * elsewhere. Fires inside text fields too, but not while another modal dialog is open. `null`
51
+ * turns it off.
52
+ * @default 'Mod+K'
53
+ */
54
+ shortcut?: Shortcut | null;
55
+ /** Accessible name of the dialog. */
56
+ label?: string;
57
+ }
@@ -104,3 +104,12 @@ the view on click or typing, not on focus: closing hands focus back to the bar.
104
104
  through the `role="option"` items (wrapping, skipping disabled ones) via
105
105
  `aria-activedescendant`, so focus stays in the field while typing; Enter picks the highlighted
106
106
  one. The highlighted item gets the M3 focus indicator.
107
+ - **Submitting.** Enter with no suggestion highlighted, or Enter in the bar itself, calls
108
+ `onsearch(query)` with the query as typed (never for an empty one). The view stays open; close
109
+ it yourself (`open = false`) when you navigate to results. This is where search differs from the
110
+ command palette (`CommandDialog`): a palette always highlights its best match and runs it, a
111
+ search can always search for exactly what was typed.
112
+ - **Shortcut.** `/` anywhere on the page, outside a text field, focuses the bar (or opens its view
113
+ when it has `results`), and the input advertises it with `aria-keyshortcuts`. `shortcut` changes
114
+ the key; `null` turns it off. With several bars on a page the first mounted takes it. It doesn't
115
+ fire while another modal dialog is open. ⌘K belongs to the command palette (`CommandDialog`).
@@ -47,6 +47,11 @@
47
47
  query ? topics.filter((t) => t.toLowerCase().includes(query.toLowerCase())) : recent
48
48
  );
49
49
 
50
+ const submit = (q: string) => {
51
+ picked = `search for “${q}”`;
52
+ open = false;
53
+ };
54
+
50
55
  const pick = (item: string) => {
51
56
  picked = item;
52
57
  query = item;
@@ -101,7 +106,8 @@
101
106
 
102
107
  <!--
103
108
  Click or type in the bar. The view is full-screen below the medium window class and docked from
104
- it up: resize the viewport to see both. Arrow keys move through the suggestions; Enter picks one.
109
+ it up: resize the viewport to see both. Arrow keys move through the suggestions; Enter picks one,
110
+ or with nothing highlighted searches for what was typed (onsearch). / from anywhere opens it.
105
111
  -->
106
112
  <Story
107
113
  name="Search view"
@@ -112,7 +118,13 @@
112
118
  }}
113
119
  >
114
120
  <div class="gap-spacing-200 p-spacing-300 flex min-h-dvh flex-col items-center">
115
- <Search placeholder="Search components" bind:value={query} bind:open results={suggestions}>
121
+ <Search
122
+ placeholder="Search components"
123
+ bind:value={query}
124
+ bind:open
125
+ results={suggestions}
126
+ onsearch={submit}
127
+ >
116
128
  {#snippet trailing()}
117
129
  <ButtonIcon variant="standard" iconProps={{ name: 'mic' }} aria-label="Voice search" />
118
130
  {/snippet}