handy-fluentui 0.1.1 → 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 +330 -31
  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 -14
  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,7 +7,7 @@ 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
- **Live showcase:** [kc2wong.github.io/handy-fluentui](https://kc2wong.github.io/handy-fluentui/)
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
11
 
12
12
  ---
13
13
 
@@ -90,6 +90,7 @@ Component-specific labels (`FuiTable` pagination text, `FuiImageCarousel` toolti
90
90
  |------|---------|-------------|
91
91
  | `useTheme()` | `{ currentTheme, switchTheme }` | Read and change the active theme |
92
92
  | `useIsMobile()` | `boolean` | True when viewport ≤ `mobileBreakpoint` |
93
+ | `useBreadcrumb()` | `{ items, isCollapsed, toggleCollapsed, start, append, peek, popTill }` | Read and update the trail rendered by `FuiBreadcrumb` |
93
94
  | `useToast()` | `{ success, error, info, warning }` | Show toast notifications |
94
95
  | `useSpinner()` | `{ show, hide }` | Show/hide the global overlay spinner |
95
96
  | `useDialog()` | `{ openDialog }` | Show an imperative confirmation dialog |
@@ -142,7 +143,7 @@ type LocalDate = {
142
143
 
143
144
  ## Common field props (`FieldLayoutProps`)
144
145
 
145
- 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:
146
147
 
147
148
  | Prop | Type | Default | Description |
148
149
  |------|------|---------|-------------|
@@ -189,6 +190,8 @@ Text input with optional show/hide toggle for passwords.
189
190
  | `value` | `string \| null` | Yes | Current value |
190
191
  | `onChange` | `(value: string \| null) => void` | Yes | Change callback |
191
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'`. |
192
195
 
193
196
  ---
194
197
 
@@ -211,6 +214,8 @@ Multi-line text area with optional character counter.
211
214
  | `value` | `string \| null` | Yes | Current value |
212
215
  | `onChange` | `(value: string \| null) => void` | Yes | Change callback |
213
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>` |
214
219
 
215
220
  ---
216
221
 
@@ -251,6 +256,8 @@ Number input with keystroke filtering and optional SpinButton mode.
251
256
  | `max` | `number` | No | Maximum value |
252
257
  | `allowNegative` | `boolean` | No | When `false`, blocks the minus key. Defaults to `true`. |
253
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 |
254
261
 
255
262
  ---
256
263
 
@@ -270,8 +277,10 @@ Date picker. Renders a FluentUI `DatePicker` on desktop and a bottom-sheet calen
270
277
  | Prop | Type | Required | Description |
271
278
  |------|------|----------|-------------|
272
279
  | `value` | `Date \| null` | Yes | Selected date |
273
- | `onChange` | `(date: Date \| null \| undefined) => void` | Yes | Change callback |
280
+ | `onChange` | `(date: Date \| null) => void` | Yes | Change callback |
274
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 |
275
284
  | `readOnly` | `boolean` | No | Suppresses the calendar popup/drawer. Desktop renders a plain read-only `Input`; mobile hides the calendar icon and ignores clicks. |
276
285
 
277
286
  ---
@@ -281,7 +290,7 @@ Date picker. Renders a FluentUI `DatePicker` on desktop and a bottom-sheet calen
281
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.
282
291
 
283
292
  ```tsx
284
- import { FuiInputTime, FuiTime } from './components/input-time';
293
+ import { FuiInputTime, FuiTime } from './components/fui-input-time';
285
294
 
286
295
  const [shiftStart, setShiftStart] = useState<FuiTime | null>(null);
287
296
 
@@ -366,12 +375,17 @@ const options = [
366
375
  |------|------|----------|-------------|
367
376
  | `value` | `string \| string[] \| null` | Yes | Selected value(s) |
368
377
  | `onChange` | `(value: string \| string[] \| null) => void` | Yes | Change callback |
369
- | `options` | `InputDropdownOption[]` | Yes | Option list |
370
- | `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 |
371
382
  | `readOnly` | `boolean` | No | Silently ignores selection changes |
372
- | `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 |
373
387
 
374
- **`InputDropdownOption`**
388
+ **`FuiInputDropdownOption`**
375
389
 
376
390
  | Prop | Type | Required | Description |
377
391
  |------|------|----------|-------------|
@@ -382,58 +396,75 @@ const options = [
382
396
 
383
397
  ---
384
398
 
385
- ### `FuiInputRadio`
399
+ ### `FuiRadioGroup` / `FuiRadio`
386
400
 
387
- Radio group with a shared label.
401
+ Radio group with a shared label. `FuiRadio` items are independent components (not raw Fluent `<Radio>` elements).
388
402
 
389
403
  ```tsx
390
- <FuiInputRadio
404
+ <FuiRadioGroup
391
405
  label="Gender"
392
406
  value={gender}
393
- onChange={(data) => setGender(data.value)}
394
- direction="horizontal"
407
+ onChange={setGender}
408
+ layout="horizontal"
395
409
  >
396
- <Radio label="Male" value="male" />
397
- <Radio label="Female" value="female" />
398
- <Radio label="Other" value="other" />
399
- </FuiInputRadio>
410
+ <FuiRadio label="Male" value="male" />
411
+ <FuiRadio label="Female" value="female" />
412
+ <FuiRadio label="Other" value="other" />
413
+ </FuiRadioGroup>
400
414
  ```
401
415
 
416
+ **`FuiRadioGroup` props**
417
+
402
418
  | Prop | Type | Required | Description |
403
419
  |------|------|----------|-------------|
404
- | `onChange` | `(data: RadioGroupOnChangeData) => void` | No | Change callback |
405
- | `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. |
406
428
 
407
- `horizontal-stacked` layout is not supported.
429
+ **`FuiRadio` props**
430
+
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 |
408
437
 
409
438
  ---
410
439
 
411
- ### `FuiInputCheckbox`
440
+ ### `FuiCheckbox`
412
441
 
413
442
  Checkbox with optional read-only mode.
414
443
 
415
444
  ```tsx
416
- <FuiInputCheckbox
445
+ <FuiCheckbox
417
446
  label="I agree to the terms"
418
- labelPosition="after"
419
447
  checked={agreed}
420
- onChange={(data) => setAgreed(!!data.checked)}
448
+ onChange={setAgreed}
421
449
  />
422
450
  ```
423
451
 
424
452
  | Prop | Type | Required | Description |
425
453
  |------|------|----------|-------------|
426
- | `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 |
427
458
  | `readOnly` | `boolean` | No | Visually interactive but ignores changes |
428
459
 
429
460
  ---
430
461
 
431
- ### `FuiInputSwitch`
462
+ ### `FuiSwitch`
432
463
 
433
464
  Toggle switch. `onChange` delivers a `boolean` directly.
434
465
 
435
466
  ```tsx
436
- <FuiInputSwitch
467
+ <FuiSwitch
437
468
  label="Receive notifications"
438
469
  checked={notifications}
439
470
  onChange={setNotifications}
@@ -442,14 +473,18 @@ Toggle switch. `onChange` delivers a `boolean` directly.
442
473
 
443
474
  | Prop | Type | Required | Description |
444
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 |
445
479
  | `onChange` | `(value: boolean) => void` | Yes | Change callback — receives `boolean` directly |
480
+ | `disabled` | `boolean` | No | Disables interaction |
446
481
  | `readOnly` | `boolean` | No | Silently ignores changes |
447
482
 
448
483
  ---
449
484
 
450
485
  ### `FuiInputMultiLangText`
451
486
 
452
- 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).
453
488
 
454
489
  ```tsx
455
490
  <FuiInputMultiLangText
@@ -474,8 +509,8 @@ type MultiLangText = {
474
509
  | `value` | `MultiLangText \| null` | Yes | Multi-language text value |
475
510
  | `onChange` | `(value: MultiLangText \| null) => void` | Yes | Change callback |
476
511
  | `label` | `string` | Yes | Field label — also used as the drawer title |
477
- | `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. |
478
- | `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`. |
479
514
 
480
515
  ---
481
516
 
@@ -601,7 +636,7 @@ Tabbed panel. On mobile the tab bar becomes horizontally scrollable. Vertical la
601
636
 
602
637
  ### `FuiImageCarousel`
603
638
 
604
- 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.
605
640
 
606
641
  ```tsx
607
642
  <FuiImageCarousel
@@ -637,6 +672,270 @@ Flex row of action buttons. Collapses to full-width stacked column on mobile.
637
672
 
638
673
  ---
639
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
+
640
939
  ## Toast notifications
641
940
 
642
941
  Use the `useToast()` hook to show non-blocking feedback.