handy-fluentui 0.1.0 → 1.0.0

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 (78) hide show
  1. package/README.md +331 -30
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +1148 -45
  5. package/dist/index.js +1000 -636
  6. package/dist/index.js.map +1 -1
  7. package/package.json +7 -6
  8. package/dist/components/fui-button-panel.d.ts +0 -13
  9. package/dist/components/fui-button-panel.d.ts.map +0 -1
  10. package/dist/components/fui-image-carousell.d.ts +0 -17
  11. package/dist/components/fui-image-carousell.d.ts.map +0 -1
  12. package/dist/components/fui-tab.d.ts +0 -29
  13. package/dist/components/fui-tab.d.ts.map +0 -1
  14. package/dist/components/fui-table.d.ts +0 -89
  15. package/dist/components/fui-table.d.ts.map +0 -1
  16. package/dist/components/input-checkbox.d.ts +0 -17
  17. package/dist/components/input-checkbox.d.ts.map +0 -1
  18. package/dist/components/input-date.d.ts +0 -27
  19. package/dist/components/input-date.d.ts.map +0 -1
  20. package/dist/components/input-dropdown.d.ts +0 -33
  21. package/dist/components/input-dropdown.d.ts.map +0 -1
  22. package/dist/components/input-group.d.ts +0 -25
  23. package/dist/components/input-group.d.ts.map +0 -1
  24. package/dist/components/input-multi-lang.d.ts +0 -35
  25. package/dist/components/input-multi-lang.d.ts.map +0 -1
  26. package/dist/components/input-number.d.ts +0 -39
  27. package/dist/components/input-number.d.ts.map +0 -1
  28. package/dist/components/input-radio.d.ts +0 -33
  29. package/dist/components/input-radio.d.ts.map +0 -1
  30. package/dist/components/input-switch.d.ts +0 -17
  31. package/dist/components/input-switch.d.ts.map +0 -1
  32. package/dist/components/input-text.d.ts +0 -19
  33. package/dist/components/input-text.d.ts.map +0 -1
  34. package/dist/components/input-textarea.d.ts +0 -20
  35. package/dist/components/input-textarea.d.ts.map +0 -1
  36. package/dist/components/input-time.d.ts +0 -31
  37. package/dist/components/input-time.d.ts.map +0 -1
  38. package/dist/components/with-input-field.d.ts +0 -49
  39. package/dist/components/with-input-field.d.ts.map +0 -1
  40. package/dist/contexts/breadcrumb-context.d.ts +0 -20
  41. package/dist/contexts/breadcrumb-context.d.ts.map +0 -1
  42. package/dist/contexts/dialog-context.d.ts +0 -28
  43. package/dist/contexts/dialog-context.d.ts.map +0 -1
  44. package/dist/contexts/handy-fluent-ui-context.d.ts +0 -48
  45. package/dist/contexts/handy-fluent-ui-context.d.ts.map +0 -1
  46. package/dist/contexts/spinner-context.d.ts +0 -19
  47. package/dist/contexts/spinner-context.d.ts.map +0 -1
  48. package/dist/contexts/toast-context.d.ts +0 -17
  49. package/dist/contexts/toast-context.d.ts.map +0 -1
  50. package/dist/hooks/use-breadcrumb.d.ts +0 -19
  51. package/dist/hooks/use-breadcrumb.d.ts.map +0 -1
  52. package/dist/hooks/use-dialog.d.ts +0 -6
  53. package/dist/hooks/use-dialog.d.ts.map +0 -1
  54. package/dist/hooks/use-logger.d.ts +0 -9
  55. package/dist/hooks/use-logger.d.ts.map +0 -1
  56. package/dist/hooks/use-mobile.d.ts +0 -4
  57. package/dist/hooks/use-mobile.d.ts.map +0 -1
  58. package/dist/hooks/use-spinner.d.ts +0 -7
  59. package/dist/hooks/use-spinner.d.ts.map +0 -1
  60. package/dist/hooks/use-theme.d.ts +0 -8
  61. package/dist/hooks/use-theme.d.ts.map +0 -1
  62. package/dist/hooks/use-time-zone.d.ts +0 -17
  63. package/dist/hooks/use-time-zone.d.ts.map +0 -1
  64. package/dist/hooks/use-toast.d.ts +0 -9
  65. package/dist/hooks/use-toast.d.ts.map +0 -1
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/providers/breadcrumb-provider.d.ts +0 -10
  68. package/dist/providers/breadcrumb-provider.d.ts.map +0 -1
  69. package/dist/providers/dialog-provider.d.ts +0 -6
  70. package/dist/providers/dialog-provider.d.ts.map +0 -1
  71. package/dist/providers/handy-fluent-ui-provider.d.ts +0 -10
  72. package/dist/providers/handy-fluent-ui-provider.d.ts.map +0 -1
  73. package/dist/providers/spinner-provider.d.ts +0 -10
  74. package/dist/providers/spinner-provider.d.ts.map +0 -1
  75. package/dist/providers/toast-provider.d.ts +0 -9
  76. package/dist/providers/toast-provider.d.ts.map +0 -1
  77. package/dist/utils/string-util.d.ts +0 -4
  78. package/dist/utils/string-util.d.ts.map +0 -1
