@twentyfirstmedia/ui-core 0.5.0 → 0.6.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 (148) hide show
  1. package/dist/components/admin.d.ts.map +1 -1
  2. package/dist/components/admin.js +31 -21
  3. package/dist/components/admin.js.map +1 -1
  4. package/dist/components/agents.d.ts.map +1 -1
  5. package/dist/components/agents.js +21 -18
  6. package/dist/components/agents.js.map +1 -1
  7. package/dist/components/app-chrome.d.ts.map +1 -1
  8. package/dist/components/app-chrome.js +5 -5
  9. package/dist/components/app-chrome.js.map +1 -1
  10. package/dist/components/approvals.d.ts.map +1 -1
  11. package/dist/components/approvals.js +2 -2
  12. package/dist/components/approvals.js.map +1 -1
  13. package/dist/components/building-blocks.d.ts.map +1 -1
  14. package/dist/components/building-blocks.js +13 -10
  15. package/dist/components/building-blocks.js.map +1 -1
  16. package/dist/components/charts.d.ts +2 -2
  17. package/dist/components/charts.d.ts.map +1 -1
  18. package/dist/components/charts.js +14 -11
  19. package/dist/components/charts.js.map +1 -1
  20. package/dist/components/chat.d.ts +2 -2
  21. package/dist/components/chat.d.ts.map +1 -1
  22. package/dist/components/chat.js +22 -18
  23. package/dist/components/chat.js.map +1 -1
  24. package/dist/components/commerce.d.ts.map +1 -1
  25. package/dist/components/commerce.js +20 -16
  26. package/dist/components/commerce.js.map +1 -1
  27. package/dist/components/data-display.d.ts +0 -5
  28. package/dist/components/data-display.d.ts.map +1 -1
  29. package/dist/components/data-display.js +41 -22
  30. package/dist/components/data-display.js.map +1 -1
  31. package/dist/components/data-table.d.ts +2 -2
  32. package/dist/components/data-table.d.ts.map +1 -1
  33. package/dist/components/data-table.js +3 -3
  34. package/dist/components/data-table.js.map +1 -1
  35. package/dist/components/developer.d.ts.map +1 -1
  36. package/dist/components/developer.js +7 -6
  37. package/dist/components/developer.js.map +1 -1
  38. package/dist/components/feedback.d.ts.map +1 -1
  39. package/dist/components/feedback.js +5 -4
  40. package/dist/components/feedback.js.map +1 -1
  41. package/dist/components/forms.d.ts.map +1 -1
  42. package/dist/components/forms.js +63 -21
  43. package/dist/components/forms.js.map +1 -1
  44. package/dist/components/foundation.d.ts.map +1 -1
  45. package/dist/components/foundation.js +13 -9
  46. package/dist/components/foundation.js.map +1 -1
  47. package/dist/components/index.d.ts +2 -1
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/components/index.js +2 -1
  50. package/dist/components/index.js.map +1 -1
  51. package/dist/components/inputs.d.ts.map +1 -1
  52. package/dist/components/inputs.js +33 -8
  53. package/dist/components/inputs.js.map +1 -1
  54. package/dist/components/mail.d.ts.map +1 -1
  55. package/dist/components/mail.js +5 -5
  56. package/dist/components/mail.js.map +1 -1
  57. package/dist/components/media.d.ts.map +1 -1
  58. package/dist/components/media.js +62 -11
  59. package/dist/components/media.js.map +1 -1
  60. package/dist/components/navigation.d.ts +3 -1
  61. package/dist/components/navigation.d.ts.map +1 -1
  62. package/dist/components/navigation.js +287 -20
  63. package/dist/components/navigation.js.map +1 -1
  64. package/dist/components/operations.d.ts.map +1 -1
  65. package/dist/components/operations.js +10 -10
  66. package/dist/components/operations.js.map +1 -1
  67. package/dist/components/overlays.d.ts.map +1 -1
  68. package/dist/components/overlays.js +2 -2
  69. package/dist/components/overlays.js.map +1 -1
  70. package/dist/components/previews.js +2 -2
  71. package/dist/components/previews.js.map +1 -1
  72. package/dist/components/primitives.js +2 -2
  73. package/dist/components/primitives.js.map +1 -1
  74. package/dist/components/qr-code.d.ts +8 -0
  75. package/dist/components/qr-code.d.ts.map +1 -0
  76. package/dist/components/qr-code.js +18 -0
  77. package/dist/components/qr-code.js.map +1 -0
  78. package/dist/components/real-estate.d.ts.map +1 -1
  79. package/dist/components/real-estate.js +5 -5
  80. package/dist/components/real-estate.js.map +1 -1
  81. package/dist/components/rich-text-editor.d.ts.map +1 -1
  82. package/dist/components/rich-text-editor.js +1 -1
  83. package/dist/components/rich-text-editor.js.map +1 -1
  84. package/dist/components/select-menu.js +1 -1
  85. package/dist/components/select-menu.js.map +1 -1
  86. package/dist/components/shell.d.ts +0 -4
  87. package/dist/components/shell.d.ts.map +1 -1
  88. package/dist/components/shell.js +100 -7
  89. package/dist/components/shell.js.map +1 -1
  90. package/dist/components/site.d.ts.map +1 -1
  91. package/dist/components/site.js +8 -6
  92. package/dist/components/site.js.map +1 -1
  93. package/dist/components/status.d.ts.map +1 -1
  94. package/dist/components/status.js +2 -2
  95. package/dist/components/status.js.map +1 -1
  96. package/dist/components/system.d.ts.map +1 -1
  97. package/dist/components/system.js +3 -2
  98. package/dist/components/system.js.map +1 -1
  99. package/dist/components/types.d.ts +257 -18
  100. package/dist/components/types.d.ts.map +1 -1
  101. package/dist/components/workspace.d.ts.map +1 -1
  102. package/dist/components/workspace.js +15 -12
  103. package/dist/components/workspace.js.map +1 -1
  104. package/dist/qr.d.ts +14 -0
  105. package/dist/qr.d.ts.map +1 -0
  106. package/dist/qr.js +298 -0
  107. package/dist/qr.js.map +1 -0
  108. package/dist/strings.d.ts +30 -4
  109. package/dist/strings.d.ts.map +1 -1
  110. package/dist/strings.js +15 -0
  111. package/dist/strings.js.map +1 -1
  112. package/dist/themes.css +2345 -176
  113. package/package.json +2 -1
  114. package/src/components/admin.tsx +66 -23
  115. package/src/components/agents.tsx +22 -20
  116. package/src/components/app-chrome.tsx +9 -5
  117. package/src/components/approvals.tsx +3 -1
  118. package/src/components/building-blocks.tsx +24 -13
  119. package/src/components/charts.tsx +17 -12
  120. package/src/components/chat.tsx +54 -31
  121. package/src/components/commerce.tsx +58 -24
  122. package/src/components/data-display.tsx +106 -38
  123. package/src/components/data-table.tsx +11 -7
  124. package/src/components/developer.tsx +15 -6
  125. package/src/components/feedback.tsx +5 -3
  126. package/src/components/forms.tsx +101 -23
  127. package/src/components/foundation.tsx +9 -5
  128. package/src/components/index.ts +10 -1
  129. package/src/components/inputs.tsx +44 -7
  130. package/src/components/mail.tsx +7 -3
  131. package/src/components/media.tsx +88 -16
  132. package/src/components/navigation.tsx +651 -71
  133. package/src/components/operations.tsx +17 -8
  134. package/src/components/overlays.tsx +7 -2
  135. package/src/components/previews.tsx +2 -2
  136. package/src/components/primitives.tsx +2 -2
  137. package/src/components/qr-code.tsx +30 -0
  138. package/src/components/real-estate.tsx +7 -4
  139. package/src/components/rich-text-editor.tsx +7 -1
  140. package/src/components/select-menu.tsx +1 -1
  141. package/src/components/shell.tsx +237 -11
  142. package/src/components/site.tsx +18 -4
  143. package/src/components/status.tsx +2 -0
  144. package/src/components/system.tsx +3 -2
  145. package/src/components/types.ts +261 -18
  146. package/src/components/workspace.tsx +37 -15
  147. package/src/qr.ts +300 -0
  148. package/src/strings.ts +56 -1
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import { cx } from "../cx.js";
2
3
  import { Button, TintIcon } from "./foundation.js";
