pixelize-design-library 2.4.2-beta.63 → 2.4.2-beta.65
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.
- package/CHANGELOG.md +130 -8
- package/COMPONENT-GOTCHAS.md +63 -7
- package/DESIGN-GUIDE.md +8 -3
- package/dist/Components/Button/Button.styles.js +7 -2
- package/dist/Components/CopyButton/CopyButton.d.ts +3 -1
- package/dist/Components/CopyButton/CopyButton.js +2 -2
- package/dist/Components/Drawer/Drawer.d.ts +1 -1
- package/dist/Components/Drawer/Drawer.js +6 -2
- package/dist/Components/Drawer/DrawerProps.d.ts +4 -0
- package/dist/Components/Dropdown/DropDown.js +2 -9
- package/dist/Components/FilePreview/FilePreview.js +2 -1
- package/dist/Components/FileUploader/FileUploader.js +4 -0
- package/dist/Components/FileUploader/FileUploaderProps.d.ts +1 -1
- package/dist/Components/FormDrawer/FormDrawer.d.ts +1 -1
- package/dist/Components/FormDrawer/FormDrawer.js +3 -3
- package/dist/Components/FormDrawer/FormDrawerProps.d.ts +2 -0
- package/dist/Components/Input/TextInput.styles.js +10 -6
- package/dist/Components/KanbanBoard/KanbanBoard.js +6 -5
- package/dist/Components/Modal/dialogNaming.d.ts +8 -0
- package/dist/Components/Modal/dialogNaming.js +21 -0
- package/dist/Components/NavLauncher/LauncherTabs.js +2 -2
- package/dist/Components/NavLauncher/NavLauncher.d.ts +1 -1
- package/dist/Components/NavLauncher/NavLauncher.js +257 -47
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +79 -3
- package/dist/Components/NavLauncher/ScopeBar.d.ts +14 -0
- package/dist/Components/NavLauncher/ScopeBar.js +137 -0
- package/dist/Components/NavLauncher/engine/distance.d.ts +4 -0
- package/dist/Components/NavLauncher/engine/distance.js +41 -0
- package/dist/Components/NavLauncher/engine/engine.d.ts +84 -0
- package/dist/Components/NavLauncher/engine/engine.js +471 -0
- package/dist/Components/NavLauncher/engine/fold.d.ts +15 -0
- package/dist/Components/NavLauncher/engine/fold.js +83 -0
- package/dist/Components/NavLauncher/engine/index.d.ts +21 -0
- package/dist/Components/NavLauncher/engine/index.js +17 -0
- package/dist/Components/NavLauncher/launcherParts.d.ts +2 -0
- package/dist/Components/NavLauncher/launcherParts.js +11 -7
- package/dist/Components/NavLauncher/match.d.ts +13 -2
- package/dist/Components/NavLauncher/match.js +26 -12
- package/dist/Components/NavLauncher/searchLayout.d.ts +60 -0
- package/dist/Components/NavLauncher/searchLayout.js +108 -0
- package/dist/Components/NavLauncher/spotlight.d.ts +39 -0
- package/dist/Components/NavLauncher/spotlight.js +202 -0
- package/dist/Components/NavLauncher/spotlightParts.d.ts +49 -0
- package/dist/Components/NavLauncher/spotlightParts.js +108 -0
- package/dist/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/Components/OrgSwitcher/OrgSwitcher.js +2 -4
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +6 -5
- package/dist/Components/ScrollToTop/ScrollToTop.js +4 -3
- package/dist/Components/SearchSelect/SearchSelect.js +4 -16
- package/dist/Components/Select/SelectPopup.js +1 -3
- package/dist/Components/Select/optionRowInk.d.ts +12 -0
- package/dist/Components/Select/optionRowInk.js +10 -1
- package/dist/Components/SelectSearch/SelectSearch.js +1 -10
- package/dist/Components/Table/Table.js +3 -2
- package/dist/Components/Table/TableProps.d.ts +1 -1
- package/dist/Components/Table/components/TableBody.js +9 -1
- package/dist/Components/Table/components/TableHeader.js +9 -2
- package/dist/Components/Table/components/TableLoading.d.ts +4 -1
- package/dist/Components/Table/components/TableLoading.js +13 -6
- package/dist/Components/Table/filters/CompactSelect.js +1 -10
- package/dist/Components/Table/variants/TableVariantContext.d.ts +1 -1
- package/dist/Components/Table/variants/TableVariantContext.js +3 -3
- package/dist/Components/WizardDialog/WizardDialog.d.ts +1 -1
- package/dist/Components/WizardDialog/WizardDialog.js +15 -3
- package/dist/Components/WizardDialog/WizardDialogProps.d.ts +5 -3
- package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -2
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +26 -18
- package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +38 -8
- package/dist/Theme/chakra/Menu.styles.d.ts +8 -0
- package/dist/Theme/chakra/Menu.styles.js +7 -0
- package/dist/Theme/chakra/componentStyles.d.ts +8 -0
- package/dist/Theme/chakra/componentStyles.js +2 -2
- package/dist/Theme/chakra/ringShadow.d.ts +3 -0
- package/dist/Theme/chakra/ringShadow.js +7 -0
- package/dist/Theme/tokens/builders/accentText.d.ts +2 -0
- package/dist/Theme/tokens/builders/accentText.js +7 -1
- package/dist/Theme/tokens/builders/buildDarkPalette.d.ts +1 -1
- package/dist/Theme/tokens/builders/buildDarkPalette.js +5 -5
- package/dist/Theme/tokens/builders/filledSurface.d.ts +5 -1
- package/dist/Theme/tokens/builders/filledSurface.js +17 -4
- package/dist/Theme/tokens/builders/planeStep.d.ts +1 -0
- package/dist/Theme/tokens/builders/planeStep.js +5 -4
- package/dist/Utils/table.js +2 -2
- package/dist/esm/Components/Button/Button.styles.js +8 -3
- package/dist/esm/Components/CopyButton/CopyButton.js +2 -2
- package/dist/esm/Components/Drawer/Drawer.js +6 -2
- package/dist/esm/Components/Dropdown/DropDown.js +2 -9
- package/dist/esm/Components/FilePreview/FilePreview.js +3 -2
- package/dist/esm/Components/FileUploader/FileUploader.js +4 -0
- package/dist/esm/Components/FormDrawer/FormDrawer.js +3 -3
- package/dist/esm/Components/Input/TextInput.styles.js +10 -6
- package/dist/esm/Components/KanbanBoard/KanbanBoard.js +6 -5
- package/dist/esm/Components/Modal/dialogNaming.js +18 -0
- package/dist/esm/Components/NavLauncher/LauncherTabs.js +2 -2
- package/dist/esm/Components/NavLauncher/NavLauncher.js +259 -49
- package/dist/esm/Components/NavLauncher/ScopeBar.js +134 -0
- package/dist/esm/Components/NavLauncher/engine/distance.js +37 -0
- package/dist/esm/Components/NavLauncher/engine/engine.js +458 -0
- package/dist/esm/Components/NavLauncher/engine/fold.js +74 -0
- package/dist/esm/Components/NavLauncher/engine/index.js +14 -0
- package/dist/esm/Components/NavLauncher/launcherParts.js +10 -6
- package/dist/esm/Components/NavLauncher/match.js +25 -12
- package/dist/esm/Components/NavLauncher/searchLayout.js +100 -0
- package/dist/esm/Components/NavLauncher/spotlight.js +194 -0
- package/dist/esm/Components/NavLauncher/spotlightParts.js +95 -0
- package/dist/esm/Components/NavigationBar/NavigationBar.js +2 -1
- package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +2 -4
- package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +7 -6
- package/dist/esm/Components/ScrollToTop/ScrollToTop.js +5 -4
- package/dist/esm/Components/SearchSelect/SearchSelect.js +6 -18
- package/dist/esm/Components/Select/SelectPopup.js +2 -4
- package/dist/esm/Components/Select/optionRowInk.js +7 -0
- package/dist/esm/Components/SelectSearch/SelectSearch.js +2 -11
- package/dist/esm/Components/Table/Table.js +4 -3
- package/dist/esm/Components/Table/components/TableBody.js +10 -2
- package/dist/esm/Components/Table/components/TableHeader.js +10 -3
- package/dist/esm/Components/Table/components/TableLoading.js +10 -6
- package/dist/esm/Components/Table/filters/CompactSelect.js +2 -11
- package/dist/esm/Components/Table/variants/TableVariantContext.js +3 -3
- package/dist/esm/Components/WizardDialog/WizardDialog.js +15 -3
- package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
- package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +28 -20
- package/dist/esm/Theme/chakra/Menu.styles.js +7 -0
- package/dist/esm/Theme/chakra/componentStyles.js +2 -2
- package/dist/esm/Theme/chakra/ringShadow.js +3 -0
- package/dist/esm/Theme/tokens/builders/accentText.js +5 -0
- package/dist/esm/Theme/tokens/builders/buildDarkPalette.js +5 -5
- package/dist/esm/Theme/tokens/builders/filledSurface.js +18 -5
- package/dist/esm/Theme/tokens/builders/planeStep.js +3 -3
- package/dist/esm/Utils/table.js +2 -2
- package/dist/esm/index.js +6 -5
- package/dist/index.d.ts +10 -8
- package/dist/index.js +14 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,31 @@ next major.
|
|
|
18
18
|
|
|
19
19
|
### Added
|
|
20
20
|
|
|
21
|
+
- **`WorkspaceTray` is usable over an open modal.** It is portalled above the modal layer, labelled as a live region
|
|
22
|
+
(so Chakra's modal aria-hidden skips it) and outside the focus trap, so a window minimised over a wizard can be
|
|
23
|
+
restored without closing the wizard. New props: `placement` (`"bottom-end"` | `"bottom-start"`), `offset`,
|
|
24
|
+
`closeLabel(item)`, `dirtyLabel`, `label`; `WorkspaceTrayItem` gains `isDirty` (unsaved dot) and `subtitle`.
|
|
25
|
+
`WorkspaceWindow` gains `labels` ({minimize, close, openFullPage}) and `finalFocusRef`.
|
|
26
|
+
The tray now renders in a portal, so a test that queries inside its render container must query `document.body`.
|
|
27
|
+
- **`WorkspaceTray` `renderItem` receives the built-in pill** as a third argument, `renderItem(item, api, pill)`: the pill
|
|
28
|
+
already carries the unsaved dot, subtitle and close button, so a consumer can return it or wrap it (in
|
|
29
|
+
`WorkspaceTrayPreview` for a hover preview, say) instead of rebuilding it. Two-argument callbacks and the default
|
|
30
|
+
pill are unchanged.
|
|
31
|
+
- **`FormDrawer` `isSubmitDisabled`**: disables the primary button and makes the form ignore Enter and any other submit,
|
|
32
|
+
so `onSubmit` never fires, not even from `requestSubmit()` or a dispatched submit event (the drawer is a real
|
|
33
|
+
`<form>`; the button alone does not guard those). Unlike `isSubmitting` it shows no spinner, and Cancel and every
|
|
34
|
+
close path stay live. Omitted, nothing changes.
|
|
35
|
+
- **`WizardDialog` `chrome="inline"`** (default `"modal"`): draws the wizard (title, stepper, step, review, footer) in place
|
|
36
|
+
with no Modal, for a host that is already the dialog, such as a `WorkspaceWindow`: no overlay, focus trap, close X,
|
|
37
|
+
Esc-to-close or `role="dialog"`. It renders nothing while `isOpen` is false, ignores `size`, and Cancel still calls
|
|
38
|
+
`onClose`. It reuses the same flow as the modal (`useWizardFlow`, `validate`, `finishDisabled`, `finishBlockedReason`,
|
|
39
|
+
`stepId`); test ids are `wizard-inline` / `${data-testid}-inline`. `Wizard` does not take the prop.
|
|
40
|
+
- **`optionRowFill(rows, isActive)`** and **`optionRowCursorSx(rows)`** are exported from the package root: the one
|
|
41
|
+
fill an option row paints (the cursor's), and its `:hover`/`:focus` form for roving-focus lists.
|
|
42
|
+
- **`accentTextOnPlanes(scale, planes)`** is exported from the package root: the first accent rung clearing AA on every
|
|
43
|
+
plane in the list, for chrome that can land on the canvas, a card or an overlay.
|
|
44
|
+
- **`ruleOn(colors, plane)`** and **`MOTION_PRESS_SCALE`** are exported from the package root. `ruleOn` draws a divider at the
|
|
45
|
+
in-card weight on any plane (`cardRule` is `ruleOn` on `background[50]`); the press scale was only reachable by deep import.
|
|
21
46
|
- **`Card variant="elevated"`**: borderless, `background[50]` lifted by `shadows.card`; declares a 0 border width so it
|
|
22
47
|
never inherits an outline parent's rule.
|
|
23
48
|
- **`bandOn`, `recessOn`, `cardRule`**: plane-step helpers that return the input unchanged in light and a visible step
|
|
@@ -83,6 +108,53 @@ next major.
|
|
|
83
108
|
document-wide `prefers-reduced-motion` rule; `pz-motion-keep` (`MOTION_KEEP_CLASS`) opts an element out.
|
|
84
109
|
The default is unchanged. `CustomThemeProps` gains `zIndices.popoverNested` and the two font sizes, so
|
|
85
110
|
code that hand-builds a full `CustomThemeProps` needs those keys.
|
|
111
|
+
- **`LauncherSearchEngine.matches` and `.createMatcher`**: the boolean form of the engine for table and list filters.
|
|
112
|
+
`matches(query, ...texts)` is true when every query word hits some text (same folding, typo tolerance and `synonyms`
|
|
113
|
+
option as `search`, no ranking); `createMatcher(query)` compiles the query once for a loop over rows, and an empty query
|
|
114
|
+
matches every row. `LauncherMatcher` is exported as a type.
|
|
115
|
+
- **`LauncherSearchEngine` searches Hindi and Tamil by written letter**: Latin accents, the Devanagari nukta and
|
|
116
|
+
zero-width joiners are ignored, chandrabindu folds into anusvara, and every other combining mark stays part of its
|
|
117
|
+
letter, so a bare vowel sign never matches ("का" does not find "कमाई"). Typo length and distance count letters
|
|
118
|
+
("कम्मचारी" finds "कर्मचारी"), and a Tamil word ending in ம் also finds titles that drop it ("நேரம்" finds "நேர அட்டவணை").
|
|
119
|
+
`editDistance` accepts arrays of letters. Latin results are unchanged. `engine/conformance.fixture.json` is the
|
|
120
|
+
behaviour contract shared with HRMS mobile search, asserted by `engine/conformance.test.ts`.
|
|
121
|
+
- **`NavLauncher` spotlight search** (all additive; an app passing none of these renders as before):
|
|
122
|
+
- `searchMode="spotlight"` ranks every row by relevance (exact > prefix > word > word-prefix >
|
|
123
|
+
initialism > substring > typo > subsequence, label > keyword > context), tolerates typos
|
|
124
|
+
(Damerau-Levenshtein 1 from 4 letters, 2 from 8), folds case and accents, expands `synonyms`
|
|
125
|
+
both ways, boosts `pinnedIds` / `frequentIds` / `recentIds`, and shows the best row first as a
|
|
126
|
+
**Top hit** card naming the keyword or fact that matched. Groups collapse to `groupLimit` rows
|
|
127
|
+
(default 5 in spotlight, unlimited in classic) behind a "Show N more" option.
|
|
128
|
+
- `scopes` + `activeScope` / `onScopeChange`: filter chips in the header row beside the field
|
|
129
|
+
(their own row below `md`), always shown so they say what can be searched; while typing each
|
|
130
|
+
chip shows its live match count and is named "People, 3 results". One Tab stop (radiogroup,
|
|
131
|
+
roving tabindex, arrows / Home / End); the strip scrolls sideways behind an edge fade with a
|
|
132
|
+
mouse page arrow and keeps the selected chip in view, so the field never drops below 15rem (or a
|
|
133
|
+
third of a wide row). "All" is prepended, `screens`
|
|
134
|
+
and `actions` are built in and `LauncherResultGroup.scope` (default its `id`) places any other
|
|
135
|
+
group. A chip other than All / Screens picked with an empty field lists that scope's items as
|
|
136
|
+
passed, with the facts pane; `LauncherScope.placeholder` narrows the field's placeholder.
|
|
137
|
+
- `quickActions`: up to 5 pill buttons at the top of the menu while the query is empty and the
|
|
138
|
+
scope is All.
|
|
139
|
+
- `LauncherSearchItem.facts` (`{ label, value, copyable? }`): a right preview pane at `lg`+ for the
|
|
140
|
+
highlighted row with Copy buttons (blocked clipboard selects the value instead), docked under the
|
|
141
|
+
list below `lg`. `answers` (items with `answer: { label, value, hint? }`) render as direct-answer
|
|
142
|
+
cards above every result; Enter copies an answer that has no `run` / `href`.
|
|
143
|
+
- `LauncherResultGroup.matchLocally`: the launcher filters and ranks a static group itself.
|
|
144
|
+
- `LauncherSearchEngine` — the framework-free engine behind it (`createIndex`, `search`,
|
|
145
|
+
`searchDocuments`, `highlightRanges`, `usageBoosts`, `foldText`, `tokenize`, `editDistance`; options
|
|
146
|
+
`synonyms`, `boosts`, `fuzzy`, `relativeCutoff`, `limit`) for any other list or palette; `highlightSegments` now exported, with an opt-in `{ fuzzy, synonyms }`.
|
|
147
|
+
- New optional `LauncherLabels` keys: `topHit`, `matchedOn`, `answers`, `hintCopy`, `scopes`,
|
|
148
|
+
`scopeAll`, `scopeChanged`, `scopeCount`, `scopeEmpty`, `quickActions`, `showMore`, `preview`,
|
|
149
|
+
`copy`, `copied`, `copySelected`.
|
|
150
|
+
- The dialog is now always named `labels.title`; the search value and `headerActions` no longer
|
|
151
|
+
join its accessible name.
|
|
152
|
+
- Types `LauncherFact`, `LauncherAnswer`, `LauncherScope`, `LauncherSearchMode`, `HighlightOptions`,
|
|
153
|
+
`LauncherSearch*` / `LauncherSynonyms` / `LauncherMatchKind` / `LauncherTokenMatch`.
|
|
154
|
+
- `CopyButton` `onCopyError` — fires when the clipboard is missing or denied, instead of failing silently.
|
|
155
|
+
- `Drawer` `aria-label` / `aria-labelledby` — name the dialog instead of its whole header (a header
|
|
156
|
+
holding a field or buttons otherwise becomes the name); `contentProps["aria-labelledby"]` never
|
|
157
|
+
worked.
|
|
86
158
|
- **HRMS library requests** — a navigation launcher, key caps, a merge-field `Editor`, a wizard and
|
|
87
159
|
smaller gaps found by the HRMS migration:
|
|
88
160
|
- `NavLauncher` — full-height navigation launcher (browse modules, pins, Frequent, Recent, search
|
|
@@ -248,8 +320,9 @@ next major.
|
|
|
248
320
|
library; bring your own.
|
|
249
321
|
|
|
250
322
|
- **Dark mode.** `PixelizeThemeProvider` (defaults to `system`), `useThemeMode`, `ThemeSwitcher`.
|
|
251
|
-
Dark palettes are derived per brand from that brand's own light palette
|
|
252
|
-
|
|
323
|
+
Dark palettes are derived per brand from that brand's own light palette: surfaces are hue-neutral,
|
|
324
|
+
and the brand hue carries through on the accent, ink, rules and rail. `withTheme` stays light-only,
|
|
325
|
+
so existing apps are visually unchanged — dark is opt-in.
|
|
253
326
|
- **`MediaViewer`** — a full-screen viewer for images, video, audio, PDF, Office documents, text
|
|
254
327
|
and archives, with a filmstrip, keyboard navigation and per-kind fallbacks. `index` is optional:
|
|
255
328
|
omit it and the viewer owns navigation itself.
|
|
@@ -381,6 +454,45 @@ next major.
|
|
|
381
454
|
|
|
382
455
|
### Changed (visual)
|
|
383
456
|
|
|
457
|
+
- **Dark surfaces are hue-neutral on every brand.** The canvas, card, overlay, named `backgroundColor` ladder,
|
|
458
|
+
`background[50-900]` and `table.hover` no longer take a brand tint (`SURFACE_SAT` 14 to 0): all 9 brands share
|
|
459
|
+
canvas `#121212`, card `#212121` (1.163:1 over the canvas) and overlay `#2c2c2c` (1.153:1 over the card), so
|
|
460
|
+
switching brand no longer recolours the app background. The brand hue stays on the accent, ink, rules and the
|
|
461
|
+
sidebar rail. Light palettes are byte-identical to before (SHA-256 of all 9 resolved light palettes). Muted dark
|
|
462
|
+
text re-derives against the neutral surfaces and shifts a few hex values on lavender, rosewood, slate and zinc
|
|
463
|
+
(lavender `#9f92aa` to `#a498ae`); every muted token still passes the same AA gate. `planes: "flat"` keeps its
|
|
464
|
+
meaning (card = canvas) on the same canvas.
|
|
465
|
+
- **`Table` loading state.** With `isLoading` and no rows, each row now draws one skeleton per column (a checkbox
|
|
466
|
+
placeholder in the checkbox column, blank spacer cells) with the variant's row rule, instead of one bar spanning the
|
|
467
|
+
table. If `columns` is still empty while loading, the header shows four placeholder cells and a disabled select-all
|
|
468
|
+
instead of a lone checkbox stretched across the row. The select-all checkbox is disabled while `isLoading`.
|
|
469
|
+
- **`Table` defaults to `variant="studio"`.** Every `Table` rendered without `variant` (or with `variant={undefined}`)
|
|
470
|
+
now draws studio: flat small-caps header, one row rule, the select slot resting on the row number, status as a pill.
|
|
471
|
+
Keep the old look with `variant="classic"`. Measured 2026-10-14: crm-frontend has 38 `<Table>` call sites, none
|
|
472
|
+
passing `variant` (all 38 change); account-frontend 8 of 18 change (the other 10 already pass `"studio"`); hrms,
|
|
473
|
+
tickets and lfour render `Table` only through an `AppTable` wrapper that already defaults to studio (0 change).
|
|
474
|
+
- **Option rows fill only under the cursor; selection is the trailing check and weight 500.** `Select`, `SearchSelect`,
|
|
475
|
+
`SelectSearch`, `CompactSelect` and `OrgSwitcher` no longer fill the selected row: the selected fill sat 1.00-1.02:1
|
|
476
|
+
from the hover fill on all 18 brand x mode palettes, so the two read as one state. `SelectSearch`/`CompactSelect` no
|
|
477
|
+
longer fill row 0 on open beside the hovered row. `Dropdown` items keep `text[700]` under the cursor instead of turning
|
|
478
|
+
`accentText`. `SearchSelect` rows sit inset (`mx`, rounded) with the check at the trailing edge and no hairline
|
|
479
|
+
between rows. Text on every row state measures 7.69:1 or more (selected 12.43-19.90:1).
|
|
480
|
+
- **Chakra `Menu` items use option-row ink:** `text[700]` instead of `text[900]`; a checked `MenuItemOption` is weight
|
|
481
|
+
500 with an `accentText` check, matching `Select`.
|
|
482
|
+
- **Dark `Button` `outline`/`ghost`/`link` ink is chosen against the canvas, the card and the overlay.** It cleared AA
|
|
483
|
+
on the canvas only, so in a dialog or popover it fell to 4.06-4.50:1 in 24 scheme x brand combos (lavender outline
|
|
484
|
+
4.46:1, rosewood 4.10:1); now AA on all three planes (lavender 6.28:1, rosewood 5.57:1). Light mode is unchanged
|
|
485
|
+
(0 light combos move); 62 dark combos step lighter (emerald primary `#0f9d8a` -> `#14e3c7`).
|
|
486
|
+
- **Dark rules and pressed solids (owner review before release).** In dark only: the `NavigationBar` header rule and
|
|
487
|
+
leading divider, the `KanbanBoard` column edge, header rule and count pill, the filled `TextInput` (a visible band off
|
|
488
|
+
the card) and the Kanban drag-over column's dashed border (`focusRing.ring`, 3.1:1 or more instead of 1.7-2.7:1)
|
|
489
|
+
now read at the in-card rule weight instead of 1.03-1.16:1. Solid `hover`/`active` fills never fall below 1.5:1 against
|
|
490
|
+
the canvas (a pressed warning solid reached 1.00:1) and never flip the label colour, so zinc dark's pressed primary stays
|
|
491
|
+
light with dark ink instead of dropping to `#52525b`. `ScrollToTop`, `FilePreview`, `ProfilePhotoViewer`, the Table
|
|
492
|
+
clear-filters badge, Chakra `Badge`/`Radio` and the Table status pills now pass the canvas to `solidFillRung`: zinc dark
|
|
493
|
+
primary and every brand's dark gray solid move from `#52525b`/`#525252` (2.4:1) to `#e4e4e7`/`#a3a3a3` with dark ink.
|
|
494
|
+
Light mode and every other dark colour keep their fill; error/warning/info pressed fills in dark are lighter than before
|
|
495
|
+
and hover and press now share one rung.
|
|
384
496
|
- **Elevation follows Pebble, per mode.** `shadows.sm`/`base`/`md`/`lg` are the Pebble card-press/
|
|
385
497
|
card/float/dialog stacks, with new `card`, `float`, `dialog` and `sheet` role keys. Light shadows are
|
|
386
498
|
softer and wider; dark mode gets its own heavier black ramp, so overlays cast a visible shadow on a
|
|
@@ -418,11 +530,10 @@ next major.
|
|
|
418
530
|
2px on hover; the shadow still deepens.
|
|
419
531
|
- **`Button` (solid, outline, ghost) and `IconButton` scale to 0.98 while pressed** (`MOTION_PRESS_SCALE`).
|
|
420
532
|
Never when disabled or loading, nor under `prefers-reduced-motion`.
|
|
421
|
-
- **Dark planes
|
|
422
|
-
(`
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
lighter so they clear AA on selected list rows.
|
|
533
|
+
- **Dark planes separate by lift.** Cards (`background[50]`) sit one step (1.16:1) above the canvas, with
|
|
534
|
+
overlays (`surfaceRaised`) one step (1.15:1) above the card; every filled mark keeps 3:1 on all three. Light
|
|
535
|
+
mode is unchanged. A brand opts out of the lift with `buildDarkPalette(light, { planes: "flat" })`. `meadow`
|
|
536
|
+
and `rosewood` dark `accentText` move one rung lighter so they clear AA on selected list rows.
|
|
426
537
|
- **`zinc`:** muted text in light mode is `#515157` (was `#27272a`, 14.9:1), settled under a new 8:1 muted
|
|
427
538
|
ceiling; zinc's dark card is no longer darker than its canvas.
|
|
428
539
|
- **A brand's primary never reads as danger.** `radiant`'s danger colour (`red` and `semantic.error`) is
|
|
@@ -444,6 +555,10 @@ next major.
|
|
|
444
555
|
|
|
445
556
|
### Fixed
|
|
446
557
|
|
|
558
|
+
- **`WorkspaceTrayPreview`** reports `onOpenChange(false)` only after the card actually opened (it used to fire when
|
|
559
|
+
the pointer left before the open delay).
|
|
560
|
+
- **`FileUploader`** revokes the blob URL of an image the moment it is removed, and of images it rejects (over the total
|
|
561
|
+
size or past `maxFiles`); those URLs were created and never freed. Surviving URLs stay yours to revoke.
|
|
447
562
|
- **`Table` settings sheet** fades in place instead of dropping in when the user prefers reduced motion.
|
|
448
563
|
- **`SearchSelect`**: the Select All and Add New rows are reachable from the keyboard. Arrow keys, Home and End walk
|
|
449
564
|
Select All, then the options, then Add New, and Enter runs them. Both are now `role="option"` rows, so a test that
|
|
@@ -491,6 +606,8 @@ next major.
|
|
|
491
606
|
- **`FileUpload` no longer leaks a blob URL per image thumbnail per render.** Each file gets one URL,
|
|
492
607
|
revoked when the file is removed or the component unmounts.
|
|
493
608
|
- **`Table` column search shows the current search when reopened**, instead of stale text.
|
|
609
|
+
- **`NavLauncher` search rows shorten the `· keyword` hint before the label**, so a long keyword list
|
|
610
|
+
no longer cuts "Apply for leave" down to "Apply…".
|
|
494
611
|
- **`NavigationBar` right cluster no longer overflows onto the avatar at phone widths.** The title
|
|
495
612
|
truncates first (overflow-only tooltip when it is a string); the `moreIcon` cluster can shrink, while
|
|
496
613
|
the sidebar toggle, back button and avatar menu never do. (HRMS, beta.60 launcher trigger overflow)
|
|
@@ -744,7 +861,12 @@ Nothing is removed yet — each of these still ships its full implementation.
|
|
|
744
861
|
- `CustomModulesTableLabels.loading` — never rendered; the loading state shows skeleton bars only.
|
|
745
862
|
- `WorkspaceWindow` `onMedium` and `onMaximize` — never called; the window has no such controls.
|
|
746
863
|
- `NavLauncher` `openShortcutHint` and `LauncherLabels.hintOpenMenu` — no longer rendered (the launcher
|
|
747
|
-
is already open when its footer shows); bind the open chord in the app only.
|
|
864
|
+
is already open when its footer shows); bind the open chord in the app only. `hintOpenMenu` is now
|
|
865
|
+
optional, so a full `LauncherLabels` object type-checks without it.
|
|
866
|
+
- `searchLauncher` — legacy flat search (one Screens group); `NavLauncher` never calls it. Classic mode
|
|
867
|
+
runs `searchLauncherSections`, which matches a screen on its own label and keywords only ("leave
|
|
868
|
+
balance" finds nothing unless one screen's keywords carry both words); `searchMode="spotlight"`
|
|
869
|
+
ranks and also matches a screen on its module and section.
|
|
748
870
|
- `SelectV2` — use `Select` with `native={false}`; it is already a thin wrapper over it.
|
|
749
871
|
- `SearchSelect`, `SelectSearch` are **no longer `@deprecated`.** Both now say "prefer `Select`
|
|
750
872
|
for new work" instead: `@deprecated` claimed a migration that loses real capabilities
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -342,7 +342,6 @@ rows per page does not reset `page` either. Reset it yourself, in `onPageSizeCha
|
|
|
342
342
|
|
|
343
343
|
### Four components with a behaviour their props do not hint at
|
|
344
344
|
|
|
345
|
-
- `WorkspaceTrayPreview` `onOpenChange(false)` also fires when the pointer leaves before the card ever opened; track your own open state from `true`.
|
|
346
345
|
- `Notification`'s countdown is computed when its dates change and never ticks; remount or change a date to refresh it. Overdue labels read "overdue by N hours ago".
|
|
347
346
|
- `NoteTextArea` checks `maxNoteWordCount` against the title only until the note is next edited, so a long title can be saved; `handleCancel` always receives `false`.
|
|
348
347
|
- `VerifyEmailOtp` fields set `autocomplete="off"`, so browser or OS one-time-code autofill never offers the code.
|
|
@@ -548,6 +547,19 @@ use `bandOn(fill, surface)` for a header strip, hover or selection tint, `recess
|
|
|
548
547
|
or off cell, `cardRule(colors)` for a rule inside a card and `raisedRule(colors)` for a rule inside an overlay.
|
|
549
548
|
All four return the old value unchanged in light mode.
|
|
550
549
|
|
|
550
|
+
### A Chakra `ModalHeader` inside a coloured box keeps its own ink
|
|
551
|
+
|
|
552
|
+
The theme gives `ModalHeader` `color: colors.header[900]` and a bottom rule. Wrapping it in a
|
|
553
|
+
`Box bgGradient=… color={ink}` does not recolour it: the header paints `header[900]` itself, so the
|
|
554
|
+
title is dark navy on a blue/purple gradient (3.11–4.11:1 in light across the 9 brands, against
|
|
555
|
+
4.62:1 for the gradient's own ink). Icons and badges inside it that read `currentColor` follow the
|
|
556
|
+
header, not the box.
|
|
557
|
+
|
|
558
|
+
**Apply:** put the ink on the header itself — `<ModalHeader color={resolved.ink} borderBottomWidth={0}>`
|
|
559
|
+
with `resolved = resolveGradient(stops, colors.white, colors.black)` — or use `LimitReachedModal`.
|
|
560
|
+
For an accent icon on the gradient, use `resolved.ink` unless the accent clears 3:1 on both
|
|
561
|
+
`resolved.from` and `resolved.to`; a theme `yellow[300]` drops to 2.61:1 in dark.
|
|
562
|
+
|
|
551
563
|
### `Drawer` / `Modal` / `ProfileCard` drop any child that isn't a named slot — use `portalChildren`
|
|
552
564
|
|
|
553
565
|
All three match children by exact type (`child.type === XHeader/XBody/XFooter`) and render only
|
|
@@ -574,7 +586,8 @@ It sets `noValidate`: validate in `onSubmit` and pass messages via `errors`. Err
|
|
|
574
586
|
fires only after a submit; if validation is async, set `isSubmitting` synchronously in `onSubmit` so
|
|
575
587
|
focus waits for the result, otherwise errors that arrive later are not focused. The footer submit
|
|
576
588
|
button is tied to the form with the `form` attribute — Enter-to-submit works in browsers, but
|
|
577
|
-
jsdom / user-event
|
|
589
|
+
jsdom / user-event only simulates it for a form with exactly one text field, so use
|
|
590
|
+
`fireEvent.submit(form)` in tests. `isSubmitDisabled` swallows both: `onSubmit` is not called while it is true.
|
|
578
591
|
|
|
579
592
|
### `QueryStateBoundary` evaluates JSX `children` even while loading
|
|
580
593
|
|
|
@@ -700,13 +713,13 @@ whose defaults arrive asynchronously doesn't open already red.
|
|
|
700
713
|
### A wrapper presetting a prop must destructure with a default, not rely on JSX prop order
|
|
701
714
|
|
|
702
715
|
Wrapping a library component to preset a prop (e.g. `Table`'s `variant`) as
|
|
703
|
-
`<Table variant="
|
|
716
|
+
`<Table variant="classic" {...props} />` silently reverts to the library's own default the moment a
|
|
704
717
|
caller passes `variant={undefined}` — a later spread always wins in JSX, even with `undefined`,
|
|
705
718
|
and the library component resolves that `undefined` via its own default parameter.
|
|
706
719
|
|
|
707
720
|
**Apply:**
|
|
708
721
|
```tsx
|
|
709
|
-
const AppTable = ({ variant = '
|
|
722
|
+
const AppTable = ({ variant = 'classic', ...props }: TableProps) => <Table variant={variant} {...props} />;
|
|
710
723
|
```
|
|
711
724
|
Applies to any library prop with a default value, not just `Table`'s `variant`.
|
|
712
725
|
|
|
@@ -1280,7 +1293,9 @@ remount, wrap `<Editor>` itself in a keyed parent, not via `Editor`'s own `key`
|
|
|
1280
1293
|
|
|
1281
1294
|
`validate` runs on Continue, on Finish, and again for every step on Finish. Passing `stepId` controls
|
|
1282
1295
|
the visible step only: stages past the furthest valid step stay disabled. Esc, close, Back and Cancel
|
|
1283
|
-
are disabled while `isFinishing`.
|
|
1296
|
+
are disabled while `isFinishing`. With `chrome="inline"` there is no Modal at all: no focus trap, no Esc,
|
|
1297
|
+
no close X and no `role="dialog"` (the host window supplies them), `size` is ignored, and nothing renders
|
|
1298
|
+
while `isOpen` is false; Cancel still calls `onClose`, so the host must close itself there.
|
|
1284
1299
|
|
|
1285
1300
|
### `PageHeader` actions wrap by default
|
|
1286
1301
|
|
|
@@ -1401,8 +1416,9 @@ the `Search` listeners see it — otherwise the first mounted `Search` takes Ctr
|
|
|
1401
1416
|
- **Esc is handled by the launcher, not the Drawer** (`closeOnEsc` is off): it clears a non-empty
|
|
1402
1417
|
query and keeps focus in the field, and closes only when the query is empty. The keydown is stopped inside
|
|
1403
1418
|
the launcher, so a consumer `Escape` listener on `document` never sees it.
|
|
1404
|
-
-
|
|
1405
|
-
|
|
1419
|
+
- **The dialog's accessible name is always `labels.title`** (a hidden heading, through `Drawer`'s
|
|
1420
|
+
`aria-labelledby`); the search value, scope chips and `headerActions` no longer join it. A consumer
|
|
1421
|
+
test asserting the old "Menu leave" style name must assert `labels.title` instead.
|
|
1406
1422
|
- **The result count is `aria-hidden`; the announcement is a separate visually hidden `aria-live="polite"`
|
|
1407
1423
|
node, deliberately not `role="status"`**, so `getByRole("status")` still matches only the no-results
|
|
1408
1424
|
message and the loading skeleton.
|
|
@@ -1417,6 +1433,7 @@ the `Search` listeners see it — otherwise the first mounted `Search` takes Ctr
|
|
|
1417
1433
|
a consumer that annotates a *full* `LauncherLabels` object fails to compile.
|
|
1418
1434
|
- **`openShortcutHint` and `labels.hintOpenMenu` are deprecated no-ops:** the open chord is no longer
|
|
1419
1435
|
shown in the footer. Drop them; the footer's hints are arrows, ENTER, ⌘ ENTER, ⌘ + click, P, ESC.
|
|
1436
|
+
Both are optional, so a full `LauncherLabels` object needs no `Omit<LauncherLabels, "hintOpenMenu">`.
|
|
1420
1437
|
- **`apps` is data-driven.** Gate by plan by changing the array or setting `locked` / `lockedReason`;
|
|
1421
1438
|
keep `id`s stable (hues and `key` derive from them), and pass `color` on an app whose mark must not
|
|
1422
1439
|
shift when siblings are added or removed. Locked rows are not links, not focusable, `aria-disabled`,
|
|
@@ -1429,6 +1446,34 @@ the `Search` listeners see it — otherwise the first mounted `Search` takes Ctr
|
|
|
1429
1446
|
stylesheet.
|
|
1430
1447
|
- **A search row without `href` (an action with `run`) cannot open in a new tab:** any click, with or
|
|
1431
1448
|
without Cmd/Ctrl, runs it and closes the launcher, same as Enter.
|
|
1449
|
+
- **Spotlight search is opt-in (`searchMode="spotlight"`).** Without it matching, ranking and row
|
|
1450
|
+
order are exactly as before; `scopes`, `quickActions`, `answers`, `facts` and `groupLimit` work in
|
|
1451
|
+
both modes. Facts are searchable in spotlight only.
|
|
1452
|
+
- **Two search models, and classic never matches a screen on its module.** Classic (the default) runs
|
|
1453
|
+
`searchLauncherSections`: registry order, no ranking, and every query word must appear in one row's
|
|
1454
|
+
own label or keywords. "leave balance" therefore finds nothing unless one screen's keywords carry
|
|
1455
|
+
both words. Spotlight ranks and also matches a screen on its module and section labels, so the same
|
|
1456
|
+
query finds Leave › Balances. The exported `searchLauncher` (one flat Screens group) is legacy and
|
|
1457
|
+
`@deprecated`; the component never calls it.
|
|
1458
|
+
- **Scope chips live in the header row and are always shown** (their own header row below `md`);
|
|
1459
|
+
while a query is active each chip's name carries its count ("People, 3 results"), so a test that
|
|
1460
|
+
types first must match `getByRole("radio", { name: /^People/ })`, not the bare label.
|
|
1461
|
+
- **Picking a chip other than All or Screens with an empty field lists that scope** exactly as
|
|
1462
|
+
passed: its `actions` or `extraResults` groups, so a remote group with nothing loaded yet shows
|
|
1463
|
+
`labels.scopeEmpty`. Load the scope's items when `onScopeChange` fires if it should list them.
|
|
1464
|
+
- **Quick actions show only under All with an empty query, as the first block of the menu** (not
|
|
1465
|
+
while `isLoading`); only the first 5 render.
|
|
1466
|
+
- **Closing resets the scope to "all"** and calls `onScopeChange("all")`; a controlled `activeScope`
|
|
1467
|
+
must apply it, like `onQueryChange("")`.
|
|
1468
|
+
- **`facts` swap the Quick access rail for a Preview pane at `lg`** whenever any visible result has
|
|
1469
|
+
facts; below `lg` the active result's facts dock under the list. Copy buttons are never inside an
|
|
1470
|
+
`option` or the listbox (axe `nested-interactive`, `aria-required-children`).
|
|
1471
|
+
- **An answer without `run` or `href` copies `answer.value` on Enter/click**; answers show in the
|
|
1472
|
+
All scope only.
|
|
1473
|
+
- **`extraResults` groups are not filtered** unless `matchLocally: true`; remote rows the query does not
|
|
1474
|
+
match stay listed (score 0) and can never be the Top hit.
|
|
1475
|
+
- **`LauncherSearchEngine` searches what it is given.** It has no React, DOM or network; build the
|
|
1476
|
+
index with `createIndex` once per data change, not per keystroke.
|
|
1432
1477
|
- **`LauncherTrigger` does not bind its shortcut**; `shortcut={null}` removes the hint and
|
|
1433
1478
|
`aria-keyshortcuts`. An explicit `aria-label` hides the visible label from assistive tech, so keep
|
|
1434
1479
|
the visible text inside the label (WCAG 2.5.3). `display="icon"` has no visible text: the label is
|
|
@@ -1843,6 +1888,17 @@ from `optionRowDescriptionInk(theme.colors)` (exported from the package root) ra
|
|
|
1843
1888
|
`theme.colors.textMuted`. It is a no-op in light mode. The row's main label is
|
|
1844
1889
|
`optionRowInk(theme.colors, isSelected)` with `optionRowWeight(isSelected)` — never brand ink.
|
|
1845
1890
|
|
|
1891
|
+
### A custom option list must not fill the selected row
|
|
1892
|
+
|
|
1893
|
+
`listRowBackgrounds(colors).selected` and `.active` sit within 1.02:1 of each other on every brand
|
|
1894
|
+
and mode, so a list that fills both the selected row and the hovered row shows two identical fills
|
|
1895
|
+
and the user cannot tell which one is chosen. Library option lists paint one fill — the cursor's —
|
|
1896
|
+
and mark selection with `optionRowWeight` plus a trailing `Check` in `colors.accentText`.
|
|
1897
|
+
|
|
1898
|
+
**Apply:** `bg={optionRowFill(listRowBackgrounds(colors), isActive)}` for an index-driven list, or
|
|
1899
|
+
`{...optionRowCursorSx(listRowBackgrounds(colors))}` for rows that take DOM focus; both are exported
|
|
1900
|
+
from the package root. Text is `optionRowInk(colors, isSelected)`, unchanged on hover.
|
|
1901
|
+
|
|
1846
1902
|
### `Table`'s node column `label` needs `labelText` unless its text is plain children
|
|
1847
1903
|
|
|
1848
1904
|
`columns[].label` accepts a node (an icon, a tooltip trigger) as well as a string or number. Every
|
package/DESIGN-GUIDE.md
CHANGED
|
@@ -77,6 +77,7 @@ stay light in dark mode.
|
|
|
77
77
|
| Status text/icon | `colors.semanticText.success \| error \| warning \| info` |
|
|
78
78
|
| Status chip | `<Tag label="Won" colorScheme="green" />` (subtle, `sm` by default) — never a painted `Box`. Tone → `colorScheme`: success `green`, warning `orange`, error `red`, info `blue`, neutral `gray`. Quiet rows: `<Tag tone="success" display="dot" label=… />`; map an app's status vocabulary once with `<StatusTag status map label display />` |
|
|
79
79
|
| Selectable list row (inbox, picker) | `listRowBackgrounds(colors)` for hover/selected + `sx={focusVisibleStyles(colors)}`; pad the list `p={2}` so the ring isn't clipped |
|
|
80
|
+
| Option row in a dropdown/menu/select | `optionRowFill` / `optionRowCursorSx` (fill = cursor only) + `optionRowInk` + a trailing `Check` for the selected row — never a selected fill |
|
|
80
81
|
| Any filled surface you paint | `resolveFilledSurface(colors.<scale>, colors)` → `{ fill, color }` |
|
|
81
82
|
| Icon tint chip, avatar tint | `bg={colors.primary.opacity[8]}` + `color={colors.accentText}` (`opacity` exists on `primary`/`secondary`/`tertiary` only) |
|
|
82
83
|
|
|
@@ -151,7 +152,7 @@ sections are `Card` with the title in `header`.
|
|
|
151
152
|
|
|
152
153
|
| Pattern | Component — don't hand-build it |
|
|
153
154
|
| --- | --- |
|
|
154
|
-
| Multi-step flow (send, import, onboard) with validated Continue and a review step | `WizardDialog` (`Wizard` inline) — don't hand-roll Back / Continue / Finish around `StageProgress`; each step's `validate` returns its error messages, future steps stay locked until valid, each step's `summary` drives the review |
|
|
155
|
+
| Multi-step flow (send, import, onboard) with validated Continue and a review step | `WizardDialog` (`Wizard` inline; `chrome="inline"` inside a `WorkspaceWindow` or other host that is already the dialog) — don't hand-roll Back / Continue / Finish around `StageProgress`; each step's `validate` returns its error messages, future steps stay locked until valid, each step's `summary` drives the review |
|
|
155
156
|
| Rich-text template with `${field}` placeholders | `Editor mergeFields={…}` — not a `Select` + caret tracking + dialog beside it; use its `label` / `helperText` / `errorMessage` |
|
|
156
157
|
| A PDF behind auth, shown inline | `PdfViewer getBlob={…}` — the toolbar's Open / Download then work with the token, so don't add your own buttons |
|
|
157
158
|
| Toolbar: search · filters · active-filter chips · count · actions | `FilterBar` (`search`, children, `activeFilters` + `onRemoveFilter`/`onClearAll`, `summary`, `actions`) |
|
|
@@ -202,6 +203,10 @@ scrim `Box`. `Loading` is the older API and stays on the heavier house scrim by
|
|
|
202
203
|
`headerActions` for the app's own icon button (help, customise) with an `aria-label`.
|
|
203
204
|
- Show the suite's apps with `appsVisibility="always"` and drive plan gating by editing `apps` /
|
|
204
205
|
`locked`.
|
|
206
|
+
- For Spotlight-style search pass `searchMode="spotlight"` plus `synonyms` for the app's jargon
|
|
207
|
+
(`pf`, `wfh`), `scopes` for its entity groups, up to 5 `quickActions`, copyable `facts` on reference
|
|
208
|
+
items and `answers` for one-line lookups. Reuse `LauncherSearchEngine` for any other in-app search
|
|
209
|
+
instead of writing a matcher.
|
|
205
210
|
- Show shortcut hints with `KbdShortcut`, never a hand-styled `<kbd>`.
|
|
206
211
|
|
|
207
212
|
## 7. Every page has four states
|
|
@@ -259,8 +264,8 @@ not shipped in this package). Start from the closest one:
|
|
|
259
264
|
| Settings / long form | `settings.tsx` | `SectionRail` (rail from `lg`, `Select` below, dirty dots), `SettingsSection` + `SettingRow`, `SaveBar`, danger zone |
|
|
260
265
|
| Board | `pipeline.tsx` | `KanbanBoard` with custom cards and column totals (`height`/`maxH` to size it) |
|
|
261
266
|
| Inbox / master–detail | `inbox.tsx` | selectable list rows, conversation pane, composer, mobile list↔detail |
|
|
262
|
-
| App launcher (browse, pins, recents, search with actions) | `launcher.tsx` (`/examples/launcher`) | `NavLauncher` + `LauncherTrigger` in `NavigationBar leading`, app-owned registry and permission filter, pinned / frequent / recent ids, `linkAs`, remote results via `extraResults`, `isLoading`, `SideBar pinnedItems
|
|
263
|
-
| Wizard / multi-step dialog | `wizard.tsx` (`/examples/wizard`) | `WizardDialog` and inline `
|
|
267
|
+
| App launcher (browse, pins, recents, search with actions) | `launcher.tsx` (`/examples/launcher`) | `NavLauncher` + `LauncherTrigger` in `NavigationBar leading`, app-owned registry and permission filter, pinned / frequent / recent ids, `linkAs`, remote results via `extraResults`, `isLoading`, `SideBar pinnedItems`, Classic / Spotlight toggle, synonyms, scopes, quick actions, company facts with Copy, answers |
|
|
268
|
+
| Wizard / multi-step dialog | `wizard.tsx` (`/examples/wizard`) | `WizardDialog`, inline `Wizard` and `WizardDialog chrome="inline"` in a `WorkspaceWindow`: per-step `validate` messages, locked future steps, error summary, `summary`-driven review, `isFinishing` |
|
|
264
269
|
| Template editor with merge fields | `templateEditor.tsx` (`/examples/template-editor`) | `Editor mergeFields`: grouped Insert field, chips, unknown-field error chips and `onUnknownTokens`, Make dynamic, side preview, label / helper / error |
|
|
265
270
|
| Shop front (home → listing → product → bag → checkout → order) | `storefront/` | `ProductCard` grid / rail / quick-add, `ProductDetails` + `StickyBuyBar`, `CartItem`, `OrderSummary`, `CouponField`, address/payment groups, `OrderStatusTimeline`, `BagBar`; mobile frame + desktop |
|
|
266
271
|
|
|
@@ -24,7 +24,12 @@ const controlSize = (size) => {
|
|
|
24
24
|
return { h: height, minW: height, fontSize, px: paddingX };
|
|
25
25
|
};
|
|
26
26
|
// NOTE: [500] alone measures ~4.2 on a dark canvas — resolved per-mode so outline/ghost/link text stays readable.
|
|
27
|
-
|
|
27
|
+
// NOTE: a bare button sits on the canvas, a card or an overlay; dark overlays are the hardest plane.
|
|
28
|
+
const onCanvasText = (theme, colorScheme) => (0, accentText_1.accentTextOnPlanes)(theme.colors[colorScheme], [
|
|
29
|
+
theme.colors.backgroundColor.main,
|
|
30
|
+
theme.colors.background[50],
|
|
31
|
+
theme.colors.surfaceRaised,
|
|
32
|
+
]);
|
|
28
33
|
// NOTE: [50] alone measured ~1.04 (invisible) in both modes — gray borrows the row-state steps.
|
|
29
34
|
const onCanvasHoverBg = (theme, colorScheme, step) => {
|
|
30
35
|
const scale = theme.colors[colorScheme];
|
|
@@ -36,7 +41,7 @@ const onCanvasHoverBg = (theme, colorScheme, step) => {
|
|
|
36
41
|
return step === 16 ? rows.active : rows.selectedActive;
|
|
37
42
|
};
|
|
38
43
|
const swatch = (theme, colorScheme) => theme.colors[colorScheme];
|
|
39
|
-
const buttonSolidRung = (theme, colorScheme) => (0, filledSurface_1.solidFillRung)(swatch(theme, colorScheme), theme.colors.white);
|
|
44
|
+
const buttonSolidRung = (theme, colorScheme) => (0, filledSurface_1.solidFillRung)(swatch(theme, colorScheme), theme.colors.white, theme.colors.backgroundColor.main);
|
|
40
45
|
const darkerFill = (theme, colorScheme, step) => {
|
|
41
46
|
const scale = swatch(theme, colorScheme);
|
|
42
47
|
const rung = (0, filledSurface_1.darkerFillRung)(scale, theme.colors.backgroundColor.main, theme.colors.white, step);
|
|
@@ -16,8 +16,10 @@ export interface CopyButtonProps {
|
|
|
16
16
|
iconSize?: number;
|
|
17
17
|
/** Only affects the default "icon" variant's IconButton — "button" and "menu-item" ignore it. */
|
|
18
18
|
size?: "xs" | "sm" | "md";
|
|
19
|
-
/** Fires only if the clipboard write resolves
|
|
19
|
+
/** Fires only if the clipboard write resolves. */
|
|
20
20
|
onCopy?: (value: string) => void;
|
|
21
|
+
/** Fires when the clipboard is missing or denied, instead of failing silently. */
|
|
22
|
+
onCopyError?: (value: string) => void;
|
|
21
23
|
}
|
|
22
24
|
/** Copies `value` to the clipboard in 3 layouts (icon/button/menu-item) — not a full share/export action. */
|
|
23
25
|
declare const CopyButton: React.FC<CopyButtonProps>;
|
|
@@ -15,7 +15,7 @@ const primitives_1 = require("../../Theme/tokens/primitives");
|
|
|
15
15
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
16
16
|
const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
|
|
17
17
|
/** Copies `value` to the clipboard in 3 layouts (icon/button/menu-item) — not a full share/export action. */
|
|
18
|
-
const CopyButton = ({ value, variant = "icon", label = "Copy", copiedLabel = "Copied!", tooltip = "Copy", feedbackDuration = 1500, iconSize = 16, size = "sm", onCopy, }) => {
|
|
18
|
+
const CopyButton = ({ value, variant = "icon", label = "Copy", copiedLabel = "Copied!", tooltip = "Copy", feedbackDuration = 1500, iconSize = 16, size = "sm", onCopy, onCopyError, }) => {
|
|
19
19
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
20
20
|
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
21
21
|
const timer = (0, react_1.useRef)();
|
|
@@ -30,7 +30,7 @@ const CopyButton = ({ value, variant = "icon", label = "Copy", copiedLabel = "Co
|
|
|
30
30
|
timer.current = setTimeout(() => setCopied(false), feedbackDuration);
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
|
-
|
|
33
|
+
onCopyError === null || onCopyError === void 0 ? void 0 : onCopyError(value);
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
// NOTE: primary[500] fails AA as canvas ink in 5/7 brands (dark) — accentTextOnCanvas steps to a clearing rung.
|
|
@@ -3,4 +3,4 @@ import { DrawerProps, DrawerHeaderSlotProps, DrawerBodySlotProps, DrawerFooterSl
|
|
|
3
3
|
export declare const DrawerHeader: React.FC<DrawerHeaderSlotProps>;
|
|
4
4
|
export declare const DrawerBody: React.FC<DrawerBodySlotProps>;
|
|
5
5
|
export declare const DrawerFooter: React.FC<DrawerFooterSlotProps>;
|
|
6
|
-
export default function Drawer({ isOpen, onClose, placement, size, id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, portalChildren, children, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default function Drawer({ isOpen, onClose, placement, size, id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, portalChildren, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,6 +9,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
9
9
|
const react_1 = __importDefault(require("react"));
|
|
10
10
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
11
11
|
const surfaceRaised_1 = require("../../Theme/tokens/builders/surfaceRaised");
|
|
12
|
+
const dialogNaming_1 = require("../Modal/dialogNaming");
|
|
12
13
|
const warnDroppedSlotChildren_1 = require("../Modal/warnDroppedSlotChildren");
|
|
13
14
|
const react_2 = require("@chakra-ui/react");
|
|
14
15
|
const DrawerHeader = ({ children }) => {
|
|
@@ -39,12 +40,15 @@ const findSlot = (children, slot) => {
|
|
|
39
40
|
}
|
|
40
41
|
return undefined;
|
|
41
42
|
};
|
|
42
|
-
function
|
|
43
|
+
function DialogContent({ label, labelledBy, ...props }) {
|
|
44
|
+
return ((0, jsx_runtime_1.jsx)(dialogNaming_1.DialogNaming, { label: label, labelledBy: labelledBy, children: (0, jsx_runtime_1.jsx)(react_2.DrawerContent, { ...props }) }));
|
|
45
|
+
}
|
|
46
|
+
function Drawer({ isOpen, onClose, placement = "right", size = "xl", id, variant, vaiant, initialFocusRef, finalFocusRef, closeOnOverlayClick, closeOnEsc, contentProps, hideCloseButton, portalChildren, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }) {
|
|
43
47
|
var _a, _b;
|
|
44
48
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
45
49
|
const headerSlot = findSlot(children, exports.DrawerHeader);
|
|
46
50
|
const bodySlot = findSlot(children, exports.DrawerBody);
|
|
47
51
|
const footerSlot = findSlot(children, exports.DrawerFooter);
|
|
48
52
|
(0, warnDroppedSlotChildren_1.warnDroppedSlotChildren)("Drawer", children, [exports.DrawerHeader, exports.DrawerBody, exports.DrawerFooter]);
|
|
49
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Drawer, { isOpen: isOpen, placement: placement, onClose: onClose, size: size, variant: (_a = variant !== null && variant !== void 0 ? variant : vaiant) !== null && _a !== void 0 ? _a : "solid", id: id, initialFocusRef: initialFocusRef, finalFocusRef: finalFocusRef, closeOnOverlayClick: closeOnOverlayClick, closeOnEsc: closeOnEsc, children: [(0, jsx_runtime_1.jsx)(react_2.DrawerOverlay, {}), (0, jsx_runtime_1.jsxs)(
|
|
53
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Drawer, { isOpen: isOpen, placement: placement, onClose: onClose, size: size, variant: (_a = variant !== null && variant !== void 0 ? variant : vaiant) !== null && _a !== void 0 ? _a : "solid", id: id, initialFocusRef: initialFocusRef, finalFocusRef: finalFocusRef, closeOnOverlayClick: closeOnOverlayClick, closeOnEsc: closeOnEsc, children: [(0, jsx_runtime_1.jsx)(react_2.DrawerOverlay, {}), (0, jsx_runtime_1.jsxs)(DialogContent, { ...contentProps, label: ariaLabel, labelledBy: ariaLabelledBy, children: [!hideCloseButton && (0, jsx_runtime_1.jsx)(react_2.DrawerCloseButton, {}), headerSlot && ((0, jsx_runtime_1.jsx)(react_2.DrawerHeader, { borderBottomWidth: "1px", borderColor: (0, surfaceRaised_1.raisedRule)(colors), ...headerSlot.props, children: headerSlot })), (0, jsx_runtime_1.jsx)(react_2.DrawerBody, { ...((_b = bodySlot === null || bodySlot === void 0 ? void 0 : bodySlot.props) !== null && _b !== void 0 ? _b : {}), children: bodySlot }), footerSlot && ((0, jsx_runtime_1.jsx)(react_2.DrawerFooter, { borderTopWidth: "1px", borderColor: (0, surfaceRaised_1.raisedRule)(colors), ...footerSlot.props, children: footerSlot })), portalChildren] })] }));
|
|
50
54
|
}
|
|
@@ -14,6 +14,10 @@ export type DrawerProps = Pick<ChakraDrawerProps, "isOpen" | "onClose" | "placem
|
|
|
14
14
|
hideCloseButton?: boolean;
|
|
15
15
|
/** Rendered inside the drawer's dialog after the slots (so live regions stay announced); nested dialogs portal out and keep focus here. Unmounts with the drawer. */
|
|
16
16
|
portalChildren?: React.ReactNode;
|
|
17
|
+
/** Names the dialog instead of the whole header, for a header that holds fields or buttons. */
|
|
18
|
+
"aria-label"?: string;
|
|
19
|
+
/** Id of the element that names the dialog, e.g. a heading inside the header; wins over `aria-label`. */
|
|
20
|
+
"aria-labelledby"?: string;
|
|
17
21
|
};
|
|
18
22
|
/** Chakra style props (padding, borderBottom, gap, sx, ...) spread onto the rendered DrawerHeader; omit to keep theme defaults. Chakra's Drawer reuses Modal's header part internally. */
|
|
19
23
|
export type DrawerHeaderSlotProps = ModalHeaderProps;
|
|
@@ -8,6 +8,7 @@ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
|
8
8
|
const surfaceRaised_1 = require("../../Theme/tokens/builders/surfaceRaised");
|
|
9
9
|
const scrollbar_1 = require("../../Theme/tokens/builders/scrollbar");
|
|
10
10
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
11
|
+
const optionRowInk_1 = require("../Select/optionRowInk");
|
|
11
12
|
const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
12
13
|
const lucide_react_1 = require("lucide-react");
|
|
13
14
|
const rafThrottle_1 = require("../../Utils/rafThrottle");
|
|
@@ -248,16 +249,8 @@ const Dropdown = (0, react_1.forwardRef)(({ dropDownButtonStyle, ButtonText, opt
|
|
|
248
249
|
}, role: "menuitem", tabIndex: index === activeIndex ? 0 : -1, onClick: () => {
|
|
249
250
|
handleListItemClick(option.id, option.label);
|
|
250
251
|
focusTrigger();
|
|
251
|
-
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx: rowSpec.mx, px: rowSpec.px, py: rowSpec.py, minH: rowSpec.minH, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, display: "flex", alignItems: "center", gap: rowSpec.gap, fontSize: rowSpec.fontSize, cursor: "pointer", color: theme.colors
|
|
252
|
+
}, onKeyDown: (e) => handleItemKeyDown(e, option.id, option.label), mx: rowSpec.mx, px: rowSpec.px, py: rowSpec.py, minH: rowSpec.minH, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, display: "flex", alignItems: "center", gap: rowSpec.gap, fontSize: rowSpec.fontSize, cursor: "pointer", color: (0, optionRowInk_1.optionRowInk)(theme.colors, false), transition: ROW_TRANSITION, _hover: { bg: hoverTint }, _focus: { bg: hoverTint, outline: "none" }, _focusVisible: {
|
|
252
253
|
bg: hoverTint,
|
|
253
|
-
color: theme.colors.accentText,
|
|
254
|
-
}, _focus: {
|
|
255
|
-
bg: hoverTint,
|
|
256
|
-
color: theme.colors.accentText,
|
|
257
|
-
outline: "none",
|
|
258
|
-
}, _focusVisible: {
|
|
259
|
-
bg: hoverTint,
|
|
260
|
-
color: theme.colors.accentText,
|
|
261
254
|
boxShadow: `inset 0 0 0 2px ${theme.colors.focusRing.ring}`,
|
|
262
255
|
}, borderBottom: divider
|
|
263
256
|
? `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, surfaceRaised_1.raisedRule)(theme.colors)}`
|
|
@@ -49,6 +49,7 @@ const downloadFile = async (fileUrl, fileName) => {
|
|
|
49
49
|
/** Modal previewer inferring image/pdf/video/other from the extension — for an always-inline PDF-only panel use PdfViewer. */
|
|
50
50
|
const FilePreview = ({ isOpen, onClose, fileUrl, fileName, fileType, hideDownload = false, size = "xl", }) => {
|
|
51
51
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
52
|
+
const downloadFill = (0, filledSurface_1.resolveFilledSurface)(theme.colors.primary, theme.colors);
|
|
52
53
|
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
53
54
|
const [rotation, setRotation] = (0, react_1.useState)(0);
|
|
54
55
|
const kind = fileType || inferKind(fileUrl, fileName);
|
|
@@ -70,7 +71,7 @@ const FilePreview = ({ isOpen, onClose, fileUrl, fileName, fileType, hideDownloa
|
|
|
70
71
|
if (kind === "video") {
|
|
71
72
|
return ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "video", src: fileUrl, controls: true, width: "100%", borderRadius: "lg" }));
|
|
72
73
|
}
|
|
73
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", align: "center", justify: "center", py: 12, gap: 3, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileQuestion, { size: 48, color: theme.colors.textMuted }), (0, jsx_runtime_1.jsx)(react_2.Text, { color: theme.colors.textMuted, fontSize: "sm", children: "Preview isn't available for this file type." }), !hideDownload && ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "button", align: "center", gap: 2, px: 4, py: 2, borderRadius: "lg", bg:
|
|
74
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", align: "center", justify: "center", py: 12, gap: 3, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.FileQuestion, { size: 48, color: theme.colors.textMuted }), (0, jsx_runtime_1.jsx)(react_2.Text, { color: theme.colors.textMuted, fontSize: "sm", children: "Preview isn't available for this file type." }), !hideDownload && ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "button", align: "center", gap: 2, px: 4, py: 2, borderRadius: "lg", bg: downloadFill.fill, color: downloadFill.color, fontSize: "sm", fontWeight: 500, onClick: () => downloadFile(fileUrl, fileName), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Download, { size: 16 }), " Download file"] }))] }));
|
|
74
75
|
};
|
|
75
76
|
return ((0, jsx_runtime_1.jsxs)(react_2.Modal, { isOpen: isOpen, onClose: handleClose, size: size, isCentered: true, children: [(0, jsx_runtime_1.jsx)(react_2.ModalOverlay, { ...(0, overlay_styles_1.overlayScrimStyles)(theme.colors) }), (0, jsx_runtime_1.jsxs)(MotionContent, { bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", initial: { opacity: 0, scale: 0.96, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, transition: { duration: 0.2, ease: "easeOut" }, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", justify: "space-between", px: 4, py: 3, borderBottom: "1px solid", borderColor: theme.colors.boxborder[200], children: [(0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: fileName || "File", placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", fontWeight: 600, maxW: "60%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileName || "Preview" }) }), (0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 1, children: [kind === "image" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: "Zoom out", placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Zoom out", size: "sm", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Minus, { size: 16 }), onClick: () => setZoom((z) => Math.max(0.25, z - 0.25)) }) }), (0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: "Zoom in", placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Zoom in", size: "sm", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16 }), onClick: () => setZoom((z) => Math.min(4, z + 0.25)) }) }), (0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: "Rotate", placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Rotate", size: "sm", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.RotateCw, { size: 16 }), onClick: () => setRotation((r) => (r + 90) % 360) }) })] })), !hideDownload && ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: "Download", placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Download", size: "sm", variant: "ghost", color: theme.colors.semanticText.success, icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Download, { size: 18 }), onClick: () => downloadFile(fileUrl, fileName) }) })), (0, jsx_runtime_1.jsx)(react_2.ModalCloseButton, { position: "static" })] })] }), (0, jsx_runtime_1.jsx)(react_2.ModalBody, { p: 5, children: renderBody() })] })] }));
|
|
76
77
|
};
|
|
@@ -10,6 +10,7 @@ const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
|
10
10
|
const fieldStyles_1 = require("../Common/fieldStyles");
|
|
11
11
|
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
12
12
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
13
|
+
const revokePreviews = (files) => files.forEach((f) => f.previewUrl && URL.revokeObjectURL(f.previewUrl));
|
|
13
14
|
/** Self-contained widget; onFilesChange returns {id,name,size,previewUrl} metadata, never real File objects — cannot be used to upload bytes. */
|
|
14
15
|
const FileUploader = ({ maxFiles = 10, maxTotalSize = 20, onFilesChange, acceptTypes = ["jpeg", "jpg", "png"], }) => {
|
|
15
16
|
const { showToast } = (0, Toaster_1.useToaster)();
|
|
@@ -38,7 +39,9 @@ const FileUploader = ({ maxFiles = 10, maxTotalSize = 20, onFilesChange, acceptT
|
|
|
38
39
|
}));
|
|
39
40
|
const combinedFiles = [...uploadedFiles, ...newFiles].slice(0, maxFiles);
|
|
40
41
|
const totalSizeBytes = combinedFiles.reduce((acc, f) => acc + f.size, 0);
|
|
42
|
+
revokePreviews(newFiles.filter((f) => !combinedFiles.includes(f)));
|
|
41
43
|
if (totalSizeBytes > maxTotalBytes) {
|
|
44
|
+
revokePreviews(newFiles);
|
|
42
45
|
showToast({
|
|
43
46
|
title: `Total file size exceeds ${maxTotalSize} MB limit.`,
|
|
44
47
|
duration: 3000,
|
|
@@ -68,6 +71,7 @@ const FileUploader = ({ maxFiles = 10, maxTotalSize = 20, onFilesChange, acceptT
|
|
|
68
71
|
const handleDragLeave = () => setIsDragOver(false);
|
|
69
72
|
const removeFile = (id) => {
|
|
70
73
|
const filtered = uploadedFiles.filter((f) => f.id !== id);
|
|
74
|
+
revokePreviews(uploadedFiles.filter((f) => f.id === id));
|
|
71
75
|
setUploadedFiles(filtered);
|
|
72
76
|
onFilesChange === null || onFilesChange === void 0 ? void 0 : onFilesChange(filtered);
|
|
73
77
|
};
|
|
@@ -18,6 +18,6 @@ export type FileUploaderProps = {
|
|
|
18
18
|
maxTotalSize?: number;
|
|
19
19
|
/** Extensions without dots, default jpeg/jpg/png; filters the picker only, drops are never checked. */
|
|
20
20
|
acceptTypes?: string[];
|
|
21
|
-
/** Whole list after each add or remove;
|
|
21
|
+
/** Whole list after each add or remove; the uploader revokes the blob of a removed or rejected image; revoke the rest yourself when done. */
|
|
22
22
|
onFilesChange?: (files: UploadedFile[]) => void;
|
|
23
23
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { FormDrawerProps } from "./FormDrawerProps";
|
|
2
|
-
export default function FormDrawer({ isOpen, onClose, title, description, sections, errors, isDirty, onSubmit, isSubmitting, submitLabel, cancelLabel, secondaryAction, size, labels, "data-testid": testId, }: FormDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export default function FormDrawer({ isOpen, onClose, title, description, sections, errors, isDirty, onSubmit, isSubmitting, isSubmitDisabled, submitLabel, cancelLabel, secondaryAction, size, labels, "data-testid": testId, }: FormDrawerProps): import("react/jsx-runtime").JSX.Element;
|