@siyahi/ui 0.1.9-next.17 → 0.1.9-next.18
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 +77 -207
- package/agents/examples/accordion.tsx +39 -0
- package/agents/examples/alert.tsx +25 -0
- package/agents/examples/app-sidebar.tsx +38 -0
- package/agents/examples/avatar.tsx +14 -0
- package/agents/examples/badge.tsx +18 -0
- package/agents/examples/bar-list-labels.tsx +22 -0
- package/agents/examples/bar-list-sides.tsx +30 -0
- package/agents/examples/bar-list.tsx +24 -0
- package/agents/examples/breadcrumb.tsx +28 -0
- package/agents/examples/button.tsx +21 -0
- package/agents/examples/calendar.tsx +15 -0
- package/agents/examples/card.tsx +34 -0
- package/agents/examples/chart-area.tsx +38 -0
- package/agents/examples/chart-bar-flows.tsx +31 -0
- package/agents/examples/chart-bar-hourly.tsx +28 -0
- package/agents/examples/chart-bar-losses.tsx +29 -0
- package/agents/examples/chart-bar-sectors.tsx +70 -0
- package/agents/examples/chart-bar-years.tsx +40 -0
- package/agents/examples/chart-bar.tsx +56 -0
- package/agents/examples/chart-category-ticks.tsx +50 -0
- package/agents/examples/chart-line-events.tsx +47 -0
- package/agents/examples/chart-line-timeline.tsx +41 -0
- package/agents/examples/chart-line.tsx +57 -0
- package/agents/examples/code-block.tsx +38 -0
- package/agents/examples/collapsible.tsx +16 -0
- package/agents/examples/command.tsx +27 -0
- package/agents/examples/context-menu.tsx +15 -0
- package/agents/examples/copy-button.tsx +8 -0
- package/agents/examples/data-bar.tsx +121 -0
- package/agents/examples/data-table-filters.tsx +103 -0
- package/agents/examples/data-table.tsx +98 -0
- package/agents/examples/delta.tsx +13 -0
- package/agents/examples/dialog.tsx +28 -0
- package/agents/examples/distribution-bar-budget.tsx +30 -0
- package/agents/examples/distribution-bar-tug.tsx +72 -0
- package/agents/examples/dropdown-menu.tsx +31 -0
- package/agents/examples/formatters.tsx +42 -0
- package/agents/examples/heatmap-calendar.tsx +50 -0
- package/agents/examples/heatmap-status.tsx +51 -0
- package/agents/examples/heatmap-weekday.tsx +53 -0
- package/agents/examples/input.tsx +17 -0
- package/agents/examples/local-time.tsx +14 -0
- package/agents/examples/metric-strip.tsx +40 -0
- package/agents/examples/page-section.tsx +52 -0
- package/agents/examples/popover.tsx +23 -0
- package/agents/examples/price-chart.tsx +32 -0
- package/agents/examples/progress-tone.tsx +28 -0
- package/agents/examples/progress.tsx +10 -0
- package/agents/examples/scroll-area.tsx +20 -0
- package/agents/examples/section-cards-select.tsx +27 -0
- package/agents/examples/section-cards-trend.tsx +30 -0
- package/agents/examples/section-cards.tsx +48 -0
- package/agents/examples/select.tsx +27 -0
- package/agents/examples/separator.tsx +13 -0
- package/agents/examples/sheet.tsx +15 -0
- package/agents/examples/skeleton.tsx +11 -0
- package/agents/examples/slider.tsx +5 -0
- package/agents/examples/surfaces.tsx +115 -0
- package/agents/examples/table.tsx +50 -0
- package/agents/examples/tabs.tsx +16 -0
- package/agents/examples/toggle-group.tsx +12 -0
- package/agents/examples/toggle.tsx +11 -0
- package/agents/examples/tooltip.tsx +13 -0
- package/agents/examples/treemap.tsx +87 -0
- package/agents/manifest.json +931 -0
- package/agents/report-rules.json +7 -0
- package/dist/components/registry/charts/bar-chart-card.js +1 -0
- package/dist/components/registry/dashboardcn/bar-list.js +8 -7
- package/dist/components/registry/heatmap/calendar-heatmap.js +8 -1
- package/dist/components/registry/heatmap/status-heatmap.js +8 -1
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +6 -2
- package/dist/components/registry/heatmap/weekday-heatmap.js +56 -37
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +2 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +21 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
- package/dist/components/registry/niko-table/core/data-table-structure.js +15 -12
- package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
- package/dist/components/registry/niko-table/core/data-table.js +5 -1
- package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
- package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
- package/dist/components/registry/niko-table/lib/styles.js +2 -2
- package/dist/components/registry/sidebar-07/nav-main.js +7 -1
- package/dist/components/siyahi/treemap.js +46 -4
- package/dist/lib/chart-frame.js +180 -124
- package/dist/lib/chart-series.d.ts +1 -1
- package/dist/lib/format.js +32 -13
- package/dist/lib/heatmap-navigation.d.ts +8 -0
- package/dist/lib/heatmap-navigation.js +49 -0
- package/package.json +3 -1
- package/styles/siyahi.css +24 -0
|
@@ -22,6 +22,7 @@ function BarChartCard({ stacked = false, selected = null, onSelect, ...props })
|
|
|
22
22
|
dataKey: key,
|
|
23
23
|
name: label,
|
|
24
24
|
fill: seriesFill(item, props.data),
|
|
25
|
+
maxBarSize: props.horizontal ? 24 : void 0,
|
|
25
26
|
stackId: stacked ? "stack" : void 0,
|
|
26
27
|
radius: !stacked ? 2 : index === props.series.length - 1 ? props.horizontal ? [
|
|
27
28
|
0,
|
|
@@ -6,10 +6,11 @@ import * as React$1 from "react";
|
|
|
6
6
|
//#region src/components/registry/dashboardcn/bar-list.tsx
|
|
7
7
|
function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(--chart-1)", showPercentage = false, max: scale, onItemClick, className, ...props }) {
|
|
8
8
|
const locale = useLocale();
|
|
9
|
-
const
|
|
9
|
+
const defaultFormat = React$1.useMemo(() => new Intl.NumberFormat(locale.numberLocale, {
|
|
10
10
|
notation: "compact",
|
|
11
11
|
maximumFractionDigits: 1
|
|
12
|
-
}).format
|
|
12
|
+
}).format, [locale.numberLocale]);
|
|
13
|
+
const format = valueFormatter ?? defaultFormat;
|
|
13
14
|
const items = React$1.useMemo(() => {
|
|
14
15
|
if (sortOrder === "none") return data;
|
|
15
16
|
const sorted = [...data].sort((a, b) => a.value - b.value);
|
|
@@ -19,7 +20,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
19
20
|
const total = items.reduce((sum, item) => sum + item.value, 0);
|
|
20
21
|
return /* @__PURE__ */ jsx("div", {
|
|
21
22
|
"data-slot": "bar-list",
|
|
22
|
-
className: cn("flex flex-col gap-1.5", className),
|
|
23
|
+
className: cn("@container/bar-list flex flex-col gap-1.5", className),
|
|
23
24
|
...props,
|
|
24
25
|
children: items.map((item) => {
|
|
25
26
|
const width = max > 0 ? item.value / max * 100 : 0;
|
|
@@ -28,13 +29,13 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
28
29
|
const Label = item.href ? "a" : onItemClick ? "button" : "div";
|
|
29
30
|
return /* @__PURE__ */ jsxs("div", {
|
|
30
31
|
"data-slot": "bar-list-row",
|
|
31
|
-
className: "flex items-center gap-3 text-sm",
|
|
32
|
+
className: "flex items-center gap-3 text-sm @max-sm/bar-list:items-start",
|
|
32
33
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
33
|
-
className: "relative flex h-8 min-w-0 flex-1 items-center",
|
|
34
|
+
className: "relative flex h-8 min-w-0 flex-1 items-center @max-sm/bar-list:h-auto @max-sm/bar-list:min-h-8 @max-sm/bar-list:items-start @max-sm/bar-list:pb-3",
|
|
34
35
|
children: [
|
|
35
36
|
/* @__PURE__ */ jsx("div", {
|
|
36
37
|
"aria-hidden": "true",
|
|
37
|
-
className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300",
|
|
38
|
+
className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300 @max-sm/bar-list:inset-y-auto @max-sm/bar-list:bottom-0 @max-sm/bar-list:h-1.5",
|
|
38
39
|
style: {
|
|
39
40
|
width: `${width}%`,
|
|
40
41
|
backgroundColor: `color-mix(in oklab, ${color} 20%, transparent)`
|
|
@@ -51,7 +52,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
|
|
|
51
52
|
className: "text-muted-foreground shrink-0 [&>svg]:size-4",
|
|
52
53
|
children: item.icon
|
|
53
54
|
}) : null, /* @__PURE__ */ jsx("span", {
|
|
54
|
-
className: "truncate",
|
|
55
|
+
className: "truncate @max-sm/bar-list:whitespace-normal",
|
|
55
56
|
children: item.name
|
|
56
57
|
})]
|
|
57
58
|
}),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
4
5
|
import { differenceInCalendarDays, eachDayOfInterval, format, formatISO, getDay, getMonth, getYear, nextDay, parseISO, subWeeks } from "date-fns";
|
|
@@ -290,7 +291,7 @@ const CalendarHeatmap = ({ data, weekStart = 0, continuousMonths = true, hasEmpt
|
|
|
290
291
|
value: contextValue,
|
|
291
292
|
children: /* @__PURE__ */ jsx("div", {
|
|
292
293
|
"data-slot": "calendar-heatmap",
|
|
293
|
-
className: cn("flex w-max max-w-full flex-col gap-2
|
|
294
|
+
className: cn("flex w-max max-w-full flex-col gap-2", className),
|
|
294
295
|
style: {
|
|
295
296
|
fontSize,
|
|
296
297
|
...style
|
|
@@ -349,6 +350,7 @@ const CalendarHeatmapBlock = ({ ref, activity, dayIndex, weekIndex, highlighted
|
|
|
349
350
|
};
|
|
350
351
|
CalendarHeatmapBlock.displayName = "CalendarHeatmapBlock";
|
|
351
352
|
const CalendarHeatmapBody = ({ hideMonthLabels = false, hideWeekdayLabels = false, hideYearLabels = false, className, labelClassName, yearClassName, children, renderYearFooter, ...props }) => {
|
|
353
|
+
const navigation = useHeatmapNavigation();
|
|
352
354
|
const { yearRows, blockSize, blockWidth, blockMargin, labels, labelHeight, fontSize, weekStart, data } = useCalendarHeatmap();
|
|
353
355
|
const weekdayLabelWidth = hideWeekdayLabels ? 0 : 40;
|
|
354
356
|
const strokePadding = 3;
|
|
@@ -374,8 +376,13 @@ const CalendarHeatmapBody = ({ hideMonthLabels = false, hideWeekdayLabels = fals
|
|
|
374
376
|
const totalWidth = weekdayLabelWidth + Math.max(...rowData.map((r) => r.width)) + 6;
|
|
375
377
|
return /* @__PURE__ */ jsx("div", {
|
|
376
378
|
"data-slot": "calendar-heatmap-body",
|
|
379
|
+
ref: navigation.ref,
|
|
377
380
|
className: cn("flex max-w-full flex-col gap-6 overflow-x-auto overflow-y-hidden py-4", className),
|
|
378
381
|
...props,
|
|
382
|
+
onKeyDown: (event) => {
|
|
383
|
+
props.onKeyDown?.(event);
|
|
384
|
+
if (!event.defaultPrevented) navigation.onKeyDown(event);
|
|
385
|
+
},
|
|
379
386
|
children: rowData.map(({ yearRow, height, monthLabels, yearTotalCount }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
380
387
|
!hideYearLabels && /* @__PURE__ */ jsx("div", {
|
|
381
388
|
className: cn("mb-2 text-muted-foreground", yearClassName),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
4
5
|
import { format } from "date-fns";
|
|
@@ -155,7 +156,7 @@ const StatusHeatmap = ({ data, dateFormat = "MMM d", blockSize = 40, blockMargin
|
|
|
155
156
|
value: contextValue,
|
|
156
157
|
children: /* @__PURE__ */ jsx("div", {
|
|
157
158
|
"data-slot": "status-heatmap",
|
|
158
|
-
className: cn("flex w-max max-w-full flex-col gap-2
|
|
159
|
+
className: cn("flex w-max max-w-full flex-col gap-2", className),
|
|
159
160
|
style: {
|
|
160
161
|
fontSize,
|
|
161
162
|
...style
|
|
@@ -214,6 +215,7 @@ const StatusHeatmapBlock = ({ ref, activity, dayIndex, highlighted = false, onCe
|
|
|
214
215
|
};
|
|
215
216
|
StatusHeatmapBlock.displayName = "StatusHeatmapBlock";
|
|
216
217
|
const StatusHeatmapBody = ({ hideDateLabels = false, labelInterval = 30, className, labelClassName, children, ...props }) => {
|
|
218
|
+
const navigation = useHeatmapNavigation();
|
|
217
219
|
const { data, dates, width, height, blockWidth, blockMargin, fontSize, dateFormat, locale, labels } = useStatusHeatmap();
|
|
218
220
|
const activityMap = useMemo(() => {
|
|
219
221
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -234,8 +236,13 @@ const StatusHeatmapBody = ({ hideDateLabels = false, labelInterval = 30, classNa
|
|
|
234
236
|
const labelHeight = hideDateLabels ? 0 : labelFontSize * 2;
|
|
235
237
|
return /* @__PURE__ */ jsx("div", {
|
|
236
238
|
"data-slot": "status-heatmap-body",
|
|
239
|
+
ref: navigation.ref,
|
|
237
240
|
className: cn("max-w-full overflow-x-auto overflow-y-hidden", className),
|
|
238
241
|
...props,
|
|
242
|
+
onKeyDown: (event) => {
|
|
243
|
+
props.onKeyDown?.(event);
|
|
244
|
+
if (!event.defaultPrevented) navigation.onKeyDown(event);
|
|
245
|
+
},
|
|
239
246
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
240
247
|
role: "img",
|
|
241
248
|
"aria-label": labels.heatmapLabel ?? "Status heatmap",
|
|
@@ -36,6 +36,10 @@ export type ColorConfig = {
|
|
|
36
36
|
export type WeekdayHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
37
37
|
data: WeekdayHourlyActivity[];
|
|
38
38
|
weekStart?: Day;
|
|
39
|
+
/** Displayed hours, in order; defaults to the full day. */
|
|
40
|
+
hours?: readonly WeekdayHour[];
|
|
41
|
+
/** Displayed days, in order; defaults to the full week from weekStart. */
|
|
42
|
+
weekdays?: readonly WeekdayIndex[];
|
|
39
43
|
use12Hour?: boolean;
|
|
40
44
|
blockSize?: number;
|
|
41
45
|
blockMargin?: number;
|
|
@@ -86,7 +90,7 @@ export type WeekdayHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
86
90
|
* @param extraRow - Appends an aggregated row below the grid. `compute` receives all activities and must return 24 values (one per hour). Rendered through `renderExtraRow` on the body, or falls back to `children`.
|
|
87
91
|
* @param extraColumn - Appends an aggregated column to the right of the grid. `compute` receives all activities and must return 7 values indexed by weekday 0–6. Rendered through `renderExtraColumn` on the body, or falls back to `children`.
|
|
88
92
|
*/
|
|
89
|
-
export declare const WeekdayHeatmap: ({ data, weekStart, use12Hour, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style, className, children, ...props }: WeekdayHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
|
|
93
|
+
export declare const WeekdayHeatmap: ({ data, weekStart, hours, weekdays: displayedWeekdays, use12Hour, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style, className, children, ...props }: WeekdayHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
|
|
90
94
|
export type WeekdayHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
91
95
|
activity: WeekdayHourlyActivityWithLevel;
|
|
92
96
|
extra?: "row" | "column";
|
|
@@ -97,7 +101,7 @@ export type WeekdayHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
|
97
101
|
};
|
|
98
102
|
export declare function WeekdayHeatmapBlock({ ref, activity, extra, highlighted, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: WeekdayHeatmapBlockProps & {
|
|
99
103
|
ref?: React.RefObject<SVGRectElement | null>;
|
|
100
|
-
}): import("react").JSX.Element;
|
|
104
|
+
}): import("react").JSX.Element | null;
|
|
101
105
|
export declare namespace WeekdayHeatmapBlock {
|
|
102
106
|
var displayName: string;
|
|
103
107
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
|
|
4
5
|
import { format } from "date-fns";
|
|
@@ -84,8 +85,9 @@ const useWeekdayHeatmap = () => {
|
|
|
84
85
|
* @param extraRow - Appends an aggregated row below the grid. `compute` receives all activities and must return 24 values (one per hour). Rendered through `renderExtraRow` on the body, or falls back to `children`.
|
|
85
86
|
* @param extraColumn - Appends an aggregated column to the right of the grid. `compute` receives all activities and must return 7 values indexed by weekday 0–6. Rendered through `renderExtraColumn` on the body, or falls back to `children`.
|
|
86
87
|
*/
|
|
87
|
-
const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24, blockMargin = 4, blockRadius = 2, blockAspectRatio = 1, levels: levelsProp = 5, isNormalized = false, colors, locale, labels: labelsProp, fontSize = 14, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style = EMPTY_STYLE, className, children, ...props }) => {
|
|
88
|
+
const WeekdayHeatmap = ({ data, weekStart = 0, hours = HOURS, weekdays: displayedWeekdays, use12Hour = false, blockSize = 24, blockMargin = 4, blockRadius = 2, blockAspectRatio = 1, levels: levelsProp = 5, isNormalized = false, colors, locale, labels: labelsProp, fontSize = 14, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style = EMPTY_STYLE, className, children, ...props }) => {
|
|
88
89
|
const levels = Math.max(1, levelsProp);
|
|
90
|
+
const weekdays = useMemo(() => displayedWeekdays ?? Array.from({ length: 7 }, (_, i) => (weekStart + i) % 7), [displayedWeekdays, weekStart]);
|
|
89
91
|
const dataWithLevels = useMemo(() => {
|
|
90
92
|
if (data.length === 0) return [];
|
|
91
93
|
const maxRegular = data.reduce((m, d) => Math.max(m, d.value), 1);
|
|
@@ -141,7 +143,7 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
141
143
|
const weekdayLabels = locale ? generateWeekdayLabels(locale) : DEFAULT_WEEKDAY_LABELS;
|
|
142
144
|
return {
|
|
143
145
|
hours: use12Hour ? TWELVE_HOUR_LABELS : DEFAULT_HOUR_LABELS,
|
|
144
|
-
endHour: use12Hour ? "12" : "00",
|
|
146
|
+
endHour: hours.at(-1) === 23 ? use12Hour ? "12" : "00" : (use12Hour ? TWELVE_HOUR_LABELS : DEFAULT_HOUR_LABELS)[(hours.at(-1) ?? -1) + 1],
|
|
145
147
|
weekdays: weekdayLabels,
|
|
146
148
|
cellLabel: "{{weekday}} {{hour}}: {{value}}",
|
|
147
149
|
heatmapLabel: "Activity heatmap by weekday and hour",
|
|
@@ -152,7 +154,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
152
154
|
}, [
|
|
153
155
|
locale,
|
|
154
156
|
use12Hour,
|
|
155
|
-
labelsProp
|
|
157
|
+
labelsProp,
|
|
158
|
+
hours
|
|
156
159
|
]);
|
|
157
160
|
const labelWidth = fontSize * 3.5 + LABEL_MARGIN;
|
|
158
161
|
const totalCount = typeof totalCountProp === "number" ? totalCountProp : dataWithLevels.reduce((sum, a) => sum + a.value, 0);
|
|
@@ -160,8 +163,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
160
163
|
const labelHeight = fontSize + LABEL_MARGIN;
|
|
161
164
|
const hasExtraColumn = resolvedExtraColumn !== null;
|
|
162
165
|
const hasExtraRow = resolvedExtraRow !== null;
|
|
163
|
-
const width =
|
|
164
|
-
const rowCount = hasExtraRow ?
|
|
166
|
+
const width = hours.length * (blockWidth + blockMargin) + (hasExtraColumn ? blockSize / 2 + (blockWidth + blockMargin) * 2 : 0) - blockMargin + labelWidth;
|
|
167
|
+
const rowCount = weekdays.length + (hasExtraRow ? 1 : 0);
|
|
165
168
|
const extraRowGap = hasExtraRow ? blockSize + blockMargin : 0;
|
|
166
169
|
const height = rowCount * (blockSize + blockMargin) + extraRowGap - blockMargin + labelHeight;
|
|
167
170
|
const contextValue = useMemo(() => ({
|
|
@@ -183,6 +186,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
183
186
|
width,
|
|
184
187
|
height,
|
|
185
188
|
weekStart,
|
|
189
|
+
hours,
|
|
190
|
+
weekdays,
|
|
186
191
|
colors
|
|
187
192
|
}), [
|
|
188
193
|
dataWithLevels,
|
|
@@ -203,6 +208,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
203
208
|
width,
|
|
204
209
|
height,
|
|
205
210
|
weekStart,
|
|
211
|
+
hours,
|
|
212
|
+
weekdays,
|
|
206
213
|
colors
|
|
207
214
|
]);
|
|
208
215
|
if (data.length === 0) return emptyState ? emptyState : null;
|
|
@@ -210,7 +217,7 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
210
217
|
value: contextValue,
|
|
211
218
|
children: /* @__PURE__ */ jsx("div", {
|
|
212
219
|
"data-slot": "weekday-heatmap",
|
|
213
|
-
className: cn("flex w-max max-w-full flex-col gap-2
|
|
220
|
+
className: cn("flex w-max max-w-full flex-col gap-2", className),
|
|
214
221
|
style: {
|
|
215
222
|
fontSize,
|
|
216
223
|
...style
|
|
@@ -221,15 +228,17 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
|
|
|
221
228
|
});
|
|
222
229
|
};
|
|
223
230
|
const WeekdayHeatmapBlock = ({ ref, activity, extra, highlighted = false, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }) => {
|
|
224
|
-
const { blockSize, blockWidth, blockMargin, blockRadius, labelWidth, labelHeight, levels, isNormalized,
|
|
231
|
+
const { blockSize, blockWidth, blockMargin, blockRadius, labelWidth, labelHeight, levels, isNormalized, hours, weekdays, labels, colors } = useWeekdayHeatmap();
|
|
225
232
|
const level = Math.max(0, Math.min(colorStepCount(levels, isNormalized), activity.level));
|
|
226
233
|
const isExtraRow = extra === "row";
|
|
227
234
|
const isExtraColumn = extra === "column";
|
|
228
|
-
const rowIndex = isExtraRow ?
|
|
235
|
+
const rowIndex = isExtraRow ? weekdays.length : weekdays.findIndex((day) => day === activity.weekday);
|
|
236
|
+
const hourIndex = isExtraColumn ? hours.length : hours.findIndex((hour) => hour === activity.hour);
|
|
237
|
+
if (rowIndex < 0 || hourIndex < 0) return null;
|
|
229
238
|
const extraRowGap = isExtraRow ? blockSize + blockMargin : 0;
|
|
230
239
|
const yPosition = labelHeight + (blockSize + blockMargin) * rowIndex + extraRowGap;
|
|
231
240
|
const extraColumnGap = isExtraColumn ? blockWidth + blockMargin : 0;
|
|
232
|
-
const xPosition = isExtraColumn ? labelWidth +
|
|
241
|
+
const xPosition = isExtraColumn ? labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + extraColumnGap : labelWidth + (blockWidth + blockMargin) * hourIndex;
|
|
233
242
|
const weekdayToken = isExtraRow ? "extra" : labels.weekdays?.[activity.weekday] ?? String(activity.weekday);
|
|
234
243
|
const hourToken = isExtraColumn ? "extra" : `${String(activity.hour).padStart(2, "0")}:00`;
|
|
235
244
|
const ariaLabel = (labels.cellLabel ?? "{{weekday}} {{hour}}: {{value}}").replace("{{weekday}}", weekdayToken).replace("{{hour}}", hourToken).replace("{{value}}", String(activity.value));
|
|
@@ -279,7 +288,8 @@ const WeekdayHeatmapBlock = ({ ref, activity, extra, highlighted = false, onCell
|
|
|
279
288
|
};
|
|
280
289
|
WeekdayHeatmapBlock.displayName = "WeekdayHeatmapBlock";
|
|
281
290
|
const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false, className, labelClassName, children, renderExtraRow, renderExtraColumn, ...props }) => {
|
|
282
|
-
const
|
|
291
|
+
const navigation = useHeatmapNavigation();
|
|
292
|
+
const { data, extraRow, extraColumn, blockSize, blockWidth, blockMargin, labels, labelWidth, labelHeight, fontSize, hours, weekdays } = useWeekdayHeatmap();
|
|
283
293
|
const hasExtraRow = extraRow !== null;
|
|
284
294
|
const hasExtraColumn = extraColumn !== null;
|
|
285
295
|
const activityMap = useMemo(() => {
|
|
@@ -299,24 +309,24 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
299
309
|
}, [extraColumn]);
|
|
300
310
|
const regularActivities = useMemo(() => {
|
|
301
311
|
const activities = [];
|
|
302
|
-
for (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
weekdayIndex: di
|
|
312
|
-
});
|
|
313
|
-
}
|
|
312
|
+
for (const [di, weekday] of weekdays.entries()) for (const hour of hours) activities.push({
|
|
313
|
+
activity: activityMap.get(`${weekday}-${hour}`) ?? {
|
|
314
|
+
weekday,
|
|
315
|
+
hour,
|
|
316
|
+
value: 0,
|
|
317
|
+
level: 0
|
|
318
|
+
},
|
|
319
|
+
weekdayIndex: di
|
|
320
|
+
});
|
|
314
321
|
return activities;
|
|
315
|
-
}, [
|
|
322
|
+
}, [
|
|
323
|
+
activityMap,
|
|
324
|
+
weekdays,
|
|
325
|
+
hours
|
|
326
|
+
]);
|
|
316
327
|
const extraColumnActivities = useMemo(() => {
|
|
317
328
|
if (!extraColumnMap) return [];
|
|
318
|
-
return
|
|
319
|
-
const weekday = (weekStart + di) % 7;
|
|
329
|
+
return weekdays.map((weekday, di) => {
|
|
320
330
|
const entry = extraColumnMap.get(weekday);
|
|
321
331
|
return entry ? {
|
|
322
332
|
activity: {
|
|
@@ -328,29 +338,38 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
328
338
|
weekdayIndex: di
|
|
329
339
|
} : null;
|
|
330
340
|
}).filter((a) => a !== null);
|
|
331
|
-
}, [extraColumnMap,
|
|
341
|
+
}, [extraColumnMap, weekdays]);
|
|
332
342
|
const extraRowActivities = useMemo(() => {
|
|
333
343
|
if (!extraRow) return [];
|
|
334
|
-
return extraRow.values.map(({ hour, value, level }) => ({
|
|
344
|
+
return extraRow.values.filter(({ hour }) => hours.includes(hour)).map(({ hour, value, level }) => ({
|
|
335
345
|
activity: {
|
|
336
346
|
weekday: 0,
|
|
337
347
|
hour,
|
|
338
348
|
value,
|
|
339
349
|
level
|
|
340
350
|
},
|
|
341
|
-
weekdayIndex:
|
|
351
|
+
weekdayIndex: weekdays.length
|
|
342
352
|
}));
|
|
343
|
-
}, [
|
|
344
|
-
|
|
353
|
+
}, [
|
|
354
|
+
extraRow,
|
|
355
|
+
hours,
|
|
356
|
+
weekdays.length
|
|
357
|
+
]);
|
|
358
|
+
const rowCount = weekdays.length + (hasExtraRow ? 1 : 0);
|
|
345
359
|
const extraRowGap = hasExtraRow ? blockSize + blockMargin : 0;
|
|
346
360
|
const svgHeight = rowCount * (blockSize + blockMargin) + extraRowGap - blockMargin + labelHeight;
|
|
347
361
|
const extraColumnGap = hasExtraColumn ? blockWidth + blockMargin : 0;
|
|
348
|
-
const svgWidth = hasExtraColumn ? labelWidth +
|
|
349
|
-
const orderedWeekdayIndices =
|
|
362
|
+
const svgWidth = hasExtraColumn ? labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + extraColumnGap : labelWidth + hours.length * (blockWidth + blockMargin) - blockMargin;
|
|
363
|
+
const orderedWeekdayIndices = weekdays;
|
|
350
364
|
return /* @__PURE__ */ jsx("div", {
|
|
351
365
|
"data-slot": "weekday-heatmap-body",
|
|
366
|
+
ref: navigation.ref,
|
|
352
367
|
className: cn("max-w-full overflow-x-auto overflow-y-hidden", className),
|
|
353
368
|
...props,
|
|
369
|
+
onKeyDown: (event) => {
|
|
370
|
+
props.onKeyDown?.(event);
|
|
371
|
+
if (!event.defaultPrevented) navigation.onKeyDown(event);
|
|
372
|
+
},
|
|
354
373
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
355
374
|
role: "img",
|
|
356
375
|
"aria-label": labels.heatmapLabel ?? "Activity heatmap by weekday and hour",
|
|
@@ -362,10 +381,10 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
362
381
|
!hideHourLabels && /* @__PURE__ */ jsxs("g", {
|
|
363
382
|
className: cn("fill-current font-mono", labelClassName),
|
|
364
383
|
children: [
|
|
365
|
-
|
|
384
|
+
hours.map((hour, index) => {
|
|
366
385
|
const label = labels.hours?.[hour] ?? "";
|
|
367
386
|
return /* @__PURE__ */ jsx("text", {
|
|
368
|
-
x: labelWidth + (blockWidth + blockMargin) *
|
|
387
|
+
x: labelWidth + (blockWidth + blockMargin) * index,
|
|
369
388
|
y: 0,
|
|
370
389
|
textAnchor: "middle",
|
|
371
390
|
dominantBaseline: "hanging",
|
|
@@ -374,7 +393,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
374
393
|
}, `hour-${hour}`);
|
|
375
394
|
}),
|
|
376
395
|
labels.endHour != null && /* @__PURE__ */ jsx("text", {
|
|
377
|
-
x: labelWidth + (blockWidth + blockMargin) *
|
|
396
|
+
x: labelWidth + (blockWidth + blockMargin) * hours.length,
|
|
378
397
|
y: 0,
|
|
379
398
|
textAnchor: "middle",
|
|
380
399
|
dominantBaseline: "hanging",
|
|
@@ -382,7 +401,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
382
401
|
children: labels.endHour
|
|
383
402
|
}, "hour-end"),
|
|
384
403
|
hasExtraColumn && /* @__PURE__ */ jsx("text", {
|
|
385
|
-
x: labelWidth +
|
|
404
|
+
x: labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + blockWidth / 2,
|
|
386
405
|
y: 0,
|
|
387
406
|
textAnchor: "middle",
|
|
388
407
|
dominantBaseline: "hanging",
|
|
@@ -406,7 +425,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
|
|
|
406
425
|
}, `weekday-${weekday}`);
|
|
407
426
|
}), hasExtraRow && /* @__PURE__ */ jsx("text", {
|
|
408
427
|
x: labelWidth - 8,
|
|
409
|
-
y: labelHeight + (blockSize + blockMargin) *
|
|
428
|
+
y: labelHeight + (blockSize + blockMargin) * weekdays.length + (blockSize + blockMargin) + blockSize / 2,
|
|
410
429
|
dominantBaseline: "middle",
|
|
411
430
|
textAnchor: "end",
|
|
412
431
|
style: { fontSize: `${fontSize * .75}px` },
|
|
@@ -91,6 +91,8 @@ type DataTableContextProps<TData extends RowData> = DataTableContextState & {
|
|
|
91
91
|
* `null` until the container mounts (or when no `<DataTable>` is used).
|
|
92
92
|
*/
|
|
93
93
|
scrollContainer: HTMLElement | null;
|
|
94
|
+
/** True below 40rem of table space; use for compact toolbar and record layouts. */
|
|
95
|
+
isCompact: boolean;
|
|
94
96
|
/** Internal: `<DataTable>` registers its scroll container element here. */
|
|
95
97
|
registerScrollContainer: (el: HTMLElement | null) => void;
|
|
96
98
|
/**
|
|
@@ -127,6 +127,25 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
127
127
|
}, []);
|
|
128
128
|
const [scrollContainer, setScrollContainer] = React.useState(null);
|
|
129
129
|
const registerScrollContainer = useCallback((el) => setScrollContainer(el), []);
|
|
130
|
+
const [isCompact, setIsCompact] = React.useState(false);
|
|
131
|
+
React.useLayoutEffect(() => {
|
|
132
|
+
if (!scrollContainer) return;
|
|
133
|
+
const measure = () => {
|
|
134
|
+
const width = scrollContainer.clientWidth;
|
|
135
|
+
if (width > 0) {
|
|
136
|
+
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
137
|
+
setIsCompact(width < 40 * rem);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
measure();
|
|
141
|
+
const observer = new ResizeObserver(measure);
|
|
142
|
+
observer.observe(scrollContainer);
|
|
143
|
+
window.addEventListener("resize", measure);
|
|
144
|
+
return () => {
|
|
145
|
+
observer.disconnect();
|
|
146
|
+
window.removeEventListener("resize", measure);
|
|
147
|
+
};
|
|
148
|
+
}, [scrollContainer]);
|
|
130
149
|
const scrollRowIntoView = useCallback((index, opts) => {
|
|
131
150
|
if (rowScrollerRef.current) {
|
|
132
151
|
rowScrollerRef.current(index, opts);
|
|
@@ -269,6 +288,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
269
288
|
scrollRowIntoView,
|
|
270
289
|
registerRowScroller,
|
|
271
290
|
scrollContainer,
|
|
291
|
+
isCompact,
|
|
272
292
|
registerScrollContainer,
|
|
273
293
|
headerMinWidths,
|
|
274
294
|
setColumnResizePreview: setManualResizePreview,
|
|
@@ -286,6 +306,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
286
306
|
scrollRowIntoView,
|
|
287
307
|
registerRowScroller,
|
|
288
308
|
scrollContainer,
|
|
309
|
+
isCompact,
|
|
289
310
|
registerScrollContainer,
|
|
290
311
|
headerMinWidths,
|
|
291
312
|
setManualResizePreview,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Button } from "../../../ui/button.js";
|
|
2
2
|
import { cn } from "../../../../lib/utils.js";
|
|
3
|
+
import { useDataTable } from "./data-table-context.js";
|
|
3
4
|
import { DataTableGroupRowProvider, useDataTableGroupRow } from "../components/data-table-grouped-rows.js";
|
|
4
5
|
import { TableCell, TableRow } from "../ui/table.js";
|
|
5
6
|
import { isInteractiveClickTarget } from "../lib/row-click.js";
|
|
@@ -18,12 +19,13 @@ import { flexRender } from "@tanstack/react-table";
|
|
|
18
19
|
* groups you don't want to special-case.
|
|
19
20
|
*/
|
|
20
21
|
function DataTableGroupedRowCells() {
|
|
22
|
+
const { isCompact } = useDataTable();
|
|
21
23
|
const { row, groupLabel, count, isExpanded, toggle, columnWidths } = useDataTableGroupRow();
|
|
22
24
|
const countLabel = `${count} ${count === 1 ? "row" : "rows"}`;
|
|
23
25
|
return /* @__PURE__ */ jsx(Fragment, { children: row.getVisibleCells().map((cell) => {
|
|
24
26
|
const cellStyle = {
|
|
25
27
|
width: columnWidths.get(cell.column.id),
|
|
26
|
-
...getCommonPinningStyles(cell.column, false)
|
|
28
|
+
...getCommonPinningStyles(cell.column, false, !isCompact)
|
|
27
29
|
};
|
|
28
30
|
let content = null;
|
|
29
31
|
if (cell.getIsGrouped()) content = /* @__PURE__ */ jsxs("div", {
|
|
@@ -20,11 +20,11 @@ import React from "react";
|
|
|
20
20
|
import { flexRender } from "@tanstack/react-table";
|
|
21
21
|
//#region src/components/registry/niko-table/core/data-table-structure.tsx
|
|
22
22
|
const DataTableHeader = React.memo(function DataTableHeader({ className, sticky = true }) {
|
|
23
|
-
const { table, headerMinWidths, setColumnResizePreview } = useDataTable();
|
|
23
|
+
const { table, headerMinWidths, setColumnResizePreview, isCompact } = useDataTable();
|
|
24
24
|
const resizing = table?.options.enableColumnResizing ?? false;
|
|
25
25
|
const headerGroups = table?.getHeaderGroups() ?? [];
|
|
26
26
|
if (headerGroups.length === 0) return null;
|
|
27
|
-
const flexColumnIds = resolveFlexColumnIds(table);
|
|
27
|
+
const flexColumnIds = resolveFlexColumnIds(table, !isCompact);
|
|
28
28
|
const columnSizing = table.state.columnSizing;
|
|
29
29
|
return /* @__PURE__ */ jsx(TableHeader, {
|
|
30
30
|
className: cn(sticky && "sticky top-0 z-30 bg-card", sticky && "after:absolute after:right-0 after:bottom-0 after:left-0 after:h-px after:bg-border", className),
|
|
@@ -37,12 +37,12 @@ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky
|
|
|
37
37
|
columnSizing,
|
|
38
38
|
headerMinWidths
|
|
39
39
|
}),
|
|
40
|
-
...getCommonPinningStyles(header.column, true)
|
|
40
|
+
...getCommonPinningStyles(header.column, true, !isCompact)
|
|
41
41
|
};
|
|
42
42
|
return /* @__PURE__ */ jsxs(TableHead, {
|
|
43
43
|
"data-col-id": header.column.id,
|
|
44
44
|
style: headerStyle,
|
|
45
|
-
className: cn(header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
|
|
45
|
+
className: cn(!isCompact && header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
|
|
46
46
|
children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(DataTableColumnHeaderRoot, {
|
|
47
47
|
column: header.column,
|
|
48
48
|
children: resizing && typeof header.column.columnDef.header === "string" ? /* @__PURE__ */ jsx("span", {
|
|
@@ -61,7 +61,7 @@ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky
|
|
|
61
61
|
DataTableHeader.displayName = "DataTableHeader";
|
|
62
62
|
/** Fade-pulse animation applied to a flashing cell (keyframe in the provider). */
|
|
63
63
|
const FLASH_ANIMATION = "niko-row-flash 1.2s ease-out";
|
|
64
|
-
const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId, isClickable, isExpanded, isSelected, columnWidths, isRowFlashing, flashingCellKeys, renderRowContextMenu }) {
|
|
64
|
+
const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId, isCompact, isClickable, isExpanded, isSelected, columnWidths, isRowFlashing, flashingCellKeys, renderRowContextMenu }) {
|
|
65
65
|
const expandCell = isExpanded && expandColumnId ? row.getAllCells().find((c) => c.column.id === expandColumnId) : void 0;
|
|
66
66
|
const visibleCells = row.getVisibleCells();
|
|
67
67
|
const rowElement = /* @__PURE__ */ jsx(TableRow, {
|
|
@@ -76,14 +76,14 @@ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId,
|
|
|
76
76
|
const flashing = isRowFlashing || flashingCellKeys.has(flashCellKey(row.id, cell.column.id));
|
|
77
77
|
const cellStyle = {
|
|
78
78
|
width: columnWidths.get(cell.column.id),
|
|
79
|
-
...getCommonPinningStyles(cell.column, false),
|
|
79
|
+
...getCommonPinningStyles(cell.column, false, !isCompact),
|
|
80
80
|
...flashing ? { animation: FLASH_ANIMATION } : {}
|
|
81
81
|
};
|
|
82
82
|
return /* @__PURE__ */ jsx(TableCell, {
|
|
83
83
|
"data-flash": flashing ? "true" : void 0,
|
|
84
84
|
"data-col-id": cell.column.id,
|
|
85
85
|
style: cellStyle,
|
|
86
|
-
className: cn(!cell.getIsGrouped() && "truncate", cell.column.getIsPinned() && "bg-linear-to-r group-hover:from-muted/50 group-hover:to-muted/50 group-data-[context-menu-open]:from-muted/50 group-data-[context-menu-open]:to-muted/50 group-data-[state=selected]:from-muted group-data-[state=selected]:to-muted"),
|
|
86
|
+
className: cn(!cell.getIsGrouped() && "truncate", !isCompact && cell.column.getIsPinned() && "bg-linear-to-r group-hover:from-muted/50 group-hover:to-muted/50 group-data-[context-menu-open]:from-muted/50 group-data-[context-menu-open]:to-muted/50 group-data-[state=selected]:from-muted group-data-[state=selected]:to-muted"),
|
|
87
87
|
children: renderCellContent(cell)
|
|
88
88
|
}, cell.id);
|
|
89
89
|
})
|
|
@@ -101,7 +101,7 @@ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId,
|
|
|
101
101
|
});
|
|
102
102
|
BodyRow.displayName = "BodyRow";
|
|
103
103
|
function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolledBottom, scrollThreshold = 50, onRowClick, getRowMemoKey, renderRowContextMenu }) {
|
|
104
|
-
const { table, columns, isLoading, flashingRowIds, flashingCellKeys, registerRowScroller, headerMinWidths } = useDataTable();
|
|
104
|
+
const { table, columns, isLoading, flashingRowIds, flashingCellKeys, registerRowScroller, headerMinWidths, isCompact } = useDataTable();
|
|
105
105
|
const { rows } = table.getRowModel();
|
|
106
106
|
const containerRef = React.useRef(null);
|
|
107
107
|
React.useEffect(() => {
|
|
@@ -139,13 +139,14 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
|
|
|
139
139
|
const expandColumnId = React.useMemo(() => table.getAllColumns().find((col) => col.columnDef.meta?.expandedContent)?.id, [table, columns]);
|
|
140
140
|
const { columnVisibility, columnOrder, columnPinning, columnSizing } = table.state;
|
|
141
141
|
const resizing = table.options.enableColumnResizing ?? false;
|
|
142
|
-
const flexColumnIds = React.useMemo(() => resolveFlexColumnIds(table), [
|
|
142
|
+
const flexColumnIds = React.useMemo(() => resolveFlexColumnIds(table, !isCompact), [
|
|
143
143
|
table,
|
|
144
144
|
columnVisibility,
|
|
145
145
|
columnOrder,
|
|
146
146
|
columnPinning,
|
|
147
147
|
columnSizing,
|
|
148
|
-
resizing
|
|
148
|
+
resizing,
|
|
149
|
+
isCompact
|
|
149
150
|
]);
|
|
150
151
|
const columnWidths = React.useMemo(() => {
|
|
151
152
|
const widths = /* @__PURE__ */ new Map();
|
|
@@ -166,14 +167,15 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
|
|
|
166
167
|
resizing
|
|
167
168
|
]);
|
|
168
169
|
const columnLayoutSignature = React.useMemo(() => table.getVisibleLeafColumns().map((c) => {
|
|
169
|
-
const pinned = c.getIsPinned();
|
|
170
|
+
const pinned = !isCompact && c.getIsPinned();
|
|
170
171
|
const base = pinned ? `${c.id}:${pinned}` : c.id;
|
|
171
172
|
return resizing ? `${base}:${columnWidths.get(c.id) ?? "flex"}` : base;
|
|
172
173
|
}).join(","), [
|
|
173
174
|
table,
|
|
174
175
|
columns,
|
|
175
176
|
columnWidths,
|
|
176
|
-
resizing
|
|
177
|
+
resizing,
|
|
178
|
+
isCompact
|
|
177
179
|
]);
|
|
178
180
|
const isClickable = !!onRowClick;
|
|
179
181
|
const resolvedRenderRowContextMenu = useResolvedRowContextMenuRenderer(renderRowContextMenu, children);
|
|
@@ -232,6 +234,7 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
|
|
|
232
234
|
row,
|
|
233
235
|
displayIndex,
|
|
234
236
|
expandColumnId,
|
|
237
|
+
isCompact,
|
|
235
238
|
isClickable,
|
|
236
239
|
isExpanded: row.getIsExpanded(),
|
|
237
240
|
isSelected: row.getIsSelected(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
//#region src/components/registry/niko-table/core/data-table.d.ts
|
|
3
|
-
export interface DataTableContainerProps {
|
|
3
|
+
export interface DataTableContainerProps extends React.AriaAttributes {
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
/**
|
|
6
6
|
* Additional CSS classes for the container.
|
|
@@ -23,7 +23,7 @@ export interface DataTableContainerProps {
|
|
|
23
23
|
*/
|
|
24
24
|
maxHeight?: number | string;
|
|
25
25
|
}
|
|
26
|
-
export declare function DataTable({ children, className, height, maxHeight }: DataTableContainerProps): React.JSX.Element;
|
|
26
|
+
export declare function DataTable({ children, className, height, maxHeight, "aria-label": label, "aria-labelledby": labelledBy }: DataTableContainerProps): React.JSX.Element;
|
|
27
27
|
export declare namespace DataTable {
|
|
28
28
|
var displayName: string;
|
|
29
29
|
}
|
|
@@ -105,7 +105,7 @@ function ColumnResizePreviewLine() {
|
|
|
105
105
|
}
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
-
function DataTable({ children, className, height, maxHeight }) {
|
|
108
|
+
function DataTable({ children, className, height, maxHeight, "aria-label": label = "Data table", "aria-labelledby": labelledBy }) {
|
|
109
109
|
const parsed = React.useMemo(() => parseHeightFromClassName(className), [className]);
|
|
110
110
|
const finalHeight = height ?? parsed.height;
|
|
111
111
|
const finalMaxHeight = maxHeight ?? parsed.maxHeight ?? finalHeight;
|
|
@@ -113,6 +113,10 @@ function DataTable({ children, className, height, maxHeight }) {
|
|
|
113
113
|
return /* @__PURE__ */ jsxs("div", {
|
|
114
114
|
ref: registerScrollContainer,
|
|
115
115
|
"data-slot": "table-container",
|
|
116
|
+
role: "region",
|
|
117
|
+
"aria-label": labelledBy ? void 0 : label,
|
|
118
|
+
"aria-labelledby": labelledBy,
|
|
119
|
+
tabIndex: 0,
|
|
116
120
|
className: cn("relative isolate w-full overflow-auto rounded-lg border", "[&::-webkit-scrollbar]:h-2.5 [&::-webkit-scrollbar]:w-2.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/40", "hover:[&::-webkit-scrollbar-thumb]:bg-border", "[&::-webkit-scrollbar-thumb:hover]:bg-border/80!", "scrollbar-thin scrollbar-thumb-border/40 scrollbar-track-transparent", "hover:scrollbar-thumb-border", parsed.safeClassName),
|
|
117
121
|
style: {
|
|
118
122
|
height: finalHeight,
|
|
@@ -62,7 +62,7 @@ function TableSearchFilter({ table, className, placeholder = "Search...", label
|
|
|
62
62
|
]);
|
|
63
63
|
const hasValue = currentValue.length > 0;
|
|
64
64
|
return /* @__PURE__ */ jsxs("div", {
|
|
65
|
-
className: cn("relative flex flex-1 items-center", className),
|
|
65
|
+
className: cn("relative flex min-w-0 max-w-full flex-1 items-center", className),
|
|
66
66
|
role: "search",
|
|
67
67
|
children: [
|
|
68
68
|
/* @__PURE__ */ jsx(Search, {
|
|
@@ -26,7 +26,7 @@ import { ColumnSizingState, RowData } from "@tanstack/react-table";
|
|
|
26
26
|
* Returns the set of column ids to render widthless (empty when fill is off or
|
|
27
27
|
* no eligible column exists — the table then sizes to its columns as before).
|
|
28
28
|
*/
|
|
29
|
-
export declare function resolveFlexColumnIds<TData extends RowData>(table: DataTableInstance<TData
|
|
29
|
+
export declare function resolveFlexColumnIds<TData extends RowData>(table: DataTableInstance<TData>, pinningEnabled?: boolean): ReadonlySet<string>;
|
|
30
30
|
/**
|
|
31
31
|
* The width to render for a single column. Centralizes the flex + header-fit
|
|
32
32
|
* rules so the header, body cells, and the min-width lock all agree.
|