3
4
  import type { AuthCardProps, IconName, StatusScreenProps, Tint } from "./types.js";
4
5
 
@@ -40,7 +41,7 @@ export function StatusScreen(p: StatusScreenProps) {
40
41
  } as Record<string, [string, string, IconName, Tint]>
41
42
  )[code] || [p.title, p.text, "sparkle", "sky"];
42
43
  return (
43
- <div className="tf-status-screen">
44
+ <div style={p.style} className={cx("tf-status-screen", p.className)}>
44
45
  <div className="tf-status-art" aria-hidden>
45
46
  {/^\d+$/.test(code) && <span className="tf-status-code">{code}</span>}
46
47
  <TintIcon icon={p.icon || d[2]} tint={p.tint || d[3]} size={56} />
@@ -70,7 +71,7 @@ export function StatusScreen(p: StatusScreenProps) {
70
71
 
71
72
  export function AuthCard(p: AuthCardProps) {
72
73
  return (
73
- <div className="tf-auth">
74
+ <div style={p.style} className={cx("tf-auth", p.className)}>
74
75
  {p.brand && (
75
76
  <div className="tf-auth-brand">
76
77
  <span className="tf-brand-word">{p.brand}</span>
@@ -1,4 +1,14 @@
1
- import type { ChangeEvent, CSSProperties, MouseEvent, ReactNode } from "react";
1
+ import type {
2
+ ChangeEvent,
3
+ CSSProperties,
4
+ ElementType,
5
+ FocusEvent,
6
+ InputHTMLAttributes,
7
+ KeyboardEvent,
8
+ MouseEvent,
9
+ ReactNode,
10
+ Ref,
11
+ } from "react";
2
12
  import type { IconName } from "../icons.js";
3
13
  import type { ToastOptions } from "../toast-store.js";
4
14
  import type { Surface } from "../provider.js";
@@ -137,9 +147,21 @@ export interface SearchFieldProps extends BaseProps {
137
147
  placeholder?: string;
138
148
  /** Accessible name (default: the placeholder, or "Search"). */
139
149
  label?: string;
150
+ /** The key hint, e.g. "⌘K" (hidden on touch screens). */
140
151
  shortcut?: string;
152
+ /** ⌘/Ctrl + the hint's last key (K for "⌘K") focuses the field, except while typing elsewhere. */
153
+ bindShortcut?: boolean;
141
154
  value?: string;
155
+ defaultValue?: string;
142
156
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
157
+ onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
158
+ onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
159
+ onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
160
+ id?: string;
161
+ name?: string;
162
+ autoFocus?: boolean;
163
+ /** For a search with suggestions (combobox): role, aria-controls, aria-activedescendant … */
164
+ inputProps?: InputHTMLAttributes<HTMLInputElement>;
143
165
  }
144
166
  export interface CheckboxProps extends BaseProps {
145
167
  name?: string;
@@ -214,7 +236,10 @@ export interface PanelProps extends BaseProps {
214
236
  surface?: Surface;
215
237
  }
216
238
  export interface PageHeaderProps extends BaseProps {
217
- crumbs?: string[];
239
+ /** A breadcrumb above the title (see `Breadcrumb`): labels, or items with `href`/`onClick`. */
240
+ crumbs?: (string | Crumb)[];
241
+ /** The element crumb links render as, e.g. Next.js `Link`. */
242
+ crumbLinkAs?: ElementType;
218
243
  title: string;
219
244
  subtitle?: string;
220
245
  actions?: ReactNode[];
@@ -225,7 +250,8 @@ export interface NavItem {
225
250
  count?: number;
226
251
  }
227
252
  export interface TopBarProps extends BaseProps {
228
- brand?: string;
253
+ /** The product name, or a node (a logo mark with the name). */
254
+ brand?: ReactNode;
229
255
  tag?: string;
230
256
  items: NavItem[];
231
257
  active?: string;
@@ -245,6 +271,14 @@ export interface RailItem {
245
271
  export interface SideRailProps extends BaseProps {
246
272
  top?: RailItem[];
247
273
  bottom?: RailItem[];
274
+ /** Names next to the icons (controlled); every button has a tooltip while collapsed. */
275
+ expanded?: boolean;
276
+ defaultExpanded?: boolean;
277
+ onExpandedChange?: (expanded: boolean) => void;
278
+ /** A button at the foot that expands and collapses the rail. */
279
+ toggle?: boolean;
280
+ /** Accessible name of the rail (default "Tools"). */
281
+ label?: string;
248
282
  }
249
283
  export interface FloatingLayoutProps extends BaseProps {
250
284
  /** What the UI floats over, filling the layout: a map, a 3D canvas, a video, a photo. */
@@ -257,7 +291,23 @@ export interface FloatingLayoutProps extends BaseProps {
257
291
  end?: ReactNode;
258
292
  /** Bar along the bottom edge (dock, playback, a summary). */
259
293
  bottom?: ReactNode;
260
- /** Height of the layout (default 640). Below 720px of width the slots stack: top, start, end, bottom. */
294
+ /** Controls of the background (zoom, compass, locate) in the free corner above the bottom bar. */
295
+ controls?: ReactNode;
296
+ /** Source notes of the background (map attribution), under `controls`, clear of every slot. */
297
+ attribution?: ReactNode;
298
+ /** Width of the start column (default 320px) and of the end column (default 340px). */
299
+ startWidth?: number | string;
300
+ endWidth?: number | string;
301
+ /**
302
+ * Below 720px of width the end slot becomes a bottom sheet over the background, with a handle
303
+ * that expands and collapses it (default true). `false` stacks it with the others.
304
+ */
305
+ endSheet?: boolean;
306
+ /**
307
+ * Height of the layout (default 640). Below 720px of width the slots stack: top, start, end,
308
+ * bottom. The layout sets `--floating-safe-top/-start/-end/-bottom` (px) to the space its
309
+ * slots take, so map libraries can keep their own controls clear of them.
310
+ */
261
311
  height?: number | string;
262
312
  /** Glass for every surface in the slots (default) or solid surfaces. */
263
313
  surface?: Surface;
@@ -304,6 +354,11 @@ export interface TableProps extends BaseProps {
304
354
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- rows are app data
305
355
  rows: Record<string, any>[];
306
356
  animate?: boolean;
357
+ /**
358
+ * Cards instead of columns when the table's box is narrow (up to 600px): every row stacks its
359
+ * cells, each with its column label. Without it narrow tables scroll sideways.
360
+ */
361
+ stack?: boolean;
307
362
  }
308
363
  export interface ProgressProps extends BaseProps {
309
364
  value: number;
@@ -324,8 +379,21 @@ export interface EmptyStateProps extends BaseProps {
324
379
  tint?: Tint;
325
380
  action?: ReactNode;
326
381
  }
382
+ /** A breadcrumb: a plain label, or one with an address or a handler. */
383
+ export interface Crumb {
384
+ label: string;
385
+ href?: string;
386
+ onClick?: () => void;
387
+ }
327
388
  export interface BreadcrumbProps extends BaseProps {
328
- items: string[];
389
+ /** The trail; the last one is the current page. Items with `href` are links, with `onClick` buttons. */
390
+ items: (string | Crumb)[];
391
+ /** The element links render as (default `a`), e.g. Next.js `Link`. */
392
+ linkAs?: ElementType;
393
+ /** Makes every crumb but the last a button; receives its index. */
394
+ onNavigate?: (index: number) => void;
395
+ /** Accessible name of the navigation (default "Breadcrumb"). */
396
+ label?: string;
329
397
  }
330
398
  export interface PaginationProps extends BaseProps {
331
399
  /** Number of pages. */
@@ -339,10 +407,17 @@ export interface PaginationProps extends BaseProps {
339
407
  defaultPage?: number;
340
408
  /** A page was picked (previous, next or a number); receives the new 1-based page. */
341
409
  onChange?: (page: number) => void;
410
+ /** Link mode for server-rendered lists: the address of a page; pages become links. */
411
+ href?: (page: number) => string;
412
+ /** The element links render as (default `a`), e.g. Next.js `Link`. */
413
+ linkAs?: ElementType;
414
+ /** Accessible name of the navigation (default "Pagination"), e.g. "Search results pages". */
415
+ label?: string;
342
416
  }
343
417
  export interface AppShellProps extends BaseProps {
344
418
  active?: string;
345
- brand?: string;
419
+ /** The product name, or a node (a logo mark with the name; collapsed sidebars show it whole). */
420
+ brand?: ReactNode;
346
421
  tag?: string;
347
422
  nav?: NavItem[];
348
423
  user?: { name: string; email?: string };
@@ -355,6 +430,19 @@ export interface AppShellProps extends BaseProps {
355
430
  dockItems?: DockProps["items"];
356
431
  railTop?: RailItem[];
357
432
  railBottom?: RailItem[];
433
+ /**
434
+ * `top` (default): navigation in the top bar, tools in the side rail. `sidebar`: a labelled
435
+ * sidebar on the frame, with groups, nesting and a drawer on phones.
436
+ */
437
+ navigation?: "top" | "sidebar";
438
+ /** With `navigation="sidebar"`: groups instead of the flat `nav` list. */
439
+ sidebarGroups?: SidebarGroup[];
440
+ /** With `navigation="sidebar"`: more sidebar props (collapsed, storageKey, shortcut, linkAs …). */
441
+ sidebar?: Partial<Omit<SidebarProps, "variant" | "groups">>;
442
+ /** With `navigation="sidebar"`: the workspace switcher at the top instead of the brand. */
443
+ workspaces?: WorkspaceSwitcherProps;
444
+ /** The account menu's entries (with `navigation="sidebar"` at the foot of the sidebar). */
445
+ userMenu?: (MenuItem | "-")[];
358
446
  children?: ReactNode;
359
447
  }
360
448
  export interface StaggerProps extends BaseProps {
@@ -372,6 +460,8 @@ export interface CountUpProps extends BaseProps {
372
460
  duration?: number;
373
461
  delay?: number;
374
462
  className?: string;
463
+ /** `false`: show the value without counting (it is always the value on the server). */
464
+ animate?: boolean;
375
465
  }
376
466
  export interface ListRowProps extends BaseProps {
377
467
  avatar?: string;
@@ -385,6 +475,18 @@ export interface ListRowProps extends BaseProps {
385
475
  unread?: boolean;
386
476
  active?: boolean;
387
477
  onClick?: () => void;
478
+ /** Makes the row a link (with `linkAs` for a router's link). */
479
+ href?: string;
480
+ linkAs?: ElementType;
481
+ /**
482
+ * Buttons of the row's own (rename, remove …), outside the clickable area so nothing interactive
483
+ * is nested. Without `onClick` and `href` the row is not interactive at all.
484
+ */
485
+ actions?: ReactNode;
486
+ /** Lines the title may take before it ends in an ellipsis (default: it wraps freely). */
487
+ titleLines?: number;
488
+ /** Lines the subtitle may take (default 1); badges and facts wrap within them. */
489
+ subtitleLines?: number;
388
490
  }
389
491
  export interface TimelineProps extends BaseProps {
390
492
  items: {
@@ -673,6 +775,14 @@ export interface TextFieldProps extends Omit<FormFieldProps, "children"> {
673
775
  inputMode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search";
674
776
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
675
777
  onBlur?: (e: ChangeEvent<HTMLInputElement>) => void;
778
+ onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
779
+ /**
780
+ * An interactive control at the end of the field (an `IconButton`: clear, copy, locate …). Unlike
781
+ * `suffix` it can be clicked, and it stays when the field shows an error or success mark.
782
+ */
783
+ action?: ReactNode;
784
+ /** For `type="password"`: a button that shows and hides the password (default true). */
785
+ reveal?: boolean;
676
786
  }
677
787
  export interface RadioGroupProps extends BaseProps {
678
788
  label?: string;
@@ -766,10 +876,18 @@ export interface SliderProps extends BaseProps {
766
876
  marks?: boolean;
767
877
  hint?: string;
768
878
  }
879
+ export interface OtpInputHandle {
880
+ /** Empties every cell and focuses the first. */
881
+ reset: () => void;
882
+ }
769
883
  export interface OtpInputProps extends BaseProps {
770
884
  length?: number;
771
885
  onComplete?: (code: string) => void;
772
886
  error?: boolean;
887
+ /** Empties the cells when `error` turns on, ready for the next try (default true). */
888
+ clearOnError?: boolean;
889
+ /** `ref.current.reset()` empties the cells on demand. */
890
+ ref?: Ref<OtpInputHandle>;
773
891
  label?: string;
774
892
  }
775
893
  export interface UploadFile {
@@ -789,9 +907,10 @@ export interface FileDropProps extends BaseProps {
789
907
  /**
790
908
  * The person picked or dropped files: the real `File` objects, after `accept` and `multiple`.
791
909
  * Upload them yourself. To show progress, control `files` and set each `progress`; an
792
- * uncontrolled list shows them with their size only.
910
+ * uncontrolled list shows them with their size only. Return `false` to list none of them, or
911
+ * the files to list (for example after checking their size).
793
912
  */
794
- onFiles?: (files: File[]) => void;
913
+ onFiles?: (files: File[]) => void | boolean | File[];
795
914
  /** Dropped files that do not match `accept` (show an inline Alert). */
796
915
  onReject?: (files: File[]) => void;
797
916
  /** A listed file was removed or its upload cancelled. */
@@ -806,6 +925,10 @@ export interface FileDropProps extends BaseProps {
806
925
  /** Accepted types, as for `<input type="file">` ("image/*,.pdf"); also checked on drop. */
807
926
  accept?: string;
808
927
  multiple?: boolean;
928
+ /** No picking or dropping; the list stays visible. */
929
+ disabled?: boolean;
930
+ /** Takes part in a surrounding form under this name: the listed files are submitted with it. */
931
+ name?: string;
809
932
  }
810
933
  export interface TooltipProps extends BaseProps {
811
934
  label: ReactNode;
@@ -962,6 +1085,8 @@ export interface LegendProps extends BaseProps {
962
1085
  /* ── Kit 3: AI chat & agents ── */
963
1086
  export interface Source {
964
1087
  title: string;
1088
+ /** More about the source in a tooltip: dataset, publisher, date ("Stand") … */
1089
+ detail?: ReactNode;
965
1090
  icon?: IconName;
966
1091
  url?: string;
967
1092
  onClick?: () => void;
@@ -1175,6 +1300,11 @@ export interface CheckoutStepsProps extends BaseProps {
1175
1300
  steps?: string[];
1176
1301
  current?: number;
1177
1302
  onPick?: (i: number) => void;
1303
+ /**
1304
+ * Numbers only, with the label of the current step (screen readers still hear every label).
1305
+ * Narrow containers (up to 560px) get this on their own.
1306
+ */
1307
+ compact?: boolean;
1178
1308
  }
1179
1309
  export interface OrderTimelineProps extends BaseProps {
1180
1310
  status: "Placed" | "Paid" | "Packed" | "Shipped" | "Delivered" | "Refunded" | "Cancelled";
@@ -1183,7 +1313,15 @@ export interface OrderTimelineProps extends BaseProps {
1183
1313
  export interface PlanCardProps extends BaseProps {
1184
1314
  name: string;
1185
1315
  text?: string;
1186
- price: number | null;
1316
+ /**
1317
+ * A number is formatted as money (0 = "Free", null = "Let's talk"); any other node is shown as
1318
+ * it is, e.g. a placeholder ("—") or a price the app formatted itself.
1319
+ */
1320
+ price: number | null | ReactNode;
1321
+ /** Before the price, e.g. "from". */
1322
+ pricePrefix?: ReactNode;
1323
+ /** Under the price: net or gross, billed yearly, VAT note … */
1324
+ priceNote?: ReactNode;
1187
1325
  was?: number;
1188
1326
  per?: string;
1189
1327
  features?: string[];
@@ -1194,6 +1332,9 @@ export interface PlanCardProps extends BaseProps {
1194
1332
  /** Disable the button, e.g. while a checkout starts or when the plan cannot be picked. */
1195
1333
  disabled?: boolean;
1196
1334
  onPick?: () => void;
1335
+ /** The action as a link (checkout or sign-up page), with `linkAs` for a router's link. */
1336
+ href?: string;
1337
+ linkAs?: ElementType;
1197
1338
  }
1198
1339
 
1199
1340
  /* ── Kit 3: system screens ── */
@@ -1885,6 +2026,8 @@ export interface TilePickerProps extends BaseProps {
1885
2026
  multiple?: boolean;
1886
2027
  min?: number;
1887
2028
  label?: string;
2029
+ /** `list`: one compact row per option (small picture or icon beside the label), e.g. on phones. */
2030
+ layout?: "grid" | "list";
1888
2031
  }
1889
2032
  export interface ColorFieldProps extends BaseProps {
1890
2033
  label?: ReactNode;
@@ -2134,6 +2277,8 @@ export interface WorkspaceSwitcherProps extends BaseProps {
2134
2277
  footer?: ReactNode;
2135
2278
  variant?: "frame";
2136
2279
  width?: number;
2280
+ /** Only the workspace's mark, its name as the accessible name (a collapsed sidebar). */
2281
+ compact?: boolean;
2137
2282
  }
2138
2283
  export interface ExportDialogProps extends BaseProps {
2139
2284
  open?: boolean;
@@ -2158,6 +2303,12 @@ export interface ExportDialogProps extends BaseProps {
2158
2303
  }
2159
2304
  export interface TwoFactorSetupProps extends BaseProps {
2160
2305
  qr?: ReactNode;
2306
+ /** The otpauth:// link: the kit draws its QR code (instead of `qr`). */
2307
+ qrValue?: string;
2308
+ /** A "Trust this device" checkbox under the code field; a string replaces its label. */
2309
+ trustDevice?: boolean | string;
2310
+ /** The trust checkbox changed. */
2311
+ onTrustChange?: (trusted: boolean) => void;
2161
2312
  secret: string;
2162
2313
  onVerify?: (code: string) => boolean | Promise<boolean>;
2163
2314
  codes?: string[];
@@ -2214,7 +2365,13 @@ export interface GalleryProps extends BaseProps {
2214
2365
  loading?: "lazy" | "eager";
2215
2366
  /** `referrerPolicy` of every image, e.g. `"no-referrer"` for photos from other hosts. */
2216
2367
  referrerPolicy?: ImageReferrerPolicy;
2368
+ /** First image when uncontrolled. */
2217
2369
  start?: number;
2370
+ /** The shown image (controlled, 0-based), with `onIndexChange`. */
2371
+ index?: number;
2372
+ onIndexChange?: (index: number) => void;
2373
+ /** Accessible name of the gallery (default "Images"), e.g. the listing's title. */
2374
+ label?: string;
2218
2375
  ratio?: string;
2219
2376
  thumbs?: boolean;
2220
2377
  fullscreen?: boolean;
@@ -2356,13 +2513,21 @@ export interface CookieConsentProps extends BaseProps {
2356
2513
  categories?: {
2357
2514
  id: string;
2358
2515
  label: string;
2359
- text?: string;
2516
+ /** What the category covers; may hold a link to the details. */
2517
+ text?: ReactNode;
2360
2518
  required?: boolean;
2361
2519
  default?: boolean;
2362
2520
  }[];
2363
2521
  onSave?: (choices: Record<string, boolean>) => void;
2364
2522
  open?: boolean;
2365
2523
  inline?: boolean;
2524
+ /**
2525
+ * "Only necessary" and "Accept all" with the same weight (both secondary buttons), as German
2526
+ * data protection authorities expect on the first layer.
2527
+ */
2528
+ equalChoice?: boolean;
2529
+ /** Id of the banner (its text is referenced by `aria-describedby`). */
2530
+ id?: string;
2366
2531
  }
2367
2532
  export interface AnnouncementBarProps extends BaseProps {
2368
2533
  children: ReactNode;
@@ -2432,6 +2597,8 @@ export interface ToggleGroupProps extends BaseProps {
2432
2597
  /** Accessible name of the group. */
2433
2598
  label: string;
2434
2599
  size?: "sm" | "md";
2600
+ /** Fills the width and shares it evenly; labels end in an ellipsis when space runs short. */
2601
+ block?: boolean;
2435
2602
  }
2436
2603
  export interface ButtonGroupProps extends BaseProps {
2437
2604
  /** Accessible name of the group. */
@@ -2515,6 +2682,8 @@ export interface SelectMenuProps extends FieldProps, BaseProps {
2515
2682
  onChange?: (value: string) => void;
2516
2683
  placeholder?: string;
2517
2684
  disabled?: boolean;
2685
+ /** `sm`: a compact trigger, e.g. a sort menu next to a heading. */
2686
+ size?: "sm" | "md";
2518
2687
  }
2519
2688
 
2520
2689
  /* ── Navigation ── */
@@ -2551,31 +2720,91 @@ export interface NavigationMenuProps extends BaseProps {
2551
2720
  export interface SidebarItem {
2552
2721
  id?: string;
2553
2722
  label: string;
2554
- icon: IconName;
2723
+ /** Required for pages; options in a filter group usually go without. */
2724
+ icon?: IconName;
2555
2725
  count?: number;
2556
- /** One level of nested pages, shown when the item is open. */
2726
+ /** A small marker instead of a number: `"dot"` for new activity, or a short word such as "New". */
2727
+ badge?: string;
2728
+ /** Renders the item as a link to this address (with `linkAs` for a router's link, e.g. Next.js `Link`). */
2729
+ href?: string;
2730
+ /** Opens `href` in a new tab and says so. */
2731
+ external?: boolean;
2732
+ disabled?: boolean;
2733
+ /** A menu behind a "…" button on the item (rename, share, remove …). */
2734
+ actions?: (MenuItem | "-")[];
2735
+ /** One level of nested pages, shown when the item is open (as a flyout when the sidebar is collapsed). */
2557
2736
  items?: Omit<SidebarItem, "items">[];
2737
+ /**
2738
+ * Makes the item a filter option: a checkbox (or a radio in a group with `select: "single"`)
2739
+ * with its label and `count`, instead of a page.
2740
+ */
2741
+ checked?: boolean;
2742
+ onCheckedChange?: (checked: boolean) => void;
2558
2743
  }
2559
2744
  export interface SidebarGroup {
2745
+ id?: string;
2560
2746
  label?: string;
2561
- items: SidebarItem[];
2747
+ items?: SidebarItem[];
2748
+ /** Any content instead of (or after) the items: a `RangeSlider`, `DateRangePicker`, chips … */
2749
+ content?: ReactNode;
2750
+ /** For filter options (items with `checked`): `single` makes them radios (one choice per group). */
2751
+ select?: "multiple" | "single";
2752
+ /** The label folds the group open and shut. */
2753
+ collapsible?: boolean;
2754
+ /** For a collapsible group: starts open (default true). */
2755
+ defaultOpen?: boolean;
2756
+ /** A small button next to the label, e.g. "+" to add a project. */
2757
+ action?: { label: string; icon?: IconName; onClick?: () => void };
2562
2758
  }
2563
2759
  export interface SidebarProps extends BaseProps {
2564
2760
  groups: SidebarGroup[];
2565
2761
  /** Id (or label) of the current page. */
2566
2762
  current?: string;
2567
2763
  onPick?: (id: string) => void;
2764
+ /**
2765
+ * `floating` (default): a card with rounded corners and a shadow. `attached`: full height, flush
2766
+ * with the edge of the screen, divided by a line. `shell`: on the dark frame of `AppShell`.
2767
+ */
2768
+ variant?: "floating" | "attached" | "shell";
2568
2769
  /** Icons only. */
2569
2770
  collapsed?: boolean;
2570
2771
  defaultCollapsed?: boolean;
2571
2772
  onCollapsedChange?: (collapsed: boolean) => void;
2572
- /** Top of the sidebar (brand, workspace switcher). */
2573
- header?: ReactNode;
2574
- /** Bottom of the sidebar (account, settings). */
2575
- footer?: ReactNode;
2576
- /** Accessible name of the navigation. */
2773
+ /** Remember the collapsed state in the browser under this key. */
2774
+ storageKey?: string;
2775
+ /** ⌘B (Ctrl+B) collapses and expands the sidebar, except while typing in a field. */
2776
+ shortcut?: boolean;
2777
+ /** The element items with an `href` render as (default `a`), e.g. Next.js `Link`. */
2778
+ linkAs?: ElementType;
2779
+ /** On phones (up to 760px wide): whether the drawer is open (controlled). */
2780
+ mobileOpen?: boolean;
2781
+ onMobileOpenChange?: (open: boolean) => void;
2782
+ /** Top of the sidebar (brand, `WorkspaceSwitcher`); a function gets the collapsed state. */
2783
+ header?: ReactNode | ((state: { collapsed: boolean }) => ReactNode);
2784
+ /** Bottom of the sidebar (`UserMenu`, settings); a function gets the collapsed state. */
2785
+ footer?: ReactNode | ((state: { collapsed: boolean }) => ReactNode);
2786
+ /**
2787
+ * `nav` (default) for app navigation. `aside` for filters, settings or other tools beside the
2788
+ * content: a complementary region instead of a navigation landmark.
2789
+ */
2790
+ as?: "nav" | "aside";
2791
+ /** Offers the collapse button and the icons-only mode (default true; filters usually set false). */
2792
+ collapsible?: boolean;
2793
+ /** Accessible name of the navigation (or the aside). */
2577
2794
  label?: string;
2578
2795
  }
2796
+ export interface UserMenuProps extends BaseProps {
2797
+ name: string;
2798
+ email?: string;
2799
+ /** Picture URL; initials otherwise. */
2800
+ avatar?: string;
2801
+ /** The menu: profile, settings, sign out … */
2802
+ items?: (MenuItem | "-")[];
2803
+ /** Only the avatar (a collapsed sidebar). */
2804
+ compact?: boolean;
2805
+ /** Where the menu opens (default `top`, for the foot of a sidebar). */
2806
+ side?: "top" | "bottom";
2807
+ }
2579
2808
 
2580
2809
  /* ── ResizablePanels ── */
2581
2810
  export interface ResizablePanelsProps extends BaseProps {
@@ -2590,3 +2819,17 @@ export interface ResizablePanelsProps extends BaseProps {
2590
2819
  /** Accessible names of the panels, used for the resize handles. */
2591
2820
  labels?: string[];
2592
2821
  }
2822
+
2823
+ /* ── QrCode ── */
2824
+ export interface QrCodeProps extends BaseProps {
2825
+ /** The text to encode: an otpauth:// link, a URL … */
2826
+ value: string;
2827
+ /** Error correction: L, M (default), Q or H. */
2828
+ level?: "L" | "M" | "Q" | "H";
2829
+ /** Width and height (default: the width of its container). */
2830
+ size?: number | string;
2831
+ /** Light modules around the code (default 4, the standard quiet zone). */
2832
+ quiet?: number;
2833
+ /** Accessible name (default "QR code"). */
2834
+ label?: string;
2835
+ }
@@ -31,7 +31,11 @@ import type {
31
31
  export function SubNav(p: SubNavProps) {
32
32
  const t = useStrings(WORKSPACE_STRINGS);
33
33
  return (
34
- <nav className="tf-subnav" aria-label={t.sectionPages(p.eyebrow)}>
34
+ <nav
35
+ style={p.style}
36
+ className={cx("tf-subnav", p.className)}
37
+ aria-label={t.sectionPages(p.eyebrow)}
38
+ >
35
39
  <div className="tf-subnav-head">
36
40
  {p.eyebrow && <span className="tf-subnav-eyebrow">{p.eyebrow}</span>}
37
41
  {p.action && (
@@ -74,8 +78,9 @@ export function Drawer(p: DrawerProps) {
74
78
  if (!pr.mounted) return null;
75
79
  return (
76
80
  <div
81
+ style={p.style}
77
82
  {...leaving(pr.closing)}
78
- className={cx("tf-drawer-wrap", p.inline && "inline")}
83
+ className={cx("tf-drawer-wrap", p.inline && "inline", p.className)}
79
84
  onMouseDown={(e) => {
80
85
  if (e.target === e.currentTarget && p.onClose) p.onClose?.();
81
86
  }}
@@ -180,8 +185,9 @@ export function CommandPalette(p: CommandPaletteProps) {
180
185
  if (!pr.mounted) return null;
181
186
  return (
182
187
  <div
188
+ style={p.style}
183
189
  {...leaving(pr.closing)}
184
- className={cx("tf-palette-wrap", p.inline && "inline")}
190
+ className={cx("tf-palette-wrap", p.inline && "inline", p.className)}
185
191
  onMouseDown={(e) => {
186
192
  if (e.target === e.currentTarget && p.onClose) p.onClose?.();
187
193
  }}
@@ -273,7 +279,12 @@ export function NotificationList(p: NotificationListProps) {
273
279
  const f = React.useState<"all" | "unread">("all"),
274
280
  items = (p.items || []).filter((n) => f[0] === "all" || n.unread);
275
281
  return (
276
- <div className="tf-notes" role="region" aria-label={t.notifications}>
282
+ <div
283
+ style={p.style}
284
+ className={cx("tf-notes", p.className)}
285
+ role="region"
286
+ aria-label={t.notifications}
287
+ >
277
288
  <div
278
289
  className="tf-header"
279
290
  style={{
@@ -353,12 +364,13 @@ export function Flow(p: FlowProps) {
353
364
  const t = useStrings(WORKSPACE_STRINGS);
354
365
  return (
355
366
  <ol
356
- className="tf-flow tf-stagger"
357
- style={
358
- {
367
+ className={cx("tf-flow tf-stagger", p.className)}
368
+ style={{
369
+ ...({
359
370
  "--base": "120ms",
360
- } as React.CSSProperties
361
- }
371
+ } as React.CSSProperties),
372
+ ...p.style,
373
+ }}
362
374
  >
363
375
  {(p.steps || []).map((s, i) => {
364
376
  const steps = STEP(),
@@ -467,7 +479,7 @@ export function FileThumb(p: FileThumbProps) {
467
479
  </span>
468
480
  );
469
481
  return (
470
- <span className="tf-t-doc">
482
+ <span style={p.style} className={cx("tf-t-doc", p.className)}>
471
483
  <b>{(p.title || "").replace(/\.[^.]+$/, "")}</b>
472
484
  {Array.from(
473
485
  {
@@ -489,7 +501,12 @@ export function FileCard(p: FileCardProps) {
489
501
  const t = useStrings(WORKSPACE_STRINGS);
490
502
  const k = FILE_KIND[p.kind] || FILE_KIND.doc;
491
503
  return (
492
- <button type="button" className="tf-filecard" onClick={p.onClick}>
504
+ <button
505
+ style={p.style}
506
+ type="button"
507
+ className={cx("tf-filecard", p.className)}
508
+ onClick={p.onClick}
509
+ >
493
510
  <span className={"tf-thumb tf-bg-" + k[1]}>
494
511
  <FileThumb kind={p.kind} title={p.name} />
495
512
  </span>
@@ -507,7 +524,12 @@ export function Wizard(p: WizardProps) {
507
524
  cur = p.current || 0,
508
525
  done = p.done || {};
509
526
  return (
510
- <div className="tf-wizard" role="group" aria-label={p.label || t.setup}>
527
+ <div
528
+ style={p.style}
529
+ className={cx("tf-wizard", p.className)}
530
+ role="group"
531
+ aria-label={p.label || t.setup}
532
+ >
511
533
  <aside className="tf-wizard-rail">
512
534
  {p.brand && (
513
535
  <span
@@ -570,7 +592,7 @@ export function PermissionMatrix(p: PermissionMatrixProps) {
570
592
  const box = React.useRef<HTMLDivElement>(null);
571
593
  useScrollFocus(box);
572
594
  return (
573
- <div ref={box} className="tf-scroll">
595
+ <div style={p.style} ref={box} className={cx("tf-scroll", p.className)}>
574
596
  <table className="tf-table tf-perm">
575
597
  <thead>
576
598
  <tr>
@@ -648,7 +670,7 @@ export function InvoicePreview(p: InvoicePreviewProps) {
648
670
  });
649
671
  const fmt = p.format || money2;
650
672
  return (
651
- <div className="tf-paper">
673
+ <div style={p.style} className={cx("tf-paper", p.className)}>
652
674
  <div className="tf-paper-top">
653
675
  <div>
654
676
  {p.brand && (
@@ -720,7 +742,7 @@ export function TransactionRow(p: TransactionRowProps) {
720
742
  const open = React.useState(false),
721
743
  inc = p.amount > 0;
722
744
  return (
723
- <div className={cx("tf-tx", !p.matched && "unmatched")}>
745
+ <div style={p.style} className={cx("tf-tx", !p.matched && "unmatched", p.className)}>
724
746
  <div className="tf-tx-row">
725
747
  <TintIcon icon={inc ? "download" : "send"} tint={inc ? "mint" : "plain"} />
726
748
  <div className="tf-tx-text">