pixelize-design-library 2.4.2-beta.58 → 2.4.2-beta.59
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 +75 -0
- package/COMPONENT-GOTCHAS.md +88 -2
- package/DESIGN-GUIDE.md +26 -3
- package/dist/Components/AppSwitcher/AppSwitcher.js +5 -1
- package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +2 -0
- package/dist/Components/AppSwitcher/AppSwitcherProps.js +7 -4
- package/dist/Components/Breadcrumbs/Breadcrumbs.d.ts +1 -1
- package/dist/Components/Breadcrumbs/Breadcrumbs.js +33 -10
- package/dist/Components/Breadcrumbs/BreadcrumbsProps.d.ts +16 -2
- package/dist/Components/Card/Card.d.ts +3 -1
- package/dist/Components/Card/Card.js +12 -2
- package/dist/Components/Card/CardProps.d.ts +5 -1
- package/dist/Components/Editor/Editor.js +50 -2
- package/dist/Components/Editor/EditorFieldBar.d.ts +15 -0
- package/dist/Components/Editor/EditorFieldBar.js +19 -0
- package/dist/Components/Editor/EditorProps.d.ts +74 -1
- package/dist/Components/Editor/MakeDynamicDialog.d.ts +12 -0
- package/dist/Components/Editor/MakeDynamicDialog.js +59 -0
- package/dist/Components/Editor/editorLabels.d.ts +4 -0
- package/dist/Components/Editor/editorLabels.js +24 -0
- package/dist/Components/Editor/editorTokenStyles.d.ts +28 -0
- package/dist/Components/Editor/editorTokenStyles.js +30 -0
- package/dist/Components/Editor/editorTokens.d.ts +26 -0
- package/dist/Components/Editor/editorTokens.js +134 -0
- package/dist/Components/Editor/useEditorBridge.d.ts +40 -0
- package/dist/Components/Editor/useEditorBridge.js +141 -0
- package/dist/Components/Kbd/Kbd.d.ts +6 -0
- package/dist/Components/Kbd/Kbd.js +33 -0
- package/dist/Components/Kbd/KbdProps.d.ts +17 -0
- package/dist/Components/Kbd/KbdProps.js +2 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +4 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.js +41 -0
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +16 -0
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.js +2 -0
- package/dist/Components/NavLauncher/NavLauncher.d.ts +3 -0
- package/dist/Components/NavLauncher/NavLauncher.js +395 -0
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +187 -0
- package/dist/Components/NavLauncher/NavLauncherProps.js +2 -0
- package/dist/Components/NavLauncher/NewTabLink.d.ts +3 -0
- package/dist/Components/NavLauncher/NewTabLink.js +14 -0
- package/dist/Components/NavLauncher/PinToggle.d.ts +3 -0
- package/dist/Components/NavLauncher/PinToggle.js +21 -0
- package/dist/Components/NavLauncher/launcherParts.d.ts +93 -0
- package/dist/Components/NavLauncher/launcherParts.js +130 -0
- package/dist/Components/NavLauncher/match.d.ts +53 -0
- package/dist/Components/NavLauncher/match.js +178 -0
- package/dist/Components/NavLauncher/revealStyles.d.ts +4 -0
- package/dist/Components/NavLauncher/revealStyles.js +19 -0
- package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -1
- package/dist/Components/NavigationBar/NavigationBar.js +2 -2
- package/dist/Components/NavigationBar/NavigationBarProps.d.ts +2 -0
- package/dist/Components/PageHeader/PageHeader.d.ts +1 -1
- package/dist/Components/PageHeader/PageHeader.js +9 -2
- package/dist/Components/PageHeader/PageHeaderProps.d.ts +8 -0
- package/dist/Components/PdfViewer/PdfViewer.d.ts +21 -4
- package/dist/Components/PdfViewer/PdfViewer.js +124 -3
- package/dist/Components/Search/Search.js +2 -1
- package/dist/Components/SideBar/SecondaryBar.js +4 -3
- package/dist/Components/SideBar/SideBar.d.ts +1 -1
- package/dist/Components/SideBar/SideBar.js +30 -9
- package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
- package/dist/Components/SideBar/components/MenuItemBox.d.ts +6 -0
- package/dist/Components/SideBar/components/MenuItemBox.js +8 -4
- package/dist/Components/SideBar/components/MenuPopoverContent.js +3 -2
- package/dist/Components/SideBar/components/OtherApps.js +2 -1
- package/dist/Components/SideBar/components/PinnedSection.d.ts +17 -0
- package/dist/Components/SideBar/components/PinnedSection.js +98 -0
- package/dist/Components/SideBar/sidebarFocus.d.ts +23 -0
- package/dist/Components/SideBar/sidebarFocus.js +20 -0
- package/dist/Components/StageProgress/StageProgress.js +2 -2
- package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
- package/dist/Components/StageProgress/StepperStage.js +5 -2
- package/dist/Components/WizardDialog/WizardDialog.d.ts +9 -0
- package/dist/Components/WizardDialog/WizardDialog.js +102 -0
- package/dist/Components/WizardDialog/WizardDialogProps.d.ts +48 -0
- package/dist/Components/WizardDialog/WizardDialogProps.js +2 -0
- package/dist/Components/WizardDialog/useWizardFlow.d.ts +22 -0
- package/dist/Components/WizardDialog/useWizardFlow.js +47 -0
- package/dist/Components/WizardDialog/wizardSteps.d.ts +31 -0
- package/dist/Components/WizardDialog/wizardSteps.js +40 -0
- package/dist/esm/Components/AppSwitcher/AppSwitcher.js +2 -1
- package/dist/esm/Components/AppSwitcher/AppSwitcherProps.js +5 -3
- package/dist/esm/Components/Breadcrumbs/Breadcrumbs.js +35 -12
- package/dist/esm/Components/Card/Card.js +12 -2
- package/dist/esm/Components/Editor/Editor.js +48 -3
- package/dist/esm/Components/Editor/EditorFieldBar.js +13 -0
- package/dist/esm/Components/Editor/MakeDynamicDialog.js +20 -0
- package/dist/esm/Components/Editor/editorLabels.js +20 -0
- package/dist/esm/Components/Editor/editorTokenStyles.js +25 -0
- package/dist/esm/Components/Editor/editorTokens.js +121 -0
- package/dist/esm/Components/Editor/useEditorBridge.js +138 -0
- package/dist/esm/Components/Kbd/Kbd.js +28 -0
- package/dist/esm/Components/Kbd/KbdProps.js +1 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +37 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTriggerProps.js +1 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +356 -0
- package/dist/esm/Components/NavLauncher/NavLauncherProps.js +1 -0
- package/dist/esm/Components/NavLauncher/NewTabLink.js +11 -0
- package/dist/esm/Components/NavLauncher/PinToggle.js +18 -0
- package/dist/esm/Components/NavLauncher/launcherParts.js +110 -0
- package/dist/esm/Components/NavLauncher/match.js +172 -0
- package/dist/esm/Components/NavLauncher/revealStyles.js +15 -0
- package/dist/esm/Components/NavigationBar/NavigationBar.js +3 -3
- package/dist/esm/Components/PageHeader/PageHeader.js +9 -2
- package/dist/esm/Components/PdfViewer/PdfViewer.js +123 -2
- package/dist/esm/Components/Search/Search.js +3 -2
- package/dist/esm/Components/SideBar/SecondaryBar.js +4 -3
- package/dist/esm/Components/SideBar/SideBar.js +31 -10
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +4 -2
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +3 -2
- package/dist/esm/Components/SideBar/components/OtherApps.js +2 -1
- package/dist/esm/Components/SideBar/components/PinnedSection.js +92 -0
- package/dist/esm/Components/SideBar/sidebarFocus.js +15 -0
- package/dist/esm/Components/StageProgress/StageProgress.js +2 -2
- package/dist/esm/Components/StageProgress/StepperStage.js +5 -2
- package/dist/esm/Components/WizardDialog/WizardDialog.js +58 -0
- package/dist/esm/Components/WizardDialog/WizardDialogProps.js +1 -0
- package/dist/esm/Components/WizardDialog/useWizardFlow.js +44 -0
- package/dist/esm/Components/WizardDialog/wizardSteps.js +35 -0
- package/dist/esm/index.js +7 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +21 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,57 @@ next major.
|
|
|
18
18
|
|
|
19
19
|
### Added
|
|
20
20
|
|
|
21
|
+
- **HRMS library requests** — a navigation launcher, key caps, a merge-field `Editor`, a wizard and
|
|
22
|
+
smaller gaps found by the HRMS migration:
|
|
23
|
+
- `NavLauncher` — full-height navigation launcher (browse modules, pins, Frequent, Recent, search
|
|
24
|
+
with actions and remote groups, optional suite-apps section below `md`), with `PinToggle`,
|
|
25
|
+
`NewTabLink`, `searchLauncher`, `searchLauncherSections` (sectioned search: modules, then their
|
|
26
|
+
screens, keyword-only hits get a `· keyword` hint) and `flattenScreens`. The pin control is a
|
|
27
|
+
star, shown with the ↗ link on row hover/focus only (always on touch); pinned rail items are tiles. It binds no global chord. `LauncherTrigger`
|
|
28
|
+
is the chip (10px radius, 36px, accent ink, one ⌘K hint) that opens an app-owned launcher (`aria-haspopup="dialog"`, `aria-expanded`,
|
|
29
|
+
`aria-keyshortcuts`). Types: `NavLauncherProps`, `LauncherScreen`, `LauncherModule`,
|
|
30
|
+
`LauncherSection`, `LauncherSearchItem`, `LauncherLabels` and friends, `LauncherTriggerProps`.
|
|
31
|
+
`NavLauncher` follows canvas `Launcher.dc.html`: stacked sections with "(n)" counts and a 1/2/3-column
|
|
32
|
+
grid, a 300px "Quick access" rail (Pinned tiles, Frequently visited, Recent), "Pins follow your
|
|
33
|
+
account" footer. New optional props `frequentLimit`, `suggestions` (no-results chips), `searchHint`,
|
|
34
|
+
`LauncherModule.isNew`, and labels `quickAccess`, `newBadge`, `screens`, `pinShort`, `footerNote`,
|
|
35
|
+
`suggestionsHint`, `found`, `pinTip`, `unpinTip`. The rail landmark is named "Quick access".
|
|
36
|
+
Keyboard: ←/→ and ↑/↓ are grid-aware (row-major, ↑/↓ cross sections), and `P` pins or unpins the
|
|
37
|
+
focused item (never while typing). New optional `openShortcutHint` (e.g. "⌘K or /", app-owned),
|
|
38
|
+
labels `hintOpenMenu` and `hintPin`; hint label defaults are lowercase and follow the canvas order.
|
|
39
|
+
- `Kbd` / `KbdShortcut` — a key cap and a chord (`mod+k` renders ⌘ K or Ctrl K), in the
|
|
40
|
+
`AppSwitcher` notation. `Search` and `AppSwitcher` render their shortcut hints with `Kbd`.
|
|
41
|
+
- `NavigationBar` `leading` — a slot between the sidebar toggle/back button and the title, outside
|
|
42
|
+
the title `<p>`.
|
|
43
|
+
- `SideBar` pinned section: `pinnedItems`, `pinnedLabel`, `onUnpin`, `activePinnedId`,
|
|
44
|
+
`pinnedLimit`, `pinnedLabels`; `collapseTransition` (`"none"` or `{ speed, animateShadow }`);
|
|
45
|
+
`autoCollapse` (default `true`; `false` stops the rail calling `changeToggle` itself, which also
|
|
46
|
+
silences the `open` prop). Types `SidebarPinnedItem`, `SidebarPinnedLabels`,
|
|
47
|
+
`SidebarCollapseTransition`.
|
|
48
|
+
- `Breadcrumbs` `overflow` (`"nowrap"` | `"wrap"` | `"truncate"`), `maxItems` (default 3) and
|
|
49
|
+
`labels.showMore`; `"truncate"` folds middle crumbs into a "…" menu. A `BreadcrumbItem` without
|
|
50
|
+
`path` renders as plain text.
|
|
51
|
+
- `PageHeader` `titleSize` (`"md"` | `"lg"` | `"xl"`) and `actionsLayout`
|
|
52
|
+
(`"auto"` | `"stacked"` | `"inline"`).
|
|
53
|
+
- `Card` `padding` (`"none"` | `"2xs"` | `"xs"` | `"sm"` | `"md"` | `"lg"`) — one padding for
|
|
54
|
+
header, body and footer.
|
|
55
|
+
- `Editor` merge fields: `mergeFields` adds an Insert field menu (grouped, searchable),
|
|
56
|
+
non-editable token chips (stored HTML keeps the raw `${path}`), unknown-field error chips with
|
|
57
|
+
`onUnknownTokens`, and a Make dynamic dialog; plus `tokenSyntax`, `showMakeDynamic`,
|
|
58
|
+
`toolbarButtons`, `apiRef` / `onReady` (`EditorApi`) and `labels` (incl. `dynamicFieldPlaceholder`, default "Choose a field"). `Editor` also takes `label`,
|
|
59
|
+
`isRequired`, `helperText`, `error`, `errorMessage` and `data-testid`; the editable area gets
|
|
60
|
+
`role="textbox"` with `aria-labelledby` / `aria-invalid` / `aria-describedby` / `aria-required`.
|
|
61
|
+
Types `EditorProps`, `EditorApi`, `EditorMergeField`, `EditorTokenSyntax`, `EditorToolbarButton`,
|
|
62
|
+
`EditorLabels`.
|
|
63
|
+
- `WizardDialog` and inline `Wizard` — a multi-step flow that owns step state (validated
|
|
64
|
+
Continue, locked future steps, error summary, review on the last step); `wizardTransition` and
|
|
65
|
+
`firstInvalidStep` are exported.
|
|
66
|
+
- `PdfViewer` `getBlob` (authed files: the viewer makes and revokes the object URL, skeleton while
|
|
67
|
+
loading), `fallback`, `onLoad`, `onError`, `labels`; inline support is detected with
|
|
68
|
+
`navigator.pdfViewerEnabled`. `PdfViewerProps` / `PdfViewerLabels` are now exported types.
|
|
69
|
+
- Page examples: **App launcher** (`/examples/launcher`), **Wizard** (`/examples/wizard`),
|
|
70
|
+
**Template editor** (`/examples/template-editor`).
|
|
71
|
+
|
|
21
72
|
- **Commerce kit** — the components for a shop front, designed mobile-first (Pebble principles:
|
|
22
73
|
chromeless tonal cards, one loud action per view, selection never by colour alone). Product:
|
|
23
74
|
`Rating`, `PriceBlock`, `StockStatus`, `ProductBadges`, `WishlistToggle`, `OptionChip`,
|
|
@@ -86,6 +137,22 @@ next major.
|
|
|
86
137
|
|
|
87
138
|
### Changed
|
|
88
139
|
|
|
140
|
+
- **`PageHeader` `actionsLayout` defaults to `"auto"`**: at `md` and up the actions wrap below the
|
|
141
|
+
title when they do not fit (before, they squeezed the title). Identical whenever title and
|
|
142
|
+
actions fit on one row; pass `actionsLayout="inline"` for the old behaviour.
|
|
143
|
+
- **`Card` `padding` is no longer Chakra's style prop** — it is omitted from the type and now takes
|
|
144
|
+
the `CardPadding` steps. A consumer passing `padding={4}` gets a compile error; use `p` or `sx`.
|
|
145
|
+
Staged for the next major (see Breaking); consumer blast radius not measured.
|
|
146
|
+
- **`SideBar` default box-shadow transition is 260ms** (was 200ms) so it follows `collapseTransition`
|
|
147
|
+
`speed="slow"`. The width transition is unchanged.
|
|
148
|
+
- **`StageProgress variant="stepper"` renders an `ol` of `li`** (`StepperStageProps.asListItem`).
|
|
149
|
+
Visual layout is unchanged; selectors that expected a `div` list no longer match.
|
|
150
|
+
- **`PdfViewer` `pdfUrl` is optional** — pass exactly one of `pdfUrl` / `getBlob`. In `getBlob` mode
|
|
151
|
+
the toolbar Open and Download are real buttons that fetch through it.
|
|
152
|
+
- `Editor` binds Jodit's `change` / `blur` itself on every Jodit instance and renders Jodit only once
|
|
153
|
+
its popup root exists (no throwaway first instance). Behaviour for existing props is unchanged.
|
|
154
|
+
- `BreadcrumbItem.path` is optional (type widening).
|
|
155
|
+
|
|
89
156
|
- **`ProductCard` and `ProductDetails` are rewritten** — see Breaking below. `ProductCard` is a
|
|
90
157
|
chromeless 4:5 card (grid / list / rail, swatch dots that swap the photo, quick-add sheet or
|
|
91
158
|
direct add that morphs into a stepper, sold-out "Notify me", slots). `ProductDetails` is a
|
|
@@ -125,6 +192,10 @@ next major.
|
|
|
125
192
|
|
|
126
193
|
### Fixed
|
|
127
194
|
|
|
195
|
+
- **`SideBar` rail controls now show a `:focus-visible` ring** (rows, submenu and flyout rows,
|
|
196
|
+
`SecondaryBar`, collapse toggle, Add New, user footer, Collapse/Hide/Show, switch-app cells,
|
|
197
|
+
overlay close) — WCAG 2.4.7 / 2.4.11.
|
|
198
|
+
|
|
128
199
|
- **Commerce performance (consumer report on 2.4.2-beta.57).** `ProductCard` no longer loads the
|
|
129
200
|
hover image on devices that cannot hover; `QuickAddSheet` mounts on first open, not on first
|
|
130
201
|
paint; `formatPrice` caches its `Intl.NumberFormat`; every `matchMedia` consumer
|
|
@@ -379,6 +450,10 @@ known consumers in the Pixelize repos. `ProductCard` drops `id`, `label`, `descr
|
|
|
379
450
|
option and cart-callback props (it takes `product`, `selection`, `onAddToBag` and slots). Video
|
|
380
451
|
and YouTube/Vimeo frames are now `ProductMedia` items in `MediaGallery`.
|
|
381
452
|
|
|
453
|
+
**`Card` `padding` narrows from Chakra's style prop to `CardPadding`.** `padding={4}` stops
|
|
454
|
+
compiling; use `p` / `sx` for arbitrary values. No use inside this repo; consumers not measured, so
|
|
455
|
+
run `tsc --noEmit` in each before upgrading.
|
|
456
|
+
|
|
382
457
|
**`star` is a required `PaletteProps` token.** A hand-written palette without it throws in
|
|
383
458
|
`assertPixelizePalette` naming `star`; palettes built from the library's builders get it for free.
|
|
384
459
|
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -233,6 +233,8 @@ and wires `aria-controls` to them. Pick by that, never by the picture. A screen
|
|
|
233
233
|
| Top bar: back control + user-avatar profile/logout dropdown | `NavigationBar` | No page-nav links at all — `navMenu` is only the avatar dropdown's own items |
|
|
234
234
|
| Top bar: horizontal page links with active-state highlighting + submenus | `Header` | Desktop submenus are hover-only, see gotcha below |
|
|
235
235
|
| Collapsible/dockable left rail — primary in-app navigation + secondary panel | `SideBar` | The persistent nav shell, not for switching between whole apps |
|
|
236
|
+
| In-app launcher: browse modules, pins, recents, search | `NavLauncher` opened by a `LauncherTrigger` in `NavigationBar`'s `leading` | An app-owned screen registry, not a cross-app switcher; pins can also surface in `SideBar` `pinnedItems` |
|
|
237
|
+
| Key cap or chord hint (`mod+k`) | `Kbd` / `KbdShortcut` | Never hand-style a `<kbd>`; `Search` and `AppSwitcher` already use it |
|
|
236
238
|
| Cross-app launcher (CRM/HRMS/Tickets/Billing/...) | `AppSwitcher` | Two-column popover grid of separate app origins — `SideBar`'s `otherApps` is the legacy predecessor; don't wire both into one shell |
|
|
237
239
|
| Floating Jira-style dialog to view/edit one record without leaving the page | `WorkspaceWindow` | Modal-based floating panel, not page navigation; pairs with `WorkspaceTray` |
|
|
238
240
|
|
|
@@ -253,10 +255,10 @@ Four components open a file, and only one of them is the general answer.
|
|
|
253
255
|
| --- | --- | --- |
|
|
254
256
|
| A gallery of mixed media with next/prev, a filmstrip and per-type stages | `MediaViewer` | The full lightbox: image/video/audio/pdf/doc stages, keyboard nav, fullscreen, download. Collapses the filmstrip and stepper automatically for a single item |
|
|
255
257
|
| One file, click-to-open, no gallery | `FilePreview` | Lighter controlled modal; auto-detects type, zoom/rotate for images |
|
|
256
|
-
| Always-visible inline PDF panel in the page flow | `PdfViewer` | Non-modal, PDF-only, small open/download toolbar |
|
|
258
|
+
| Always-visible inline PDF panel in the page flow | `PdfViewer` | Non-modal, PDF-only, small open/download toolbar; `getBlob` for files behind auth |
|
|
257
259
|
| Crop/preview a user avatar | `ProfilePhotoViewer` | Avatar-specific, not a general file viewer |
|
|
258
260
|
|
|
259
|
-
`PdfViewer`
|
|
261
|
+
`PdfViewer` shows a fallback when the browser cannot render a PDF inline (`navigator.pdfViewerEnabled === false`) or `getBlob` rejects; `FilePreview`'s `"pdf"` kind renders a bare `<embed type="application/pdf">` and tells you nothing. `MediaViewer` also handles it: formats a browser cannot decode (`.avi`, `.mkv`, `.m3u8`, `.heic`, `.tiff`) render an icon and a file-type chip with a download action instead of a blank frame.
|
|
260
262
|
|
|
261
263
|
`MediaViewer.labels` takes a `Partial<MediaViewerLabels>` — that type and `defaultMediaViewerLabels` are both exported from the package root, so you can merge rather than restate every string.
|
|
262
264
|
|
|
@@ -948,6 +950,11 @@ draw nothing.
|
|
|
948
950
|
|
|
949
951
|
**Apply:** drop the nested `CardBody`, or pass `bodyProps={{ p: 0 }}`.
|
|
950
952
|
|
|
953
|
+
To change the padding, use `padding="none" | "2xs" | "xs" | "sm" | "md" | "lg"`. `sx={{ p: ... }}` on
|
|
954
|
+
the container *adds* to the body padding (double inset) and `sx={{ p: 0 }}` does nothing. `padding`
|
|
955
|
+
is no longer Chakra's style prop (omitted from the type) — use `p` for a raw value. With
|
|
956
|
+
`padding="none"` the header keeps `xs` padding.
|
|
957
|
+
|
|
951
958
|
### Commerce kit — read before building a shop screen
|
|
952
959
|
|
|
953
960
|
`ProductCard`, `ProductDetails`, `VariantSelector` and the bag pieces share one contract: pass
|
|
@@ -1114,6 +1121,39 @@ is also `null` until Jodit's lazy-loaded chunk resolves — a mount-time effect
|
|
|
1114
1121
|
**Apply:** never set `theme`/`popupRoot` inside the `config` you pass to `Editor`. To force a
|
|
1115
1122
|
remount, wrap `<Editor>` itself in a keyed parent, not via `Editor`'s own `key` prop.
|
|
1116
1123
|
|
|
1124
|
+
### `PdfViewer` `getBlob` — fetched on mount only; the toolbar already has Open and Download
|
|
1125
|
+
|
|
1126
|
+
- **Swapping the file needs a new React `key`.** `getBlob` is read through a ref and fetched on mount,
|
|
1127
|
+
so an inline arrow is safe, but changing it never refetches — the old document silently stays.
|
|
1128
|
+
- **The default fallback shows only a message while the toolbar is visible**; with
|
|
1129
|
+
`showToolbar={false}` it adds its own Open and Download buttons. `getBlob` mode makes the toolbar's
|
|
1130
|
+
actions work for authed files, so an app need not render its own — or pass `showToolbar={false}`.
|
|
1131
|
+
- Pass exactly one of `pdfUrl` / `getBlob`.
|
|
1132
|
+
|
|
1133
|
+
### `WizardDialog` — `validate` must be pure; controlled `stepId` does not unlock steps
|
|
1134
|
+
|
|
1135
|
+
`validate` runs on Continue, on Finish, and again for every step on Finish. Passing `stepId` controls
|
|
1136
|
+
the visible step only: stages past the furthest valid step stay disabled. Esc, close, Back and Cancel
|
|
1137
|
+
are disabled while `isFinishing`.
|
|
1138
|
+
|
|
1139
|
+
### `PageHeader` actions wrap by default
|
|
1140
|
+
|
|
1141
|
+
`actionsLayout` defaults to `"auto"`, so actions that do not fit drop below the title at `md`+. A
|
|
1142
|
+
test asserting the old single-row layout needs `actionsLayout="inline"`.
|
|
1143
|
+
|
|
1144
|
+
### `Editor` merge fields — chips, raw values and stale closures
|
|
1145
|
+
|
|
1146
|
+
- **Chips are `<span data-field contenteditable="false">`.** Do not style or strip `[data-field]` in
|
|
1147
|
+
consumer CSS or sanitisers, and sanitise the raw `${path}` value `onChange` reports, not Jodit's HTML.
|
|
1148
|
+
- **A hand-typed `${path}` stays plain text** until `value` is next applied from outside; only tokens
|
|
1149
|
+
inserted through the menu / `apiRef` or already present in `value` become chips. Unknown-token
|
|
1150
|
+
detection still runs on the raw value.
|
|
1151
|
+
- **`config.extraButtons[].exec` goes stale** (the memo key is `JSON.stringify`, which drops functions).
|
|
1152
|
+
Use `toolbarButtons` — React buttons, always current.
|
|
1153
|
+
- **`jodit-react` binds `onChange` / `onBlur` once, keyed on handler identity, not `config`.** After
|
|
1154
|
+
Jodit is re-created (readonly or height change) a stable handler stops receiving events. `Editor`
|
|
1155
|
+
binds them itself per instance; don't copy the pattern when wrapping Jodit directly.
|
|
1156
|
+
|
|
1117
1157
|
### `Editor`'s Jodit toolbar is fixed for screen readers and keyboard, from our wrapper only
|
|
1118
1158
|
|
|
1119
1159
|
Jodit's own markup (`node_modules/jodit`) put `aria-label` on each button's `role="listitem"`
|
|
@@ -1182,6 +1222,34 @@ Each mounted `Search` registers its own `window` Ctrl+K listener, so two on one
|
|
|
1182
1222
|
keypress. **Apply:** pass `shortcut={false}` on all but one. `showShortcutHint={false}` only hides
|
|
1183
1223
|
the hint; the listener stays.
|
|
1184
1224
|
|
|
1225
|
+
The same applies to a launcher: `NavLauncher` and `LauncherTrigger` bind no chord, and the library
|
|
1226
|
+
reserves `mod+k` for the app's own palette. The app must pass `shortcut={false}` on every page
|
|
1227
|
+
`Search`, or claim the chord in capture phase (`keydown` on `window` with `capture: true`) before
|
|
1228
|
+
the `Search` listeners see it — otherwise the first mounted `Search` takes Ctrl+K.
|
|
1229
|
+
|
|
1230
|
+
### `NavLauncher` — the app owns routing, registry, query and chord
|
|
1231
|
+
|
|
1232
|
+
- **`linkAs` must forward `className`, `ref`, `onClick`, `href`, `target`, `rel`, `tabIndex` and
|
|
1233
|
+
`aria-hidden`** to its anchor. Dropping `className` loses all row styling; dropping `onClick` means
|
|
1234
|
+
a click never closes the launcher.
|
|
1235
|
+
- **`onNavigate` fires for keyboard opens only** (Enter, mod+Enter). Pointer clicks follow the
|
|
1236
|
+
rendered links, so a router `linkAs` must do the navigation.
|
|
1237
|
+
- **Controlled `query`:** the launcher still calls `onQueryChange("")` when it closes; the app must
|
|
1238
|
+
apply it or the stale query reopens.
|
|
1239
|
+
- **Search rows are not nested links.** The visible label is a text-only `role="option"`; the real
|
|
1240
|
+
anchor is an `aria-hidden`, `tabIndex={-1}` overlay (so copy-link and mod-click work) and pin / ↗
|
|
1241
|
+
are `aria-hidden` siblings. Screen readers use Enter / mod+Enter. Do not nest links inside the
|
|
1242
|
+
option — axe `nested-interactive` fails.
|
|
1243
|
+
- **The apps section decides "compact" with `matchMedia("(min-width: 48em)")`** and falls back to
|
|
1244
|
+
desktop (hidden) where `matchMedia` is missing (SSR, jsdom). Tests must stub it to see the section.
|
|
1245
|
+
- **`P` on a focused NavLauncher row pins it** instead of starting type-ahead; in the search field it types the letter. Rows without a star (Recent, key screens, apps) keep type-ahead.
|
|
1246
|
+
- **`NavLauncher` `suggestions` empty falls back to `noResultsHint`** instead of showing chips.
|
|
1247
|
+
- **`PinToggle` / `NewTabLink` are opacity 0 until row hover or focus-within**, so a consumer's own row must carry `data-group` (or `role="group"`) to reveal them.
|
|
1248
|
+
- **`LauncherLabels.screensGroup` is unused** by search now (sections replaced the flat group); kept so label objects still type-check.
|
|
1249
|
+
- **`LauncherTrigger` does not bind its shortcut**; `shortcut={null}` removes the hint and
|
|
1250
|
+
`aria-keyshortcuts`. An explicit `aria-label` hides the visible label from assistive tech, so keep
|
|
1251
|
+
the visible text inside the label (WCAG 2.5.3).
|
|
1252
|
+
|
|
1185
1253
|
### `Header`'s desktop submenus open on hover only — no keyboard or click trigger
|
|
1186
1254
|
|
|
1187
1255
|
Dropdown children are gated on local hover state (`onMouseEnter`/`onMouseLeave`), with no `onFocus`.
|
|
@@ -1217,6 +1285,9 @@ It's declared on the type but omitted from the component's destructure entirely.
|
|
|
1217
1285
|
|
|
1218
1286
|
**Apply:** don't pass it expecting it to mark or style the final crumb — there's nothing it can do.
|
|
1219
1287
|
|
|
1288
|
+
With `overflow="wrap" | "truncate"` each crumb is `noOfLines={1}` with a `title`, so one very long
|
|
1289
|
+
label ellipsizes; the default `"nowrap"` is unchanged.
|
|
1290
|
+
|
|
1220
1291
|
### `Accordion`'s `items[].buttonProps` replaces, not merges, the expanded/hover highlight
|
|
1221
1292
|
|
|
1222
1293
|
It spreads after the built-in `bg`/`_hover` that encode `isExpanded` state — passing `bg` or
|
|
@@ -1378,6 +1449,21 @@ it; now `@deprecated` in `SideBarProps.tsx`.
|
|
|
1378
1449
|
|
|
1379
1450
|
**Apply:** don't pass `selectedSubMenu` expecting it to highlight a submenu row.
|
|
1380
1451
|
|
|
1452
|
+
### `SideBar` pinned rows, `autoCollapse` and `collapseTransition`
|
|
1453
|
+
|
|
1454
|
+
- **`autoCollapse={false}` also silences the `open` prop** (`open` works by calling `changeToggle`).
|
|
1455
|
+
Drive collapse with `toggle` only.
|
|
1456
|
+
- **A pinned id highlights only in the pinned block** while `activePinnedId` matches, and the parent
|
|
1457
|
+
submenu of a pinned child does not auto-open. An `activePinnedId` past `pinnedLimit` expands the
|
|
1458
|
+
list so the active state never disappears.
|
|
1459
|
+
- **Pinned rows are anchors with `href={url}`**: a plain click calls `handleMenuClick` (and closes the
|
|
1460
|
+
overlay), modified clicks fall through to the browser. Unpin is a sibling button, never nested.
|
|
1461
|
+
- **`collapseTransition` affects the `lg`+ inline rail only** — overlay slide, drag and `mobileNav`
|
|
1462
|
+
are untouched.
|
|
1463
|
+
- **The rail focus ring is lifted per surface** (`sidebarFocus.ts`) because `colors.focusRing.ring` is
|
|
1464
|
+
derived for the page canvas and fails 3:1 on the rail for five light brands. The lasting fix is a
|
|
1465
|
+
`sidebar.focusRing` theme token.
|
|
1466
|
+
|
|
1381
1467
|
### `KanbanBoard`'s `kanbanSelect`/`onOpen` don't check `permissions` — decision: accepted as-is
|
|
1382
1468
|
|
|
1383
1469
|
Looked like an asymmetry with the gated `kanbanEdit`/`kanbanCreate`/`onDelete`, but on inspection
|
package/DESIGN-GUIDE.md
CHANGED
|
@@ -29,7 +29,7 @@ Calm, dense, professional SaaS admin — Linear/Stripe-dashboard, not a landing
|
|
|
29
29
|
| Page padding | `px={{ base: 4, md: 6 }} py={{ base: 4, md: 5 }}` |
|
|
30
30
|
| Between page sections | `gap={5}` (a `Flex direction="column"`) |
|
|
31
31
|
| Between cards in a grid | `gap={4}` |
|
|
32
|
-
| Inside a card | `Card`'s own padding — don't add more; `bodyProps={{ p: 0 }}` for edge-to-edge content |
|
|
32
|
+
| Inside a card | `Card`'s own padding — don't add more; `padding="none"` (or `"2xs"`/`"xs"` for dense) instead of `sx={{ p }}`, `bodyProps={{ p: 0 }}` for edge-to-edge content |
|
|
33
33
|
| Between form fields | `gap={4}` vertical, `gap={3}` side by side |
|
|
34
34
|
| Inline clusters (tags, meta, icon + text) | `gap={1.5}`–`gap={2}` |
|
|
35
35
|
| Page header → content | the section gap (`gap={5}`) — no extra margin |
|
|
@@ -41,7 +41,7 @@ Borders are the one px value: `borderWidth="1px"`. Fixed widths (side panel, lis
|
|
|
41
41
|
|
|
42
42
|
| Role | Props |
|
|
43
43
|
| --- | --- |
|
|
44
|
-
| Page title + subtitle | `PageHeader` (
|
|
44
|
+
| Page title + subtitle | `PageHeader` (18px/600 h1 by default, `sm` muted subtitle; `titleSize="xl"` for a top-level page) — don't hand-build it |
|
|
45
45
|
| Section / card title | `Heading as="h2" fontSize="md" fontWeight={600} color={colors.header[900]}`, in `Card`'s `header` prop |
|
|
46
46
|
| Body | `Text fontSize="sm" color={colors.text[800]}` |
|
|
47
47
|
| Label / meta / helper | `Text fontSize="xs" color={colors.textMuted}` |
|
|
@@ -139,11 +139,15 @@ the row title is the label: `<Text as="label" htmlFor={id}>` pointing at the fie
|
|
|
139
139
|
```
|
|
140
140
|
|
|
141
141
|
`PageHeader` also takes `breadcrumbs`, `leading` (avatar), `titleAddon` (status `Tag`), `meta` (icon
|
|
142
|
-
row) and `tabs` — a record page's whole header.
|
|
142
|
+
row) and `tabs` — a record page's whole header. Actions wrap under the title on their own when they do
|
|
143
|
+
not fit (`actionsLayout="auto"`), so don't hand-build a stacked variant. KPI rows are a `SimpleGrid` of `StatCard`s; card
|
|
143
144
|
sections are `Card` with the title in `header`.
|
|
144
145
|
|
|
145
146
|
| Pattern | Component — don't hand-build it |
|
|
146
147
|
| --- | --- |
|
|
148
|
+
| 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 |
|
|
149
|
+
| Rich-text template with `${field}` placeholders | `Editor mergeFields={…}` — not a `Select` + caret tracking + dialog beside it; use its `label` / `helperText` / `errorMessage` |
|
|
150
|
+
| 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 |
|
|
147
151
|
| Toolbar: search · filters · active-filter chips · count · actions | `FilterBar` (`search`, children, `activeFilters` + `onRemoveFilter`/`onClearAll`, `summary`, `actions`) |
|
|
148
152
|
| Read-only label/value grid (record details, key facts) | `DescriptionList` (`items`, `columns`, `layout="inline"` for a side panel, `copyable`, `isLoading`) |
|
|
149
153
|
| Settings row (title + description left, control right) | `SettingRow` inside a `SettingsSection` (`isDanger` for the danger zone, `action` for a header-right button) |
|
|
@@ -160,6 +164,22 @@ sections are `Card` with the title in `header`.
|
|
|
160
164
|
scrim `Box`. `Loading` is the older API and stays on the heavier house scrim by default; pass
|
|
161
165
|
`variant="dim"` to route it through `Backdrop`, or just use `Backdrop` directly.
|
|
162
166
|
|
|
167
|
+
### Navigation shell
|
|
168
|
+
|
|
169
|
+
`SideBar` is the rail, `NavigationBar` the top bar, `NavLauncher` the full-height "go anywhere" panel.
|
|
170
|
+
**The app owns the registry, permissions and persistence**; the library owns the look and the keyboard.
|
|
171
|
+
|
|
172
|
+
- Keep one registry (modules → screens) and derive the rail, the launcher and route guards from it.
|
|
173
|
+
Filter by permission *before* passing `sections` to `NavLauncher`; use `isLoading` while access is
|
|
174
|
+
unknown, `linkAs` for the router link, and `extraResults` + `isSearching` for remote (entity) results.
|
|
175
|
+
- Open the launcher with `<NavigationBar leading={<LauncherTrigger onClick isOpen />} />` — never inside
|
|
176
|
+
`navigationBarText.text`. Neither component binds the chord: the app owns the `mod+k` listener
|
|
177
|
+
(capture phase) and passes `shortcut={false}` to any page `Search`.
|
|
178
|
+
- Pins live in the app (server or local). Pass them to `NavLauncher` (`pinnedIds`, `onTogglePin`) *and*
|
|
179
|
+
to `SideBar pinnedItems` + `onUnpin` so the rail shows what the launcher pinned. Persist collapse with
|
|
180
|
+
`useSidebarPrefs`, and pass `autoCollapse={false}` if the app, not the rail, decides.
|
|
181
|
+
- Show shortcut hints with `KbdShortcut`, never a hand-styled `<kbd>`.
|
|
182
|
+
|
|
163
183
|
## 7. Every page has four states
|
|
164
184
|
|
|
165
185
|
Design all four, not just the happy path.
|
|
@@ -213,6 +233,9 @@ not shipped in this package). Start from the closest one:
|
|
|
213
233
|
| Settings / long form | `settings.tsx` | section nav, `SettingsSection` + `SettingRow`, `SaveBar`, danger zone |
|
|
214
234
|
| Board | `pipeline.tsx` | `KanbanBoard` with custom cards and column totals (`height`/`maxH` to size it) |
|
|
215
235
|
| Inbox / master–detail | `inbox.tsx` | selectable list rows, conversation pane, composer, mobile list↔detail |
|
|
236
|
+
| 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` |
|
|
237
|
+
| Wizard / multi-step dialog | `wizard.tsx` (`/examples/wizard`) | `WizardDialog` and inline `Wizard`: per-step `validate` messages, locked future steps, error summary, `summary`-driven review, `isFinishing` |
|
|
238
|
+
| 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 |
|
|
216
239
|
| 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 |
|
|
217
240
|
|
|
218
241
|
## 10. Shop screens (e-commerce)
|
|
@@ -32,6 +32,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
37
40
|
const react_1 = require("react");
|
|
@@ -43,6 +46,7 @@ const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
|
43
46
|
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
44
47
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
45
48
|
const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
49
|
+
const Kbd_1 = __importDefault(require("../Kbd/Kbd"));
|
|
46
50
|
const AppMark_1 = __importStar(require("./AppMark"));
|
|
47
51
|
const AppSwitcherProps_1 = require("./AppSwitcherProps");
|
|
48
52
|
const useAppSwitcherShortcut_1 = require("./useAppSwitcherShortcut");
|
|
@@ -225,7 +229,7 @@ const AppSwitcher = ({ apps, currentAppId, onSelect, onOpenChange, isSearchable,
|
|
|
225
229
|
return;
|
|
226
230
|
}
|
|
227
231
|
handlePanelKeyDown(e);
|
|
228
|
-
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Grip, { size: s.icon + 1 }) }) }), (0, jsx_runtime_1.jsx)(react_2.PopoverContent, { w: width, maxW: "calc(100vw - 1.5rem)", textAlign: "start", onKeyDown: handlePanelKeyDown, bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}`, _focus: { outline: "none", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}` }, children: (0, jsx_runtime_1.jsxs)(react_2.PopoverBody, { p: 0, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, px: 3, pt: 3, pb: showSearch ? 2 : 1, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "0.6875rem", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: theme.colors.textMuted, children: HEADING }), (0, jsx_runtime_1.jsx)(react_2.Box, { flex: "1" }), hint && (
|
|
232
|
+
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Grip, { size: s.icon + 1 }) }) }), (0, jsx_runtime_1.jsx)(react_2.PopoverContent, { w: width, maxW: "calc(100vw - 1.5rem)", textAlign: "start", onKeyDown: handlePanelKeyDown, bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}`, _focus: { outline: "none", boxShadow: `0 10px 30px ${theme.colors.boxShadow.default}` }, children: (0, jsx_runtime_1.jsxs)(react_2.PopoverBody, { p: 0, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, px: 3, pt: 3, pb: showSearch ? 2 : 1, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "0.6875rem", fontWeight: 600, letterSpacing: "0.08em", textTransform: "uppercase", color: theme.colors.textMuted, children: HEADING }), (0, jsx_runtime_1.jsx)(react_2.Box, { flex: "1" }), hint && (0, jsx_runtime_1.jsx)(Kbd_1.default, { size: "xs", children: hint })] }), showSearch && ((0, jsx_runtime_1.jsx)(react_2.Box, { px: 3, pb: 2, children: (0, jsx_runtime_1.jsxs)(react_2.InputGroup, { size: "sm", children: [(0, jsx_runtime_1.jsx)(react_2.InputLeftElement, { pointerEvents: "none", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, color: theme.colors.textMuted }) }), (0, jsx_runtime_1.jsx)(react_2.Input, { ref: searchRef, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search apps", borderRadius: "lg", "aria-label": "Search apps", onKeyDown: (e) => {
|
|
229
233
|
if (e.key === "ArrowDown" && visible.length > 0) {
|
|
230
234
|
e.preventDefault();
|
|
231
235
|
focusItem(0);
|
|
@@ -52,6 +52,8 @@ export declare const filterApps: (apps: AppOption[], query: string) => AppOption
|
|
|
52
52
|
export type ShortcutEvent = Pick<KeyboardEvent, "key" | "altKey" | "ctrlKey" | "metaKey" | "shiftKey">;
|
|
53
53
|
/** "mod" is Cmd on Apple platforms and Ctrl everywhere else. */
|
|
54
54
|
export declare const matchesShortcut: (event: ShortcutEvent, shortcut: string, isApple?: boolean) => boolean;
|
|
55
|
+
/** One label per key in a chord, so a named key such as `enter` stays a single cap. */
|
|
56
|
+
export declare const shortcutKeys: (shortcut: string, isApple?: boolean) => string[];
|
|
55
57
|
export declare const formatShortcut: (shortcut: string, isApple?: boolean) => string;
|
|
56
58
|
/** Input to `buildSuiteApps`: an `AppOption` keyed by its subdomain label, with `url` optional since it can be derived. */
|
|
57
59
|
export interface SuiteAppSource extends Omit<AppOption, "url"> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildSuiteApps = exports.formatShortcut = exports.matchesShortcut = exports.filterApps = exports.appInitials = void 0;
|
|
3
|
+
exports.buildSuiteApps = exports.formatShortcut = exports.shortcutKeys = exports.matchesShortcut = exports.filterApps = exports.appInitials = void 0;
|
|
4
4
|
const NON_WORD = /[^\p{L}\p{N}]+/u;
|
|
5
5
|
/** Up to two initials, so a mark stays legible without an icon. */
|
|
6
6
|
const appInitials = (name) => {
|
|
@@ -48,12 +48,15 @@ const matchesShortcut = (event, shortcut, isApple = typeof navigator !== "undefi
|
|
|
48
48
|
exports.matchesShortcut = matchesShortcut;
|
|
49
49
|
const APPLE_SYMBOLS = { mod: "⌘", cmd: "⌘", meta: "⌘", ctrl: "⌃", shift: "⇧", alt: "⌥" };
|
|
50
50
|
const OTHER_LABELS = { mod: "Ctrl", cmd: "Win", meta: "Win", ctrl: "Ctrl", shift: "Shift", alt: "Alt" };
|
|
51
|
-
const
|
|
51
|
+
const isAppleNow = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
52
|
+
/** One label per key in a chord, so a named key such as `enter` stays a single cap. */
|
|
53
|
+
const shortcutKeys = (shortcut, isApple = isAppleNow()) => {
|
|
52
54
|
const parts = shortcut.toLowerCase().split("+").map((p) => p.trim()).filter(Boolean);
|
|
53
55
|
const table = isApple ? APPLE_SYMBOLS : OTHER_LABELS;
|
|
54
|
-
|
|
55
|
-
return isApple ? rendered.join("") : rendered.join("+");
|
|
56
|
+
return parts.map((p, i) => { var _a; return (i === parts.length - 1 ? p.toUpperCase() : ((_a = table[p]) !== null && _a !== void 0 ? _a : p.toUpperCase())); });
|
|
56
57
|
};
|
|
58
|
+
exports.shortcutKeys = shortcutKeys;
|
|
59
|
+
const formatShortcut = (shortcut, isApple = isAppleNow()) => (0, exports.shortcutKeys)(shortcut, isApple).join(isApple ? "" : "+");
|
|
57
60
|
exports.formatShortcut = formatShortcut;
|
|
58
61
|
/**
|
|
59
62
|
* Derives every app's origin from the host the consumer is already served on, so one deployment
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { BreadcrumbsProps } from "./BreadcrumbsProps";
|
|
2
|
-
export default function Breadcrumbs({ spacing, separator, fontWeight, fontSize, isCurrentPage, handleClick, items, }: BreadcrumbsProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export default function Breadcrumbs({ spacing, separator, fontWeight, fontSize, isCurrentPage, handleClick, items, overflow, maxItems, labels, "data-testid": testId, }: BreadcrumbsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,19 +4,42 @@ exports.default = Breadcrumbs;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("@chakra-ui/react");
|
|
6
6
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
7
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
7
8
|
const breadcrumbInk_1 = require("./breadcrumbInk");
|
|
8
|
-
|
|
9
|
+
const DEFAULT_MAX_ITEMS = 3;
|
|
10
|
+
const MIN_MAX_ITEMS = 2;
|
|
11
|
+
const OVERFLOW_SX = {
|
|
12
|
+
nowrap: undefined,
|
|
13
|
+
wrap: { "& ol": { flexWrap: "wrap", rowGap: "4px" }, "& li": { maxWidth: "100%" } },
|
|
14
|
+
truncate: { "& ol": { flexWrap: "nowrap" }, "& li": { minWidth: 0 } },
|
|
15
|
+
};
|
|
16
|
+
const collapse = (entries, maxItems) => {
|
|
17
|
+
const keep = Math.max(MIN_MAX_ITEMS, Math.floor(maxItems));
|
|
18
|
+
if (entries.length <= keep)
|
|
19
|
+
return { head: entries, hidden: [], tail: [] };
|
|
20
|
+
return { head: entries.slice(0, 1), hidden: entries.slice(1, entries.length - (keep - 1)), tail: entries.slice(entries.length - (keep - 1)) };
|
|
21
|
+
};
|
|
22
|
+
function Breadcrumbs({ spacing, separator, fontWeight, fontSize, isCurrentPage, handleClick, items, overflow = "nowrap", maxItems = DEFAULT_MAX_ITEMS, labels, "data-testid": testId, }) {
|
|
9
23
|
const theme = (0, react_1.useTheme)();
|
|
10
24
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
11
25
|
const ink = (0, breadcrumbInk_1.breadcrumbInk)(colors);
|
|
12
26
|
const effectiveSeparator = separator !== null && separator !== void 0 ? separator : ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", color: ink.separator, children: "/" }));
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
const clip = overflow !== "nowrap";
|
|
28
|
+
const clipProps = clip ? { noOfLines: 1 } : {};
|
|
29
|
+
const renderCrumb = ({ crumb, index }) => {
|
|
30
|
+
const isLast = Boolean(isCurrentPage) && index === items.length - 1;
|
|
31
|
+
const color = isLast ? ink.current : ink.link;
|
|
32
|
+
const common = { ...clipProps, title: clip ? crumb.label : undefined, color, fontWeight: isLast ? 600 : fontWeight };
|
|
33
|
+
return ((0, jsx_runtime_1.jsx)(react_1.BreadcrumbItem, { isCurrentPage: isLast, children: crumb.path === undefined ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-current": isLast ? "page" : undefined, ...common, children: crumb.label })) : ((0, jsx_runtime_1.jsx)(react_1.BreadcrumbLink, { href: crumb.path, ...common, sx: { "&:hover, &:active": { textDecoration: "none", color: isLast ? ink.current : ink.linkHover } }, onClick: (event) => {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
handleClick(crumb.path);
|
|
36
|
+
}, children: crumb.label })) }, index));
|
|
37
|
+
};
|
|
38
|
+
const renderMenu = (hidden) => {
|
|
39
|
+
var _a;
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)(react_1.BreadcrumbItem, { sx: { flexShrink: 0 }, children: (0, jsx_runtime_1.jsxs)(react_1.Menu, { isLazy: true, placement: "bottom-start", children: [(0, jsx_runtime_1.jsx)(react_1.MenuButton, { type: "button", "aria-label": ((_a = labels === null || labels === void 0 ? void 0 : labels.showMore) !== null && _a !== void 0 ? _a : ((n) => `Show ${n} more`))(hidden.length), color: ink.link, borderRadius: "md", px: 1, _hover: { color: ink.linkHover }, sx: (0, focusRing_styles_1.focusVisibleStyles)(colors), children: "\u2026" }), (0, jsx_runtime_1.jsx)(react_1.MenuList, { children: hidden.map(({ crumb, index }) => ((0, jsx_runtime_1.jsx)(react_1.MenuItem, { isDisabled: crumb.path === undefined, onClick: () => crumb.path !== undefined && handleClick(crumb.path), children: crumb.label }, index))) })] }) }, "overflow"));
|
|
41
|
+
};
|
|
42
|
+
const entries = items.map((crumb, index) => ({ crumb, index }));
|
|
43
|
+
const { head, hidden, tail } = overflow === "truncate" ? collapse(entries, maxItems) : { head: entries, hidden: [], tail: [] };
|
|
44
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Breadcrumb, { spacing: spacing, separator: effectiveSeparator, fontWeight: fontWeight, fontSize: fontSize, fontFamily: theme.fonts.body, sx: OVERFLOW_SX[overflow], "data-testid": testId, children: [head.map(renderCrumb), hidden.length > 0 && renderMenu(hidden), tail.map(renderCrumb)] }));
|
|
22
45
|
}
|
|
@@ -4,11 +4,25 @@ export type BreadcrumbsProps = Pick<BreadcrumbProps, "spacing" | "separator" | "
|
|
|
4
4
|
isCurrentPage?: boolean;
|
|
5
5
|
/** Accepted but never read by Breadcrumbs — has no effect at all. */
|
|
6
6
|
isLastChild?: boolean;
|
|
7
|
+
/** Called with a crumb's path; never called for a label-only crumb (no `path`). */
|
|
7
8
|
handleClick: (path: string) => void;
|
|
8
9
|
items: BreadcrumbItem[];
|
|
10
|
+
/** "wrap" flows crumbs onto new lines; "truncate" keeps one line and folds middle crumbs into a "…" menu. Default "nowrap". */
|
|
11
|
+
overflow?: "nowrap" | "wrap" | "truncate";
|
|
12
|
+
/** With overflow "truncate": most crumbs shown (first plus the last `maxItems - 1`); the rest go in the menu. Default 3, minimum 2. */
|
|
13
|
+
maxItems?: number;
|
|
14
|
+
/** Visible-string overrides; `showMore` is the collapsed menu button's aria-label. */
|
|
15
|
+
labels?: BreadcrumbsLabels;
|
|
16
|
+
"data-testid"?: string;
|
|
9
17
|
};
|
|
10
|
-
/**
|
|
18
|
+
/** Visible strings, each with an English default. */
|
|
19
|
+
export type BreadcrumbsLabels = {
|
|
20
|
+
/** Default: `Show ${count} more`. */
|
|
21
|
+
showMore?: (count: number) => string;
|
|
22
|
+
};
|
|
23
|
+
/** One crumb: `label` is the visible text; `path` goes to the link's href and `handleClick`. */
|
|
11
24
|
export type BreadcrumbItem = {
|
|
12
|
-
|
|
25
|
+
/** Omit for a label-only crumb: rendered as text, not a link, never passed to `handleClick`. */
|
|
26
|
+
path?: string;
|
|
13
27
|
label: string;
|
|
14
28
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { CardPadding } from "./CardProps";
|
|
2
|
+
declare const Card: import("react").ForwardRefExoticComponent<Omit<import("@chakra-ui/react").CardProps, "color" | "colorScheme" | "direction" | "padding" | "size" | "justify" | "children" | "variant" | "align"> & {
|
|
2
3
|
dividercolor?: "gray" | "red" | "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" | "purple" | "pink" | "whiteAlpha" | "blackAlpha";
|
|
3
4
|
dividersize?: string;
|
|
4
5
|
dividervariant?: "solid" | "dashed";
|
|
@@ -13,6 +14,7 @@ declare const Card: import("react").ForwardRefExoticComponent<Omit<import("@chak
|
|
|
13
14
|
direction?: import("@chakra-ui/react").ResponsiveValue<"row" | "column" | "row-reverse" | "column-reverse">;
|
|
14
15
|
align?: import("@chakra-ui/react").CardBodyProps["alignItems"];
|
|
15
16
|
justify?: import("@chakra-ui/react").CardBodyProps["justifyContent"];
|
|
17
|
+
padding?: CardPadding;
|
|
16
18
|
bodyProps?: import("@chakra-ui/react").CardBodyProps;
|
|
17
19
|
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
18
20
|
export default Card;
|
|
@@ -3,11 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const react_2 = require("@chakra-ui/react");
|
|
6
|
-
const
|
|
6
|
+
const HEADER_PADDING_WHEN_FLUSH = "0.75rem";
|
|
7
|
+
const PADDING_VALUES = {
|
|
8
|
+
none: "0",
|
|
9
|
+
"2xs": "0.375rem",
|
|
10
|
+
xs: "0.75rem",
|
|
11
|
+
sm: "0.75rem 1rem",
|
|
12
|
+
md: "1rem 1.25rem",
|
|
13
|
+
lg: "1.25rem 1.5rem",
|
|
14
|
+
};
|
|
15
|
+
const Card = (0, react_1.forwardRef)(function Card({ header, footer, children, color, colorScheme, direction = "column", align, justify, bodyProps, padding, ...rest }, ref) {
|
|
7
16
|
const cardProps = { ...rest };
|
|
8
17
|
delete cardProps.dividercolor;
|
|
9
18
|
delete cardProps.dividersize;
|
|
10
19
|
delete cardProps.dividervariant;
|
|
11
|
-
|
|
20
|
+
const sx = padding ? { "--card-padding": PADDING_VALUES[padding], ...cardProps.sx } : cardProps.sx;
|
|
21
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Card, { ref: ref, colorScheme: colorScheme !== null && colorScheme !== void 0 ? colorScheme : color, ...cardProps, sx: sx, children: [header && ((0, jsx_runtime_1.jsx)(react_2.CardHeader, { sx: padding === "none" ? { "--card-padding": HEADER_PADDING_WHEN_FLUSH } : undefined, children: header })), (0, jsx_runtime_1.jsx)(react_2.CardBody, { display: "flex", flexDirection: direction, alignItems: align, justifyContent: justify, ...bodyProps, children: children }), footer && (0, jsx_runtime_1.jsx)(react_2.CardFooter, { children: footer })] }));
|
|
12
22
|
});
|
|
13
23
|
exports.default = Card;
|
|
@@ -6,8 +6,10 @@ type DeadDividerProps = {
|
|
|
6
6
|
dividersize?: string;
|
|
7
7
|
dividervariant?: "solid" | "dashed";
|
|
8
8
|
};
|
|
9
|
+
/** Padding steps: "none" flush, "2xs" 0.375rem, "xs" 0.75rem all round, "sm" / "md" / "lg" match `size`. */
|
|
10
|
+
export type CardPadding = "none" | "2xs" | "xs" | "sm" | "md" | "lg";
|
|
9
11
|
/** Themed outline surface with optional header/footer slots; children and direction/align/justify apply to the body. */
|
|
10
|
-
export type CardProps = Omit<ChakraCardProps, "children" | "variant" | "size" | "color" | "colorScheme" | "direction" | "align" | "justify"> & DeadDividerProps & {
|
|
12
|
+
export type CardProps = Omit<ChakraCardProps, "children" | "variant" | "size" | "color" | "colorScheme" | "direction" | "align" | "justify" | "padding"> & DeadDividerProps & {
|
|
11
13
|
/** Rendered in CardHeader (semibold header ink) only when truthy. */
|
|
12
14
|
header?: React.ReactNode;
|
|
13
15
|
/** Rendered in CardFooter only when truthy. */
|
|
@@ -27,6 +29,8 @@ export type CardProps = Omit<ChakraCardProps, "children" | "variant" | "size" |
|
|
|
27
29
|
align?: CardBodyProps["alignItems"];
|
|
28
30
|
/** Body justifyContent; `bodyProps` wins if it sets the same thing. */
|
|
29
31
|
justify?: CardBodyProps["justifyContent"];
|
|
32
|
+
/** Overrides `size`'s padding for header, body and footer; "none" is flush (a header keeps "xs"), "2xs" is 6px. */
|
|
33
|
+
padding?: CardPadding;
|
|
30
34
|
/** Props for the body wrapper, e.g. `p={0}` for edge-to-edge media. */
|
|
31
35
|
bodyProps?: CardBodyProps;
|
|
32
36
|
};
|