aplance-ui 1.5.1 → 1.6.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/README.md +1 -1
- package/dist/api.d.ts +2 -1
- package/dist/api.js +103 -68
- package/dist/api.js.map +1 -1
- package/dist/frameworks/vite-react/vite.config.ts.template +1 -1
- package/dist/index.js +103 -68
- package/dist/index.js.map +1 -1
- package/package.json +12 -6
- package/registry/components/primitives/modal/modal.module.css +9 -3
- package/registry/components/primitives/popover/popover-context.ts +2 -0
- package/registry/components/primitives/popover/popover.tsx +47 -2
- package/registry/components/ui/chart/chart.tsx +51 -30
- package/registry/components/ui/chart/types.ts +5 -1
- package/registry/components/ui/data-table/checkbox-column.tsx +12 -8
- package/registry/components/ui/data-table/data-table.module.css +41 -19
- package/registry/components/ui/data-table/data-table.tsx +1285 -1238
- package/registry/components/ui/data-table/filter-bar.tsx +56 -13
- package/registry/components/ui/data-table/filter-modal.tsx +16 -82
- package/registry/components/ui/data-table/filter-sheet.tsx +97 -0
- package/registry/components/ui/data-table/filters.tsx +21 -21
- package/registry/components/ui/data-table/pagination-bar.tsx +4 -6
- package/registry/components/ui/data-table/types.ts +149 -80
- package/registry/components/ui/data-table/useDataTable.tsx +35 -16
- package/registry/components/ui/data-table/utils.ts +691 -471
- package/registry/components/ui/input-fields/date-picker/calendar.tsx +7 -15
- package/registry/components/ui/input-fields/date-picker/date-picker.tsx +1 -1
- package/registry/components/ui/input-fields/date-picker/types.ts +3 -3
- package/registry/components/ui/input-fields/emoji-picker/emoji-palette.tsx +1 -1
- package/registry/registry.json +17 -12
- package/registry/styles/media.css +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aplance-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "The aplance-ui CLI - scaffold the design system and copy components into your project as source.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -50,14 +50,20 @@
|
|
|
50
50
|
"lint": "eslint src",
|
|
51
51
|
"prepublishOnly": "npm run build && npm run typecheck && npm run lint"
|
|
52
52
|
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=20.12.0"
|
|
55
|
+
},
|
|
53
56
|
"dependencies": {
|
|
54
|
-
"@clack/prompts": "^
|
|
55
|
-
"commander": "^
|
|
57
|
+
"@clack/prompts": "^1.9.0",
|
|
58
|
+
"commander": "^14.0.3",
|
|
56
59
|
"picocolors": "^1.1.1"
|
|
57
60
|
},
|
|
58
61
|
"devDependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
+
"@eslint/js": "^10.0.1",
|
|
63
|
+
"@types/node": "^24.12.2",
|
|
64
|
+
"eslint": "^10.15.0",
|
|
65
|
+
"tsup": "^9.0.0",
|
|
66
|
+
"typescript": "~5.9.3",
|
|
67
|
+
"typescript-eslint": "^8.72.2"
|
|
62
68
|
}
|
|
63
69
|
}
|
|
@@ -120,9 +120,13 @@
|
|
|
120
120
|
flex-direction: column;
|
|
121
121
|
max-height: calc(100dvh - 2 * var(--spacing-6));
|
|
122
122
|
|
|
123
|
-
/*
|
|
123
|
+
/* A sheet stops short of the far edge, so the page behind it still shows
|
|
124
|
+
and a tap there closes it: --modal-sheet-max-height, 80dvh by default.
|
|
125
|
+
Drawers and full placements fill the viewport. */
|
|
124
126
|
&[data-placement='bottom'],
|
|
125
|
-
&[data-placement='top']
|
|
127
|
+
&[data-placement='top'] {
|
|
128
|
+
max-height: var(--modal-sheet-max-height, 80dvh);
|
|
129
|
+
}
|
|
126
130
|
&[data-placement='left'],
|
|
127
131
|
&[data-placement='right'],
|
|
128
132
|
&[data-placement='full'] {
|
|
@@ -390,7 +394,9 @@
|
|
|
390
394
|
max-height: calc(100dvh - 2 * var(--spacing-6));
|
|
391
395
|
}
|
|
392
396
|
.scroll_inside[data-mobile-placement='bottom'],
|
|
393
|
-
.scroll_inside[data-mobile-placement='top']
|
|
397
|
+
.scroll_inside[data-mobile-placement='top'] {
|
|
398
|
+
max-height: var(--modal-sheet-max-height, 80dvh);
|
|
399
|
+
}
|
|
394
400
|
.scroll_inside[data-mobile-placement='left'],
|
|
395
401
|
.scroll_inside[data-mobile-placement='right'],
|
|
396
402
|
.scroll_inside[data-mobile-placement='full'] {
|
|
@@ -17,6 +17,8 @@ export type PopoverContextType = {
|
|
|
17
17
|
availableWidth?: number;
|
|
18
18
|
availableHeight?: number;
|
|
19
19
|
autoPosition: boolean;
|
|
20
|
+
/** Above the trigger's layer (e.g. a modal), or null for the popover token. */
|
|
21
|
+
menuZIndex: number | null;
|
|
20
22
|
hoverTimeoutRef: RefObject<ReturnType<typeof setTimeout> | null>;
|
|
21
23
|
renderOnPortal: boolean;
|
|
22
24
|
mounted: boolean;
|
|
@@ -41,6 +41,37 @@ const useIsomorphicLayoutEffect =
|
|
|
41
41
|
const isVerticalPosition = (position: Position): boolean =>
|
|
42
42
|
position === "top" || position === "bottom";
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* The z-index a portaled menu needs to show above the layer its trigger lives
|
|
46
|
+
* in: a modal's overlay, a sheet, an app's own fixed panel. The menu portals
|
|
47
|
+
* to <body>, where it competes with the trigger's outermost positioned
|
|
48
|
+
* ancestor that has a z-index. When that layer sits at or above
|
|
49
|
+
* `--z-index-popover`, the menu goes one level above it; null keeps the token
|
|
50
|
+
* (a trigger on the page itself, or under a lower layer like a sticky header).
|
|
51
|
+
*/
|
|
52
|
+
const zIndexAboveLayer = (trigger: HTMLElement): number | null => {
|
|
53
|
+
let layer: number | null = null;
|
|
54
|
+
for (
|
|
55
|
+
let node: HTMLElement | null = trigger;
|
|
56
|
+
node && node !== document.body;
|
|
57
|
+
node = node.parentElement
|
|
58
|
+
) {
|
|
59
|
+
const style = getComputedStyle(node);
|
|
60
|
+
const zIndex = Number.parseInt(style.zIndex, 10);
|
|
61
|
+
if (style.position !== "static" && Number.isFinite(zIndex)) layer = zIndex;
|
|
62
|
+
}
|
|
63
|
+
if (layer === null) return null;
|
|
64
|
+
const popoverLevel = Number.parseInt(
|
|
65
|
+
getComputedStyle(document.documentElement).getPropertyValue(
|
|
66
|
+
"--z-index-popover",
|
|
67
|
+
),
|
|
68
|
+
10,
|
|
69
|
+
);
|
|
70
|
+
return Number.isFinite(popoverLevel) && layer < popoverLevel
|
|
71
|
+
? null
|
|
72
|
+
: layer + 1;
|
|
73
|
+
};
|
|
74
|
+
|
|
44
75
|
const clamp = (value: number, min: number, max: number): number =>
|
|
45
76
|
Math.min(Math.max(value, min), Math.max(min, max));
|
|
46
77
|
|
|
@@ -120,6 +151,7 @@ export const Popover: FC<PopoverProps> = ({
|
|
|
120
151
|
width?: number;
|
|
121
152
|
height?: number;
|
|
122
153
|
}>({});
|
|
154
|
+
const [menuZIndex, setMenuZIndex] = useState<number | null>(null);
|
|
123
155
|
|
|
124
156
|
// Calculate Menu Position
|
|
125
157
|
const calculatePosition = useCallback(() => {
|
|
@@ -224,8 +256,19 @@ export const Popover: FC<PopoverProps> = ({
|
|
|
224
256
|
width: availableWidth,
|
|
225
257
|
height: availableHeight,
|
|
226
258
|
});
|
|
259
|
+
// An in-place menu already sits inside its trigger's layer.
|
|
260
|
+
setMenuZIndex(
|
|
261
|
+
renderOnPortal ? zIndexAboveLayer(popoverRef.current) : null,
|
|
262
|
+
);
|
|
227
263
|
setIsMeasured(true);
|
|
228
|
-
}, [
|
|
264
|
+
}, [
|
|
265
|
+
position,
|
|
266
|
+
align,
|
|
267
|
+
offset,
|
|
268
|
+
shouldAutoPosition,
|
|
269
|
+
collisionPadding,
|
|
270
|
+
renderOnPortal,
|
|
271
|
+
]);
|
|
229
272
|
|
|
230
273
|
// Open / Close Handling
|
|
231
274
|
// Visibility (opacity / pointer-events) is derived from `isOpen && isMeasured`
|
|
@@ -352,6 +395,7 @@ export const Popover: FC<PopoverProps> = ({
|
|
|
352
395
|
availableWidth: availableSize.width,
|
|
353
396
|
availableHeight: availableSize.height,
|
|
354
397
|
autoPosition: shouldAutoPosition,
|
|
398
|
+
menuZIndex,
|
|
355
399
|
mounted,
|
|
356
400
|
}}
|
|
357
401
|
>
|
|
@@ -484,6 +528,7 @@ export const PopoverMenu: FC<PopoverMenuProps> = ({
|
|
|
484
528
|
availableWidth,
|
|
485
529
|
availableHeight,
|
|
486
530
|
autoPosition,
|
|
531
|
+
menuZIndex,
|
|
487
532
|
} = usePopover();
|
|
488
533
|
|
|
489
534
|
const handleMouseEnter = () => {
|
|
@@ -544,7 +589,7 @@ export const PopoverMenu: FC<PopoverMenuProps> = ({
|
|
|
544
589
|
position: "fixed",
|
|
545
590
|
top: coords.top,
|
|
546
591
|
left: coords.left,
|
|
547
|
-
zIndex: "var(--z-index-popover)",
|
|
592
|
+
zIndex: menuZIndex ?? "var(--z-index-popover)",
|
|
548
593
|
...(constrainedMaxWidth !== undefined && { maxWidth: constrainedMaxWidth }),
|
|
549
594
|
...(autoPosition &&
|
|
550
595
|
availableHeight !== undefined && { maxHeight: availableHeight }),
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
Bar,
|
|
7
7
|
BarChart as RechartsBarChart,
|
|
8
8
|
CartesianGrid,
|
|
9
|
-
Cell,
|
|
10
9
|
ComposedChart as RechartsComposedChart,
|
|
11
10
|
Line,
|
|
12
11
|
LineChart as RechartsLineChart,
|
|
@@ -15,14 +14,19 @@ import {
|
|
|
15
14
|
PolarAngleAxis,
|
|
16
15
|
RadialBar,
|
|
17
16
|
RadialBarChart as RechartsRadialBarChart,
|
|
17
|
+
Rectangle,
|
|
18
18
|
ReferenceLine,
|
|
19
19
|
ResponsiveContainer,
|
|
20
20
|
Scatter as RechartsScatter,
|
|
21
21
|
ScatterChart as RechartsScatterChart,
|
|
22
|
+
Sector,
|
|
22
23
|
Tooltip,
|
|
23
24
|
XAxis,
|
|
24
25
|
YAxis,
|
|
25
26
|
ZAxis,
|
|
27
|
+
type BarShapeProps,
|
|
28
|
+
type PieSectorShapeProps,
|
|
29
|
+
type RadialBarSectorProps,
|
|
26
30
|
} from "recharts";
|
|
27
31
|
import type { ReactNode } from "react";
|
|
28
32
|
import s from "./chart.module.css";
|
|
@@ -428,6 +432,27 @@ function ChartTooltip({
|
|
|
428
432
|
);
|
|
429
433
|
}
|
|
430
434
|
|
|
435
|
+
// ─── Per-item colours ─────────────────────────────────────────────────────────
|
|
436
|
+
// Recharts 3 deprecates <Cell> (removed in 4.0): each bar or sector takes its
|
|
437
|
+
// own colour through `shape`, from the `__color` stamped on its datum here.
|
|
438
|
+
|
|
439
|
+
const itemColor = (payload: unknown, fallback: string | undefined) => {
|
|
440
|
+
const color = (payload as { __color?: unknown } | null | undefined)?.__color;
|
|
441
|
+
return typeof color === "string" ? color : fallback;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const renderColoredBar = (props: BarShapeProps) => (
|
|
445
|
+
<Rectangle {...props} fill={itemColor(props.payload, props.fill)} />
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const renderColoredSector = (props: PieSectorShapeProps) => (
|
|
449
|
+
<Sector {...props} fill={itemColor(props.payload, props.fill)} />
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
const renderColoredRadialBar = (props: RadialBarSectorProps) => (
|
|
453
|
+
<Sector {...props} fill={itemColor(props.payload, props.fill)} />
|
|
454
|
+
);
|
|
455
|
+
|
|
431
456
|
// ─── Shared legend ────────────────────────────────────────────────────────────
|
|
432
457
|
|
|
433
458
|
function ChartLegend({ items }: { items: LegendItem[] }) {
|
|
@@ -444,8 +469,8 @@ function ChartLegend({ items }: { items: LegendItem[] }) {
|
|
|
444
469
|
}
|
|
445
470
|
|
|
446
471
|
// ─── Cartesian frame ──────────────────────────────────────────────────────────
|
|
447
|
-
//
|
|
448
|
-
//
|
|
472
|
+
// Grid, axes, tooltip and reference lines shared by every cartesian chart,
|
|
473
|
+
// returned as a keyed array of chart children.
|
|
449
474
|
|
|
450
475
|
const renderCartesianFrame = ({
|
|
451
476
|
categoryKey,
|
|
@@ -465,6 +490,18 @@ const renderCartesianFrame = ({
|
|
|
465
490
|
orientation?: "vertical" | "horizontal";
|
|
466
491
|
}): ReactNode[] => {
|
|
467
492
|
const isHorizontal = orientation === "horizontal";
|
|
493
|
+
const showLeftYAxis = options.showYAxis && axisConfig.showLeftAxis;
|
|
494
|
+
const showRightYAxis =
|
|
495
|
+
options.showYAxis && axisConfig.showRightAxis && !isHorizontal;
|
|
496
|
+
// The rendered Y axis that the grid and reference lines measure against.
|
|
497
|
+
// Recharts 3 drops a grid or reference line (a vertical one included) whose
|
|
498
|
+
// yAxisId names no rendered axis, and the axes here use named ids. With the
|
|
499
|
+
// Y axis hidden, everything shares Recharts' default axis instead.
|
|
500
|
+
const frameYAxisId = showLeftYAxis
|
|
501
|
+
? "left"
|
|
502
|
+
: showRightYAxis
|
|
503
|
+
? "right"
|
|
504
|
+
: undefined;
|
|
468
505
|
|
|
469
506
|
const items: ReactNode[] = [
|
|
470
507
|
options.showGrid ? (
|
|
@@ -474,6 +511,7 @@ const renderCartesianFrame = ({
|
|
|
474
511
|
vertical={isHorizontal}
|
|
475
512
|
horizontal={!isHorizontal}
|
|
476
513
|
stroke={palette.grid}
|
|
514
|
+
yAxisId={frameYAxisId}
|
|
477
515
|
/>
|
|
478
516
|
) : null,
|
|
479
517
|
|
|
@@ -505,7 +543,7 @@ const renderCartesianFrame = ({
|
|
|
505
543
|
) : null,
|
|
506
544
|
|
|
507
545
|
// Left Y axis - value axis (vertical) or category axis (horizontal)
|
|
508
|
-
|
|
546
|
+
showLeftYAxis ? (
|
|
509
547
|
isHorizontal ? (
|
|
510
548
|
<YAxis
|
|
511
549
|
key="y-axis-left"
|
|
@@ -535,7 +573,7 @@ const renderCartesianFrame = ({
|
|
|
535
573
|
) : null,
|
|
536
574
|
|
|
537
575
|
// Right Y axis - only for vertical orientation with dual-axis series
|
|
538
|
-
|
|
576
|
+
showRightYAxis ? (
|
|
539
577
|
<YAxis
|
|
540
578
|
key="y-axis-right"
|
|
541
579
|
yAxisId="right"
|
|
@@ -569,12 +607,8 @@ const renderCartesianFrame = ({
|
|
|
569
607
|
items.push(
|
|
570
608
|
<ReferenceLine
|
|
571
609
|
key={`rl-${i}`}
|
|
572
|
-
{...(rl.axis === "x"
|
|
573
|
-
|
|
574
|
-
: {
|
|
575
|
-
y: rl.value,
|
|
576
|
-
yAxisId: options.showYAxis ? (rl.yAxisId ?? "left") : undefined,
|
|
577
|
-
})}
|
|
610
|
+
{...(rl.axis === "x" ? { x: rl.value } : { y: rl.value })}
|
|
611
|
+
yAxisId={options.showYAxis ? (rl.yAxisId ?? frameYAxisId) : undefined}
|
|
578
612
|
stroke={resolveColorToken(rl.color, palette.muted)}
|
|
579
613
|
strokeDasharray={rl.strokeDasharray ?? "4 4"}
|
|
580
614
|
strokeWidth={1}
|
|
@@ -689,15 +723,8 @@ function BarChartRenderer({
|
|
|
689
723
|
fill={item.color}
|
|
690
724
|
stackId={item.stackId}
|
|
691
725
|
yAxisId={options.showYAxis ? item.yAxisId : undefined}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
chartData.map((entry, index) => {
|
|
695
|
-
const key = toLookupKey(entry[categoryKey]);
|
|
696
|
-
const fill =
|
|
697
|
-
typeof entry.__color === "string" ? entry.__color : item.color;
|
|
698
|
-
return <Cell key={`${item.key}-${key}-${index}`} fill={fill} />;
|
|
699
|
-
})}
|
|
700
|
-
</Bar>
|
|
726
|
+
shape={usesCategoryColorMap ? renderColoredBar : undefined}
|
|
727
|
+
/>
|
|
701
728
|
))}
|
|
702
729
|
</RechartsBarChart>
|
|
703
730
|
</ResponsiveContainer>
|
|
@@ -1093,11 +1120,8 @@ function DonutChartRenderer({
|
|
|
1093
1120
|
nameKey={nameKey}
|
|
1094
1121
|
stroke="var(--color-background)"
|
|
1095
1122
|
strokeWidth={2}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
<Cell key={`${item.key}-${index}`} fill={item.color} />
|
|
1099
|
-
))}
|
|
1100
|
-
</Pie>
|
|
1123
|
+
shape={renderColoredSector}
|
|
1124
|
+
/>
|
|
1101
1125
|
<Tooltip
|
|
1102
1126
|
content={
|
|
1103
1127
|
<ChartTooltip
|
|
@@ -1183,11 +1207,8 @@ function RadialBarChartRenderer({
|
|
|
1183
1207
|
dataKey="__value"
|
|
1184
1208
|
cornerRadius={6}
|
|
1185
1209
|
background={{ fill: palette.grid }}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
<Cell key={index} fill={item.__color} />
|
|
1189
|
-
))}
|
|
1190
|
-
</RadialBar>
|
|
1210
|
+
shape={renderColoredRadialBar}
|
|
1211
|
+
/>
|
|
1191
1212
|
<Tooltip
|
|
1192
1213
|
content={
|
|
1193
1214
|
<ChartTooltip
|
|
@@ -23,7 +23,11 @@ export type ReferenceLineConfig = {
|
|
|
23
23
|
/** CSS token (e.g. "--color-chart-secondary") or raw CSS color. */
|
|
24
24
|
color?: string;
|
|
25
25
|
strokeDasharray?: string;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Which Y axis the line measures against: a "y" line sits on its scale, an
|
|
28
|
+
* "x" line spans its height. Default: the left axis, or the right one when
|
|
29
|
+
* only right-axis series are shown.
|
|
30
|
+
*/
|
|
27
31
|
yAxisId?: "left" | "right";
|
|
28
32
|
};
|
|
29
33
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { ColumnDef } from "@tanstack/react-table";
|
|
2
1
|
import { Input } from "../input-fields/input/input";
|
|
2
|
+
import type { DataTableColumnDef } from "./types";
|
|
3
|
+
import { SELECT_COLUMN_ID } from "./utils";
|
|
3
4
|
import s from "./data-table.module.css";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Pre-built select-all / per-row checkbox column using `<Input type="checkbox">`.
|
|
7
|
-
* Handles indeterminate state on the header checkbox.
|
|
8
|
+
* Handles indeterminate state on the header checkbox. Shift-click a row's
|
|
9
|
+
* checkbox to select (or clear) every row between it and the last one clicked.
|
|
8
10
|
*
|
|
9
11
|
* @example
|
|
10
12
|
* const cols = useMemo(
|
|
@@ -14,9 +16,9 @@ import s from "./data-table.module.css";
|
|
|
14
16
|
*/
|
|
15
17
|
export function checkboxColumn<TData extends { id: string }>(
|
|
16
18
|
editingRowId?: string | null,
|
|
17
|
-
):
|
|
19
|
+
): DataTableColumnDef<TData, unknown> {
|
|
18
20
|
return {
|
|
19
|
-
id:
|
|
21
|
+
id: SELECT_COLUMN_ID,
|
|
20
22
|
// Compact fixed width keeps checkbox cells visually balanced while
|
|
21
23
|
// reserving enough room for focus ring rendering.
|
|
22
24
|
size: 42,
|
|
@@ -30,19 +32,21 @@ export function checkboxColumn<TData extends { id: string }>(
|
|
|
30
32
|
align: "center",
|
|
31
33
|
truncate: false,
|
|
32
34
|
},
|
|
35
|
+
// The header box selects the rows on screen: the current page.
|
|
33
36
|
header: ({ table }) => (
|
|
34
37
|
<Input
|
|
35
38
|
type="checkbox"
|
|
36
|
-
checked={table.
|
|
39
|
+
checked={table.getIsAllPageRowsSelected()}
|
|
37
40
|
className={s.selection_checkbox}
|
|
38
41
|
disabled={editingRowId != null}
|
|
39
42
|
ref={(el: HTMLInputElement | null) => {
|
|
40
43
|
if (el)
|
|
41
44
|
el.indeterminate =
|
|
42
|
-
table.
|
|
45
|
+
table.getIsSomePageRowsSelected() &&
|
|
46
|
+
!table.getIsAllPageRowsSelected();
|
|
43
47
|
}}
|
|
44
|
-
onChange={table.
|
|
45
|
-
aria-label="Select all rows"
|
|
48
|
+
onChange={table.getToggleAllPageRowsSelectedHandler()}
|
|
49
|
+
aria-label="Select all rows on this page"
|
|
46
50
|
/>
|
|
47
51
|
),
|
|
48
52
|
cell: ({ row }) => (
|
|
@@ -277,18 +277,36 @@
|
|
|
277
277
|
overflow: visible;
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
|
+
|
|
281
|
+
/* Footer row (columns with a `footer`, e.g. totals): sticks to the bottom
|
|
282
|
+
of the scrolling body so it stays in view. Its top line is an inset
|
|
283
|
+
shadow, so it never doubles the last row's line. */
|
|
284
|
+
tfoot td {
|
|
285
|
+
position: sticky;
|
|
286
|
+
bottom: 0;
|
|
287
|
+
z-index: 1;
|
|
288
|
+
padding: var(--spacing-5) var(--spacing-6);
|
|
289
|
+
font-size: var(--font-size-sm);
|
|
290
|
+
font-weight: var(--font-weight-semibold);
|
|
291
|
+
color: var(--color-foreground);
|
|
292
|
+
vertical-align: middle;
|
|
293
|
+
box-shadow: inset 0 var(--border-1) 0 var(--color-border);
|
|
294
|
+
/* explicit background so the rows scrolling under it stay hidden */
|
|
295
|
+
background: var(--color-muted);
|
|
296
|
+
}
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
/* ─────────────────────────────────────────────
|
|
283
300
|
Vertical column borders (showVerticalBorders prop)
|
|
284
|
-
Drawn on the
|
|
301
|
+
Drawn on the START side of every cell except the first, so we never get a
|
|
285
302
|
double border at the table's outer edge.
|
|
286
303
|
───────────────────────────────────────────── */
|
|
287
304
|
.table_vertical_borders {
|
|
288
|
-
/*
|
|
289
|
-
thead th:not(.col_pinned_last.
|
|
290
|
-
tbody td:not(.col_pinned_last.
|
|
291
|
-
|
|
305
|
+
/* Skip the cell after the start group's divider column, which already draws that line. */
|
|
306
|
+
thead th:not(.col_pinned_last.col_pinned_start) + th,
|
|
307
|
+
tbody td:not(.col_pinned_last.col_pinned_start) + td,
|
|
308
|
+
tfoot td:not(.col_pinned_last.col_pinned_start) + td {
|
|
309
|
+
border-inline-start: var(--border-1) solid var(--color-border);
|
|
292
310
|
}
|
|
293
311
|
}
|
|
294
312
|
|
|
@@ -472,13 +490,14 @@
|
|
|
472
490
|
|
|
473
491
|
/* ─────────────────────────────────────────────
|
|
474
492
|
Column pinning - sticky offsets are written inline by JS
|
|
475
|
-
The hairline divider lives only on the
|
|
476
|
-
(`col_pinned_last
|
|
493
|
+
The hairline divider lives only on the pinned column that meets the
|
|
494
|
+
scrolling center (`col_pinned_last`: the last one counted inward from its
|
|
495
|
+
edge), so adjacent pinned columns don't get double dividers.
|
|
477
496
|
───────────────────────────────────────────── */
|
|
478
|
-
.table th.
|
|
479
|
-
.table th.
|
|
480
|
-
.table td.
|
|
481
|
-
.table td.
|
|
497
|
+
.table th.col_pinned_start,
|
|
498
|
+
.table th.col_pinned_end,
|
|
499
|
+
.table td.col_pinned_start,
|
|
500
|
+
.table td.col_pinned_end {
|
|
482
501
|
position: sticky;
|
|
483
502
|
z-index: 1;
|
|
484
503
|
|
|
@@ -486,19 +505,22 @@
|
|
|
486
505
|
background-color: var(--color-muted);
|
|
487
506
|
}
|
|
488
507
|
|
|
489
|
-
|
|
490
|
-
.table thead th.
|
|
508
|
+
/* Pinned header and footer cells stick on both axes: above everything else. */
|
|
509
|
+
.table thead th.col_pinned_start,
|
|
510
|
+
.table thead th.col_pinned_end,
|
|
511
|
+
.table tfoot td.col_pinned_start,
|
|
512
|
+
.table tfoot td.col_pinned_end {
|
|
491
513
|
z-index: 2;
|
|
492
514
|
}
|
|
493
515
|
|
|
494
|
-
/*
|
|
495
|
-
.col_pinned_last.
|
|
496
|
-
border-
|
|
516
|
+
/* Start group: the divider on its inner (end) side. */
|
|
517
|
+
.col_pinned_last.col_pinned_start {
|
|
518
|
+
border-inline-end: var(--border-1) solid var(--color-border);
|
|
497
519
|
}
|
|
498
520
|
|
|
499
|
-
/*
|
|
500
|
-
.col_pinned_last.
|
|
501
|
-
border-
|
|
521
|
+
/* End group: the divider on its inner (start) side. */
|
|
522
|
+
.col_pinned_last.col_pinned_end {
|
|
523
|
+
border-inline-start: var(--border-1) solid var(--color-border);
|
|
502
524
|
}
|
|
503
525
|
|
|
504
526
|
/* Keep interactive cell popovers above sticky neighbors when they are rendered
|