package/README.md CHANGED
@@ -7,6 +7,8 @@ Opinionated React components built on top of [FluentUI v9](https://react.fluentu
7
7
  - Imperative `useToast`, `useSpinner`, `useDialog` APIs
8
8
  - i18n-ready label overrides through the provider
9
9
 
10
+ An equivalent library built on [shadcn/ui](https://ui.shadcn.com/) instead of FluentUI, mirroring the same component API surface under a `Hui*` prefix, is available at [kc2wong/handy-shadcnui](https://github.com/kc2wong/handy-shadcnui).
11
+
10
12
  ---
11
13
 
12
14
  ## Commands
@@ -88,6 +90,7 @@ Component-specific labels (`FuiTable` pagination text, `FuiImageCarousel` toolti
88
90
  |------|---------|-------------|
89
91
  | `useTheme()` | `{ currentTheme, switchTheme }` | Read and change the active theme |
90
92
  | `useIsMobile()` | `boolean` | True when viewport ≤ `mobileBreakpoint` |
93
+ | `useBreadcrumb()` | `{ items, isCollapsed, toggleCollapsed, start, append, peek, popTill }` | Read and update the trail rendered by `FuiBreadcrumb` |
91
94
  | `useToast()` | `{ success, error, info, warning }` | Show toast notifications |
92
95
  | `useSpinner()` | `{ show, hide }` | Show/hide the global overlay spinner |
93
96
  | `useDialog()` | `{ openDialog }` | Show an imperative confirmation dialog |
@@ -140,7 +143,7 @@ type LocalDate = {
140
143
 
141
144
  ## Common field props (`FieldLayoutProps`)
142
145
 
143
- Every `input-*` component inherits these props from the `withInputField` HOC:
146
+ Every form input component (`FuiInputText`, `FuiInputDate`, `FuiRadioGroup`, ...) inherits these props from the `withInputField` HOC:
144
147
 
145
148
  | Prop | Type | Default | Description |
146
149
  |------|------|---------|-------------|
@@ -187,6 +190,8 @@ Text input with optional show/hide toggle for passwords.
187
190
  | `value` | `string \| null` | Yes | Current value |
188
191
  | `onChange` | `(value: string \| null) => void` | Yes | Change callback |
189
192
  | `type` | `'text' \| 'email' \| 'password'` | No | Defaults to `'text'`. Password adds show/hide toggle; email blocks duplicate `@`. |
193
+ | `contentBefore` | `ReactElement` | No | Content rendered inside the input, left-aligned (e.g. an icon) |
194
+ | `contentAfter` | `ReactElement` | No | Content rendered inside the input, right-aligned (e.g. an icon button). Ignored when `type` is `'password'`. |
190
195
 
191
196
  ---
192
197
 
@@ -209,6 +214,8 @@ Multi-line text area with optional character counter.
209
214
  | `value` | `string \| null` | Yes | Current value |
210
215
  | `onChange` | `(value: string \| null) => void` | Yes | Change callback |
211
216
  | `maxLength` | `number` | No | Automatically appends a counter (`n / max`) unless `additionalMessage` is set |
217
+ | `rows` | `number` | No | Number of visible text lines. Defaults to `4`. |
218
+ | `readOnly` | `boolean` | No | Native HTML read-only attribute, forwarded as-is to the `<textarea>` |
212
219
 
213
220
  ---
214
221
 
@@ -249,6 +256,8 @@ Number input with keystroke filtering and optional SpinButton mode.
249
256
  | `max` | `number` | No | Maximum value |
250
257
  | `allowNegative` | `boolean` | No | When `false`, blocks the minus key. Defaults to `true`. |
251
258
  | `formatter` | `(value: number) => string` | No | Formats the display value when the field is unfocused |
259
+ | `appearance` | `'outline' \| 'underline' \| 'filled-darker' \| 'filled-lighter' \| 'filled-darker-shadow' \| 'filled-lighter-shadow'` | No | Visual appearance of the underlying `Input`/`SpinButton` |
260
+ | `size` | `'small' \| 'medium' \| 'large'` | No | Input size |
252
261
 
253
262
  ---
254
263
 
@@ -268,8 +277,10 @@ Date picker. Renders a FluentUI `DatePicker` on desktop and a bottom-sheet calen
268
277
  | Prop | Type | Required | Description |
269
278
  |------|------|----------|-------------|
270
279
  | `value` | `Date \| null` | Yes | Selected date |
271
- | `onChange` | `(date: Date \| null \| undefined) => void` | Yes | Change callback |
280
+ | `onChange` | `(date: Date \| null) => void` | Yes | Change callback |
272
281
  | `formatter` | `(date: Date \| null) => string` | No | Custom date format function. Defaults to `toLocaleDateString()`. |
282
+ | `placeholder` | `string` | No | Placeholder text shown when empty |
283
+ | `disabled` | `boolean` | No | Disables interaction |
273
284
  | `readOnly` | `boolean` | No | Suppresses the calendar popup/drawer. Desktop renders a plain read-only `Input`; mobile hides the calendar icon and ignores clicks. |
274
285
 
275
286
  ---
@@ -279,7 +290,7 @@ Date picker. Renders a FluentUI `DatePicker` on desktop and a bottom-sheet calen
279
290
  Time picker with up/down arrow buttons. Clicking an arrow increments or decrements the time segment under the cursor. On mobile the arrows are laid out in a horizontal row with larger icons.
280
291
 
281
292
  ```tsx
282
- import { FuiInputTime, FuiTime } from './components/input-time';
293
+ import { FuiInputTime, FuiTime } from './components/fui-input-time';
283
294
 
284
295
  const [shiftStart, setShiftStart] = useState<FuiTime | null>(null);
285
296
 
@@ -364,12 +375,17 @@ const options = [
364
375
  |------|------|----------|-------------|
365
376
  | `value` | `string \| string[] \| null` | Yes | Selected value(s) |
366
377
  | `onChange` | `(value: string \| string[] \| null) => void` | Yes | Change callback |
367
- | `options` | `InputDropdownOption[]` | Yes | Option list |
368
- | `multiselect` | `boolean` | No | Enable multi-select mode |
378
+ | `options` | `FuiInputDropdownOption[]` | Yes | Option list |
379
+ | `multiselect` | `boolean` | No | Enable multi-select mode. Defaults to `false`. |
380
+ | `placeholder` | `string` | No | Placeholder text shown when empty |
381
+ | `disabled` | `boolean` | No | Disables interaction |
369
382
  | `readOnly` | `boolean` | No | Silently ignores selection changes |
370
- | `listbox` | `ListboxProps` | No | Props forwarded to the inner `Listbox`. Use `style.maxHeight` to constrain dropdown height. Must pass `positioning={{ autoSize: false }}` alongside this, otherwise Floating UI overrides inline `max-height`. |
383
+ | `className` | `string` | No | Custom CSS class for the dropdown root |
384
+ | `style` | `CSSProperties` | No | Custom CSS styles for the dropdown root |
385
+ | `listbox` | `{ style?: CSSProperties }` | No | Style passthrough for the inner `Listbox`. Use `style.maxHeight` to constrain dropdown height. Must pass `positioning={{ autoSize: false }}` alongside this, otherwise Floating UI overrides inline `max-height`. |
386
+ | `positioning` | `{ autoSize?: boolean }` | No | Positioning passthrough for the dropdown popup |
371
387
 
372
- **`InputDropdownOption`**
388
+ **`FuiInputDropdownOption`**
373
389
 
374
390
  | Prop | Type | Required | Description |
375
391
  |------|------|----------|-------------|
@@ -380,58 +396,75 @@ const options = [
380
396
 
381
397
  ---
382
398
 
383
- ### `FuiInputRadio`
399
+ ### `FuiRadioGroup` / `FuiRadio`
384
400
 
385
- Radio group with a shared label.
401
+ Radio group with a shared label. `FuiRadio` items are independent components (not raw Fluent `<Radio>` elements).
386
402
 
387
403
  ```tsx
388
- <FuiInputRadio
404
+ <FuiRadioGroup
389
405
  label="Gender"
390
406
  value={gender}
391
- onChange={(data) => setGender(data.value)}
392
- direction="horizontal"
407
+ onChange={setGender}
408
+ layout="horizontal"
393
409
  >
394
- <Radio label="Male" value="male" />
395
- <Radio label="Female" value="female" />
396
- <Radio label="Other" value="other" />
397
- </FuiInputRadio>
410
+ <FuiRadio label="Male" value="male" />
411
+ <FuiRadio label="Female" value="female" />
412
+ <FuiRadio label="Other" value="other" />
413
+ </FuiRadioGroup>
398
414
  ```
399
415
 
416
+ **`FuiRadioGroup` props**
417
+
400
418
  | Prop | Type | Required | Description |
401
419
  |------|------|----------|-------------|
402
- | `onChange` | `(data: RadioGroupOnChangeData) => void` | No | Change callback |
403
- | `layout` | `'vertical' \| 'horizontal'` | No | Radio button layout direction |
420
+ | `value` | `string` | No | Controlled selected value |
421
+ | `defaultValue` | `string` | No | Uncontrolled initial value |
422
+ | `name` | `string` | No | Shared name for the underlying radio inputs on form submission |
423
+ | `onChange` | `(value: string) => void` | No | Change callback |
424
+ | `disabled` | `boolean` | No | Disables the entire group |
425
+ | `required` | `boolean` | No | Marks the group mandatory |
426
+ | `readOnly` | `boolean` | No | Silently ignores changes |
427
+ | `layout` | `'vertical' \| 'horizontal'` | No | Radio button layout direction. `horizontal-stacked` is not supported. |
428
+
429
+ **`FuiRadio` props**
404
430
 
405
- `horizontal-stacked` layout is not supported.
431
+ | Prop | Type | Required | Description |
432
+ |------|------|----------|-------------|
433
+ | `value` | `string` | Yes | Value submitted when this option is selected |
434
+ | `label` | `string` | Yes | Label rendered next to the radio button |
435
+ | `disabled` | `boolean` | No | Disables interaction |
436
+ | `id` | `string` | No | Optional id override; auto-generated when omitted |
406
437
 
407
438
  ---
408
439
 
409
- ### `FuiInputCheckbox`
440
+ ### `FuiCheckbox`
410
441
 
411
442
  Checkbox with optional read-only mode.
412
443
 
413
444
  ```tsx
414
- <FuiInputCheckbox
445
+ <FuiCheckbox
415
446
  label="I agree to the terms"
416
- labelPosition="after"
417
447
  checked={agreed}
418
- onChange={(data) => setAgreed(!!data.checked)}
448
+ onChange={setAgreed}
419
449
  />
420
450
  ```
421
451
 
422
452
  | Prop | Type | Required | Description |
423
453
  |------|------|----------|-------------|
424
- | `onChange` | `(data: CheckboxOnChangeData) => void` | No | Change callback |
454
+ | `label` | `string` | Yes | Text label rendered next to the checkbox |
455
+ | `checked` | `boolean` | No | Checked state. Defaults to `false`. |
456
+ | `onChange` | `(checked: boolean) => void` | Yes | Change callback — receives `boolean` directly |
457
+ | `disabled` | `boolean` | No | Disables interaction |
425
458
  | `readOnly` | `boolean` | No | Visually interactive but ignores changes |
426
459
 
427
460
  ---
428
461
 
429
- ### `FuiInputSwitch`
462
+ ### `FuiSwitch`
430
463
 
431
464
  Toggle switch. `onChange` delivers a `boolean` directly.
432
465
 
433
466
  ```tsx
434
- <FuiInputSwitch
467
+ <FuiSwitch
435
468
  label="Receive notifications"
436
469
  checked={notifications}
437
470
  onChange={setNotifications}
@@ -440,14 +473,18 @@ Toggle switch. `onChange` delivers a `boolean` directly.
440
473
 
441
474
  | Prop | Type | Required | Description |
442
475
  |------|------|----------|-------------|
476
+ | `label` | `string` | Yes | Text displayed next to the switch |
477
+ | `checked` | `boolean` | No | Checked state. Defaults to `false`. |
478
+ | `defaultChecked` | `boolean` | No | Initial value for uncontrolled usage |
443
479
  | `onChange` | `(value: boolean) => void` | Yes | Change callback — receives `boolean` directly |
480
+ | `disabled` | `boolean` | No | Disables interaction |
444
481
  | `readOnly` | `boolean` | No | Silently ignores changes |
445
482
 
446
483
  ---
447
484
 
448
485
  ### `FuiInputMultiLangText`
449
486
 
450
- Text input for multi-language values. A translate icon opens a drawer with one field per configured language (up to 3). Language names are set via the `inputMultiLang.label.languages` provider config.
487
+ Text input for multi-language values. A translate icon opens a drawer with one field per configured language (up to 3).
451
488
 
452
489
  ```tsx
453
490
  <FuiInputMultiLangText
@@ -472,8 +509,8 @@ type MultiLangText = {
472
509
  | `value` | `MultiLangText \| null` | Yes | Multi-language text value |
473
510
  | `onChange` | `(value: MultiLangText \| null) => void` | Yes | Change callback |
474
511
  | `label` | `string` | Yes | Field label — also used as the drawer title |
475
- | `languages` | `string[]` | No | Names of each language slot shown in the drawer (up to 3). When fewer than 2 are provided, the translate icon is hidden. |
476
- | `textComponent` | `ComponentType<InputTextProps>` | No | Overrides the inner text component. Defaults to `FuiInputText`. |
512
+ | `langLabel` | `{ languages: string[] }` | No | Names of each language slot shown in the drawer (up to 3). When fewer than 2 are provided, the translate icon is hidden. |
513
+ | `textComponent` | `ComponentType<FuiInputTextProps>` | No | Overrides the inner text component. Defaults to `FuiInputText`. |
477
514
 
478
515
  ---
479
516
 
@@ -599,7 +636,7 @@ Tabbed panel. On mobile the tab bar becomes horizontally scrollable. Vertical la
599
636
 
600
637
  ### `FuiImageCarousel`
601
638
 
602
- Circular image carousel with autoplay and navigation controls. Tooltip labels are read from `component.imageCarousell.label` in the provider config.
639
+ Circular image carousel with autoplay and navigation controls.
603
640
 
604
641
  ```tsx
605
642
  <FuiImageCarousel
@@ -635,6 +672,270 @@ Flex row of action buttons. Collapses to full-width stacked column on mobile.
635
672
 
636
673
  ---
637
674
 
675
+ ### `FuiButton` / `FuiIconButton`
676
+
677
+ Button with appearance/size/icon. `FuiIconButton` is a thin wrapper that always renders icon-only (no children) and defaults to a `subtle` appearance, for toolbar/close-button usage.
678
+
679
+ ```tsx
680
+ <FuiButton appearance="primary" onClick={onSave}>Save</FuiButton>
681
+
682
+ <FuiIconButton aria-label="Close" icon={<DismissRegular />} onClick={onClose} />
683
+ ```
684
+
685
+ **`FuiButton` props**
686
+
687
+ | Prop | Type | Required | Description |
688
+ |------|------|----------|-------------|
689
+ | `appearance` | `'primary' \| 'outline' \| 'subtle' \| 'transparent' \| 'secondary'` | No | Defaults to `'secondary'` |
690
+ | `size` | `'small' \| 'medium' \| 'large'` | No | Defaults to `'medium'` |
691
+ | `icon` | `ReactElement` | No | Icon rendered alongside the label |
692
+ | `iconPosition` | `'before' \| 'after'` | No | Side of the label the icon is rendered on. Defaults to `'before'`. |
693
+ | `children` | `ReactNode` | No | Button label |
694
+
695
+ `FuiIconButton` takes the same props minus `children`/`iconPosition`, with `icon` and `aria-label` required.
696
+
697
+ ---
698
+
699
+ ### `FuiToggle`
700
+
701
+ Pressable toggle button (`ToggleButton`).
702
+
703
+ ```tsx
704
+ <FuiToggle checked={bold} icon={<TextBoldRegular />} onClick={() => setBold((v) => !v)} />
705
+ ```
706
+
707
+ | Prop | Type | Required | Description |
708
+ |------|------|----------|-------------|
709
+ | `checked` | `boolean` | No | Controlled pressed state |
710
+ | `defaultChecked` | `boolean` | No | Initial pressed state for uncontrolled usage. Defaults to `false`. |
711
+ | `appearance` | `'primary' \| 'outline' \| 'subtle' \| 'transparent' \| 'secondary'` | No | Defaults to `'secondary'` |
712
+ | `size` | `'small' \| 'medium' \| 'large'` | No | Defaults to `'medium'` |
713
+ | `icon` | `ReactElement` | No | Icon rendered alongside the label |
714
+ | `iconPosition` | `'before' \| 'after'` | No | Defaults to `'before'` |
715
+
716
+ ---
717
+
718
+ ### `FuiDivider`
719
+
720
+ Thin horizontal rule used to separate page sections, with an optional centered label.
721
+
722
+ ```tsx
723
+ <FuiDivider>Section Title</FuiDivider>
724
+ ```
725
+
726
+ | Prop | Type | Required | Description |
727
+ |------|------|----------|-------------|
728
+ | `children` | `ReactNode` | No | Optional label rendered centered on the divider line |
729
+
730
+ ---
731
+
732
+ ### `FuiText` and typography variants
733
+
734
+ Typography element covering FluentUI's Label/Title1/Title2/Subtitle1/Subtitle2/Body1/Body2/Caption1/Caption2. `FuiLabel`, `FuiTitle1`, `FuiTitle2`, `FuiSubTitle1`, `FuiSubTitle2`, `FuiBody1`, `FuiBody2`, `FuiCaption1`, `FuiCaption2` are `FuiText` fixed to the matching `type`.
735
+
736
+ ```tsx
737
+ <FuiTitle1>Page heading</FuiTitle1>
738
+ <FuiText type="body2" text="Body copy" />
739
+ ```
740
+
741
+ | Prop | Type | Required | Description |
742
+ |------|------|----------|-------------|
743
+ | `text` | `string` | Yes | Text content to display |
744
+ | `type` | `'label' \| 'title1' \| 'title2' \| 'subTitle1' \| 'subTitle2' \| 'body1' \| 'body2' \| 'caption1' \| 'caption2'` | No | Defaults to `'label'`. Ignored on the fixed-type variants. |
745
+ | `italic` | `boolean` | No | Defaults to `false` |
746
+ | `bold` | `boolean` | No | Overrides the type's default weight. Defaults to `false`. |
747
+ | `block` | `boolean` | No | Renders as a block-level element instead of inline. Defaults to `false`. |
748
+
749
+ ---
750
+
751
+ ### `FuiTooltip`
752
+
753
+ Wraps arbitrary content with a small message shown on hover (a `Tooltip`) or on click (a `Popover`, since Fluent's `Tooltip` has no click-triggered mode). If the wrapped element is itself disabled (`disabled` or `aria-disabled`), no affordance is added and the message never shows.
754
+
755
+ ```tsx
756
+ <FuiTooltip text="Delete this record">
757
+ <FuiIconButton aria-label="Delete" icon={<DeleteRegular />} onClick={onDelete} />
758
+ </FuiTooltip>
759
+
760
+ <FuiTooltip showOn="click" text="Copied!" position="top">
761
+ <FuiButton onClick={copyToClipboard}>Copy</FuiButton>
762
+ </FuiTooltip>
763
+ ```
764
+
765
+ | Prop | Type | Required | Description |
766
+ |------|------|----------|-------------|
767
+ | `text` | `string` | Yes | Text shown in the tooltip/popover |
768
+ | `children` | `ReactElement` | Yes | A single element to wrap |
769
+ | `showOn` | `'hover' \| 'click'` | No | Defaults to `'hover'` |
770
+ | `dismissMs` | `number` | No | When `showOn` is `'click'`, ms of inactivity before auto-dismiss. Defaults to `2000`. |
771
+ | `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | No | Defaults to the underlying component's own placement |
772
+
773
+ ---
774
+
775
+ ### `FuiAccordion` / `FuiAccordionItem`
776
+
777
+ Groups collapsible `FuiAccordionItem` panels, controlling which are expanded — single- or multi-expand.
778
+
779
+ ```tsx
780
+ <FuiAccordion value={openItem} onChange={setOpenItem} collapsible>
781
+ <FuiAccordionItem header="Details" value="details">
782
+ <DetailsForm />
783
+ </FuiAccordionItem>
784
+ <FuiAccordionItem header="Preferences" value="preferences">
785
+ <PreferencesForm />
786
+ </FuiAccordionItem>
787
+ </FuiAccordion>
788
+ ```
789
+
790
+ **`FuiAccordion` props**
791
+
792
+ | Prop | Type | Required | Description |
793
+ |------|------|----------|-------------|
794
+ | `value` / `onChange` | `string` / `(value: string) => void` | Yes | Controlled expanded value (single mode, default) |
795
+ | `value` / `onChange` (when `multiple`) | `string[]` / `(value: string[]) => void` | Yes | Controlled expanded values (multi mode) |
796
+ | `multiple` | `boolean` | No | Allow more than one panel open at once. Defaults to `false`. |
797
+ | `collapsible` | `boolean` | No | When the open panel can be collapsed to leave none open. Not applicable when `multiple`. Defaults to `false`. |
798
+ | `expandIcon` | `ReactElement` | No | Overrides the default expand/collapse icon for every item |
799
+ | `expandIconPosition` | `'start' \| 'end'` | No | Defaults to `'start'` |
800
+ | `withDivider` | `boolean` | No | Renders a divider between panels. Defaults to `true`. |
801
+
802
+ **`FuiAccordionItem` props**
803
+
804
+ | Prop | Type | Required | Description |
805
+ |------|------|----------|-------------|
806
+ | `value` | `string` | Yes | Unique identifier for this panel |
807
+ | `header` | `ReactNode` | Yes | Clickable header content |
808
+ | `disabled` | `boolean` | No | Disables toggling this item |
809
+ | `expandIcon` | `ReactElement` | No | Per-item icon override, falls back to the parent's `expandIcon` |
810
+
811
+ ---
812
+
813
+ ### `FuiCard` / `FuiCardHeader` / `FuiCardPreview` / `FuiCardFooter`
814
+
815
+ Content-display container for a single topic's header, preview and footer.
816
+
817
+ ```tsx
818
+ <FuiCard>
819
+ <FuiCardHeader header="Quarterly Report" description="Q2 2026" />
820
+ <FuiCardPreview>
821
+ <img alt="" src="/report-preview.png" />
822
+ </FuiCardPreview>
823
+ <FuiCardFooter>
824
+ <FuiButton onClick={onView}>View</FuiButton>
825
+ </FuiCardFooter>
826
+ </FuiCard>
827
+ ```
828
+
829
+ | Prop (`FuiCard`) | Type | Required | Description |
830
+ |-------------------|------|----------|-------------|
831
+ | `appearance` | `'filled' \| 'filled-alternative' \| 'outline' \| 'subtle'` | No | Defaults to `'filled'` |
832
+ | `orientation` | `'horizontal' \| 'vertical'` | No | Defaults to `'vertical'` |
833
+ | `size` | `'small' \| 'medium' \| 'large'` | No | Controls border radius and inner spacing. Defaults to `'medium'`. |
834
+
835
+ | Prop (`FuiCardHeader`) | Type | Description |
836
+ |-------------------------|------|-------------|
837
+ | `image` | `ReactElement \| string \| number` | Image or avatar related to the card |
838
+ | `header` | `ReactElement \| string \| number` | Main header title |
839
+ | `description` | `ReactElement \| string \| number` | Short description related to the title |
840
+ | `action` | `ReactElement \| string \| number` | Content at the far end, e.g. an overflow menu button |
841
+
842
+ | Prop (`FuiCardPreview`) | Type | Description |
843
+ |---------------------------|------|-------------|
844
+ | `logo` | `ReactElement \| string \| number` | Small badge overlaid on the preview content |
845
+ | `children` | `ReactNode` | The preview image or content itself |
846
+
847
+ | Prop (`FuiCardFooter`) | Type | Description |
848
+ |--------------------------|------|-------------|
849
+ | `action` | `ReactElement \| string \| number` | Content at the far end, e.g. a single icon button |
850
+ | `children` | `ReactNode` | Main footer content, e.g. action buttons |
851
+
852
+ ---
853
+
854
+ ### `FuiDrawer` / `FuiDrawerHeader` / `FuiDrawerBody`
855
+
856
+ Panel that hosts supplementary content or a management experience, dismissible ('overlay') or stacked with the page ('inline'). Forced to a bottom position on mobile regardless of `position`.
857
+
858
+ ```tsx
859
+ <FuiDrawer open={open} onOpenChange={setOpen} position="end">
860
+ <FuiDrawerHeader
861
+ title="Filters"
862
+ action={<FuiIconButton aria-label="Close" icon={<DismissRegular />} onClick={() => setOpen(false)} />}
863
+ />
864
+ <FuiDrawerBody>
865
+ <FiltersForm />
866
+ </FuiDrawerBody>
867
+ </FuiDrawer>
868
+ ```
869
+
870
+ | Prop | Type | Required | Description |
871
+ |------|------|----------|-------------|
872
+ | `open` | `boolean` | Yes | Controlled open state |
873
+ | `onOpenChange` | `(open: boolean) => void` | Yes | Fires on Escape, backdrop click, etc. |
874
+ | `position` | `'start' \| 'end' \| 'bottom'` | No | Defaults to `'start'`. Ignored (always `'bottom'`) on mobile. |
875
+ | `size` | `'small' \| 'medium' \| 'large' \| 'full'` | No | Defaults to `'small'` |
876
+ | `type` | `'overlay' \| 'inline'` | No | Defaults to `'overlay'` |
877
+ | `modalType` | `'modal' \| 'non-modal' \| 'alert'` | No | Overlay-only. Defaults to `'modal'`. |
878
+ | `separator` | `boolean` | No | Inline-only. Defaults to `false`. |
879
+
880
+ `FuiDrawerHeader` takes `title`/`action`; `FuiDrawerBody` renders scrollable main content.
881
+
882
+ ---
883
+
884
+ ### `FuiMenuBar`
885
+
886
+ A horizontal bar of dropdown menus, e.g. a desktop-app-style File/Edit/View menu.
887
+
888
+ ```tsx
889
+ <FuiMenuBar>
890
+ <FuiMenuBarMenu label="File">
891
+ <FuiMenuBarItem icon={<DocumentAddRegular />} shortcut="Ctrl+N" onClick={onNew}>
892
+ New
893
+ </FuiMenuBarItem>
894
+ <FuiMenuBarSeparator />
895
+ <FuiMenuBarCheckboxItem checked={autosave} onCheckedChange={setAutosave}>
896
+ Autosave
897
+ </FuiMenuBarCheckboxItem>
898
+ <FuiMenuBarSub label="Export as">
899
+ <FuiMenuBarRadioGroup value={format} onValueChange={setFormat}>
900
+ <FuiMenuBarRadioItem value="pdf">PDF</FuiMenuBarRadioItem>
901
+ <FuiMenuBarRadioItem value="csv">CSV</FuiMenuBarRadioItem>
902
+ </FuiMenuBarRadioGroup>
903
+ </FuiMenuBarSub>
904
+ </FuiMenuBarMenu>
905
+ </FuiMenuBar>
906
+ ```
907
+
908
+ | Component | Key props |
909
+ |---|---|
910
+ | `FuiMenuBar` | `children`: one or more `FuiMenuBarMenu` |
911
+ | `FuiMenuBarMenu` | `label`, `disabled` |
912
+ | `FuiMenuBarItem` | `icon`, `shortcut`, `disabled`, `onClick` |
913
+ | `FuiMenuBarCheckboxItem` | `checked`, `onCheckedChange`, `disabled`, `shortcut` |
914
+ | `FuiMenuBarRadioGroup` | `value`, `onValueChange` — wraps `FuiMenuBarRadioItem` children |
915
+ | `FuiMenuBarRadioItem` | `value`, `disabled` |
916
+ | `FuiMenuBarSeparator` | `className` |
917
+ | `FuiMenuBarLabel` | Non-interactive group heading |
918
+ | `FuiMenuBarSub` | `label`, `disabled` — nested dropdown, triggered from within a parent menu |
919
+
920
+ ---
921
+
922
+ ### `FuiBreadcrumb`
923
+
924
+ Renders the current trail from `useBreadcrumb()`. The last item is shown as the current page; earlier items without an action are non-interactive labels. Once there are at least 3 items, clicking the last item toggles a collapsed first > … > last view.
925
+
926
+ ```tsx
927
+ const breadcrumb = useBreadcrumb();
928
+
929
+ breadcrumb.start({ label: () => 'Home', action: () => navigate('/') });
930
+ breadcrumb.append({ label: () => 'Settings', action: () => navigate('/settings') });
931
+
932
+ <FuiBreadcrumb />
933
+ ```
934
+
935
+ `FuiBreadcrumb` takes no props — it's entirely driven by `useBreadcrumb()`.
936
+
937
+ ---
938
+
638
939
  ## Toast notifications
639
940
 
640
941
  Use the `useToast()` hook to show non-blocking feedback.