pixelize-design-library 2.4.2-beta.44 → 2.4.2-beta.46
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 +58 -1
- package/dist/Components/Common/fieldStyles.d.ts +1 -1
- package/dist/Components/Common/fieldStyles.js +4 -4
- package/dist/Components/DatePicker/CalendarPanel.js +2 -5
- package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +2 -4
- package/dist/Components/FilterSidebar/FilterSidebar.js +2 -1
- package/dist/Components/Header/HeaderActions.d.ts +2 -0
- package/dist/Components/Header/HeaderActions.js +2 -2
- package/dist/Components/Input/TextInput.styles.js +1 -1
- package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
- package/dist/Components/KanbanBoard/KanbanBoardProps.d.ts +2 -0
- package/dist/Components/MultiSelect/MultiSelect.d.ts +1 -1
- package/dist/Components/MultiSelect/MultiSelect.js +23 -12
- package/dist/Components/MultiSelect/MultiSelectProps.d.ts +2 -0
- package/dist/Components/NotificationBell/notificationHelpers.d.ts +5 -2
- package/dist/Components/NotificationBell/notificationHelpers.js +13 -4
- package/dist/Components/PinInput/PinInput.js +2 -4
- package/dist/Components/SearchSelect/SearchSelect.js +1 -1
- package/dist/Components/SegmentedControl/SegmentedControl.d.ts +1 -1
- package/dist/Components/SegmentedControl/SegmentedControl.js +33 -16
- package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +2 -0
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +11 -0
- package/dist/Components/SegmentedControl/segmentedInk.js +27 -0
- package/dist/Components/Select/SelectPopup.js +13 -4
- package/dist/Components/Select/optionRowInk.d.ts +3 -0
- package/dist/Components/Select/optionRowInk.js +6 -0
- package/dist/Components/SideBar/SecondaryBar.js +5 -4
- package/dist/Components/SideBar/SideBar.js +6 -5
- package/dist/Components/SideBar/SideBarProps.d.ts +2 -0
- package/dist/Components/SideBar/components/MenuItemBox.js +5 -4
- package/dist/Components/SideBar/components/MenuItems.js +1 -1
- package/dist/Components/SideBar/components/MenuPopoverContent.js +4 -3
- package/dist/Components/SideBar/sidebarScale.d.ts +15 -0
- package/dist/Components/SideBar/sidebarScale.js +18 -0
- package/dist/Components/Switch/Switch.d.ts +2 -2
- package/dist/Components/Switch/Switch.js +3 -3
- package/dist/Components/Switch/SwitchProps.d.ts +2 -0
- package/dist/Components/Table/components/Pagination.d.ts +1 -1
- package/dist/Components/Table/components/Pagination.js +12 -4
- package/dist/Components/Table/filters/SelectOperationControls.js +9 -1
- package/dist/Components/Table/filters/TextOperationControls.js +8 -1
- package/dist/Components/Table/hooks/useTable.d.ts +2 -1
- package/dist/Components/Table/hooks/useTable.js +5 -3
- package/dist/Components/Tabs/components/Tab.js +11 -1
- package/dist/Theme/chakra/focusRing.styles.d.ts +17 -1
- package/dist/Theme/chakra/focusRing.styles.js +16 -1
- package/dist/Theme/provider/ThemeSwitcher.js +15 -8
- package/dist/esm/Components/Common/fieldStyles.js +5 -5
- package/dist/esm/Components/DatePicker/CalendarPanel.js +2 -5
- package/dist/esm/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtp.js +2 -4
- package/dist/esm/Components/FilterSidebar/FilterSidebar.js +2 -1
- package/dist/esm/Components/Header/HeaderActions.js +2 -2
- package/dist/esm/Components/Input/TextInput.styles.js +2 -2
- package/dist/esm/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
- package/dist/esm/Components/MultiSelect/MultiSelect.js +23 -12
- package/dist/esm/Components/NotificationBell/notificationHelpers.js +13 -4
- package/dist/esm/Components/PinInput/PinInput.js +2 -4
- package/dist/esm/Components/SearchSelect/SearchSelect.js +1 -1
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +32 -15
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +20 -0
- package/dist/esm/Components/Select/SelectPopup.js +14 -5
- package/dist/esm/Components/Select/optionRowInk.js +2 -0
- package/dist/esm/Components/SideBar/SecondaryBar.js +5 -4
- package/dist/esm/Components/SideBar/SideBar.js +6 -5
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +5 -4
- package/dist/esm/Components/SideBar/components/MenuItems.js +1 -1
- package/dist/esm/Components/SideBar/components/MenuPopoverContent.js +4 -3
- package/dist/esm/Components/SideBar/sidebarScale.js +15 -0
- package/dist/esm/Components/Switch/Switch.js +3 -3
- package/dist/esm/Components/Table/components/Pagination.js +13 -5
- package/dist/esm/Components/Table/filters/SelectOperationControls.js +8 -3
- package/dist/esm/Components/Table/filters/TextOperationControls.js +6 -2
- package/dist/esm/Components/Table/hooks/useTable.js +4 -3
- package/dist/esm/Components/Tabs/components/Tab.js +12 -2
- package/dist/esm/Theme/chakra/focusRing.styles.js +13 -0
- package/dist/esm/Theme/provider/ThemeSwitcher.js +16 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,12 @@ next major.
|
|
|
14
14
|
|
|
15
15
|
### Added
|
|
16
16
|
|
|
17
|
+
- **`SegmentedControl` / `Switch` — `surface`.** The background the control is drawn on. Optional:
|
|
18
|
+
the track is an opaque plate on any backdrop, so the labels read without it. Pass it to aim the
|
|
19
|
+
plate's hairline edge at the real backdrop rather than at the page canvas.
|
|
20
|
+
- **`MultiSelect` — `addOnBlur`.** Commits the typed address on focus-out as well as on Enter.
|
|
21
|
+
Defaults to `false`, so existing call sites keep Enter-only behaviour.
|
|
22
|
+
|
|
17
23
|
- **Dark mode.** `PixelizeThemeProvider` (defaults to `system`), `useThemeMode`, `ThemeSwitcher`.
|
|
18
24
|
Dark palettes are derived per brand from that brand's own light palette, so brand hue carries
|
|
19
25
|
through. `withTheme` stays light-only, so existing apps are visually unchanged — dark is opt-in.
|
|
@@ -37,6 +43,11 @@ next major.
|
|
|
37
43
|
|
|
38
44
|
### Changed
|
|
39
45
|
|
|
46
|
+
- **`SideBar` sizing is one scale.** Rail, submenu, collapsed flyout and `SecondaryBar` rows now all
|
|
47
|
+
read `SIDEBAR_ROW`, so every nav label is 13px at every level. Visible changes: submenu and flyout
|
|
48
|
+
rows grow 20px → 28px with their labels 12/12.5px → 13px, the "Add New" label 12.5px → 13px, and
|
|
49
|
+
`SecondaryBar`'s `headerFontSize`/`itemFontSize` defaults 0.75rem → 13px. Child rows previously
|
|
50
|
+
rendered a step smaller than their parent; pass the `SecondaryBar` font props to keep the old size.
|
|
40
51
|
- **Charts are lazy.** `react-apexcharts` now loads behind a dynamic `import()`. The main chart
|
|
41
52
|
entry dropped from 227,454 B to 2,448 B gzip, and an app that never renders a chart no longer
|
|
42
53
|
pays for one. Verified against real ESM build output.
|
|
@@ -52,6 +63,70 @@ next major.
|
|
|
52
63
|
|
|
53
64
|
### Fixed
|
|
54
65
|
|
|
66
|
+
- **`SegmentedControl` / `Switch` guessed the backdrop it was drawn on.** `brandSlide` painted an
|
|
67
|
+
8% brand tint composited over `backgroundColor.main` and lifted its unselected-label ink against
|
|
68
|
+
that same assumed canvas, so on a brand-filled bar the tint sank into the bar and the label
|
|
69
|
+
measured **1.03–2.03:1 across all 9 brands x 2 modes** — crm's `Overview / Timeline` view switcher
|
|
70
|
+
on a `primary[700]` bar was unreadable. The track is now an opaque plate on every backdrop: rest
|
|
71
|
+
ink 7.58–18.39:1, plate-vs-bar 6.31–19.0:1, with no change required at the call site. `surface`
|
|
72
|
+
is now only a refinement for the plate's hairline edge, and a test gates the invariant.
|
|
73
|
+
- **A selected `Select` option stacked brand ink on a brand-tinted row.** Bold `accentText` on
|
|
74
|
+
`listRowBackgrounds().selected` read as muddy, and the row cleared the panel by only 1.16–1.29:1.
|
|
75
|
+
The label is now `text[900]` with the brand carried by a trailing check, matching `OrgSwitcher`.
|
|
76
|
+
|
|
77
|
+
- **`SegmentedControl`'s pill bulged past the plate's corners.** The pill and the segments carried
|
|
78
|
+
the track's own `lg` radius while sitting 2px inside it, so the plate's band stayed even along the
|
|
79
|
+
straight edges but pinched to nothing at the corners. They now round concentrically at
|
|
80
|
+
`calc(radii.lg - TRACK_PADDING)`, and a test gates the relationship.
|
|
81
|
+
- **Every field's focus ring was too heavy.** Fields reused the button ring — a 2px surface halo
|
|
82
|
+
plus a 2px brand band — outside a 1px border that also turns brand on focus, so a focused field
|
|
83
|
+
was ringed by 5px of chrome reading as one fat stroke. Narrowing and fading that band did not fix
|
|
84
|
+
it: a `0 0 0 Npx` shadow has a hard outer edge at every opacity, and beside a same-hue border it
|
|
85
|
+
always merges into one thick stroke. Fields now draw `focusGlowShadow` as a **blurred bloom** —
|
|
86
|
+
`0 0 6px -1px primary.opacity[32]` — with the border still turning `primary[500]` as the
|
|
87
|
+
accessible 3:1 indicator. Buttons, chips, checkboxes and every borderless control keep the
|
|
88
|
+
two-tone ring unchanged.
|
|
89
|
+
- **The Switch demo's "brand-filled bar" was pale in dark mode.** It hardcoded `primary[700]`,
|
|
90
|
+
which is deep in light and pale in dark, so the bar inverted and the whole comparison was
|
|
91
|
+
unreadable. It now derives the bar from `resolveFilledSurface(...).activeFill` and **measures**
|
|
92
|
+
both contrast ratios live against the active brand and mode, instead of printing two numbers that
|
|
93
|
+
were only ever true in light mode. `segmentedTrackSurface`, `segmentedRestInk` and
|
|
94
|
+
`segmentedFocusShadow` are now one shared module, so the demo, the component and the contrast
|
|
95
|
+
gate cannot drift apart.
|
|
96
|
+
- **`SegmentedControl` on a brand-filled bar was unreadable mud.** `brandSlide`'s track is an 8%
|
|
97
|
+
brand tint, which on a brand bar composites straight back into the bar — the control lost its
|
|
98
|
+
own surface and the labels had nothing to sit on. With `surface` the track is now an opaque
|
|
99
|
+
plate (`surfaceRaised`) carrying a 1px inset edge lifted to 3:1 against the bar, because in dark mode
|
|
100
|
+
the plate and the bar can sit at nearly the same lightness (1.56:1 on radiant) and hue alone
|
|
101
|
+
would not separate them. The `/switch` demo shows the control instead of a pass/fail ratio pair.
|
|
102
|
+
- **`Tabs` drew a focus ring that was clipped into two arcs.** `TabList` scrolls, so the outer
|
|
103
|
+
two-tone ring was cut off on all four sides and rendered as two disconnected side edges rather
|
|
104
|
+
than a ring. The tab now draws it inside itself (`outline-offset: -2px`), clear of the `outline`
|
|
105
|
+
variant's own border, in an ink lifted to 3:1 against that tab's own surface.
|
|
106
|
+
- **`SegmentedControl` / `Switch` drew the button's focus ring.** A segment sits inside a track, so
|
|
107
|
+
the ring's surface-toned halo painted a white band and both bands landed outside the pill — a
|
|
108
|
+
thick double border that overflowed the track. A radiogroup is also a single tab stop, so ringing
|
|
109
|
+
one segment says nothing the selection does not already say. The **group** now carries the
|
|
110
|
+
indicator, in the same language as a focused field: a 1px brand hairline inset on the track plus
|
|
111
|
+
`focusGlowShadow`. `underlineSlide` has no track chrome, so there the segment wears it.
|
|
112
|
+
`ThemeSwitcher`'s mode segments follow the same rule through their own border.
|
|
113
|
+
- **`Switch` clipped long labels.** Its 12.5rem width was fixed, so labels wider than the track
|
|
114
|
+
overflowed the background; it is now a minimum. `SegmentedControl`'s track carries
|
|
115
|
+
`minW="max-content"` for the same reason.
|
|
116
|
+
- **`ThemeSwitcher` had no visible focus indicator.** All four controls used Chakra's `boxShadow:
|
|
117
|
+
"outline"` token, which resolves to a fully transparent shadow under these brand themes — keyboard
|
|
118
|
+
focus was invisible. They now use the library's own rings.
|
|
119
|
+
- **Four fields never used the shared focus style.** `PinInput`, `VerifyEmailOtp`, the
|
|
120
|
+
`CalendarPanel` month/year selects and the `FilterSidebar` search box each hand-rolled a focus
|
|
121
|
+
shadow (the last one suppressed it entirely with `boxShadow: "none"`). All four now read
|
|
122
|
+
`fieldFocusVisible`, so the library has one field focus treatment rather than five.
|
|
123
|
+
- `SearchSelect` drew its placeholder over the selected chips. The search input is `flex: 1`
|
|
124
|
+
with `minW: 0`, so a field holding chips shrinks it to near-zero and the placeholder text
|
|
125
|
+
overflowed across them. The placeholder is now the empty-state hint only — it is dropped as
|
|
126
|
+
soon as anything is selected, open or closed.
|
|
127
|
+
- `Select`'s popup panel truncated long option labels. It no longer inherits the trigger width
|
|
128
|
+
(`matchWidth`); it grows to `max-content` above a measured trigger-width floor, capped at
|
|
129
|
+
`min(24rem, 100vw - 2rem)`.
|
|
55
130
|
- `usePrefersReducedMotion` crashed during SSR — it now guards `window.matchMedia`. Affected
|
|
56
131
|
`Tabs`, `Toaster` and `SideBar`.
|
|
57
132
|
- `MediaViewer` coalesced several navigation calls landing in the same tick, so six rapid arrow
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -11,6 +11,28 @@ so it's available without checking out the library's source repo. Its companion
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## A colour rung flips direction between light and dark
|
|
15
|
+
|
|
16
|
+
`colors.primary[700]` is a deep purple in light mode and a **pale** lavender in dark. The scales are
|
|
17
|
+
ordered so a high rung always contrasts with the page canvas, which means its lightness inverts
|
|
18
|
+
with the mode. Using one as ink is what the ordering is for; using one as a **background fill**
|
|
19
|
+
gives you a dark bar in light mode and a pale one in dark, with whatever you put on top now
|
|
20
|
+
unreadable.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// Wrong — pale lavender bar in dark mode, and the label on it is unreadable
|
|
24
|
+
<Box bg={colors.primary[700]}>…</Box>
|
|
25
|
+
|
|
26
|
+
// Right — a brand fill that stays a brand fill, with its own guaranteed-AA foreground
|
|
27
|
+
const brand = resolveFilledSurface(colors.primary, colors);
|
|
28
|
+
<Box bg={brand.activeFill} color={brand.activeColor}>…</Box>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`resolveFilledSurface` is exported from the package root. Use `.fill`/`.color` for a standard
|
|
32
|
+
brand surface and `.activeFill`/`.activeColor` for a deeper one behind other content.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
14
36
|
## Choosing between components
|
|
15
37
|
|
|
16
38
|
### Segmented control look — `SegmentedControl` / `Toggle/TableToggle`
|
|
@@ -27,6 +49,23 @@ opt-in, never assume a non-default look).
|
|
|
27
49
|
Pick by density and emphasis, not by taste. When unsure, or migrating an existing call site, leave
|
|
28
50
|
it on `"default"` — it's the only variant guaranteed to match previously-shipped screenshots.
|
|
29
51
|
|
|
52
|
+
**`brandSlide` no longer guesses its backdrop — and `surface` is now optional.** Until
|
|
53
|
+
2.4.2-beta.46 the track was an 8% brand tint composited over the **page canvas**, and the inactive
|
|
54
|
+
label's ink was lifted against that same assumed canvas. Dropped into a brand-filled header both
|
|
55
|
+
went wrong at once: the tint sank into the bar, and the label landed at **1.03–2.03:1** — rendered,
|
|
56
|
+
effectively invisible. The track is now an opaque plate (`colors.surfaceRaised`) whatever it sits
|
|
57
|
+
on, so a control on a coloured bar is readable with no prop passed (rest ink 7.58–18.39:1).
|
|
58
|
+
|
|
59
|
+
Pass `surface` only to aim the plate's 1px edge at the real backdrop instead of the page canvas.
|
|
60
|
+
The edge is what carries the boundary: in dark mode the plate and a brand bar can sit at almost the
|
|
61
|
+
same lightness (as low as 1.56:1 on radiant), so hue alone would not separate them.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<Box bg={theme.colors.primary[700]}>
|
|
65
|
+
<Switch ... surface={theme.colors.primary[700]} /> // optional: sharpens the plate's edge
|
|
66
|
+
</Box>
|
|
67
|
+
```
|
|
68
|
+
|
|
30
69
|
### Which "select" component — `Select` is the only one to reach for now
|
|
31
70
|
|
|
32
71
|
There is now **one** picker. `Select` does both modes off the `native` prop:
|
|
@@ -80,17 +119,27 @@ is now the library's own themed component, with a real dual-thumb mode and a `ma
|
|
|
80
119
|
**Apply:** for a numeric/range form control import `RangeSlider` from the package root — never
|
|
81
120
|
`Slider`, and no longer from the `Primitives` barrel, which no longer carries it.
|
|
82
121
|
|
|
83
|
-
### Which "switch" — `ToggleSwitch` / `SegmentedControl` / `InputSwitch` / `Switch`
|
|
122
|
+
### Which "switch" — `ToggleSwitch` / `SegmentedControl` / `Tabs` / `InputSwitch` / `Switch`
|
|
84
123
|
|
|
85
124
|
| Choosing | Component | Why |
|
|
86
125
|
| --- | --- | --- |
|
|
87
126
|
| A real on/off toggle | `ToggleSwitch` | `role="switch"` + `aria-checked`, full keyboard support |
|
|
88
127
|
| One of 2-5 named options | `SegmentedControl` | `role="radiogroup"`, keyboard-navigable, supports per-option disabling |
|
|
128
|
+
| Switching between views or panels | `Tabs` | `role="tablist"` + `aria-controls`, count badges, lazy panels, vertical orientation |
|
|
89
129
|
| *(avoid)* | `InputSwitch` | `@deprecated` — now a thin wrapper that renders `ToggleSwitch` |
|
|
90
130
|
| *(avoid)* | `Switch` | `@deprecated` — a mouse-only two-label segmented control (`leftText`/`rightText`); use `SegmentedControl` |
|
|
91
131
|
|
|
92
132
|
**Apply:** "on vs off" is always `ToggleSwitch`; 2+ named options is always `SegmentedControl`.
|
|
93
133
|
|
|
134
|
+
**`SegmentedControl` and `Tabs` look near-identical and are not interchangeable.** They share the
|
|
135
|
+
size scale and tokens, so `SegmentedControl variant="underlineSlide"` and `Tabs variant="line"`
|
|
136
|
+
render almost the same picture, as do `variant="default"` and `Tabs variant="subtle"`. The
|
|
137
|
+
difference is what the control *is*: `SegmentedControl` is a **radiogroup** — it filters or sets a
|
|
138
|
+
value and owns nothing else on the page; `Tabs` is a **tablist** — it shows one of several panels
|
|
139
|
+
and wires `aria-controls` to them. Pick by that, never by the picture. A screen reader announces
|
|
140
|
+
"radio 1 of 3" versus "tab 1 of 3, selected", and only `Tabs` gives you panels, count badges,
|
|
141
|
+
`isLazy` and `orientation="vertical"`.
|
|
142
|
+
|
|
94
143
|
### Circular charts — `ApexPieChart` vs `ApexPolarCharts`
|
|
95
144
|
|
|
96
145
|
| Need | Component | Why not the other |
|
|
@@ -727,6 +776,14 @@ nothing. `colorScheme` and `isReadOnly` are real now (colorScheme used to be sil
|
|
|
727
776
|
`variant`/`spacing`/`isFocusable` stay typed `@deprecated` no-ops. **Apply:** migrate to
|
|
728
777
|
`ToggleSwitch` (`fieldLabel` for the label InputSwitch drew above the track) and drop those selectors.
|
|
729
778
|
|
|
779
|
+
**This breaks tests, not types.** Chakra's `Switch` rendered `role="checkbox"`; `ToggleSwitch`
|
|
780
|
+
renders the correct `role="switch"`, so `getByRole("checkbox", { name })` silently stops matching
|
|
781
|
+
while everything still compiles. Two consumer repos hit this on the beta.44 upgrade. **Apply:**
|
|
782
|
+
change those queries to `getByRole("switch", …)`.
|
|
783
|
+
|
|
784
|
+
Same shape of trap on `Tabs`: the owned `Tab` forwards no `data-testid`, so `getByTestId("…-tab-…")`
|
|
785
|
+
queries must become `getByRole("tab", { name })`.
|
|
786
|
+
|
|
730
787
|
### The internal `Input.tsx` silently ignores six declared props
|
|
731
788
|
|
|
732
789
|
`disabled`, `autoComplete`, `autoFocus`, `inputClassName`, `labelClassName`, and `errorClassName`
|
|
@@ -55,6 +55,6 @@ export declare const getFieldStateStyles: (theme: CustomThemeProps, { error, foc
|
|
|
55
55
|
borderRadius: string;
|
|
56
56
|
transition: string;
|
|
57
57
|
};
|
|
58
|
-
/**
|
|
58
|
+
/** Glow for `_focusWithin` on composite fields. `error` is accepted but ignored: invalid is the border's job. */
|
|
59
59
|
export declare const fieldFocusRing: (theme: CustomThemeProps, error?: boolean) => string;
|
|
60
60
|
export {};
|
|
@@ -50,7 +50,7 @@ const fieldControlStyles = (colors) => {
|
|
|
50
50
|
...(0, exports.fieldTextStyles)(colors),
|
|
51
51
|
_placeholder: { color: (0, placeholderInk_1.placeholderInk)(colors), opacity: 1 },
|
|
52
52
|
_hover: { borderColor: (0, exports.fieldHoverBorder)(colors) },
|
|
53
|
-
_focusVisible: { borderColor: colors.primary[500], ...(0, focusRing_styles_1.
|
|
53
|
+
_focusVisible: { borderColor: colors.primary[500], ...(0, focusRing_styles_1.fieldFocusVisible)(colors) },
|
|
54
54
|
_invalid: {
|
|
55
55
|
borderColor: colors.semanticText.error,
|
|
56
56
|
_hover: { borderColor: colors.semanticText.error },
|
|
@@ -68,13 +68,13 @@ const getFieldStateStyles = (theme, { error = false, focused = false, disabled =
|
|
|
68
68
|
: focused
|
|
69
69
|
? colors.primary[500]
|
|
70
70
|
: colors.boxborder[500];
|
|
71
|
-
const box = { ...(0, exports.fieldBoxStyles)(colors), borderColor, boxShadow: focused ? (0, focusRing_styles_1.
|
|
71
|
+
const box = { ...(0, exports.fieldBoxStyles)(colors), borderColor, boxShadow: focused ? (0, focusRing_styles_1.focusGlowShadow)(colors) : "none" };
|
|
72
72
|
if (!disabled)
|
|
73
73
|
return box;
|
|
74
74
|
const { bg, borderColor: disabledBorder, cursor } = disabledStyles(colors);
|
|
75
75
|
return { ...box, bg, borderColor: disabledBorder, cursor };
|
|
76
76
|
};
|
|
77
77
|
exports.getFieldStateStyles = getFieldStateStyles;
|
|
78
|
-
/**
|
|
79
|
-
const fieldFocusRing = (theme) => (0, focusRing_styles_1.
|
|
78
|
+
/** Glow for `_focusWithin` on composite fields. `error` is accepted but ignored: invalid is the border's job. */
|
|
79
|
+
const fieldFocusRing = (theme) => (0, focusRing_styles_1.focusGlowShadow)(theme.colors);
|
|
80
80
|
exports.fieldFocusRing = fieldFocusRing;
|
|
@@ -11,6 +11,7 @@ const lucide_react_1 = require("lucide-react");
|
|
|
11
11
|
const Button_1 = __importDefault(require("../Button/Button"));
|
|
12
12
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
13
13
|
const filledSurface_1 = require("../../Theme/tokens/builders/filledSurface");
|
|
14
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
14
15
|
const dayStates_1 = require("./dayStates");
|
|
15
16
|
/** Internal month grid shared by Single/RangeDatePicker; not published from index.ts, so only reachable via a deep import. */
|
|
16
17
|
const CalendarPanel = ({ currentMonth, setCurrentMonth, today, isRange, tempDate, tempRangeStart, tempRangeEnd, isSameDay, isBefore, isAfter, minDate, maxDate, disablePastDates, disableFutureDates, disableToday, onDaySelect, hoveredDate, onDayHover, renderWeekdays, dayStates, renderDayContent, }) => {
|
|
@@ -41,11 +42,7 @@ const CalendarPanel = ({ currentMonth, setCurrentMonth, today, isRange, tempDate
|
|
|
41
42
|
bg: theme.colors.background[50],
|
|
42
43
|
outline: "none",
|
|
43
44
|
_hover: { borderColor: theme.colors.primary[300] },
|
|
44
|
-
_focus: {
|
|
45
|
-
outline: "none",
|
|
46
|
-
borderColor: theme.colors.primary[500],
|
|
47
|
-
boxShadow: `0 0 0 1px ${theme.colors.primary[500]}`,
|
|
48
|
-
},
|
|
45
|
+
_focus: { borderColor: theme.colors.primary[500], ...(0, focusRing_styles_1.fieldFocusVisible)(theme.colors) },
|
|
49
46
|
style: {
|
|
50
47
|
WebkitAppearance: "menulist",
|
|
51
48
|
MozAppearance: "menulist",
|
|
@@ -4,12 +4,10 @@ exports.default = VerifyEmailOtp;
|
|
|
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 lucide_react_1 = require("lucide-react");
|
|
8
9
|
/** Full verification card (icon, title, description, resend link, digits-only PinInput) — for a bare OTP field in your own layout, use PinInput instead. */
|
|
9
10
|
function VerifyEmailOtp({ resendMail, onChange, onComplete, placeholder = "", pins = 5, description, title, isDisabled = false, isInvalid = false, styles, }) {
|
|
10
11
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
11
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { style: styles, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", p: 6, w: "100%", maxW: "25rem", mx: "auto", bg: theme.colors.background[50], border: "1px solid", borderColor: theme.colors.boxborder[200], borderRadius: "xl", children: [(0, jsx_runtime_1.jsx)(react_1.Box, { mb: 4, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MailCheck, { size: 80, color: theme.colors.accentText }) }), (0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "2xl", fontWeight: "bold", mb: 2, textAlign: "center", children: title }), (0, jsx_runtime_1.jsxs)(react_1.Text, { fontSize: "md", textAlign: "center", color: theme.colors.textMuted, mb: 6, children: [description, (0, jsx_runtime_1.jsx)("br", {}), "your new Pixel Account"] }), (0, jsx_runtime_1.jsx)(react_1.HStack, { spacing: 3, mb: 6, role: "group", "aria-label": title || "Verification code", children: (0, jsx_runtime_1.jsx)(react_1.PinInput, { placeholder: placeholder, onChange: onChange, onComplete: onComplete, isDisabled: isDisabled, isInvalid: isInvalid, size: "lg", focusBorderColor: theme.colors.primary[500], children: Array.from({ length: pins }).map((_, index) => ((0, jsx_runtime_1.jsx)(react_1.PinInputField, { "aria-label": `${title || "Verification code"}, digit ${index + 1} of ${pins}`, borderRadius: "lg", border: "0.063rem solid", borderColor: theme.colors.boxborder[500], backgroundColor: theme.colors.background[200], _focus: {
|
|
12
|
-
borderColor: theme.colors.primary[500],
|
|
13
|
-
boxShadow: `0 0 0 0.063rem ${theme.colors.primary[500]}`,
|
|
14
|
-
} }, index))) }) }), (0, jsx_runtime_1.jsx)(react_1.Flex, { justify: "center", children: (0, jsx_runtime_1.jsxs)(react_1.Text, { fontSize: "sm", color: theme.colors.textMuted, children: ["Didn\u2019t receive the code?", " ", (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "link", color: theme.colors.accentText, fontWeight: "semibold", onClick: resendMail, children: "Resend Code" })] }) })] }));
|
|
12
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { style: styles, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", p: 6, w: "100%", maxW: "25rem", mx: "auto", bg: theme.colors.background[50], border: "1px solid", borderColor: theme.colors.boxborder[200], borderRadius: "xl", children: [(0, jsx_runtime_1.jsx)(react_1.Box, { mb: 4, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MailCheck, { size: 80, color: theme.colors.accentText }) }), (0, jsx_runtime_1.jsx)(react_1.Text, { fontSize: "2xl", fontWeight: "bold", mb: 2, textAlign: "center", children: title }), (0, jsx_runtime_1.jsxs)(react_1.Text, { fontSize: "md", textAlign: "center", color: theme.colors.textMuted, mb: 6, children: [description, (0, jsx_runtime_1.jsx)("br", {}), "your new Pixel Account"] }), (0, jsx_runtime_1.jsx)(react_1.HStack, { spacing: 3, mb: 6, role: "group", "aria-label": title || "Verification code", children: (0, jsx_runtime_1.jsx)(react_1.PinInput, { placeholder: placeholder, onChange: onChange, onComplete: onComplete, isDisabled: isDisabled, isInvalid: isInvalid, size: "lg", focusBorderColor: theme.colors.primary[500], children: Array.from({ length: pins }).map((_, index) => ((0, jsx_runtime_1.jsx)(react_1.PinInputField, { "aria-label": `${title || "Verification code"}, digit ${index + 1} of ${pins}`, borderRadius: "lg", border: "0.063rem solid", borderColor: theme.colors.boxborder[500], backgroundColor: theme.colors.background[200], _focus: { borderColor: theme.colors.primary[500], ...(0, focusRing_styles_1.fieldFocusVisible)(theme.colors) } }, index))) }) }), (0, jsx_runtime_1.jsx)(react_1.Flex, { justify: "center", children: (0, jsx_runtime_1.jsxs)(react_1.Text, { fontSize: "sm", color: theme.colors.textMuted, children: ["Didn\u2019t receive the code?", " ", (0, jsx_runtime_1.jsx)(react_1.Button, { variant: "link", color: theme.colors.accentText, fontWeight: "semibold", onClick: resendMail, children: "Resend Code" })] }) })] }));
|
|
15
13
|
}
|
|
@@ -13,6 +13,7 @@ const ToolTip_1 = __importDefault(require("../ToolTip/ToolTip"));
|
|
|
13
13
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
14
14
|
const accentText_1 = require("../../Theme/tokens/builders/accentText");
|
|
15
15
|
const filterHoverStyle_1 = require("./filterHoverStyle");
|
|
16
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
16
17
|
const FilterSidebar = ({ sidebarOptions, selected, onChange, maxVisible = 10, width = 280, variant = "minimal", containerPadding, containerHeight,
|
|
17
18
|
// headerPadding = 2,
|
|
18
19
|
panelPadding = 3, clearAllButton = true, onClearAllHandler,
|
|
@@ -173,7 +174,7 @@ panelPadding = 3, clearAllButton = true, onClearAllHandler,
|
|
|
173
174
|
return Array.from({ length: 5 }, (_, i) => ((0, jsx_runtime_1.jsx)(react_2.Box, { children: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 16, color: starColor, fill: i < r ? starColor : "none" }) }, `star-${r}-${i}`)));
|
|
174
175
|
})() }), (0, jsx_runtime_1.jsx)(react_2.Text, { fontSize: "sm", color: theme.colors.text[500], children: "& Up" })] }) }, `rating-${r}`))) })) : null, section.searchable && ((0, jsx_runtime_1.jsx)(react_2.Input, { size: "xs", h: "26px", fontSize: "xs", "aria-label": `Search ${section.label}`, placeholder: `Search ${section.label}...`, borderRadius: "md", bg: theme.colors.background[50], border: "1px solid",
|
|
175
176
|
// NOTE: boxborder[500] not gray[200] — WCAG 1.4.11 needs >=3:1 for a control boundary; gray[200] fails in dark.
|
|
176
|
-
borderColor: theme.colors.boxborder[500], _focus: { borderColor: theme.colors.primary[
|
|
177
|
+
borderColor: theme.colors.boxborder[500], _focus: { borderColor: theme.colors.primary[500], ...(0, focusRing_styles_1.fieldFocusVisible)(theme.colors) }, value: searchTerms[section.id] || "", onChange: (e) => {
|
|
177
178
|
const v = e.target.value;
|
|
178
179
|
setSearchTerms((prev) => ({ ...prev, [section.id]: v }));
|
|
179
180
|
if (v)
|
|
@@ -9,6 +9,8 @@ declare const HeaderActions: ({ select, edit, create, surface, }: {
|
|
|
9
9
|
selectPlaceholderText?: string;
|
|
10
10
|
/** Accessible name for the select; defaults to the placeholder text. */
|
|
11
11
|
ariaLabel?: string;
|
|
12
|
+
/** False renders the themed popup, which looks identical on Windows and macOS; a native select's list is OS-drawn. */
|
|
13
|
+
native?: boolean;
|
|
12
14
|
width?: string;
|
|
13
15
|
value?: string;
|
|
14
16
|
};
|
|
@@ -13,14 +13,14 @@ const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
|
13
13
|
const Select_1 = __importDefault(require("../Select/Select"));
|
|
14
14
|
const Divider_1 = __importDefault(require("../Divider/Divider"));
|
|
15
15
|
const HeaderActions = ({ select, edit, create, surface, }) => {
|
|
16
|
-
var _a, _b, _c
|
|
16
|
+
var _a, _b, _c;
|
|
17
17
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
18
18
|
const canvas = surface !== null && surface !== void 0 ? surface : colors.backgroundColor.main;
|
|
19
19
|
const ink = (0, outlineRung_1.liftToContrast)(colors.secondary[500], canvas, accentText_1.AA_NORMAL_TEXT);
|
|
20
20
|
const iconInk = (0, outlineRung_1.liftToContrast)(colors.secondary[500], canvas, outlineRung_1.AA_NON_TEXT);
|
|
21
21
|
const elements = [];
|
|
22
22
|
if (select === null || select === void 0 ? void 0 : select.isSelect) {
|
|
23
|
-
elements.push((0, jsx_runtime_1.jsx)(react_1.Box, { width: (_a = select.width) !== null && _a !== void 0 ? _a : "12.5rem", children: (0, jsx_runtime_1.jsx)(Select_1.default, { options: (_b = select.options) !== null && _b !== void 0 ? _b : [], placeholder: select.selectPlaceholderText, "aria-label":
|
|
23
|
+
elements.push((0, jsx_runtime_1.jsx)(react_1.Box, { width: (_a = select.width) !== null && _a !== void 0 ? _a : "12.5rem", children: (0, jsx_runtime_1.jsx)(Select_1.default, { options: (_b = select.options) !== null && _b !== void 0 ? _b : [], placeholder: select.selectPlaceholderText, "aria-label": select.ariaLabel || select.selectPlaceholderText || "Select", native: (_c = select.native) !== null && _c !== void 0 ? _c : false, onChange: (option) => { var _a; return (_a = select.onSelectChange) === null || _a === void 0 ? void 0 : _a.call(select, option); }, value: select.value }, "select") }, "select"));
|
|
24
24
|
}
|
|
25
25
|
if (edit === null || edit === void 0 ? void 0 : edit.isEditable) {
|
|
26
26
|
elements.push((0, jsx_runtime_1.jsxs)(react_1.Box, { as: "button", type: "button", bg: "transparent", borderRadius: "lg", _focusVisible: { boxShadow: `0 0 0 2px ${iconInk}`, outline: "none" }, display: "flex", alignItems: "center", gap: "0.375rem", cursor: "pointer", fontSize: 14, onClick: () => { var _a; return (_a = edit.onEdit) === null || _a === void 0 ? void 0 : _a.call(edit); }, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.SquarePen, { size: 16, color: iconInk }), (0, jsx_runtime_1.jsx)(react_1.Text, { color: ink, children: edit.label ? edit.label : "Edit Layout" })] }, "edit"));
|
|
@@ -13,7 +13,7 @@ const fieldPartStyles = (colors) => {
|
|
|
13
13
|
borderColor: error,
|
|
14
14
|
boxShadow: "none",
|
|
15
15
|
_hover: { borderColor: error },
|
|
16
|
-
_focusVisible: { borderColor: error, ...(0, focusRing_styles_1.
|
|
16
|
+
_focusVisible: { borderColor: error, ...(0, focusRing_styles_1.fieldFocusVisible)(colors) },
|
|
17
17
|
},
|
|
18
18
|
};
|
|
19
19
|
};
|
|
@@ -13,6 +13,6 @@ const Button_1 = __importDefault(require("../../Button/Button"));
|
|
|
13
13
|
/** Standalone filter+Edit+Create action bar; NOT rendered by KanbanBoard (which uses HeaderActions instead) and not exported from the package. */
|
|
14
14
|
const KanbanActions = ({ options, onSelectChange, onEdit, onCreate, }) => {
|
|
15
15
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
16
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", alignItems: "center", gap: "0.625rem", children: [(0, jsx_runtime_1.jsx)(Select_1.default, { options: options, placeholder: "Filter by Status", width: "12.5rem", onChange: (option) => onSelectChange && onSelectChange(option) }), (0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", alignItems: "center", gap: "0.5rem", children: [(0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "ghost", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.SquarePen, { size: 20, color: colors.text[500] }), onClick: () => onEdit === null || onEdit === void 0 ? void 0 : onEdit(), children: "Edit Layout" }), (0, jsx_runtime_1.jsx)(Divider_1.default, {}), (0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "solid", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.CirclePlus, { size: 20 }), onClick: () => onCreate === null || onCreate === void 0 ? void 0 : onCreate(), children: "Create New" })] })] }));
|
|
16
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", alignItems: "center", gap: "0.625rem", children: [(0, jsx_runtime_1.jsx)(Select_1.default, { native: false, options: options, placeholder: "Filter by Status", width: "12.5rem", onChange: (option) => onSelectChange && onSelectChange(option) }), (0, jsx_runtime_1.jsxs)(react_1.Box, { display: "flex", alignItems: "center", gap: "0.5rem", children: [(0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "ghost", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.SquarePen, { size: 20, color: colors.text[500] }), onClick: () => onEdit === null || onEdit === void 0 ? void 0 : onEdit(), children: "Edit Layout" }), (0, jsx_runtime_1.jsx)(Divider_1.default, {}), (0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "solid", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.CirclePlus, { size: 20 }), onClick: () => onCreate === null || onCreate === void 0 ? void 0 : onCreate(), children: "Create New" })] })] }));
|
|
17
17
|
};
|
|
18
18
|
exports.default = KanbanActions;
|
|
@@ -90,6 +90,8 @@ export type KanbanBoardProps = {
|
|
|
90
90
|
width?: string;
|
|
91
91
|
/** Controlled value passed straight to the underlying Select. */
|
|
92
92
|
value?: string;
|
|
93
|
+
/** False renders the themed popup, which looks identical on Windows and macOS; a native select's list is OS-drawn. */
|
|
94
|
+
native?: boolean;
|
|
93
95
|
};
|
|
94
96
|
/** Button shows only when this `isEditable` AND `permissions.isEditable` (default true) both allow it. */
|
|
95
97
|
kanbanEdit?: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { MultiSelectProps } from "./MultiSelectProps";
|
|
2
|
-
export default function MultiSelect({ value, onValueChange, width, label, isRequired, isInformation, informationMessage, name, id, placeholderText, isDisabled, isReadOnly, size, inputStyle, helperText, error, errorMessage, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export default function MultiSelect({ value, onValueChange, width, label, isRequired, isInformation, informationMessage, name, id, placeholderText, isDisabled, isReadOnly, addOnBlur, size, inputStyle, helperText, error, errorMessage, }: MultiSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -25,7 +25,7 @@ const generateUniqueId = (() => {
|
|
|
25
25
|
return `${Date.now()}-${counter}`;
|
|
26
26
|
};
|
|
27
27
|
})();
|
|
28
|
-
function MultiSelect({ value, onValueChange, width = "100%", label = "label", isRequired = true, isInformation = false, informationMessage = "", name, id, placeholderText = "Type valid email and press enter", isDisabled, isReadOnly, size = controlSizes_1.DEFAULT_CONTROL_SIZE, inputStyle, helperText, error, errorMessage, }) {
|
|
28
|
+
function MultiSelect({ value, onValueChange, width = "100%", label = "label", isRequired = true, isInformation = false, informationMessage = "", name, id, placeholderText = "Type valid email and press enter", isDisabled, isReadOnly, addOnBlur = false, size = controlSizes_1.DEFAULT_CONTROL_SIZE, inputStyle, helperText, error, errorMessage, }) {
|
|
29
29
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
30
30
|
const s = (0, searchSelectSize_1.getSearchSelectSizeStyles)((size || controlSizes_1.DEFAULT_CONTROL_SIZE));
|
|
31
31
|
const options = value;
|
|
@@ -43,23 +43,34 @@ function MultiSelect({ value, onValueChange, width = "100%", label = "label", is
|
|
|
43
43
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
44
44
|
return emailRegex.test(email);
|
|
45
45
|
};
|
|
46
|
+
const commitInput = (0, react_1.useCallback)(() => {
|
|
47
|
+
const entry = inputValue.trim();
|
|
48
|
+
if (entry === "")
|
|
49
|
+
return;
|
|
50
|
+
if (!validateEmail(entry)) {
|
|
51
|
+
setLocalError("Invalid email address");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!options.some((option) => option.label === entry)) {
|
|
55
|
+
onValueChange([...options, { id: generateUniqueId(), label: entry }]);
|
|
56
|
+
}
|
|
57
|
+
setInputValue("");
|
|
58
|
+
}, [inputValue, options, onValueChange]);
|
|
46
59
|
const handleKeyDown = (e) => {
|
|
47
|
-
if (e.key === "Enter"
|
|
48
|
-
if (
|
|
49
|
-
setLocalError("Invalid email address");
|
|
60
|
+
if (e.key === "Enter") {
|
|
61
|
+
if (inputValue.trim() === "")
|
|
50
62
|
return;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (!existingOption) {
|
|
54
|
-
const newOption = { id: generateUniqueId(), label: inputValue };
|
|
55
|
-
onValueChange([...options, newOption]);
|
|
56
|
-
setInputValue("");
|
|
57
|
-
}
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
commitInput();
|
|
58
65
|
}
|
|
59
66
|
else if (e.key === "Backspace" && inputValue === "") {
|
|
60
67
|
onValueChange(options.slice(0, -1));
|
|
61
68
|
}
|
|
62
69
|
};
|
|
70
|
+
const handleBlur = (0, react_1.useCallback)(() => {
|
|
71
|
+
if (addOnBlur && !isReadOnly && !isDisabled)
|
|
72
|
+
commitInput();
|
|
73
|
+
}, [addOnBlur, isReadOnly, isDisabled, commitInput]);
|
|
63
74
|
const handleRemove = (0, react_1.useCallback)((optionToRemove) => {
|
|
64
75
|
onValueChange(options.filter((option) => option.id !== optionToRemove.id));
|
|
65
76
|
}, [options, onValueChange]);
|
|
@@ -72,5 +83,5 @@ function MultiSelect({ value, onValueChange, width = "100%", label = "label", is
|
|
|
72
83
|
}, children: [options.map((option) => ((0, jsx_runtime_1.jsx)(Tag_1.default, { size: s.tagSize, colorScheme: "primary", label: option.label, onIconClick: () => handleRemove(option), icon: lucide_react_1.X }, option.id))), (0, jsx_runtime_1.jsx)(react_2.Input, { ref: inputRef, type: "text", value: inputValue, isDisabled: isDisabled, isReadOnly: isReadOnly, isRequired: isRequired, isInvalid: isInvalid, ...ids.controlProps, name: name, size: size, variant: "unstyled", h: s.fieldHeight, minH: s.fieldHeight, fontSize: s.fieldFontSize, fontWeight: 400, color: theme.colors.text[900], flex: "1", minW: 0, _placeholder: { color: (0, placeholderInk_1.placeholderInk)(theme.colors), opacity: 1 }, style: {
|
|
73
84
|
backgroundColor: "transparent",
|
|
74
85
|
...inputStyle,
|
|
75
|
-
}, onChange: handleInputChange, onKeyDown: handleKeyDown, placeholder: options.length === 0 ? placeholderText : "" })] }), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: isInvalid, errorMessage: shownErrorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
86
|
+
}, onChange: handleInputChange, onKeyDown: handleKeyDown, onBlur: handleBlur, placeholder: options.length === 0 ? placeholderText : "" })] }), (0, jsx_runtime_1.jsx)(FieldMessage_1.default, { error: isInvalid, errorMessage: shownErrorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
76
87
|
}
|
|
@@ -14,6 +14,8 @@ export type MultiSelectProps = {
|
|
|
14
14
|
placeholderText?: string;
|
|
15
15
|
isDisabled?: boolean;
|
|
16
16
|
isReadOnly?: boolean;
|
|
17
|
+
/** Also commits the typed email when the field loses focus, not just on Enter. Defaults to false. */
|
|
18
|
+
addOnBlur?: boolean;
|
|
17
19
|
/** Marks the field invalid (`aria-invalid` + error border) and swaps `helperText` for `errorMessage`; a rejected email sets this internally too. */
|
|
18
20
|
error?: boolean;
|
|
19
21
|
/** Height on the shared control scale (xs 24 · sm 32 · md 40 · lg 48px). Defaults to `sm`. */
|
|
@@ -13,8 +13,11 @@ export interface DaySectionLabels {
|
|
|
13
13
|
earlier: string;
|
|
14
14
|
}
|
|
15
15
|
export declare const DEFAULT_DAY_LABELS: DaySectionLabels;
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Buckets rows into Today/Yesterday/Earlier, input order preserved within each bucket.
|
|
18
|
+
* `now` defaults to the wall clock; pass it to pin "today" in tests or a fixed-date view.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sectionizeByDay<T>(rows: T[], getTime: (row: T) => number | string | Date, labels?: DaySectionLabels, now?: number | Date): DaySection<T>[];
|
|
18
21
|
/** Merges only adjacent rows sharing a key; a null/undefined key never merges with anything. */
|
|
19
22
|
export declare function groupConsecutive<T>(rows: T[], getKey: (row: T) => string | null | undefined): ConsecutiveGroup<T>[];
|
|
20
23
|
/** 5 -> "5"; 150 with max 99 -> "99+". Negative/NaN input is clamped to 0. */
|
|
@@ -21,10 +21,19 @@ const toDate = (value) => {
|
|
|
21
21
|
return new Date(/^-?\d+$/.test(value) ? Number(value) : value);
|
|
22
22
|
};
|
|
23
23
|
const startOfDay = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
24
|
+
// NOTE: calendar arithmetic, not -24h — a DST shift makes the day shorter or longer.
|
|
25
|
+
const startOfPrevDay = (dayStart) => {
|
|
26
|
+
const d = new Date(dayStart);
|
|
27
|
+
d.setDate(d.getDate() - 1);
|
|
28
|
+
return startOfDay(d);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Buckets rows into Today/Yesterday/Earlier, input order preserved within each bucket.
|
|
32
|
+
* `now` defaults to the wall clock; pass it to pin "today" in tests or a fixed-date view.
|
|
33
|
+
*/
|
|
34
|
+
function sectionizeByDay(rows, getTime, labels = exports.DEFAULT_DAY_LABELS, now = Date.now()) {
|
|
35
|
+
const todayStart = startOfDay(toDate(now));
|
|
36
|
+
const yesterdayStart = startOfPrevDay(todayStart);
|
|
28
37
|
const today = [];
|
|
29
38
|
const yesterday = [];
|
|
30
39
|
const earlier = [];
|
|
@@ -7,6 +7,7 @@ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
|
7
7
|
const FormLabel_1 = require("../Common/FormLabel");
|
|
8
8
|
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
9
9
|
const pinInputCellSize_1 = require("./pinInputCellSize");
|
|
10
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
10
11
|
/** The real, published OTP input (exported as `PinInput` from index.ts) — Chakra-native boxes, paste support, `type: "alphanumeric" | "number"`. */
|
|
11
12
|
function PinInputs({ otp, mask, defaultValue, placeholder, manageFocus, errorBorderColor, focusBorderColor, onChange, onComplete, type, color = "primary", variant, pins = 3, label, id, isRequired, isInformation, informationMessage, size = controlSizes_1.DEFAULT_CONTROL_SIZE, }) {
|
|
12
13
|
var _a;
|
|
@@ -15,8 +16,5 @@ function PinInputs({ otp, mask, defaultValue, placeholder, manageFocus, errorBor
|
|
|
15
16
|
// Square cells at the same heights every other field uses — Chakra's own PinInput size
|
|
16
17
|
// scale doesn't line up with the library's, so cells are sized directly.
|
|
17
18
|
const cell = (0, pinInputCellSize_1.pinInputCellSize)(size);
|
|
18
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [label && ((0, jsx_runtime_1.jsx)(FormLabel_1.TextLabel, { label: label, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), (0, jsx_runtime_1.jsx)(react_1.HStack, { role: "group", "aria-label": fieldName, children: (0, jsx_runtime_1.jsx)(react_1.PinInput, { otp: otp, mask: mask, defaultValue: defaultValue, placeholder: placeholder, manageFocus: manageFocus, errorBorderColor: errorBorderColor, focusBorderColor: focusBorderColor, onChange: onChange, onComplete: onComplete, type: type, colorScheme: color, variant: variant, children: Array.from({ length: pins }).map((_, index) => ((0, jsx_runtime_1.jsx)(react_1.PinInputField, { "aria-label": `${fieldName}, digit ${index + 1} of ${pins}`, boxSize: cell.boxSize, fontSize: cell.fontSize, borderRadius: "lg", backgroundColor: theme.colors.background[100], borderColor: theme.colors.boxborder[500], _focus: {
|
|
19
|
-
borderColor: theme.colors.primary[500],
|
|
20
|
-
boxShadow: `0 0 0 0.063rem ${theme.colors.primary[500]}`,
|
|
21
|
-
} }, index))) }) })] }));
|
|
19
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [label && ((0, jsx_runtime_1.jsx)(FormLabel_1.TextLabel, { label: label, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), (0, jsx_runtime_1.jsx)(react_1.HStack, { role: "group", "aria-label": fieldName, children: (0, jsx_runtime_1.jsx)(react_1.PinInput, { otp: otp, mask: mask, defaultValue: defaultValue, placeholder: placeholder, manageFocus: manageFocus, errorBorderColor: errorBorderColor, focusBorderColor: focusBorderColor, onChange: onChange, onComplete: onComplete, type: type, colorScheme: color, variant: variant, children: Array.from({ length: pins }).map((_, index) => ((0, jsx_runtime_1.jsx)(react_1.PinInputField, { "aria-label": `${fieldName}, digit ${index + 1} of ${pins}`, boxSize: cell.boxSize, fontSize: cell.fontSize, borderRadius: "lg", backgroundColor: theme.colors.background[100], borderColor: theme.colors.boxborder[500], _focus: { borderColor: theme.colors.primary[500], ...(0, focusRing_styles_1.fieldFocusVisible)(theme.colors) } }, index))) }) })] }));
|
|
22
20
|
}
|
|
@@ -521,7 +521,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
521
521
|
}, onBlur: () => {
|
|
522
522
|
setFocused(false);
|
|
523
523
|
setFocusedTrigger((prev) => (prev === "input" ? null : prev));
|
|
524
|
-
}, placeholder: selectedValues.length
|
|
524
|
+
}, placeholder: selectedValues.length ? "" : placeholder })), (0, jsx_runtime_1.jsxs)(react_2.Box, { position: "absolute", right: s.rightOffset, top: "50%", transform: "translateY(-50%)", display: "flex", alignItems: "center", gap: 1, children: [isRemoveAllIcon && selectedValues.length > 0 && !isDisabled && ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { placement: "top", label: "Remove All", hasArrow: true, fontSize: "0.75rem", children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: s.clearIconSize, color: theme.colors.text[900] }), "aria-label": "Clear all", variant: "ghost", size: s.iconButtonSize, onClick: handleClearAll, sx: {
|
|
525
525
|
bg: "transparent",
|
|
526
526
|
_hover: { bg: "transparent" },
|
|
527
527
|
_active: { bg: "transparent" },
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SegmentedControlProps } from "./SegmentedControlProps";
|
|
2
2
|
/** Renders role="radiogroup" of role="radio" buttons with roving tabindex, not a native form radio group; shares variant with Toggle/TableToggle (see MODULE.md). */
|
|
3
|
-
export default function SegmentedControl<T extends string = string>({ options, value, onChange, size, isFullWidth, variant, "aria-label": ariaLabel, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export default function SegmentedControl<T extends string = string>({ options, value, onChange, size, isFullWidth, variant, surface, "aria-label": ariaLabel, }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|