@12-apps/ui 1.22.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/src/components/data-display/Alert/Alert.types.ts +2 -1
- package/src/components/data-display/Avatar/Avatar.types.ts +10 -2
- package/src/components/data-display/Badge/Badge.types.ts +3 -2
- package/src/components/data-display/Carousel/Carousel.parts.tsx +12 -10
- package/src/components/data-display/Carousel/Carousel.styles.ts +4 -3
- package/src/components/data-display/Carousel/Carousel.tsx +2 -1
- package/src/components/data-display/Carousel/Carousel.types.ts +7 -6
- package/src/components/data-display/Chart/Chart.tsx +2 -1
- package/src/components/data-display/Chart/Chart.types.ts +3 -2
- package/src/components/data-display/Chart/ChartSpec.types.ts +6 -7
- package/src/components/data-display/Chart/SpecChart.tsx +2 -1
- package/src/components/data-display/Chart/chart-internals.ts +3 -2
- package/src/components/data-display/Chart/chart-spec.ts +11 -1
- package/src/components/data-display/Chip/Chip.tsx +23 -3
- package/src/components/data-display/Chip/Chip.types.ts +37 -3
- package/src/components/data-display/Chip/chip.md +7 -3
- package/src/components/data-display/Chip/index.ts +1 -1
- package/src/components/data-display/DataViews/DataViewsGrid.tsx +10 -0
- package/src/components/data-display/DataViews/DataViewsTableBase.tsx +3 -0
- package/src/components/data-display/DataViews/RESPONSIVE-BEHAVIOUR.md +37 -3
- package/src/components/data-display/DataViews/data-views-day-input.tsx +22 -3
- package/src/components/data-display/DataViews/data-views-display-panel.tsx +4 -2
- package/src/components/data-display/DataViews/data-views-grid-bodies.tsx +113 -62
- package/src/components/data-display/DataViews/data-views-grid-parts.tsx +17 -1
- package/src/components/data-display/DataViews/data-views-more-fields.tsx +16 -23
- package/src/components/data-display/DataViews/data-views-more-filters.tsx +7 -2
- package/src/components/data-display/DataViews/data-views-number-input.tsx +14 -4
- package/src/components/data-display/DataViews/data-views-overflow-costs.ts +87 -16
- package/src/components/data-display/DataViews/data-views-overflow.ts +104 -71
- package/src/components/data-display/DataViews/index.ts +4 -0
- package/src/components/data-display/DataViews/list-card-rails.tsx +29 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +8 -5
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.types.ts +2 -1
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.types.ts +2 -1
- package/src/components/data-display/LoadingState/LoadingState.types.ts +2 -1
- package/src/components/data-display/Progress/Progress.types.ts +3 -2
- package/src/components/data-display/Sheet/Sheet.body.tsx +2 -1
- package/src/components/data-display/Sheet/Sheet.styles.ts +3 -2
- package/src/components/data-display/Sheet/Sheet.types.ts +7 -2
- package/src/components/data-display/Stepper/Stepper.types.ts +2 -1
- package/src/components/data-display/Table/Table.styles.ts +1 -1
- package/src/components/data-display/Table/Table.types.ts +3 -1
- package/src/components/data-display/Tooltip/Tooltip.types.ts +2 -1
- package/src/components/feedback/Dialog/Dialog.types.ts +2 -1
- package/src/components/feedback/Modal/Modal.types.ts +2 -1
- package/src/components/form/Button/Button.types.ts +3 -2
- package/src/components/form/Command/Command.styles.ts +8 -7
- package/src/components/form/Command/Command.types.ts +3 -2
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +2 -1
- package/src/components/form/Input/Input.types.ts +2 -1
- package/src/components/form/InputOTP/InputOTP.types.ts +3 -2
- package/src/components/form/Label/Label.styles.ts +8 -5
- package/src/components/form/Label/Label.tsx +1 -1
- package/src/components/form/Label/Label.types.ts +3 -2
- package/src/components/form/Menubar/Menubar.styles.ts +10 -8
- package/src/components/form/Menubar/Menubar.tsx +2 -1
- package/src/components/form/Menubar/Menubar.types.ts +7 -6
- package/src/components/form/RadioGroup/RadioGroup.types.ts +3 -2
- package/src/components/form/Select/Select.tsx +13 -1
- package/src/components/form/Select/Select.types.ts +5 -2
- package/src/components/form/Slider/Slider.types.ts +3 -2
- package/src/components/form/Switch/Switch.types.ts +3 -2
- package/src/components/form/Textarea/Textarea.types.ts +3 -2
- package/src/components/form/Toggle/Toggle.types.ts +3 -2
- package/src/components/form/ToggleGroup/ToggleGroup.types.ts +3 -2
- package/src/components/layout/Separator/Separator.types.ts +2 -1
- package/src/components/layout/Spacer/Spacer.types.ts +2 -1
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +1 -1
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.types.ts +7 -2
- package/src/components/navigation/ContextMenu/ContextMenu.types.ts +3 -2
- package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +1 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.types.ts +12 -2
- package/src/components/navigation/NavigationMenu/NavigationMenu.types.ts +7 -2
- package/src/components/navigation/Pagination/Pagination.tsx +3 -1
- package/src/components/navigation/Pagination/Pagination.types.ts +5 -3
- package/src/components/navigation/Tabs/Tabs.types.ts +3 -2
- package/src/components/typography/Blockquote/Blockquote.types.ts +2 -1
- package/src/components/typography/Code/Code.types.ts +2 -1
- package/src/components/typography/Heading/Heading.types.ts +2 -1
- package/src/components/typography/Paragraph/Paragraph.types.ts +3 -2
- package/src/components/typography/Text/Text.types.ts +3 -2
- package/src/components/utility/AnimatedIcon/AnimatedIcon.types.ts +2 -1
- package/src/components/utility/Overflow/index.ts +6 -0
- package/src/components/utility/Overflow/overflow.ts +125 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.types.ts +3 -2
- package/src/components/utility/index.ts +1 -0
- package/src/tokens/scales.ts +153 -0
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useRef } from "react";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { splitToFit, useMeasuredWidth } from "../../utility/Overflow";
|
|
6
|
+
import {
|
|
7
|
+
estimateWidth,
|
|
8
|
+
furnitureCost,
|
|
9
|
+
GAP,
|
|
10
|
+
isRangeSet,
|
|
11
|
+
pillText,
|
|
12
|
+
RESERVED,
|
|
13
|
+
rightClusterCost,
|
|
14
|
+
} from "./data-views-overflow-costs";
|
|
6
15
|
import type { FilterFieldConfig, RangeFieldConfig, RangeValue } from "./data-views-types";
|
|
7
16
|
|
|
8
17
|
/**
|
|
@@ -98,26 +107,6 @@ export function toOverflowFields<T extends Record<string, unknown>>(
|
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
|
|
101
|
-
/** Observe the bar's width. Returns 0 until the first measurement lands. */
|
|
102
|
-
function useBarWidth(): { barRef: React.RefObject<HTMLDivElement | null>; width: number } {
|
|
103
|
-
const barRef = useRef<HTMLDivElement | null>(null);
|
|
104
|
-
const [width, setWidth] = useState(0);
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
const element = barRef.current;
|
|
107
|
-
// jsdom has no ResizeObserver, and neither does an SSR pass — with no
|
|
108
|
-
// measurement the hook keeps every control inline, which is the honest
|
|
109
|
-
// fallback: nothing is hidden behind a menu nobody can see.
|
|
110
|
-
if (!element || typeof ResizeObserver === "undefined") return undefined;
|
|
111
|
-
const observer = new ResizeObserver((entries) => {
|
|
112
|
-
const entry = entries[0];
|
|
113
|
-
if (entry) setWidth(entry.contentRect.width);
|
|
114
|
-
});
|
|
115
|
-
observer.observe(element);
|
|
116
|
-
return () => observer.disconnect();
|
|
117
|
-
}, []);
|
|
118
|
-
return { barRef, width };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
110
|
/** Is this control carrying a value the operator set? */
|
|
122
111
|
function isActiveField<T extends Record<string, unknown>>(
|
|
123
112
|
field: OverflowField<T>,
|
|
@@ -154,24 +143,16 @@ function splitFilters<T extends Record<string, unknown>>(
|
|
|
154
143
|
pills: Record<string, string[]>,
|
|
155
144
|
ranges: Record<string, RangeValue>,
|
|
156
145
|
width: number,
|
|
146
|
+
/** Search + counter + right cluster + chrome, at the rung being considered. */
|
|
147
|
+
furniture: number,
|
|
148
|
+
/** Is the bar already down to icons? "Mais" shrinks with everything else. */
|
|
149
|
+
compact: boolean,
|
|
157
150
|
): Split<T> {
|
|
158
|
-
const none: OverflowField<T>[] = [];
|
|
159
|
-
const widthOf = (field: OverflowField<T>): number => fieldWidth(field, pills, ranges);
|
|
160
|
-
const cost = (fields: OverflowField<T>[]): number =>
|
|
161
|
-
fields.reduce((sum, field) => sum + widthOf(field) + GAP, 0);
|
|
162
151
|
const active = all.filter((field) => isActiveField(field, pills, ranges));
|
|
163
152
|
const idle = all.filter((field) => !isActiveField(field, pills, ranges));
|
|
164
153
|
|
|
165
154
|
// "Limpar" rides the end of the cluster whenever anything is applied.
|
|
166
155
|
const clearCost = active.length > 0 ? RESERVED.clearAll + GAP : 0;
|
|
167
|
-
let available =
|
|
168
|
-
width - RESERVED.search - RESERVED.counter - RESERVED.right - RESERVED.chrome - clearCost;
|
|
169
|
-
if (cost(all) <= available) {
|
|
170
|
-
return { inline: all, overflow: none, used: cost(all) + clearCost };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// There IS an overflow, so its button now costs room too.
|
|
174
|
-
available -= RESERVED.overflowButton;
|
|
175
156
|
// APPLIED FIRST, BUT NOT EXEMPT.
|
|
176
157
|
//
|
|
177
158
|
// Applied controls take the visible slots ahead of idle ones — that part was
|
|
@@ -184,22 +165,21 @@ function splitFilters<T extends Record<string, unknown>>(
|
|
|
184
165
|
// vanished silently: the trigger goes to the applied tone and says how many
|
|
185
166
|
// are in there (see `MoreTrigger`), which is the same signal the pill itself
|
|
186
167
|
// was carrying. A control you scroll off-screen carries no signal at all.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
168
|
+
//
|
|
169
|
+
// The keep-what-fits loop itself is `splitToFit` (`utility/Overflow`) — the
|
|
170
|
+
// one part of this that is not filter-shaped, and the part a second cluster
|
|
171
|
+
// in the design system would otherwise have had to reimplement.
|
|
172
|
+
const split = splitToFit(all, {
|
|
173
|
+
widthOf: (field) => fieldWidth(field, pills, ranges),
|
|
174
|
+
keyOf: (field) => field.id,
|
|
175
|
+
gap: GAP,
|
|
176
|
+
available: width - furniture - clearCost,
|
|
177
|
+
// The overflow button at the width it will actually have, which on a phone
|
|
178
|
+
// is the icon-and-badge one.
|
|
179
|
+
overflowCost: compact ? RESERVED.overflowButtonCompact : RESERVED.overflowButton,
|
|
180
|
+
priority: [...active, ...idle],
|
|
181
|
+
});
|
|
182
|
+
return { ...split, used: split.used + clearCost };
|
|
203
183
|
}
|
|
204
184
|
|
|
205
185
|
/**
|
|
@@ -224,6 +204,8 @@ function computeSplit<T extends Record<string, unknown>>(
|
|
|
224
204
|
pills: Record<string, string[]>,
|
|
225
205
|
ranges: Record<string, RangeValue>,
|
|
226
206
|
width: number,
|
|
207
|
+
/** Does the host render "Exportar" beside "Exibir"? Half the cluster if not. */
|
|
208
|
+
hasExport: boolean,
|
|
227
209
|
): Omit<OverflowSplit<T>, "barRef"> {
|
|
228
210
|
// Unmeasured (SSR, or jsdom without a ResizeObserver) ⇒ degrade nothing.
|
|
229
211
|
if (width === 0) {
|
|
@@ -237,12 +219,47 @@ function computeSplit<T extends Record<string, unknown>>(
|
|
|
237
219
|
searchTakeover: false,
|
|
238
220
|
};
|
|
239
221
|
}
|
|
240
|
-
|
|
222
|
+
// Pass 1 prices the furniture at its widest — no rung has been taken yet.
|
|
223
|
+
let split = splitFilters(all, pills, ranges, width, furnitureCost(UNCOLLAPSED, hasExport), false);
|
|
224
|
+
let flags = ladderFlags(split, width, hasExport);
|
|
225
|
+
// RE-SPEND WHAT THE LATER RUNGS FREED.
|
|
226
|
+
//
|
|
227
|
+
// Rungs 2/4/5 turn 200 + 96 + 216 of furniture into 44 + 0 + 140, and until
|
|
228
|
+
// this loop existed nobody handed those ~330px back to the filters: the
|
|
229
|
+
// split was decided once, against the uncollapsed row, so a phone shed every
|
|
230
|
+
// control and then sat with a band of empty bar beside "Mais 5".
|
|
231
|
+
//
|
|
232
|
+
// Settles rather than oscillates, and cannot overfill: a cheaper furniture
|
|
233
|
+
// can only give the filters MORE room, more filter width can only push the
|
|
234
|
+
// ladder FURTHER down, and a further rung can only make the furniture
|
|
235
|
+
// cheaper again — so the budget decreases monotonically and the loop exits
|
|
236
|
+
// the moment it stops moving. The bound is a backstop, not the exit.
|
|
237
|
+
let budget = furnitureCost(UNCOLLAPSED, hasExport);
|
|
238
|
+
for (let pass = 0; pass < 3; pass += 1) {
|
|
239
|
+
const freed = furnitureCost(flags, hasExport);
|
|
240
|
+
if (freed >= budget) break;
|
|
241
|
+
budget = freed;
|
|
242
|
+
split = splitFilters(all, pills, ranges, width, budget, flags.compactControls);
|
|
243
|
+
flags = ladderFlags(split, width, hasExport);
|
|
244
|
+
}
|
|
245
|
+
return { inline: split.inline, overflow: split.overflow, ...flags };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The widest the furniture ever is — every rung still untaken. */
|
|
249
|
+
const UNCOLLAPSED = { searchCollapsed: false, counterHidden: false, compactControls: false };
|
|
250
|
+
|
|
251
|
+
/** Rungs 2 and 4-6, priced against what the filters actually took. */
|
|
252
|
+
function ladderFlags<T extends Record<string, unknown>>(
|
|
253
|
+
split: Split<T>,
|
|
254
|
+
width: number,
|
|
255
|
+
hasExport: boolean,
|
|
256
|
+
): Omit<OverflowSplit<T>, "barRef" | "inline" | "overflow"> {
|
|
257
|
+
const { overflow, used } = split;
|
|
241
258
|
const base = width - used - RESERVED.chrome;
|
|
242
259
|
|
|
243
260
|
// Would the search still make its minimum with the labels on?
|
|
244
|
-
const compactControls = base - RESERVED.counter -
|
|
245
|
-
const rightCost = compactControls
|
|
261
|
+
const compactControls = base - RESERVED.counter - rightClusterCost(hasExport, false) < RESERVED.search;
|
|
262
|
+
const rightCost = rightClusterCost(hasExport, compactControls);
|
|
246
263
|
// …and with them off?
|
|
247
264
|
const searchCollapsed = base - RESERVED.counter - rightCost < RESERVED.search;
|
|
248
265
|
// Everything has collapsed and the row STILL overflows. The controls cannot
|
|
@@ -258,26 +275,29 @@ function computeSplit<T extends Record<string, unknown>>(
|
|
|
258
275
|
// on there BEING an overflow, because with no panel the footer does not
|
|
259
276
|
// exist and dropping this would strand the operator.
|
|
260
277
|
const clearAllHidden = counterHidden && overflow.length > 0;
|
|
261
|
-
// What the keyword box would get if the operator expanded the magnifier
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
|
|
278
|
+
// What the keyword box would get if the operator expanded the magnifier.
|
|
279
|
+
// `searchCollapsed` is a different question — it asks whether the box fits at
|
|
280
|
+
// its PREFERRED 200px, and is deliberately pessimistic — so reusing it to
|
|
281
|
+
// decide the takeover evicted the filters on screens where the box had plenty
|
|
282
|
+
// of room to simply shrink.
|
|
283
|
+
//
|
|
284
|
+
// Asked with the inline filters SHED, because shedding them is exactly what
|
|
285
|
+
// expanding the box does: the row re-splits and they go behind "Mais".
|
|
286
|
+
// Measuring against the resting row instead made a filter argue for its own
|
|
287
|
+
// eviction — now that a large phone keeps one, its width came out of `base`,
|
|
288
|
+
// pushed the box under `usableSearch`, and tipped a row that had ample space
|
|
289
|
+
// to share into a full takeover.
|
|
290
|
+
const overflowButton = compactControls
|
|
291
|
+
? RESERVED.overflowButtonCompact
|
|
292
|
+
: RESERVED.overflowButton;
|
|
293
|
+
const shedRoom = width - RESERVED.chrome - (overflow.length > 0 ? overflowButton : 0);
|
|
294
|
+
const searchBoxRoom = shedRoom - (counterHidden ? 0 : RESERVED.counter) - rightCost;
|
|
267
295
|
// Below this a box is too narrow to read what you typed into it, and shrinking
|
|
268
296
|
// further buys nothing; that is the only point at which taking the whole
|
|
269
297
|
// cluster is worth losing the filters.
|
|
270
298
|
const searchTakeover = searchCollapsed && searchBoxRoom < RESERVED.usableSearch;
|
|
271
299
|
|
|
272
|
-
return {
|
|
273
|
-
inline,
|
|
274
|
-
overflow,
|
|
275
|
-
compactControls,
|
|
276
|
-
counterHidden,
|
|
277
|
-
searchCollapsed,
|
|
278
|
-
clearAllHidden,
|
|
279
|
-
searchTakeover,
|
|
280
|
-
};
|
|
300
|
+
return { compactControls, counterHidden, searchCollapsed, clearAllHidden, searchTakeover };
|
|
281
301
|
}
|
|
282
302
|
|
|
283
303
|
/**
|
|
@@ -307,12 +327,25 @@ export function useFilterOverflow<T extends Record<string, unknown>>(
|
|
|
307
327
|
* that is mid-edit.
|
|
308
328
|
*/
|
|
309
329
|
frozen = false,
|
|
330
|
+
/** Whether "Exportar" is on the bar — see `rightClusterCost`. */
|
|
331
|
+
hasExport = true,
|
|
310
332
|
): OverflowSplit<T> {
|
|
311
|
-
|
|
312
|
-
|
|
333
|
+
// The measurement is `useMeasuredWidth` (`utility/Overflow`): the same
|
|
334
|
+
// ResizeObserver every collapsing cluster in the design system reads, so
|
|
335
|
+
// there is one loop to get right rather than one per bar. `0` until the
|
|
336
|
+
// first measurement lands (and forever under SSR or jsdom), which
|
|
337
|
+
// `computeSplit` reads as "degrade nothing".
|
|
338
|
+
const { ref: barRef, width } = useMeasuredWidth<HTMLDivElement>();
|
|
339
|
+
const signature = JSON.stringify({
|
|
340
|
+
ids: all.map((field) => field.id),
|
|
341
|
+
pills,
|
|
342
|
+
ranges,
|
|
343
|
+
width,
|
|
344
|
+
hasExport,
|
|
345
|
+
});
|
|
313
346
|
const cache = useRef<{ signature: string; split: ReturnType<typeof computeSplit<T>> } | null>(null);
|
|
314
347
|
if (cache.current === null || (!frozen && cache.current.signature !== signature)) {
|
|
315
|
-
cache.current = { signature, split: computeSplit(all, pills, ranges, width) };
|
|
348
|
+
cache.current = { signature, split: computeSplit(all, pills, ranges, width, hasExport) };
|
|
316
349
|
}
|
|
317
350
|
return { ...cache.current.split, barRef };
|
|
318
351
|
}
|
|
@@ -22,6 +22,10 @@ export {
|
|
|
22
22
|
type ListCardCellConfig,
|
|
23
23
|
DEFAULT_CELL_WIDTH,
|
|
24
24
|
} from "./list-card-cells";
|
|
25
|
+
// The list's columns declared at the TABLE, not per card — which is what makes
|
|
26
|
+
// a Lista line up by construction. Without it a consumer can still hand a cell
|
|
27
|
+
// config to each row, and they agree until one row carries a wider value.
|
|
28
|
+
export type { ListGroupConfig } from "./list-card-rails";
|
|
25
29
|
// Published by a drag container so a row can draw the marker; a consumer wiring
|
|
26
30
|
// its own DnD needs the type to build the value.
|
|
27
31
|
export { DropIndicator } from "./data-views-drag";
|
|
@@ -223,6 +223,35 @@ export function cellRailCount(cells: number): number {
|
|
|
223
223
|
return CELL_FIXED_RAILS + cells + 1;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* A {@link ListCardGroup}'s configuration, as a value a caller can pass around.
|
|
228
|
+
*
|
|
229
|
+
* The same fields the group takes as props, named once so a table can accept
|
|
230
|
+
* "the list's columns" as a single prop and hand them straight over. It is what
|
|
231
|
+
* makes a list line up BY CONSTRUCTION rather than by convention: without it
|
|
232
|
+
* every row resolves its own tracks from its own copy of the config, which
|
|
233
|
+
* agrees with its neighbours right until one row carries an unusually wide value.
|
|
234
|
+
*/
|
|
235
|
+
export interface ListGroupConfig<T extends Record<string, unknown>> {
|
|
236
|
+
/**
|
|
237
|
+
* The cells every row renders. Supplying these replaces the named-slot layout
|
|
238
|
+
* (title/subtitle/meta/value/status) for the rows inside.
|
|
239
|
+
*/
|
|
240
|
+
cells?: readonly ListCardCellConfig<T>[];
|
|
241
|
+
/**
|
|
242
|
+
* How many labelled pairs the rows carry, in the named-slot layout. The group
|
|
243
|
+
* renders its children opaquely and cannot count them.
|
|
244
|
+
*/
|
|
245
|
+
metaColumns?: number;
|
|
246
|
+
/** Override individual rails; anything omitted keeps its default. */
|
|
247
|
+
rails?: Partial<ListRails>;
|
|
248
|
+
/**
|
|
249
|
+
* Hold the drag and select gutters open even when nothing uses them. On by
|
|
250
|
+
* default, so a selectable list and a read-only one still line up.
|
|
251
|
+
*/
|
|
252
|
+
reserveGutters?: boolean;
|
|
253
|
+
}
|
|
254
|
+
|
|
226
255
|
/**
|
|
227
256
|
* A list of {@link BaseListCard}s that share one set of columns.
|
|
228
257
|
*
|
|
@@ -3,17 +3,20 @@ import { forwardRef } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { useDateTimeParts } from './DateTimeDisplay.hooks';
|
|
5
5
|
import type { DateTimeDisplayProps } from './DateTimeDisplay.types';
|
|
6
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Get font sizes based on size prop
|
|
9
10
|
*/
|
|
10
|
-
function getFontSizes(size:
|
|
11
|
+
function getFontSizes(size: SizeValue) {
|
|
11
12
|
switch (size) {
|
|
12
|
-
case '
|
|
13
|
+
case 'xs':
|
|
14
|
+
case 'sm':
|
|
13
15
|
return { date: '0.75rem', time: '0.65rem' };
|
|
14
|
-
case '
|
|
16
|
+
case 'lg':
|
|
17
|
+
case 'xl':
|
|
15
18
|
return { date: '1rem', time: '0.875rem' };
|
|
16
|
-
case '
|
|
19
|
+
case 'md':
|
|
17
20
|
default:
|
|
18
21
|
return { date: '0.875rem', time: '0.75rem' };
|
|
19
22
|
}
|
|
@@ -81,7 +84,7 @@ export const DateTimeDisplay = forwardRef<HTMLSpanElement, DateTimeDisplayProps>
|
|
|
81
84
|
dateFormat = 'short',
|
|
82
85
|
timeFormat = '12h',
|
|
83
86
|
showTimezone = false,
|
|
84
|
-
size = '
|
|
87
|
+
size = 'md',
|
|
85
88
|
showTooltip = true,
|
|
86
89
|
tooltipContent,
|
|
87
90
|
sx,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { SxProps, Theme } from '@mui/material';
|
|
2
3
|
|
|
3
4
|
export interface DateTimeDisplayProps {
|
|
@@ -25,7 +26,7 @@ export interface DateTimeDisplayProps {
|
|
|
25
26
|
* Size variant
|
|
26
27
|
* @default 'medium'
|
|
27
28
|
*/
|
|
28
|
-
size?: '
|
|
29
|
+
size?: Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
29
30
|
/**
|
|
30
31
|
* Whether to show a tooltip with full date/time
|
|
31
32
|
* @default true
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { TooltipProps as MuiTooltipProps } from '@mui/material';
|
|
2
3
|
|
|
3
4
|
export interface InteractiveTooltipProps extends Omit<MuiTooltipProps, 'variant' | 'title'> {
|
|
@@ -19,7 +20,7 @@ export interface InteractiveTooltipProps extends Omit<MuiTooltipProps, 'variant'
|
|
|
19
20
|
/**
|
|
20
21
|
* Size of the tooltip
|
|
21
22
|
*/
|
|
22
|
-
size?: 'sm' | 'md' | 'lg'
|
|
23
|
+
size?: Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Maximum width of the tooltip
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
export type LoadingStateVariant = 'spinner' | 'skeleton';
|
|
2
|
-
export type LoadingStateSize = 'sm' | 'md' | 'lg'
|
|
3
|
+
export type LoadingStateSize = Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
3
4
|
|
|
4
5
|
export interface LoadingStateProps {
|
|
5
6
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ColorValue, SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { LinearProgressProps } from '@mui/material';
|
|
2
3
|
|
|
3
4
|
export type ProgressVariant = 'linear' | 'circular' | 'segmented' | 'gradient' | 'glass';
|
|
4
|
-
export type ProgressSize = 'sm' | 'md' | 'lg'
|
|
5
|
+
export type ProgressSize = Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
5
6
|
|
|
6
7
|
export interface ProgressProps extends Omit<LinearProgressProps, 'variant' | 'color'> {
|
|
7
8
|
/**
|
|
@@ -17,7 +18,7 @@ export interface ProgressProps extends Omit<LinearProgressProps, 'variant' | 'co
|
|
|
17
18
|
/**
|
|
18
19
|
* The color of the progress
|
|
19
20
|
*/
|
|
20
|
-
color?:
|
|
21
|
+
color?: Exclude<ColorValue, 'info'>;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Whether the progress should have a glow effect
|
|
@@ -3,6 +3,7 @@ import React from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { SheetContent, SheetFooter, SheetHeader } from './Sheet.parts';
|
|
5
5
|
import type { SheetProps } from './Sheet.types';
|
|
6
|
+
import { muiColor } from '../../../tokens/scales';
|
|
6
7
|
|
|
7
8
|
export interface SheetBodyProps extends Pick<
|
|
8
9
|
SheetProps,
|
|
@@ -50,7 +51,7 @@ const SheetLoading: React.FC<Pick<SheetBodyProps, 'color'>> = ({ color }) => (
|
|
|
50
51
|
minHeight: 200,
|
|
51
52
|
}}
|
|
52
53
|
>
|
|
53
|
-
<CircularProgress color={color} />
|
|
54
|
+
<CircularProgress color={color === undefined ? undefined : muiColor(color, 'inherit')} />
|
|
54
55
|
</Box>
|
|
55
56
|
);
|
|
56
57
|
|
|
@@ -4,6 +4,7 @@ import type React from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { glowAnimation, pulseAnimation, shimmerAnimation } from './Sheet.animations';
|
|
6
6
|
import type { SheetProps } from './Sheet.types';
|
|
7
|
+
import { accentFor } from '../../../tokens/scales';
|
|
7
8
|
|
|
8
9
|
type Position = NonNullable<SheetProps['position']>;
|
|
9
10
|
type Variant = NonNullable<SheetProps['variant']>;
|
|
@@ -140,7 +141,7 @@ const SURFACES: Record<Variant, (input: SurfaceInput) => Record<string, unknown>
|
|
|
140
141
|
135deg,
|
|
141
142
|
${theme.palette.background.paper} 0%,
|
|
142
143
|
${alpha(accent, 0.08)} 50%,
|
|
143
|
-
${alpha(theme
|
|
144
|
+
${alpha(accentFor(theme, color)?.dark || accent, 0.12)} 100%
|
|
144
145
|
)`
|
|
145
146
|
: theme.palette.background.paper,
|
|
146
147
|
position: 'relative' as const,
|
|
@@ -211,7 +212,7 @@ const baseSurface = (input: VariantStyleInput, accent: string) => {
|
|
|
211
212
|
|
|
212
213
|
const variantStyles = (input: VariantStyleInput) => {
|
|
213
214
|
const { theme, variant, color, position, glow, pulse, rounded } = input;
|
|
214
|
-
const accent = theme
|
|
215
|
+
const accent = accentFor(theme, color)?.main || theme.palette.primary.main;
|
|
215
216
|
|
|
216
217
|
return {
|
|
217
218
|
...baseSurface(input, accent),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ColorValue, SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { CSSProperties,ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
export interface SheetProps {
|
|
@@ -8,8 +9,12 @@ export interface SheetProps {
|
|
|
8
9
|
description?: string;
|
|
9
10
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
10
11
|
variant?: 'default' | 'glass' | 'gradient' | 'elevated' | 'minimal' | 'draggable';
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The house scale, plus `full`. `full` is not a size on the scale — it is
|
|
14
|
+
* "the whole viewport", so it extends rather than replaces.
|
|
15
|
+
*/
|
|
16
|
+
size?: SizeValue | 'full';
|
|
17
|
+
color?: Exclude<ColorValue, 'neutral'>;
|
|
13
18
|
glow?: boolean;
|
|
14
19
|
pulse?: boolean;
|
|
15
20
|
glass?: boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
export interface Step {
|
|
@@ -40,7 +41,7 @@ export interface StepperProps {
|
|
|
40
41
|
* takes far less vertical space — useful above the fold on mobile checkouts.
|
|
41
42
|
* @default 'md'
|
|
42
43
|
*/
|
|
43
|
-
size?: 'sm' | 'md'
|
|
44
|
+
size?: Extract<SizeValue, 'sm' | 'md'>;
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
47
|
* Navigation variant controlling step access
|
|
@@ -46,7 +46,7 @@ const getStripeColorFromTheme = (theme: { palette: { primary: { main: string };
|
|
|
46
46
|
info: theme.palette.info.main,
|
|
47
47
|
success: theme.palette.success.main,
|
|
48
48
|
warning: theme.palette.warning.main,
|
|
49
|
-
|
|
49
|
+
danger: theme.palette.error.main,
|
|
50
50
|
neutral: theme.palette.action.hover };
|
|
51
51
|
return colorMap[stripeColor];
|
|
52
52
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ColorValue } from '../../../tokens/scales';
|
|
1
2
|
import type { TableProps as MuiTableProps } from '@mui/material';
|
|
2
3
|
import type React from 'react';
|
|
3
4
|
|
|
@@ -57,7 +58,8 @@ export interface SelectionConfig {
|
|
|
57
58
|
rowKeyExtractor?: (rowData: Record<string, unknown>, index: number) => string | number;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
/** The full house vocabulary — a stripe can take any accent, or none. */
|
|
62
|
+
export type TableStripeColor = ColorValue;
|
|
61
63
|
|
|
62
64
|
export interface TableProps extends Omit<MuiTableProps, 'variant'> {
|
|
63
65
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { TooltipProps as MuiTooltipProps } from '@mui/material';
|
|
2
3
|
|
|
3
4
|
export interface TooltipProps extends Omit<MuiTooltipProps, 'variant'> {
|
|
@@ -19,7 +20,7 @@ export interface TooltipProps extends Omit<MuiTooltipProps, 'variant'> {
|
|
|
19
20
|
/**
|
|
20
21
|
* Size of the tooltip
|
|
21
22
|
*/
|
|
22
|
-
size?: 'sm' | 'md' | 'lg'
|
|
23
|
+
size?: Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Maximum width of the tooltip
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { DialogProps as MuiDialogProps } from '@mui/material';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
3
4
|
|
|
4
5
|
export type DialogVariant = 'default' | 'glass' | 'fullscreen' | 'drawer';
|
|
5
|
-
export type DialogSize =
|
|
6
|
+
export type DialogSize = SizeValue;
|
|
6
7
|
|
|
7
8
|
export interface DialogProps extends Omit<MuiDialogProps, 'variant' | 'title'> {
|
|
8
9
|
children: ReactNode;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { ModalProps as MuiModalProps } from '@mui/material';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
3
4
|
|
|
4
5
|
export type ModalVariant = 'center' | 'top' | 'bottom' | 'glass';
|
|
5
|
-
export type ModalSize =
|
|
6
|
+
export type ModalSize = SizeValue;
|
|
6
7
|
|
|
7
8
|
export interface ModalProps extends Omit<MuiModalProps, 'children'> {
|
|
8
9
|
children: ReactNode;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ColorValue, SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { ButtonProps as MuiButtonProps } from '@mui/material';
|
|
2
3
|
import type * as React from 'react';
|
|
3
4
|
|
|
@@ -10,12 +11,12 @@ export interface ButtonProps extends Omit<MuiButtonProps, 'variant' | 'color' |
|
|
|
10
11
|
/**
|
|
11
12
|
* The color of the button
|
|
12
13
|
*/
|
|
13
|
-
color?:
|
|
14
|
+
color?: ColorValue;
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* The size of the button
|
|
17
18
|
*/
|
|
18
|
-
size?:
|
|
19
|
+
size?: SizeValue;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Whether the button is in loading state
|
|
@@ -2,6 +2,7 @@ import { alpha } from '@mui/material';
|
|
|
2
2
|
import type { CSSObject, Theme } from '@mui/material';
|
|
3
3
|
|
|
4
4
|
import type { CommandProps } from './Command.types';
|
|
5
|
+
import { accentFor } from '../../../tokens/scales';
|
|
5
6
|
|
|
6
7
|
type CommandVariant = NonNullable<CommandProps['variant']>;
|
|
7
8
|
type CommandColor = NonNullable<CommandProps['color']>;
|
|
@@ -24,14 +25,14 @@ const emphasisStyles = (
|
|
|
24
25
|
pulse: boolean,
|
|
25
26
|
): CSSObject => ({
|
|
26
27
|
...(glow && {
|
|
27
|
-
boxShadow: `0 0 20px ${alpha(theme
|
|
28
|
+
boxShadow: `0 0 20px ${alpha(accentFor(theme, color).main, 0.4)}`,
|
|
28
29
|
}),
|
|
29
30
|
...(pulse && {
|
|
30
31
|
animation: 'pulse 2s infinite',
|
|
31
32
|
'@keyframes pulse': {
|
|
32
|
-
'0%': { boxShadow: `0 0 0 0 ${alpha(theme
|
|
33
|
-
'70%': { boxShadow: `0 0 0 10px ${alpha(theme
|
|
34
|
-
'100%': { boxShadow: `0 0 0 0 ${alpha(theme
|
|
33
|
+
'0%': { boxShadow: `0 0 0 0 ${alpha(accentFor(theme, color).main, 0.4)}` },
|
|
34
|
+
'70%': { boxShadow: `0 0 0 10px ${alpha(accentFor(theme, color).main, 0)}` },
|
|
35
|
+
'100%': { boxShadow: `0 0 0 0 ${alpha(accentFor(theme, color).main, 0)}` },
|
|
35
36
|
},
|
|
36
37
|
}),
|
|
37
38
|
});
|
|
@@ -46,12 +47,12 @@ const surfaceStyles = (
|
|
|
46
47
|
return {
|
|
47
48
|
backgroundColor: alpha(theme.palette.background.paper, 0.1),
|
|
48
49
|
backdropFilter: 'blur(20px)',
|
|
49
|
-
border: `1px solid ${alpha(theme
|
|
50
|
+
border: `1px solid ${alpha(accentFor(theme, color).main, 0.2)}`,
|
|
50
51
|
};
|
|
51
52
|
case 'gradient':
|
|
52
53
|
return {
|
|
53
|
-
background: `linear-gradient(135deg, ${theme
|
|
54
|
-
color: theme
|
|
54
|
+
background: `linear-gradient(135deg, ${accentFor(theme, color).main}, ${accentFor(theme, color).dark})`,
|
|
55
|
+
color: accentFor(theme, color).contrastText,
|
|
55
56
|
};
|
|
56
57
|
case 'elevated':
|
|
57
58
|
return { boxShadow: theme.shadows[8] };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ColorValue, SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { CSSProperties,ReactNode } from 'react';
|
|
2
3
|
|
|
3
4
|
export interface CommandItem {
|
|
@@ -21,8 +22,8 @@ export interface CommandProps {
|
|
|
21
22
|
onValueChange?: (value: string) => void;
|
|
22
23
|
onSelect?: (item: CommandItem) => void;
|
|
23
24
|
variant?: 'default' | 'glass' | 'gradient' | 'minimal' | 'elevated';
|
|
24
|
-
size?:
|
|
25
|
-
color?:
|
|
25
|
+
size?: SizeValue;
|
|
26
|
+
color?: Exclude<ColorValue, 'neutral'>;
|
|
26
27
|
glow?: boolean;
|
|
27
28
|
pulse?: boolean;
|
|
28
29
|
loading?: boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
/** One selectable option: a stable `value` and its human `label`. */
|
|
2
3
|
export interface CreatableSelectOption {
|
|
3
4
|
value: string;
|
|
@@ -44,7 +45,7 @@ export interface CreatableSelectProps {
|
|
|
44
45
|
* Control height, matching {@link Input}'s scale so the two line up when they
|
|
45
46
|
* share a grid row (default: `md`).
|
|
46
47
|
*/
|
|
47
|
-
size?: 'sm' | 'md'
|
|
48
|
+
size?: Extract<SizeValue, 'sm' | 'md'>;
|
|
48
49
|
/** Base test id; the input is `<dataTestId>-input`. */
|
|
49
50
|
dataTestId?: string;
|
|
50
51
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
1
2
|
import type { InputLabelProps as MuiInputLabelProps } from '@mui/material';
|
|
2
3
|
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
3
4
|
import type React from 'react';
|
|
4
5
|
|
|
5
6
|
export type InputVariant = 'outlined' | 'filled' | 'glass' | 'underline' | 'gradient';
|
|
6
|
-
export type InputSize = 'sm' | 'md' | 'lg'
|
|
7
|
+
export type InputSize = Extract<SizeValue, 'sm' | 'md' | 'lg'>;
|
|
7
8
|
|
|
8
9
|
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'color'> {
|
|
9
10
|
variant?: InputVariant;
|