pixelize-design-library 2.4.2-beta.63 → 2.4.2-beta.64
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 +47 -0
- package/COMPONENT-GOTCHAS.md +26 -3
- package/DESIGN-GUIDE.md +1 -0
- package/dist/Components/Button/Button.styles.js +7 -2
- 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/Input/TextInput.styles.js +10 -6
- package/dist/Components/KanbanBoard/KanbanBoard.js +6 -5
- 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 +7 -2
- package/dist/Components/Table/components/TableLoading.d.ts +4 -1
- package/dist/Components/Table/components/TableLoading.js +6 -5
- 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/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -2
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +25 -18
- package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +38 -2
- 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/tokens/builders/accentText.d.ts +2 -0
- package/dist/Theme/tokens/builders/accentText.js +7 -1
- 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/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/Input/TextInput.styles.js +10 -6
- package/dist/esm/Components/KanbanBoard/KanbanBoard.js +6 -5
- 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 +9 -4
- package/dist/esm/Components/Table/components/TableLoading.js +5 -4
- package/dist/esm/Components/Table/filters/CompactSelect.js +2 -11
- package/dist/esm/Components/Table/variants/TableVariantContext.js +3 -3
- package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +4 -0
- package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +26 -19
- package/dist/esm/Theme/chakra/Menu.styles.js +7 -0
- package/dist/esm/Theme/chakra/componentStyles.js +2 -2
- package/dist/esm/Theme/tokens/builders/accentText.js +5 -0
- 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 +4 -4
- package/dist/index.d.ts +5 -5
- package/dist/index.js +11 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,18 @@ 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
|
+
- **`optionRowFill(rows, isActive)`** and **`optionRowCursorSx(rows)`** are exported from the package root: the one
|
|
28
|
+
fill an option row paints (the cursor's), and its `:hover`/`:focus` form for roving-focus lists.
|
|
29
|
+
- **`accentTextOnPlanes(scale, planes)`** is exported from the package root: the first accent rung clearing AA on every
|
|
30
|
+
plane in the list, for chrome that can land on the canvas, a card or an overlay.
|
|
31
|
+
- **`ruleOn(colors, plane)`** and **`MOTION_PRESS_SCALE`** are exported from the package root. `ruleOn` draws a divider at the
|
|
32
|
+
in-card weight on any plane (`cardRule` is `ruleOn` on `background[50]`); the press scale was only reachable by deep import.
|
|
21
33
|
- **`Card variant="elevated"`**: borderless, `background[50]` lifted by `shadows.card`; declares a 0 border width so it
|
|
22
34
|
never inherits an outline parent's rule.
|
|
23
35
|
- **`bandOn`, `recessOn`, `cardRule`**: plane-step helpers that return the input unchanged in light and a visible step
|
|
@@ -381,6 +393,37 @@ next major.
|
|
|
381
393
|
|
|
382
394
|
### Changed (visual)
|
|
383
395
|
|
|
396
|
+
- **`Table` loading state.** With `isLoading` and no rows, each row now draws one skeleton per column (a checkbox
|
|
397
|
+
placeholder in the checkbox column, blank spacer cells) with the variant's row rule, instead of one bar spanning the
|
|
398
|
+
table. If `columns` is still empty while loading, the header shows four placeholder cells and a disabled select-all
|
|
399
|
+
instead of a lone checkbox stretched across the row. The select-all checkbox is disabled while `isLoading`.
|
|
400
|
+
- **`Table` defaults to `variant="studio"`.** Every `Table` rendered without `variant` (or with `variant={undefined}`)
|
|
401
|
+
now draws studio: flat small-caps header, one row rule, the select slot resting on the row number, status as a pill.
|
|
402
|
+
Keep the old look with `variant="classic"`. Measured 2026-10-14: crm-frontend has 38 `<Table>` call sites, none
|
|
403
|
+
passing `variant` (all 38 change); account-frontend 8 of 18 change (the other 10 already pass `"studio"`); hrms,
|
|
404
|
+
tickets and lfour render `Table` only through an `AppTable` wrapper that already defaults to studio (0 change).
|
|
405
|
+
- **Option rows fill only under the cursor; selection is the trailing check and weight 500.** `Select`, `SearchSelect`,
|
|
406
|
+
`SelectSearch`, `CompactSelect` and `OrgSwitcher` no longer fill the selected row: the selected fill sat 1.00-1.02:1
|
|
407
|
+
from the hover fill on all 18 brand x mode palettes, so the two read as one state. `SelectSearch`/`CompactSelect` no
|
|
408
|
+
longer fill row 0 on open beside the hovered row. `Dropdown` items keep `text[700]` under the cursor instead of turning
|
|
409
|
+
`accentText`. `SearchSelect` rows sit inset (`mx`, rounded) with the check at the trailing edge and no hairline
|
|
410
|
+
between rows. Text on every row state measures 7.69:1 or more (selected 12.43-19.90:1).
|
|
411
|
+
- **Chakra `Menu` items use option-row ink:** `text[700]` instead of `text[900]`; a checked `MenuItemOption` is weight
|
|
412
|
+
500 with an `accentText` check, matching `Select`.
|
|
413
|
+
- **Dark `Button` `outline`/`ghost`/`link` ink is chosen against the canvas, the card and the overlay.** It cleared AA
|
|
414
|
+
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
|
|
415
|
+
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
|
|
416
|
+
(0 light combos move); 62 dark combos step lighter (emerald primary `#0f9d8a` -> `#14e3c7`).
|
|
417
|
+
- **Dark rules and pressed solids (owner review before release).** In dark only: the `NavigationBar` header rule and
|
|
418
|
+
leading divider, the `KanbanBoard` column edge, header rule and count pill, the filled `TextInput` (a visible band off
|
|
419
|
+
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)
|
|
420
|
+
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
|
|
421
|
+
the canvas (a pressed warning solid reached 1.00:1) and never flip the label colour, so zinc dark's pressed primary stays
|
|
422
|
+
light with dark ink instead of dropping to `#52525b`. `ScrollToTop`, `FilePreview`, `ProfilePhotoViewer`, the Table
|
|
423
|
+
clear-filters badge, Chakra `Badge`/`Radio` and the Table status pills now pass the canvas to `solidFillRung`: zinc dark
|
|
424
|
+
primary and every brand's dark gray solid move from `#52525b`/`#525252` (2.4:1) to `#e4e4e7`/`#a3a3a3` with dark ink.
|
|
425
|
+
Light mode and every other dark colour keep their fill; error/warning/info pressed fills in dark are lighter than before
|
|
426
|
+
and hover and press now share one rung.
|
|
384
427
|
- **Elevation follows Pebble, per mode.** `shadows.sm`/`base`/`md`/`lg` are the Pebble card-press/
|
|
385
428
|
card/float/dialog stacks, with new `card`, `float`, `dialog` and `sheet` role keys. Light shadows are
|
|
386
429
|
softer and wider; dark mode gets its own heavier black ramp, so overlays cast a visible shadow on a
|
|
@@ -444,6 +487,10 @@ next major.
|
|
|
444
487
|
|
|
445
488
|
### Fixed
|
|
446
489
|
|
|
490
|
+
- **`WorkspaceTrayPreview`** reports `onOpenChange(false)` only after the card actually opened (it used to fire when
|
|
491
|
+
the pointer left before the open delay).
|
|
492
|
+
- **`FileUploader`** revokes the blob URL of an image the moment it is removed, and of images it rejects (over the total
|
|
493
|
+
size or past `maxFiles`); those URLs were created and never freed. Surviving URLs stay yours to revoke.
|
|
447
494
|
- **`Table` settings sheet** fades in place instead of dropping in when the user prefers reduced motion.
|
|
448
495
|
- **`SearchSelect`**: the Select All and Add New rows are reachable from the keyboard. Arrow keys, Home and End walk
|
|
449
496
|
Select All, then the options, then Add New, and Enter runs them. Both are now `role="option"` rows, so a test that
|
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
|
|
@@ -700,13 +712,13 @@ whose defaults arrive asynchronously doesn't open already red.
|
|
|
700
712
|
### A wrapper presetting a prop must destructure with a default, not rely on JSX prop order
|
|
701
713
|
|
|
702
714
|
Wrapping a library component to preset a prop (e.g. `Table`'s `variant`) as
|
|
703
|
-
`<Table variant="
|
|
715
|
+
`<Table variant="classic" {...props} />` silently reverts to the library's own default the moment a
|
|
704
716
|
caller passes `variant={undefined}` — a later spread always wins in JSX, even with `undefined`,
|
|
705
717
|
and the library component resolves that `undefined` via its own default parameter.
|
|
706
718
|
|
|
707
719
|
**Apply:**
|
|
708
720
|
```tsx
|
|
709
|
-
const AppTable = ({ variant = '
|
|
721
|
+
const AppTable = ({ variant = 'classic', ...props }: TableProps) => <Table variant={variant} {...props} />;
|
|
710
722
|
```
|
|
711
723
|
Applies to any library prop with a default value, not just `Table`'s `variant`.
|
|
712
724
|
|
|
@@ -1843,6 +1855,17 @@ from `optionRowDescriptionInk(theme.colors)` (exported from the package root) ra
|
|
|
1843
1855
|
`theme.colors.textMuted`. It is a no-op in light mode. The row's main label is
|
|
1844
1856
|
`optionRowInk(theme.colors, isSelected)` with `optionRowWeight(isSelected)` — never brand ink.
|
|
1845
1857
|
|
|
1858
|
+
### A custom option list must not fill the selected row
|
|
1859
|
+
|
|
1860
|
+
`listRowBackgrounds(colors).selected` and `.active` sit within 1.02:1 of each other on every brand
|
|
1861
|
+
and mode, so a list that fills both the selected row and the hovered row shows two identical fills
|
|
1862
|
+
and the user cannot tell which one is chosen. Library option lists paint one fill — the cursor's —
|
|
1863
|
+
and mark selection with `optionRowWeight` plus a trailing `Check` in `colors.accentText`.
|
|
1864
|
+
|
|
1865
|
+
**Apply:** `bg={optionRowFill(listRowBackgrounds(colors), isActive)}` for an index-driven list, or
|
|
1866
|
+
`{...optionRowCursorSx(listRowBackgrounds(colors))}` for rows that take DOM focus; both are exported
|
|
1867
|
+
from the package root. Text is `optionRowInk(colors, isSelected)`, unchanged on hover.
|
|
1868
|
+
|
|
1846
1869
|
### `Table`'s node column `label` needs `labelText` unless its text is plain children
|
|
1847
1870
|
|
|
1848
1871
|
`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
|
|
|
@@ -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);
|
|
@@ -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
|
};
|
|
@@ -4,6 +4,7 @@ exports.Input = exports.fieldSizes = exports.fieldAddonSx = exports.fieldFlushed
|
|
|
4
4
|
const fieldStyles_1 = require("../Common/fieldStyles");
|
|
5
5
|
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
6
6
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
7
|
+
const planeStep_1 = require("../../Theme/tokens/builders/planeStep");
|
|
7
8
|
/** Field look shared by Input, Textarea and NumberInput; `_invalid` also clears Chakra's stock invalid shadow. */
|
|
8
9
|
const fieldPartStyles = (colors) => {
|
|
9
10
|
const error = colors.semanticText.error;
|
|
@@ -18,12 +19,15 @@ const fieldPartStyles = (colors) => {
|
|
|
18
19
|
};
|
|
19
20
|
};
|
|
20
21
|
exports.fieldPartStyles = fieldPartStyles;
|
|
21
|
-
const fieldFilledStyles = (colors) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const fieldFilledStyles = (colors) => {
|
|
23
|
+
const filledBand = (0, planeStep_1.bandOn)(colors.background[100], colors.background[50]);
|
|
24
|
+
return {
|
|
25
|
+
...(0, exports.fieldPartStyles)(colors),
|
|
26
|
+
bg: filledBand,
|
|
27
|
+
borderColor: "transparent",
|
|
28
|
+
_hover: { bg: filledBand, borderColor: "transparent" },
|
|
29
|
+
};
|
|
30
|
+
};
|
|
27
31
|
exports.fieldFilledStyles = fieldFilledStyles;
|
|
28
32
|
const fieldFlushedStyles = (colors) => ({
|
|
29
33
|
...(0, exports.fieldPartStyles)(colors),
|
|
@@ -50,6 +50,7 @@ const motion_2 = require("../../Theme/chakra/motion");
|
|
|
50
50
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
51
51
|
const usePreferences_1 = require("../../Hooks/usePreferences");
|
|
52
52
|
const AccountCard_1 = __importDefault(require("./AccountCard"));
|
|
53
|
+
const planeStep_1 = require("../../Theme/tokens/builders/planeStep");
|
|
53
54
|
const HeaderActions_1 = __importDefault(require("../Header/HeaderActions"));
|
|
54
55
|
const MeasuredItem_1 = __importDefault(require("./MeasuredItem"));
|
|
55
56
|
const OverflowTooltipText_1 = __importDefault(require("../SideBar/components/OverflowTooltipText"));
|
|
@@ -73,7 +74,7 @@ const MIN_LIST_HEIGHT = 120;
|
|
|
73
74
|
const KANBAN_VIRTUALIZE_THRESHOLD = 30;
|
|
74
75
|
const COLUMN_TRANSITION = `${(0, motion_1.transitionFor)(["background-color", "border-color"])}, ${(0, motion_1.transitionFor)(["width"], "base")}`;
|
|
75
76
|
const SKELETON_ROWS = [3, 4, 3];
|
|
76
|
-
const CardSkeleton = ({ colors, rows }) => ((0, jsx_runtime_1.jsx)(react_2.Box, { "data-testid": "kanban-card-skeleton", borderRadius: "lg", bg: colors.background[50], border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${colors
|
|
77
|
+
const CardSkeleton = ({ colors, rows }) => ((0, jsx_runtime_1.jsx)(react_2.Box, { "data-testid": "kanban-card-skeleton", borderRadius: "lg", bg: colors.background[50], border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, planeStep_1.cardRule)(colors)}`, p: 3, minH: "5rem", children: (0, jsx_runtime_1.jsx)(react_2.Flex, { direction: "column", gap: 2.5, children: Array.from({ length: rows }).map((_, i) => ((0, jsx_runtime_1.jsxs)(react_2.Flex, { gap: 2, align: "center", children: [(0, jsx_runtime_1.jsx)(react_2.Skeleton, { h: 2.5, w: "30%", borderRadius: "sm" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { h: 2.5, flex: "1", maxW: i % 2 ? "40%" : "55%", borderRadius: "sm" })] }, i))) }) }));
|
|
77
78
|
// Top buffer above the first card so its border + hover lift aren't clipped by the
|
|
78
79
|
// scroll/virtualized list's top edge.
|
|
79
80
|
const KANBAN_LIST_TOP_PAD = 6;
|
|
@@ -443,14 +444,14 @@ const KanbanBoard = ({ data, customColumns, customizeColumn, onDrag, onDelete, o
|
|
|
443
444
|
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: provided.innerRef, ...provided.droppableProps, width: isColCollapsed ? COLLAPSED_KANBAN_COLUMN_WIDTH : columnWidth, borderRadius: "xl", background: snapshot.isDraggingOver
|
|
444
445
|
? colors.primary[50]
|
|
445
446
|
: colors.background[100], border: `${borderWidths_1.BORDER_WIDTHS.hairline} ${snapshot.isDraggingOver ? "dashed" : "solid"} ${snapshot.isDraggingOver
|
|
446
|
-
? colors.
|
|
447
|
-
: colors.
|
|
447
|
+
? colors.focusRing.ring
|
|
448
|
+
: (0, planeStep_1.ruleOn)(colors, colors.background[100])}`, transition: COLUMN_TRANSITION, display: "flex", flexDirection: "column", flexShrink: 0, overflow: isColCollapsed ? "visible" : "hidden", height: COLUMN_HEIGHT, sx: (0, motion_2.reducedMotionSx)(), onMouseEnter: () => hoveredColumn.set(colId), onMouseLeave: () => hoveredColumn.set(null), onFocus: () => hoveredColumn.set(colId), onBlur: (e) => {
|
|
448
449
|
if (!e.currentTarget.contains(e.relatedTarget))
|
|
449
450
|
hoveredColumn.set(null);
|
|
450
451
|
}, children: [isColCollapsed ? ((0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", align: "stretch", flex: "1", width: "100%", minH: 0, overflow: "visible", position: "relative", children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "header", direction: "column", align: "center", flexShrink: 0, width: "100%", py: 3, px: 1, gap: 3, borderTop: "0.25rem solid", borderTopColor: (_a = column.color) !== null && _a !== void 0 ? _a : colors.primary[500], bg: colors.background[100], position: "relative", zIndex: 1, children: [enableColumnReorder && columnDragHandleProps ? ((0, jsx_runtime_1.jsx)(react_2.Flex, { align: "center", justify: "center", flexShrink: 0, cursor: "grab", ...columnDragHandleProps, "aria-label": `Reorder ${column.title} column`, children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.GripVertical, size: 14, color: colors.textMuted }) })) : null, enableColumnCollapse && ((0, jsx_runtime_1.jsx)(react_2.Flex, { flexShrink: 0, w: "100%", justify: "center", bg: colors.background[100], position: "relative", zIndex: 3, children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Expand column", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: 14 }), variant: "ghost", size: "xs", minW: "1.5rem", h: "1.75rem", onClick: () => toggleColumnCollapsed(colId) }) })), (0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", fontWeight: "700", fontSize: "xs", color: colors.text[700], sx: {
|
|
451
452
|
writingMode: "vertical-rl",
|
|
452
453
|
textOrientation: "mixed",
|
|
453
|
-
}, maxH: `${Math.max(120, containerHeight - 200)}px`, overflow: "hidden", lineHeight: "1.2", title: column.title, children: column.title }), (0, jsx_runtime_1.jsx)(react_2.Badge, { minW: 5, h: 5, borderRadius: "full", border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: colors.
|
|
454
|
+
}, maxH: `${Math.max(120, containerHeight - 200)}px`, overflow: "hidden", lineHeight: "1.2", title: column.title, children: column.title }), (0, jsx_runtime_1.jsx)(react_2.Badge, { minW: 5, h: 5, borderRadius: "full", border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: (0, planeStep_1.ruleOn)(colors, colors.background[100]), display: "flex", backgroundColor: colors.background[100], alignItems: "center", justifyContent: "center", color: colors.text[900], fontWeight: 600, fontSize: "xs", children: (_b = column === null || column === void 0 ? void 0 : column.items) === null || _b === void 0 ? void 0 : _b.length })] }), column.customNode ? ((0, jsx_runtime_1.jsx)(react_2.Box, { flexShrink: 0, minH: COLLAPSED_CUSTOM_NODE_TOP_RESERVE, w: "100%", bg: colors.background[100], "aria-hidden": true })) : null, column.customNode ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "section", "aria-label": "Column summary", flex: "1", minH: 0, w: "100%", overflow: "visible", py: 2, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", bg: colors.background[100], position: "relative", zIndex: 2, children: (0, jsx_runtime_1.jsx)(react_2.Box, { position: "relative", transform: "rotate(90deg)", transformOrigin: "center center", marginTop: '1rem', children: (0, jsx_runtime_1.jsx)(react_2.VStack, { align: "stretch", spacing: 1, minW: "max-content", fontSize: "xs", color: colors.text[600], children: column.customNode() }) }) })) : ((0, jsx_runtime_1.jsx)(react_2.Box, { flex: "1", minH: 0 }))] })) : ((0, jsx_runtime_1.jsxs)(react_2.Flex, { width: "100%", h: !isColCollapsed && !!((_c = column.customNode) === null || _c === void 0 ? void 0 : _c.call(column)) ? "auto" : "2.75rem", align: !isColCollapsed && !!((_d = column.customNode) === null || _d === void 0 ? void 0 : _d.call(column)) ? "flex-start" : "center", px: 3, py: 2, flexShrink: 0, justifyContent: "space-between", position: "relative", gap: 2, borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, planeStep_1.ruleOn)(colors, colors.background[100])}`, children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", gap: 1, minW: 0, flex: "1", children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { align: "center", gap: 2, minW: 0, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { boxSize: 2.5, borderRadius: "full", bg: (_e = column.color) !== null && _e !== void 0 ? _e : colors.primary[500], flexShrink: 0 }), enableColumnReorder && columnDragHandleProps ? ((0, jsx_runtime_1.jsx)(react_2.Flex, { align: "center", justify: "center", flexShrink: 0, cursor: "grab", ...columnDragHandleProps, "aria-label": `Reorder ${column.title} column`, children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.GripVertical, size: 14, color: colors.textMuted }) })) : null, (0, jsx_runtime_1.jsx)(react_2.Text, { as: "span", fontWeight: "600", fontSize: "sm", color: colors.text[700], noOfLines: 1, minW: 0, children: (0, jsx_runtime_1.jsx)(OverflowTooltipText_1.default, { placement: "top", children: column.title }) })] }), column.customNode ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "section", "aria-label": "Column summary", width: "100%", flexShrink: 0, fontSize: "xs", color: colors.text[600], children: (_f = column.customNode) === null || _f === void 0 ? void 0 : _f.call(column) })) : null] }), (0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", gap: 2, children: [(0, jsx_runtime_1.jsx)(react_2.Badge, { minW: 6, h: 6, px: 2, borderRadius: "full", backgroundColor: colors.backgroundColor.subtle, display: "flex", alignItems: "center", justifyContent: "center", color: colors.text[500], fontWeight: 600, fontSize: "xs", children: (_g = column === null || column === void 0 ? void 0 : column.items) === null || _g === void 0 ? void 0 : _g.length }), enableColumnCollapse && ((0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Collapse column", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { size: 14 }), variant: "ghost", size: "xs", minW: "1.5rem", h: "1.75rem", flexShrink: 0, onClick: () => toggleColumnCollapsed(colId) })), canDelete && column.items.length > 0 ? ((0, jsx_runtime_1.jsx)(ColumnDeleteAll, { colId: colId, title: column.title, hoveredColumn: hoveredColumn, colors: colors, onDelete: handleColumnDelete })) : ((0, jsx_runtime_1.jsx)(react_2.Box, { boxSize: 4, flexShrink: 0 }))] })] })), !isColCollapsed && ((0, jsx_runtime_1.jsxs)(react_2.Box, { px: 2, flex: "1", overflowY: "auto", width: "100%", sx: {
|
|
454
455
|
...(0, scrollbar_1.scrollbarStyles)(colors),
|
|
455
456
|
overflowY: "auto",
|
|
456
457
|
overflowX: "hidden",
|
|
@@ -492,7 +493,7 @@ const KanbanBoard = ({ data, customColumns, customizeColumn, onDrag, onDelete, o
|
|
|
492
493
|
}
|
|
493
494
|
return renderColumnDroppable(colId, column, null, isColCollapsed);
|
|
494
495
|
});
|
|
495
|
-
const loadingSkeletonRow = Array.from({ length: 4 }).map((_, idx) => ((0, jsx_runtime_1.jsxs)(react_2.Box, { "aria-hidden": true, width: columnWidth, borderRadius: "xl", bg: colors.background[100], border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${colors.
|
|
496
|
+
const loadingSkeletonRow = Array.from({ length: 4 }).map((_, idx) => ((0, jsx_runtime_1.jsxs)(react_2.Box, { "aria-hidden": true, width: columnWidth, borderRadius: "xl", bg: colors.background[100], border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, planeStep_1.ruleOn)(colors, colors.background[100])}`, flexShrink: 0, alignSelf: "flex-start", overflow: "hidden", children: [(0, jsx_runtime_1.jsxs)(react_2.Flex, { h: "2.75rem", align: "center", gap: 2, px: 3, borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, planeStep_1.ruleOn)(colors, colors.background[100])}`, children: [(0, jsx_runtime_1.jsx)(react_2.SkeletonCircle, { size: "2.5" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { h: 3, w: "40%", borderRadius: "sm" }), (0, jsx_runtime_1.jsx)(react_2.Skeleton, { h: 6, w: 6, ml: "auto", borderRadius: "full" })] }), (0, jsx_runtime_1.jsx)(react_2.Flex, { direction: "column", gap: 3, px: 2, pt: 1.5, pb: 3, children: SKELETON_ROWS.slice(0, 3 - (idx % 2)).map((rows, i) => ((0, jsx_runtime_1.jsx)(CardSkeleton, { colors: colors, rows: rows }, i))) })] }, idx)));
|
|
496
497
|
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(HeaderActions_1.default, { select: kanbanSelect, edit: canDrag ? kanbanEdit : undefined, create: canCreate ? kanbanCreate : undefined }), (0, jsx_runtime_1.jsx)(dnd_1.DragDropContext, { onDragEnd: onDragEnd, children: (0, jsx_runtime_1.jsxs)(react_2.Box, { ref: containerRef, "data-testid": "kanban-board", "aria-busy": isBoardLoading || undefined, bg: colors.backgroundColor.main, h: height !== null && height !== void 0 ? height : `${viewportHeight}px`, maxH: maxH, position: "relative", overflow: "hidden", mt: 2, children: [showAddColumn && !isBoardLoading && !hasAnyColumns && ((0, jsx_runtime_1.jsx)(react_2.Flex, { position: "absolute", inset: 0, align: "center", justify: "center", pointerEvents: "none", zIndex: 1, children: (0, jsx_runtime_1.jsxs)(react_2.Flex, { direction: "column", align: "center", justify: "center", gap: 2, pointerEvents: "auto", cursor: "pointer", px: 6, py: 5, borderRadius: "lg", bg: colors.background[100], border: `0.125rem dashed ${colors.boxborder[500]}`, _hover: { borderColor: colors.primary[500] }, ...addColumnKeyboard, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Plus, size: 20, color: colors.text[700] }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", color: colors.text[600], children: (customizeColumn === null || customizeColumn === void 0 ? void 0 : customizeColumn.buttonText) || "Add column" })] }) })), isBoardLoading ? ((0, jsx_runtime_1.jsx)(react_2.Flex, { role: "status", "aria-label": "Loading board", gap: 4, p: 4, pb: 6, overflowX: "auto", overflowY: "hidden", height: "100%", sx: flexScrollSx, children: loadingSkeletonRow })) : enableColumnReorder ? ((0, jsx_runtime_1.jsx)(dnd_1.Droppable, { droppableId: "COLUMN_REORDER", direction: "horizontal", type: "COLUMN", children: (colReorderProvided) => ((0, jsx_runtime_1.jsxs)(react_2.Flex, { ref: colReorderProvided.innerRef, ...colReorderProvided.droppableProps, gap: 4, p: 4, pb: 6, overflowX: "auto", overflowY: "hidden", height: "100%", sx: flexScrollSx, children: [columnsList, colReorderProvided.placeholder, addColumnBox] })) })) : ((0, jsx_runtime_1.jsxs)(react_2.Flex, { gap: 4, p: 4, pb: 6, overflowX: "auto", overflowY: "hidden", height: "100%", sx: flexScrollSx, children: [columnsList, addColumnBox] }))] }) })] }));
|
|
497
498
|
};
|
|
498
499
|
exports.default = KanbanBoard;
|
|
@@ -9,6 +9,7 @@ const react_1 = require("@chakra-ui/react");
|
|
|
9
9
|
const Avatar_1 = __importDefault(require("../Avatar/Avatar"));
|
|
10
10
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
11
11
|
const surfaceRaised_1 = require("../../Theme/tokens/builders/surfaceRaised");
|
|
12
|
+
const planeStep_1 = require("../../Theme/tokens/builders/planeStep");
|
|
12
13
|
const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
13
14
|
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
14
15
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
@@ -54,7 +55,7 @@ function NavBar({ userAvathar, userName, userRole, navMenu, moreIcon, handleNavO
|
|
|
54
55
|
_focus: { bg: menuHoverBg },
|
|
55
56
|
_active: { bg: rowBackgrounds.selectedActive },
|
|
56
57
|
};
|
|
57
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Box, { bg: theme.colors.backgroundColor.light, px: 4, borderBottom: "1px solid", borderColor: theme.colors.
|
|
58
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Box, { bg: theme.colors.backgroundColor.light, px: 4, borderBottom: "1px solid", borderColor: (0, planeStep_1.ruleOn)(theme.colors, theme.colors.backgroundColor.light), children: (0, jsx_runtime_1.jsxs)(react_1.Flex, { h: 16, alignItems: "center", justifyContent: "space-between", gap: 2, children: [(0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", gap: 1.5, alignItems: "center", flex: "1 1 0%", children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", onClick: () => onSideBarToggole === null || onSideBarToggole === void 0 ? void 0 : onSideBarToggole(), onMouseEnter: onSideBarPeek, onFocus: onSideBarPeek, "aria-label": sideBarToggole ? "Close navigation menu" : "Open navigation menu", "aria-expanded": !!sideBarToggole, ...iconButtonStyle, flexShrink: 0, display: forceSideBarToggle ? "flex" : { base: "flex", lg: "none" }, w: SIDEBAR_TOGGLE_HIT, h: SIDEBAR_TOGGLE_HIT, children: sideBarToggole ? ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelLeftClose, { size: "1.25rem" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.PanelRightClose, { size: "1.25rem" })) }), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) && ((0, jsx_runtime_1.jsx)(react_1.Box, { ...iconButtonStyle, as: "button", type: "button", "aria-label": "Go back", display: "flex", flexShrink: 0, onClick: () => { var _a; return (_a = navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.onIconClick) === null || _a === void 0 ? void 0 : _a.call(navigationBarText); }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.CircleArrowLeft, { size: "1.25rem", color: theme.colors.text[500] }) })), leading && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [leading, (0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": "true", mx: 2, w: "1px", h: "1.625rem", flexShrink: 0, bg: (0, planeStep_1.ruleOn)(theme.colors, theme.colors.backgroundColor.light) })] })), (navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text) && ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: typeof navigationBarText.text === "string" ? navigationBarText.text : "", overflowOnly: true, placement: "bottom", children: (0, jsx_runtime_1.jsx)(react_1.Text, { flex: "1 1 0%", w: 0, minW: 0, isTruncated: true, color: (0, accentText_1.accentTextOnCanvas)(theme.colors.secondary, theme.colors.backgroundColor.light), fontSize: "xl", fontWeight: 600, fontFamily: "open-sans, sans-serif", letterSpacing: "-0.013rem", children: navigationBarText === null || navigationBarText === void 0 ? void 0 : navigationBarText.text }) }))] }), (0, jsx_runtime_1.jsx)(react_1.Flex, { minW: 0, flexShrink: 1, children: (0, jsx_runtime_1.jsxs)(react_1.Stack, { direction: "row", alignItems: "center", spacing: 4, minW: 0, children: [moreIcon && ((0, jsx_runtime_1.jsx)(react_1.Box, { display: "flex", alignItems: "center", minW: 0, sx: { "& > *": { minW: 0 } }, children: moreIcon })), (0, jsx_runtime_1.jsxs)(react_1.Menu, { children: [(0, jsx_runtime_1.jsx)(react_1.MenuButton, { as: react_1.Button, rounded: "full", variant: "link", cursor: "pointer", minW: 0, flexShrink: 0, children: (0, jsx_runtime_1.jsx)(Avatar_1.default, { size: "sm", name: userName, src: userAvathar }) }), (0, jsx_runtime_1.jsx)(react_1.MenuList, { alignItems: "center", zIndex: 5, p: 0, py: "0.375rem",
|
|
58
59
|
// Without a cap one long title stretches the whole dropdown; titles truncate instead.
|
|
59
60
|
maxW: "18rem", bg: theme.colors.surfaceRaised,
|
|
60
61
|
// NOTE: Chakra's MenuItem inherits --menu-bg hardcoded to #fff via dead _dark — set it or items stay white.
|
|
@@ -12,6 +12,7 @@ const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
|
12
12
|
const chartColorsFromTheme_1 = require("../../Theme/tokens/builders/chartColorsFromTheme");
|
|
13
13
|
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
14
14
|
const rowStates_1 = require("../../Theme/tokens/builders/rowStates");
|
|
15
|
+
const optionRowInk_1 = require("../Select/optionRowInk");
|
|
15
16
|
const useExclusiveOverlay_1 = require("../../Hooks/useExclusiveOverlay");
|
|
16
17
|
const useMatchMedia_1 = require("../../Hooks/useMatchMedia");
|
|
17
18
|
const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
|
|
@@ -111,14 +112,11 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
|
|
|
111
112
|
}
|
|
112
113
|
}, borderRadius: "lg" })] }) })), (0, jsx_runtime_1.jsxs)(react_2.Box, { role: "listbox", "aria-label": "Organizations", maxH: "320px", overflowY: "auto", py: 1, onKeyDown: handleListKeyDown, children: [filtered.map((org, idx) => {
|
|
113
114
|
const isActive = org.id === value;
|
|
114
|
-
const rowHoverBg = isActive
|
|
115
|
-
? rowBackgrounds.selectedActive
|
|
116
|
-
: rowBackgrounds.active;
|
|
117
115
|
return ((0, jsx_runtime_1.jsxs)(react_2.Flex, { as: "button", type: "button", role: "option", "aria-selected": isActive, ref: (el) => {
|
|
118
116
|
optionRefs.current[idx] = el;
|
|
119
117
|
if (idx === 0)
|
|
120
118
|
firstOptionRef.current = el;
|
|
121
|
-
}, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "100%", textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, cursor: "pointer", bg:
|
|
119
|
+
}, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "100%", textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, cursor: "pointer", bg: "transparent", transition: "background 0.12s ease", ...(0, optionRowInk_1.optionRowCursorSx)(rowBackgrounds), onClick: () => handleSelect(org), children: [(0, jsx_runtime_1.jsx)(Avatar, { org: org, boxSize: s.avatar, fontSize: s.sub }), (0, jsx_runtime_1.jsxs)(react_2.Box, { flex: "1", minW: 0, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.font, fontWeight: isActive ? 600 : 500, color: theme.colors.text[900], overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.name }), showRole && org.role && ((0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.sub, color: theme.colors.text[500], overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.role }))] }), isActive && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: 16, color: theme.colors.accentText, flexShrink: 0 }))] }, org.id));
|
|
122
120
|
}), filtered.length === 0 && ((0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", color: theme.colors.textMuted, textAlign: "center", py: 6, children: "No organizations found" }))] })] }) })] }));
|
|
123
121
|
};
|
|
124
122
|
exports.default = OrgSwitcher;
|
|
@@ -26,6 +26,7 @@ function ProfilePhotoViewer({ photoUrl, onPhotoChange, imageWidth = "100px", ima
|
|
|
26
26
|
const fileInputRef = (0, react_1.useRef)(null);
|
|
27
27
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
28
28
|
const c = theme.colors;
|
|
29
|
+
const editFill = (0, filledSurface_1.resolveFilledSurface)(c.primary, c);
|
|
29
30
|
const handlePhotoChange = (event) => {
|
|
30
31
|
var _a;
|
|
31
32
|
if (!isEditable)
|
|
@@ -75,14 +76,14 @@ function ProfilePhotoViewer({ photoUrl, onPhotoChange, imageWidth = "100px", ima
|
|
|
75
76
|
"&:hover .ppv-media": {
|
|
76
77
|
boxShadow: `0 0 0 0.1875rem ${c.primary.opacity[24]}`,
|
|
77
78
|
},
|
|
78
|
-
}, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { className: "ppv-media", borderRadius: imageBorderRadius, transition: RING_TRANSITION, children: renderMedia() }), hasActions && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Box, { className: "ppv-overlay", position: "absolute", inset: 0, borderRadius: imageBorderRadius, bg: hoverScrim(0.3), opacity: 0, transition: FADE_TRANSITION, pointerEvents: "none" }), (0, jsx_runtime_1.jsxs)(react_2.HStack, { className: "ppv-actions", position: "absolute", right: editIconPositionRight, bottom: editIconPositionBottom, transform: "translate(-15%, -15%)", spacing: 1.5, opacity: revealOnHover ? 0 : 1, transition: FADE_TRANSITION, children: [isEditable && ((0, jsx_runtime_1.jsx)(react_2.Tooltip, { label: "Edit", "aria-label": "Edit", hasArrow: true, children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.SquarePen, { size: 14 }), isRound: isRound, size: editIconSize, onClick: handleClick, "aria-label": "Edit photo", bg:
|
|
79
|
-
bg:
|
|
80
|
-
color:
|
|
79
|
+
}, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { className: "ppv-media", borderRadius: imageBorderRadius, transition: RING_TRANSITION, children: renderMedia() }), hasActions && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Box, { className: "ppv-overlay", position: "absolute", inset: 0, borderRadius: imageBorderRadius, bg: hoverScrim(0.3), opacity: 0, transition: FADE_TRANSITION, pointerEvents: "none" }), (0, jsx_runtime_1.jsxs)(react_2.HStack, { className: "ppv-actions", position: "absolute", right: editIconPositionRight, bottom: editIconPositionBottom, transform: "translate(-15%, -15%)", spacing: 1.5, opacity: revealOnHover ? 0 : 1, transition: FADE_TRANSITION, children: [isEditable && ((0, jsx_runtime_1.jsx)(react_2.Tooltip, { label: "Edit", "aria-label": "Edit", hasArrow: true, children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.SquarePen, { size: 14 }), isRound: isRound, size: editIconSize, onClick: handleClick, "aria-label": "Edit photo", bg: editFill.fill, color: editFill.color, border: `0.125rem solid ${c.background[50]}`, boxShadow: (_b = theme.shadows) === null || _b === void 0 ? void 0 : _b.md, style: style, transition: ACTION_TRANSITION, _hover: {
|
|
80
|
+
bg: editFill.hoverFill,
|
|
81
|
+
color: editFill.hoverColor,
|
|
81
82
|
transform: "scale(1.1)",
|
|
82
83
|
boxShadow: (_c = theme.shadows) === null || _c === void 0 ? void 0 : _c.lg,
|
|
83
84
|
}, _active: {
|
|
84
|
-
bg:
|
|
85
|
-
color:
|
|
85
|
+
bg: editFill.activeFill,
|
|
86
|
+
color: editFill.activeColor,
|
|
86
87
|
} }) })), isView && ((0, jsx_runtime_1.jsx)(react_2.Tooltip, { label: "View", "aria-label": "View", hasArrow: true, children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Eye, { size: 14 }), isRound: isRound, size: editIconSize, onClick: handleIsView, "aria-label": "View photo", bg: c.background[50], color: c.text[700], border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${border}`, boxShadow: (_d = theme.shadows) === null || _d === void 0 ? void 0 : _d.md, style: style, transition: ACTION_TRANSITION, _hover: {
|
|
87
88
|
bg: c.gray[50],
|
|
88
89
|
color: (0, accentText_1.accentTextOnCanvas)(c.primary, c.gray[50]),
|
|
@@ -19,6 +19,7 @@ const ScrollToTop = ({ children, tooltipLabel = "Scroll to top", threshold = 300
|
|
|
19
19
|
const [visible, setVisible] = (0, react_1.useState)(false);
|
|
20
20
|
const anchorRef = (0, react_1.useRef)(null);
|
|
21
21
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
22
|
+
const fill = (0, filledSurface_1.resolveFilledSurface)(theme.colors.primary, theme.colors);
|
|
22
23
|
const getScroller = () => {
|
|
23
24
|
var _a, _b;
|
|
24
25
|
if (useWindow)
|
|
@@ -67,9 +68,9 @@ const ScrollToTop = ({ children, tooltipLabel = "Scroll to top", threshold = 300
|
|
|
67
68
|
};
|
|
68
69
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
70
|
}, [useWindow, threshold]);
|
|
70
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: anchorRef, position: "relative", children: [children, (0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: tooltipLabel, placement: "left", children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", onClick: scrollToTop, "aria-label": tooltipLabel, border: "none", position: "fixed", bottom: bottom, right: right, zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", boxSize: "44px", borderRadius: "full", cursor: "pointer", bg:
|
|
71
|
-
bg:
|
|
72
|
-
color:
|
|
71
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { ref: anchorRef, position: "relative", children: [children, (0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: tooltipLabel, placement: "left", children: (0, jsx_runtime_1.jsx)(react_2.Box, { as: "button", type: "button", onClick: scrollToTop, "aria-label": tooltipLabel, border: "none", position: "fixed", bottom: bottom, right: right, zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", boxSize: "44px", borderRadius: "full", cursor: "pointer", bg: fill.fill, color: fill.color, boxShadow: "md", opacity: visible ? 1 : 0, transform: visible ? "translateY(0) scale(1)" : "translateY(16px) scale(0.85)", pointerEvents: visible ? "auto" : "none", transition: (0, primitives_1.transitionFor)(["opacity", "transform", "box-shadow", "background-color", "color"], "base"), _hover: {
|
|
72
|
+
bg: fill.hoverFill,
|
|
73
|
+
color: fill.hoverColor,
|
|
73
74
|
transform: "translateY(-2px) scale(1.05)",
|
|
74
75
|
boxShadow: "lg",
|
|
75
76
|
}, _active: { transform: "scale(0.95)" }, sx: { ...(0, focusRing_styles_1.focusVisibleStyles)(theme.colors), ...(0, motion_1.reducedMotionSx)({ transform: "none" }) }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: 24, strokeWidth: 2.5 }) }) })] }));
|
|
@@ -79,17 +79,9 @@ const normalizeMultiValue = (val) => {
|
|
|
79
79
|
const DECORATIVE_CHECKBOX_SX = { pointerEvents: "none", "& input": { visibility: "hidden" } };
|
|
80
80
|
const OptionRow = react_1.default.memo(function OptionRow({ option, index, optionId, isSelected, isActive, isMultiple, spec, colors, rowBackgrounds, checkboxSize, onSelect, onActivate, }) {
|
|
81
81
|
const singleSelected = isSelected && !isMultiple;
|
|
82
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", bg:
|
|
83
|
-
? isActive
|
|
84
|
-
? rowBackgrounds.selectedActive
|
|
85
|
-
: rowBackgrounds.selected
|
|
86
|
-
: isActive
|
|
87
|
-
? rowBackgrounds.active
|
|
88
|
-
: "transparent", _hover: {
|
|
89
|
-
bg: isSelected ? rowBackgrounds.selectedActive : rowBackgrounds.active,
|
|
90
|
-
}, onClick: () => onSelect(option), onMouseEnter: () => onActivate(index), display: "flex", alignItems: "center", gap: spec.gap, color: (0, optionRowInk_1.optionRowInk)(colors, singleSelected),
|
|
82
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, mx: spec.mx, py: spec.py, px: spec.px, minH: spec.minH, borderRadius: optionRowSizes_1.OPTION_ROW_RADIUS, cursor: "pointer", bg: (0, optionRowInk_1.optionRowFill)(rowBackgrounds, isActive), onClick: () => onSelect(option), onMouseEnter: () => onActivate(index), display: "flex", alignItems: "center", gap: spec.gap, color: (0, optionRowInk_1.optionRowInk)(colors, singleSelected),
|
|
91
83
|
// `view` is consumer JSX with no size of its own; it inherited the 16px body font.
|
|
92
|
-
fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(singleSelected),
|
|
84
|
+
fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(singleSelected), children: [isMultiple && ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { isChecked: isSelected, sx: DECORATIVE_CHECKBOX_SX, size: checkboxSize })), option.view ? ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: option.view })) : ((0, jsx_runtime_1.jsx)(react_2.Text, { flex: 1, minW: 0, isTruncated: true, children: option.label })), singleSelected && ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: colors.accentText, flexShrink: 0 }))] }));
|
|
93
85
|
});
|
|
94
86
|
/** Full-featured combobox: search, chips, select-all, paginated infinite scroll — not DOM-virtualized despite the `virtualization` prop. */
|
|
95
87
|
/** Prefer `Select` with `native={false}` for new work — not yet a drop-in; see select-absorption-spec.md. */
|
|
@@ -656,11 +648,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
656
648
|
}, children: [(0, jsx_runtime_1.jsxs)(react_2.Box, { minW: "4.5rem", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, width: insideSelect.width, height: insideSelectBoxHeight, pl: "0.5rem", pr: "0.375rem", children: [insideLabel ? ((0, jsx_runtime_1.jsx)(SelectTruncatedLabel_1.default, { label: insideLabel, maxWidth: insideSelect.width || "6rem", fontSize: s.fieldFontSize })) : ((0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: s.fieldFontSize, fontWeight: 500, color: theme.colors.textMuted, isTruncated: true, maxW: insideSelect.width || "6rem", children: insidePlaceholder })), (0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, color: customSelectOpen ? theme.colors.accentText : theme.colors.textMuted, transition: CHEVRON_TRANSITION, transform: customSelectOpen ? "rotate(180deg)" : "rotate(0deg)", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: s.chevronSize }) })] }), customSelectOpen && ((0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsx)(react_2.Box, { ref: setInsideSelectMenuRef, id: insideSelectPortalId, role: "listbox", "aria-label": insidePlaceholder, onKeyDown: handleInsideMenuKeyDown, onFocus: stopFocusBubbling, onBlur: stopFocusBubbling, position: "fixed", style: { top: `${customSelectPos.top}px`, left: `${customSelectPos.left}px` }, zIndex: zIndices_1.default.popoverNested, bg: theme.colors.surfaceRaised, border: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, minW: `${customSelectPos.width}px`, maxW: "20rem", py: 1, overflow: "hidden", children: (_c = insideSelect.option) === null || _c === void 0 ? void 0 : _c.map((item) => {
|
|
657
649
|
var _a;
|
|
658
650
|
const selected = ((_a = insideSelect.value) === null || _a === void 0 ? void 0 : _a.id) === item.id;
|
|
659
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), bg:
|
|
660
|
-
bg: selected
|
|
661
|
-
? rowBackgrounds.selectedActive
|
|
662
|
-
: rowBackgrounds.active,
|
|
663
|
-
}, cursor: "pointer", role: "option", "aria-selected": selected, tabIndex: -1, _focusVisible: insideFocusRing, onClick: (e) => {
|
|
651
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(selected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, selected), bg: "transparent", ...(0, optionRowInk_1.optionRowCursorSx)(rowBackgrounds), cursor: "pointer", role: "option", "aria-selected": selected, tabIndex: -1, _focusVisible: insideFocusRing, onClick: (e) => {
|
|
664
652
|
e.stopPropagation();
|
|
665
653
|
handleCustomSelect(item);
|
|
666
654
|
}, children: [(0, jsx_runtime_1.jsx)(react_2.Text, { isTruncated: true, children: item.label }), selected && ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: spec.iconSize, color: theme.colors.accentText }))] }, item.id));
|
|
@@ -699,6 +687,6 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
699
687
|
top: `${dropdownPos.top}px`,
|
|
700
688
|
left: `${dropdownPos.left}px`,
|
|
701
689
|
width: `${dropdownPos.width}px`,
|
|
702
|
-
}, zIndex: zIndices_1.default.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: activeIndex === searchSelectNav_1.SELECT_ALL_ROW ? rowBackgrounds.active : undefined, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.text[700], borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, surfaceRaised_1.raisedRule)(theme.colors)}`, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { isChecked: allFilteredSelected, sx: DECORATIVE_CHECKBOX_SX, size: s.checkboxSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), (0, jsx_runtime_1.jsx)(react_2.Box, { ref: optionsListRef, flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: (0, surfaceRaised_1.raisedRule)(theme.colors), children: isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsx)(react_2.Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [filteredOptions.map((option, index) => ((0, jsx_runtime_1.jsx)(OptionRow, { option: option, index: index, optionId: getOptionId(index), isSelected: isSelected(option.id), isActive: index === activeIndex, isMultiple: isMultiple, spec: spec, colors: theme.colors, rowBackgrounds: rowBackgrounds, checkboxSize: s.checkboxSize, onSelect: selectOption, onActivate: setActiveIndex }, option.id))), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: addNewId, role: "option", "aria-selected": false, py: spec.py, px: spec.px, borderTop: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: (0, surfaceRaised_1.raisedRule)(theme.colors), bg: activeIndex === searchSelectNav_1.ADD_NEW_ROW ? rowBackgrounds.active : theme.colors.surfaceRaised, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: s.addNewIconSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
690
|
+
}, zIndex: zIndices_1.default.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: activeIndex === searchSelectNav_1.SELECT_ALL_ROW ? rowBackgrounds.active : undefined, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.text[700], borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid ${(0, surfaceRaised_1.raisedRule)(theme.colors)}`, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { isChecked: allFilteredSelected, sx: DECORATIVE_CHECKBOX_SX, size: s.checkboxSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), (0, jsx_runtime_1.jsx)(react_2.Box, { ref: optionsListRef, flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", py: 1, borderBottom: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: (0, surfaceRaised_1.raisedRule)(theme.colors), children: isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? ((0, jsx_runtime_1.jsx)(react_2.Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [filteredOptions.map((option, index) => ((0, jsx_runtime_1.jsx)(OptionRow, { option: option, index: index, optionId: getOptionId(index), isSelected: isSelected(option.id), isActive: index === activeIndex, isMultiple: isMultiple, spec: spec, colors: theme.colors, rowBackgrounds: rowBackgrounds, checkboxSize: s.checkboxSize, onSelect: selectOption, onActivate: setActiveIndex }, option.id))), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [(0, jsx_runtime_1.jsx)(react_2.Spinner, { size: s.spinnerSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: addNewId, role: "option", "aria-selected": false, py: spec.py, px: spec.px, borderTop: `${borderWidths_1.BORDER_WIDTHS.hairline} solid`, borderColor: (0, surfaceRaised_1.raisedRule)(theme.colors), bg: activeIndex === searchSelectNav_1.ADD_NEW_ROW ? rowBackgrounds.active : theme.colors.surfaceRaised, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: s.addNewIconSize }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
703
691
|
};
|
|
704
692
|
exports.default = SearchSelect;
|
|
@@ -74,9 +74,7 @@ const parseRemToPx = (value, fallbackPx) => {
|
|
|
74
74
|
};
|
|
75
75
|
const OptionRow = react_1.default.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, spec, renderOption, onSelect, onActivate, }) {
|
|
76
76
|
const isSingleSelected = isSelected && !isMulti;
|
|
77
|
-
|
|
78
|
-
const restBg = isActive ? backgrounds.active : "transparent";
|
|
79
|
-
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", opacity: option.isDisabled ? 0.4 : 1, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), bg: isSingleSelected ? selectedBg : restBg, transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), children: [isMulti ? ((0, jsx_runtime_1.jsx)(react_2.Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX })) : null, renderOption ? ((0, jsx_runtime_1.jsx)(react_2.Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [option.icon ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, (0, jsx_runtime_1.jsxs)(react_2.Box, { flex: 1, minW: 0, isTruncated: true, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", isTruncated: true, children: option.label }), option.description ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: (0, optionRowInk_1.optionRowDescriptionInk)(theme.colors), isTruncated: true, children: option.description })) : null] })] })), isSingleSelected ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 })) : null] }));
|
|
77
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", opacity: option.isDisabled ? 0.4 : 1, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: (0, optionRowInk_1.optionRowWeight)(isSingleSelected), color: (0, optionRowInk_1.optionRowInk)(theme.colors, isSingleSelected), bg: (0, optionRowInk_1.optionRowFill)(backgrounds, isActive), transition: ROW_TRANSITION, sx: (0, motion_2.reducedMotionSx)(), children: [isMulti ? ((0, jsx_runtime_1.jsx)(react_2.Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX })) : null, renderOption ? ((0, jsx_runtime_1.jsx)(react_2.Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [option.icon ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, (0, jsx_runtime_1.jsxs)(react_2.Box, { flex: 1, minW: 0, isTruncated: true, children: [(0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", isTruncated: true, children: option.label }), option.description ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: (0, optionRowInk_1.optionRowDescriptionInk)(theme.colors), isTruncated: true, children: option.description })) : null] })] })), isSingleSelected ? ((0, jsx_runtime_1.jsx)(react_2.Box, { as: lucide_react_1.Check, size: spec.iconSize, color: theme.colors.accentText, flexShrink: 0 })) : null] }));
|
|
80
78
|
});
|
|
81
79
|
const buildGroupedRows = (options) => {
|
|
82
80
|
const rows = [];
|