@siyahi/ui 0.1.9-next.20 → 0.1.9-next.22
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/DESIGN.md +42 -0
- package/README.md +3 -3
- package/agents/examples/alert.tsx +4 -6
- package/agents/examples/app-sidebar.tsx +3 -1
- package/agents/examples/bar-list-sides.tsx +13 -3
- package/agents/examples/button-states.tsx +23 -0
- package/agents/examples/calendar.tsx +3 -7
- package/agents/examples/callout.tsx +33 -0
- package/agents/examples/card.tsx +3 -1
- package/agents/examples/change-bar.tsx +104 -0
- package/agents/examples/chart-area.tsx +5 -4
- package/agents/examples/chart-bar-flows.tsx +3 -2
- package/agents/examples/chart-bar-sectors.tsx +2 -0
- package/agents/examples/collapsible.tsx +2 -2
- package/agents/examples/command.tsx +22 -20
- package/agents/examples/context-menu.tsx +4 -2
- package/agents/examples/data-table-compact.tsx +95 -0
- package/agents/examples/distribution-bar-tug.tsx +24 -22
- package/agents/examples/heatmap-calendar.tsx +19 -7
- package/agents/examples/heatmap-status.tsx +19 -11
- package/agents/examples/heatmap-weekday.tsx +34 -15
- package/agents/examples/input-states.tsx +33 -0
- package/agents/examples/metric-strip.tsx +9 -3
- package/agents/examples/overview-dashboard.tsx +18 -0
- package/agents/examples/page-hero.tsx +38 -0
- package/agents/examples/price-chart.tsx +2 -2
- package/agents/examples/range-position-table.tsx +101 -0
- package/agents/examples/range-position.tsx +55 -0
- package/agents/examples/scroll-area.tsx +12 -10
- package/agents/examples/section-cards-select.tsx +8 -5
- package/agents/examples/surfaces.tsx +7 -4
- package/agents/examples/target-rows.tsx +72 -0
- package/agents/examples/treemap.tsx +13 -10
- package/agents/manifest.json +153 -3
- package/dist/components/registry/charts/area-chart-card.js +25 -7
- package/dist/components/registry/charts/bar-chart-card.d.ts +0 -1
- package/dist/components/registry/charts/bar-chart-card.js +15 -4
- package/dist/components/registry/charts/line-chart-card.js +2 -2
- package/dist/components/registry/dashboard-01/section-cards.js +1 -1
- package/dist/components/registry/dashboardcn/bar-list.js +11 -5
- package/dist/components/registry/dashboardcn/distribution-bar.js +5 -3
- package/dist/components/registry/dashboardcn/kpi-row-card.js +2 -2
- package/dist/components/registry/dashboardcn/sparkline.d.ts +5 -1
- package/dist/components/registry/dashboardcn/sparkline.js +40 -18
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +4 -2
- package/dist/components/registry/heatmap/calendar-heatmap.js +67 -33
- package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
- package/dist/components/registry/heatmap/status-heatmap.js +31 -16
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +4 -2
- package/dist/components/registry/heatmap/weekday-heatmap.js +28 -12
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +16 -1
- package/dist/components/registry/niko-table/core/data-table-context.js +5 -20
- package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
- package/dist/components/registry/niko-table/core/data-table-structure.js +2 -0
- package/dist/components/registry/niko-table/lib/styles.js +1 -1
- package/dist/components/registry/niko-table/types/index.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -0
- package/dist/components/registry/sidebar-07/nav-main.js +9 -2
- package/dist/components/siyahi/callout.d.ts +24 -0
- package/dist/components/siyahi/callout.js +40 -0
- package/dist/components/siyahi/change-bar.d.ts +21 -0
- package/dist/components/siyahi/change-bar.js +35 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
- package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
- package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
- package/dist/components/siyahi/data-table-footnote.js +35 -0
- package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
- package/dist/components/siyahi/default-sidebar-open.js +13 -0
- package/dist/components/siyahi/delta.d.ts +5 -1
- package/dist/components/siyahi/delta.js +14 -9
- package/dist/components/siyahi/page-hero.d.ts +25 -0
- package/dist/components/siyahi/page-hero.js +41 -0
- package/dist/components/siyahi/page-toc.d.ts +20 -0
- package/dist/components/siyahi/page-toc.js +76 -0
- package/dist/components/siyahi/quote.d.ts +12 -0
- package/dist/components/siyahi/quote.js +23 -0
- package/dist/components/siyahi/range-position.d.ts +24 -0
- package/dist/components/siyahi/range-position.js +105 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +1 -1
- package/dist/components/siyahi/siyahi-provider.js +1 -1
- package/dist/components/siyahi/target-rows.d.ts +34 -0
- package/dist/components/siyahi/target-rows.js +97 -0
- package/dist/components/siyahi/treemap.d.ts +4 -1
- package/dist/components/siyahi/treemap.js +36 -28
- package/dist/components/siyahi/warning-alert.d.ts +10 -0
- package/dist/components/siyahi/warning-alert.js +20 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.js +12 -1
- package/dist/lib/chart-frame.d.ts +12 -2
- package/dist/lib/chart-frame.js +51 -7
- package/dist/lib/chart-series.d.ts +5 -2
- package/dist/lib/compact-columns.d.ts +19 -0
- package/dist/lib/compact-columns.js +55 -0
- package/dist/lib/format.d.ts +8 -3
- package/dist/lib/format.js +25 -6
- package/dist/lib/treemap.js +6 -1
- package/dist/lib/use-element-width.d.ts +5 -0
- package/dist/lib/use-element-width.js +15 -0
- package/package.json +2 -1
- package/report.css +37 -36
- package/styles/siyahi.css +243 -55
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useTableWidth } from "../../../../lib/compact-columns.js";
|
|
2
3
|
import { useGeneratedOptions } from "../hooks/use-generated-options.js";
|
|
3
4
|
import { useHeaderMinWidths } from "../lib/use-header-min-widths.js";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -113,7 +114,7 @@ function useFlashSet() {
|
|
|
113
114
|
}
|
|
114
115
|
}, [])];
|
|
115
116
|
}
|
|
116
|
-
function DataTableProvider({ children, table, columns, isLoading: externalIsLoading }) {
|
|
117
|
+
function DataTableProvider({ children, table, columns, isLoading: externalIsLoading, isCompact, onWidthChange, compactColumns }) {
|
|
117
118
|
const [state, dispatch] = useReducer(dataTableReducer, deriveInitialState(externalIsLoading));
|
|
118
119
|
const setIsLoading = useCallback((value) => {
|
|
119
120
|
dispatch({
|
|
@@ -127,25 +128,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
127
128
|
}, []);
|
|
128
129
|
const [scrollContainer, setScrollContainer] = React.useState(null);
|
|
129
130
|
const registerScrollContainer = useCallback((el) => setScrollContainer(el), []);
|
|
130
|
-
|
|
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]);
|
|
131
|
+
useTableWidth(scrollContainer, onWidthChange);
|
|
149
132
|
const scrollRowIntoView = useCallback((index, opts) => {
|
|
150
133
|
if (rowScrollerRef.current) {
|
|
151
134
|
rowScrollerRef.current(index, opts);
|
|
@@ -289,6 +272,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
289
272
|
registerRowScroller,
|
|
290
273
|
scrollContainer,
|
|
291
274
|
isCompact,
|
|
275
|
+
compactColumns,
|
|
292
276
|
registerScrollContainer,
|
|
293
277
|
headerMinWidths,
|
|
294
278
|
setColumnResizePreview: setManualResizePreview,
|
|
@@ -307,6 +291,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
|
|
|
307
291
|
registerRowScroller,
|
|
308
292
|
scrollContainer,
|
|
309
293
|
isCompact,
|
|
294
|
+
compactColumns,
|
|
310
295
|
registerScrollContainer,
|
|
311
296
|
headerMinWidths,
|
|
312
297
|
setManualResizePreview,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TooltipProvider } from "../../../ui/tooltip.js";
|
|
2
2
|
import { cn } from "../../../../lib/utils.js";
|
|
3
|
+
import { fitColumns, isCompactWidth } from "../../../../lib/compact-columns.js";
|
|
3
4
|
import { FILTER_VARIANTS, SYSTEM_COLUMN_IDS, SYSTEM_COLUMN_ID_LIST } from "../lib/constants.js";
|
|
4
5
|
import { DataTableProvider } from "./data-table-context.js";
|
|
5
6
|
import { detectFeaturesFromChildren } from "../config/feature-detection.js";
|
|
@@ -216,7 +217,23 @@ function DataTableRootInternal({ columns, data, children, className, config, get
|
|
|
216
217
|
...detectFeatures.enableColumnResizing ? { minSize: 40 } : {}
|
|
217
218
|
}), [detectFeatures.enableColumnResizing, detectFeatures.enableSorting]);
|
|
218
219
|
const controlledSorting = restState?.sorting ?? sorting;
|
|
219
|
-
const
|
|
220
|
+
const [tableWidth, setTableWidth] = React.useState(0);
|
|
221
|
+
const isCompact = isCompactWidth(tableWidth);
|
|
222
|
+
const [showAllColumns, setShowAllColumns] = React.useState(false);
|
|
223
|
+
const requestedColumnVisibility = restState?.columnVisibility ?? columnVisibility;
|
|
224
|
+
const requestedColumnOrder = restState?.columnOrder ?? columnOrder;
|
|
225
|
+
const fitted = React.useMemo(() => fitColumns(columns, requestedColumnOrder, requestedColumnVisibility, tableWidth), [
|
|
226
|
+
columns,
|
|
227
|
+
requestedColumnOrder,
|
|
228
|
+
requestedColumnVisibility,
|
|
229
|
+
tableWidth
|
|
230
|
+
]);
|
|
231
|
+
const controlledColumnVisibility = fitted && !showAllColumns ? fitted.visibility : requestedColumnVisibility;
|
|
232
|
+
const compactColumns = React.useMemo(() => fitted && fitted.hidden.size > 0 ? {
|
|
233
|
+
collapsed: !showAllColumns,
|
|
234
|
+
hidden: showAllColumns ? NOTHING_HIDDEN : fitted.hidden,
|
|
235
|
+
setCollapsed: (value) => setShowAllColumns(!value)
|
|
236
|
+
} : null, [fitted, showAllColumns]);
|
|
220
237
|
const controlledRowSelection = restState?.rowSelection ?? rowSelection;
|
|
221
238
|
const controlledColumnFilters = restState?.columnFilters ?? columnFilters;
|
|
222
239
|
const controlledGlobalFilter = restState?.globalFilter !== void 0 ? restState.globalFilter : globalFilter$1;
|
|
@@ -369,6 +386,9 @@ function DataTableRootInternal({ columns, data, children, className, config, get
|
|
|
369
386
|
table,
|
|
370
387
|
columns: processedColumns,
|
|
371
388
|
isLoading,
|
|
389
|
+
isCompact,
|
|
390
|
+
onWidthChange: setTableWidth,
|
|
391
|
+
compactColumns,
|
|
372
392
|
children: /* @__PURE__ */ jsx(TooltipProvider, {
|
|
373
393
|
...tooltipProviderDelay,
|
|
374
394
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -378,11 +398,16 @@ function DataTableRootInternal({ columns, data, children, className, config, get
|
|
|
378
398
|
})
|
|
379
399
|
});
|
|
380
400
|
}
|
|
381
|
-
function
|
|
382
|
-
|
|
383
|
-
|
|
401
|
+
function ExternalTableRoot({ table, columns, children, className, isLoading }) {
|
|
402
|
+
const [tableWidth, setTableWidth] = React.useState(0);
|
|
403
|
+
const isCompact = isCompactWidth(tableWidth);
|
|
404
|
+
return /* @__PURE__ */ jsx(DataTableProvider, {
|
|
405
|
+
table,
|
|
384
406
|
columns,
|
|
385
407
|
isLoading,
|
|
408
|
+
isCompact,
|
|
409
|
+
onWidthChange: setTableWidth,
|
|
410
|
+
compactColumns: null,
|
|
386
411
|
children: /* @__PURE__ */ jsx(TooltipProvider, {
|
|
387
412
|
...tooltipProviderDelay,
|
|
388
413
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -391,6 +416,16 @@ function DataTableRoot({ table: externalTable, columns, data, children, classNam
|
|
|
391
416
|
})
|
|
392
417
|
})
|
|
393
418
|
});
|
|
419
|
+
}
|
|
420
|
+
const NOTHING_HIDDEN = /* @__PURE__ */ new Set();
|
|
421
|
+
function DataTableRoot({ table: externalTable, columns, data, children, className, isLoading, ...rest }) {
|
|
422
|
+
if (externalTable) return /* @__PURE__ */ jsx(ExternalTableRoot, {
|
|
423
|
+
table: externalTable,
|
|
424
|
+
columns,
|
|
425
|
+
className,
|
|
426
|
+
isLoading,
|
|
427
|
+
children
|
|
428
|
+
});
|
|
394
429
|
if (!columns || !data) throw new Error("DataTableRoot: Either provide a 'table' prop or both 'columns' and 'data' props");
|
|
395
430
|
return /* @__PURE__ */ jsx(DataTableRootInternal, {
|
|
396
431
|
columns,
|
|
@@ -41,6 +41,7 @@ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky
|
|
|
41
41
|
};
|
|
42
42
|
return /* @__PURE__ */ jsxs(TableHead, {
|
|
43
43
|
"data-col-id": header.column.id,
|
|
44
|
+
"data-numeric": header.column.columnDef.meta?.variant === "number" || void 0,
|
|
44
45
|
style: headerStyle,
|
|
45
46
|
className: cn(!isCompact && header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
|
|
46
47
|
children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(DataTableColumnHeaderRoot, {
|
|
@@ -82,6 +83,7 @@ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId,
|
|
|
82
83
|
return /* @__PURE__ */ jsx(TableCell, {
|
|
83
84
|
"data-flash": flashing ? "true" : void 0,
|
|
84
85
|
"data-col-id": cell.column.id,
|
|
86
|
+
"data-numeric": cell.column.columnDef.meta?.variant === "number" || void 0,
|
|
85
87
|
style: cellStyle,
|
|
86
88
|
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
89
|
children: renderCellContent(cell)
|
|
@@ -17,7 +17,7 @@ const getCommonPinningStyles = (column, isHeader = false, pinningEnabled = true)
|
|
|
17
17
|
flexShrink: 0,
|
|
18
18
|
zIndex: isHeader ? 20 : 10,
|
|
19
19
|
backgroundColor: "var(--card)",
|
|
20
|
-
|
|
20
|
+
boxShadow: isLeft ? "inset -1px 0 0 0 var(--border)" : "inset 1px 0 0 0 var(--border)"
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
23
|
//#endregion
|
|
@@ -23,6 +23,15 @@ declare module "@tanstack/react-table" {
|
|
|
23
23
|
* `TableMeta.disableFlexFill`.
|
|
24
24
|
*/
|
|
25
25
|
flex?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Never hide this column to fit. Once any column sets it, a table whose
|
|
28
|
+
* columns do not fit its width hides the others, rightmost first, until
|
|
29
|
+
* they do, and `<DataTableCompactColumnsToggle />` shows them all again.
|
|
30
|
+
* Mark the few a reader needs to answer the table's question: the name and
|
|
31
|
+
* its result. Read `useDataTable().compactColumns?.hidden` in a cell to
|
|
32
|
+
* fold a hidden figure under a kept one.
|
|
33
|
+
*/
|
|
34
|
+
compact?: boolean;
|
|
26
35
|
variant?: FilterVariant;
|
|
27
36
|
options?: Option[];
|
|
28
37
|
range?: [number, number];
|
|
@@ -29,14 +29,21 @@ function NavMain({ items, label = "Navigation" }) {
|
|
|
29
29
|
className: "group/collapsible",
|
|
30
30
|
render: /* @__PURE__ */ jsx(SidebarMenuItem, {}),
|
|
31
31
|
children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {
|
|
32
|
-
render: /* @__PURE__ */ jsx(SidebarMenuButton, {
|
|
32
|
+
render: /* @__PURE__ */ jsx(SidebarMenuButton, {
|
|
33
|
+
tooltip: item.title,
|
|
34
|
+
isActive: item.isActive && !item.items?.some((subItem) => subItem.isActive)
|
|
35
|
+
}),
|
|
33
36
|
children: [
|
|
34
37
|
item.icon,
|
|
35
38
|
/* @__PURE__ */ jsx("span", { children: item.title }),
|
|
36
39
|
/* @__PURE__ */ jsx(ChevronRightIcon, { className: "ml-auto transition-transform duration-200 group-data-open/collapsible:rotate-90" })
|
|
37
40
|
]
|
|
38
41
|
}), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.items?.map((subItem) => /* @__PURE__ */ jsx(SidebarMenuSubItem, { children: /* @__PURE__ */ jsx(SidebarMenuSubButton, {
|
|
39
|
-
|
|
42
|
+
isActive: subItem.isActive,
|
|
43
|
+
render: /* @__PURE__ */ jsx("a", {
|
|
44
|
+
href: subItem.url,
|
|
45
|
+
"aria-current": subItem.isActive ? "page" : void 0
|
|
46
|
+
}),
|
|
40
47
|
onClick: closeDrawer,
|
|
41
48
|
children: /* @__PURE__ */ jsx("span", { children: subItem.title })
|
|
42
49
|
}) }, subItem.title)) }) })]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentProps, ReactNode } from "react";
|
|
2
|
+
//#region src/components/siyahi/callout.d.ts
|
|
3
|
+
declare const TONES: {
|
|
4
|
+
readonly neutral: "border-border bg-muted/50 [&_[data-slot=callout-icon]]:text-muted-foreground";
|
|
5
|
+
readonly accent: "border-chart-1/35 bg-chart-1/8 [&_[data-slot=callout-icon]]:text-link";
|
|
6
|
+
readonly warning: "border-warning/45 bg-warning/10 [&_[data-slot=callout-icon]]:text-warning";
|
|
7
|
+
readonly positive: "border-positive/40 bg-positive/8 [&_[data-slot=callout-icon]]:text-positive";
|
|
8
|
+
readonly negative: "border-negative/40 bg-negative/8 [&_[data-slot=callout-icon]]:text-negative";
|
|
9
|
+
};
|
|
10
|
+
export interface CalloutProps extends Omit<ComponentProps<"aside">, "title"> {
|
|
11
|
+
/** neutral for an aside, accent for a key point, warning for a caution, positive or negative for a gain or a loss. */
|
|
12
|
+
tone?: keyof typeof TONES;
|
|
13
|
+
/** A lucide icon, drawn in the tone's colour. */
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A point set apart from the prose: a takeaway, a caveat, a definition. The whole box takes the tone, a faint wash
|
|
19
|
+
* inside a border of the same colour; never a single coloured edge. For a notice that interrupts, use Alert or
|
|
20
|
+
* WarningAlert instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare function Callout({ tone, icon, title, className, children, ...props }: CalloutProps): import("react").JSX.Element;
|
|
23
|
+
//#endregion
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/siyahi/callout.tsx
|
|
4
|
+
const TONES = {
|
|
5
|
+
neutral: "border-border bg-muted/50 [&_[data-slot=callout-icon]]:text-muted-foreground",
|
|
6
|
+
accent: "border-chart-1/35 bg-chart-1/8 [&_[data-slot=callout-icon]]:text-link",
|
|
7
|
+
warning: "border-warning/45 bg-warning/10 [&_[data-slot=callout-icon]]:text-warning",
|
|
8
|
+
positive: "border-positive/40 bg-positive/8 [&_[data-slot=callout-icon]]:text-positive",
|
|
9
|
+
negative: "border-negative/40 bg-negative/8 [&_[data-slot=callout-icon]]:text-negative"
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A point set apart from the prose: a takeaway, a caveat, a definition. The whole box takes the tone, a faint wash
|
|
13
|
+
* inside a border of the same colour; never a single coloured edge. For a notice that interrupts, use Alert or
|
|
14
|
+
* WarningAlert instead.
|
|
15
|
+
*/
|
|
16
|
+
function Callout({ tone = "neutral", icon, title, className, children, ...props }) {
|
|
17
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
18
|
+
"data-slot": "callout",
|
|
19
|
+
"data-tone": tone,
|
|
20
|
+
className: cn("flex gap-3 rounded-lg border px-4 py-3 text-sm", TONES[tone], className),
|
|
21
|
+
...props,
|
|
22
|
+
children: [icon && /* @__PURE__ */ jsx("span", {
|
|
23
|
+
"data-slot": "callout-icon",
|
|
24
|
+
"aria-hidden": true,
|
|
25
|
+
className: "mt-0.5 shrink-0 [&>svg]:size-4",
|
|
26
|
+
children: icon
|
|
27
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
28
|
+
className: "flex min-w-0 flex-col gap-1",
|
|
29
|
+
children: [title && /* @__PURE__ */ jsx("p", {
|
|
30
|
+
className: "font-medium",
|
|
31
|
+
children: title
|
|
32
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
33
|
+
className: "text-pretty text-muted-foreground [&_strong]:text-foreground",
|
|
34
|
+
children
|
|
35
|
+
})]
|
|
36
|
+
})]
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { Callout };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DeltaProps } from "./delta.js";
|
|
2
|
+
//#region src/components/siyahi/change-bar.d.ts
|
|
3
|
+
export interface ChangeBarProps {
|
|
4
|
+
/** The signed change, as Delta takes it. */
|
|
5
|
+
value: number;
|
|
6
|
+
/** The column's largest change by size: Math.max(...rows.map((row) => Math.abs(row.change))). */
|
|
7
|
+
max: number;
|
|
8
|
+
/**
|
|
9
|
+
* Draws the bar from a centre line, losses leftward and gains rightward. Pass it when the column holds both signs:
|
|
10
|
+
* rows.some((row) => row.change < 0) && rows.some((row) => row.change > 0).
|
|
11
|
+
*/
|
|
12
|
+
diverging?: boolean;
|
|
13
|
+
format?: DeltaProps["format"];
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A table cell's change as a Delta over a 3px bar whose length is the change's size against the column's largest, so
|
|
18
|
+
* the rows rank by eye. The bar is neutral because it shows size; the Delta above it carries the gain or loss colour.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ChangeBar({ value, max, diverging, format, className }: ChangeBarProps): import("react").JSX.Element;
|
|
21
|
+
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Delta } from "./delta.js";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/siyahi/change-bar.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A table cell's change as a Delta over a 3px bar whose length is the change's size against the column's largest, so
|
|
7
|
+
* the rows rank by eye. The bar is neutral because it shows size; the Delta above it carries the gain or loss colour.
|
|
8
|
+
*/
|
|
9
|
+
function ChangeBar({ value, max, diverging = false, format, className }) {
|
|
10
|
+
const share = max > 0 ? Math.min(1, Math.abs(value) / max) : 0;
|
|
11
|
+
const half = diverging ? 50 : 100;
|
|
12
|
+
const side = diverging && value < 0 ? "right" : "left";
|
|
13
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
14
|
+
"data-slot": "change-bar",
|
|
15
|
+
className: cn("flex w-full flex-col items-end gap-1.5", className),
|
|
16
|
+
children: [/* @__PURE__ */ jsx(Delta, {
|
|
17
|
+
value,
|
|
18
|
+
format,
|
|
19
|
+
className: "font-medium"
|
|
20
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
21
|
+
"aria-hidden": "true",
|
|
22
|
+
className: "relative h-0.75 w-full rounded-full bg-muted",
|
|
23
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
24
|
+
"data-slot": "change-bar-fill",
|
|
25
|
+
className: "absolute inset-y-0 rounded-full bg-foreground",
|
|
26
|
+
style: {
|
|
27
|
+
[side]: diverging ? "50%" : 0,
|
|
28
|
+
width: `${share * half}%`
|
|
29
|
+
}
|
|
30
|
+
}), diverging && /* @__PURE__ */ jsx("div", { className: "absolute -inset-y-1 left-1/2 w-px -translate-x-1/2 bg-muted-foreground" })]
|
|
31
|
+
})]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { ChangeBar };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
//#region src/components/siyahi/data-table-compact-columns-toggle.d.ts
|
|
2
|
+
/** Renders only while some columns are hidden to fit, or the reader has shown them all. */
|
|
3
|
+
export declare function DataTableCompactColumnsToggle(): import("react").JSX.Element | null;
|
|
4
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Button } from "../ui/button.js";
|
|
2
|
+
import { useDataTable } from "../registry/niko-table/core/data-table-context.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/siyahi/data-table-compact-columns-toggle.tsx
|
|
5
|
+
/** Renders only while some columns are hidden to fit, or the reader has shown them all. */
|
|
6
|
+
function DataTableCompactColumnsToggle() {
|
|
7
|
+
const { compactColumns } = useDataTable();
|
|
8
|
+
if (!compactColumns) return null;
|
|
9
|
+
const { collapsed, setCollapsed } = compactColumns;
|
|
10
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
11
|
+
variant: "outline",
|
|
12
|
+
size: "sm",
|
|
13
|
+
"aria-pressed": !collapsed,
|
|
14
|
+
onClick: () => setCollapsed(!collapsed),
|
|
15
|
+
children: collapsed ? `All columns (+${compactColumns.hidden.size})` : "Key columns"
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { DataTableCompactColumnsToggle };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/siyahi/data-table-footnote.d.ts
|
|
3
|
+
export interface DataTableFootnoteProps {
|
|
4
|
+
/** One sentence on how to read the table, such as what each bar measures. */
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A full-width last row, inside `DataTable` after its body, that explains the table's encoding. On a table wider than
|
|
9
|
+
* its card the note stays put at the card's width while the columns scroll under it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function DataTableFootnote({ children }: DataTableFootnoteProps): import("react").JSX.Element;
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useDataTable } from "../registry/niko-table/core/data-table-context.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
//#region src/components/siyahi/data-table-footnote.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A full-width last row, inside `DataTable` after its body, that explains the table's encoding. On a table wider than
|
|
7
|
+
* its card the note stays put at the card's width while the columns scroll under it.
|
|
8
|
+
*/
|
|
9
|
+
function DataTableFootnote({ children }) {
|
|
10
|
+
const { table, scrollContainer } = useDataTable();
|
|
11
|
+
const [viewport, setViewport] = useState();
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!scrollContainer) return;
|
|
14
|
+
const observer = new ResizeObserver(() => setViewport(scrollContainer.clientWidth));
|
|
15
|
+
observer.observe(scrollContainer);
|
|
16
|
+
return () => observer.disconnect();
|
|
17
|
+
}, [scrollContainer]);
|
|
18
|
+
return /* @__PURE__ */ jsx("tfoot", {
|
|
19
|
+
"data-slot": "data-table-footnote",
|
|
20
|
+
children: /* @__PURE__ */ jsx("tr", {
|
|
21
|
+
className: "border-t",
|
|
22
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
23
|
+
colSpan: table.getVisibleLeafColumns().length,
|
|
24
|
+
className: "p-0",
|
|
25
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
26
|
+
className: "sticky left-0 px-2 py-3 text-sm whitespace-normal text-muted-foreground",
|
|
27
|
+
style: { maxWidth: viewport },
|
|
28
|
+
children
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { DataTableFootnote };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/components/siyahi/default-sidebar-open.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* SidebarProvider's `defaultOpen` for an app shell: the reader's last choice, which the provider keeps in the
|
|
4
|
+
* `sidebar_state` cookie, else open — except on a short landscape screen, a phone held sideways, where an open
|
|
5
|
+
* sidebar leaves no room for the page. There it starts as the icon rail.
|
|
6
|
+
*/
|
|
7
|
+
export declare function defaultSidebarOpen(): boolean;
|
|
8
|
+
//#endregion
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/components/siyahi/default-sidebar-open.ts
|
|
2
|
+
/**
|
|
3
|
+
* SidebarProvider's `defaultOpen` for an app shell: the reader's last choice, which the provider keeps in the
|
|
4
|
+
* `sidebar_state` cookie, else open — except on a short landscape screen, a phone held sideways, where an open
|
|
5
|
+
* sidebar leaves no room for the page. There it starts as the icon rail.
|
|
6
|
+
*/
|
|
7
|
+
function defaultSidebarOpen() {
|
|
8
|
+
const saved = document.cookie.split("; ").find((entry) => entry.startsWith("sidebar_state="))?.slice(14);
|
|
9
|
+
if (saved !== void 0) return saved === "true";
|
|
10
|
+
return !matchMedia("(orientation: landscape) and (max-height: 30rem)").matches;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { defaultSidebarOpen };
|
|
@@ -5,6 +5,10 @@ export interface DeltaProps {
|
|
|
5
5
|
format?: "pct" | "money";
|
|
6
6
|
className?: string;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* A signed change: the figure in the surrounding text colour with its sign, and a trend icon in --positive or
|
|
10
|
+
* --negative. The sign and the icon's shape carry the direction, so colour is never the only cue, and a column of
|
|
11
|
+
* changes reads as figures rather than a wall of red and green.
|
|
12
|
+
*/
|
|
9
13
|
export declare function Delta({ value, format, className }: DeltaProps): import("react").JSX.Element;
|
|
10
14
|
//#endregion
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { useFormat } from "../../lib/format.js";
|
|
2
2
|
import { cn } from "cn";
|
|
3
|
+
import { MinusIcon, TrendingDownIcon, TrendingUpIcon } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/siyahi/delta.tsx
|
|
5
|
-
|
|
6
|
+
const MINUS = "−";
|
|
7
|
+
/**
|
|
8
|
+
* A signed change: the figure in the surrounding text colour with its sign, and a trend icon in --positive or
|
|
9
|
+
* --negative. The sign and the icon's shape carry the direction, so colour is never the only cue, and a column of
|
|
10
|
+
* changes reads as figures rather than a wall of red and green.
|
|
11
|
+
*/
|
|
6
12
|
function Delta({ value, format = "pct", className }) {
|
|
7
13
|
const f = useFormat();
|
|
8
14
|
const size = format === "money" ? f.money(Math.abs(value)) : f.pct(Math.abs(value));
|
|
9
|
-
const direction = value > 0 ? "up" : value < 0 ? "down" :
|
|
15
|
+
const direction = value > 0 ? "up" : value < 0 ? "down" : "none";
|
|
16
|
+
const Icon = direction === "up" ? TrendingUpIcon : direction === "down" ? TrendingDownIcon : MinusIcon;
|
|
10
17
|
return /* @__PURE__ */ jsxs("span", {
|
|
11
18
|
"data-slot": "delta",
|
|
12
|
-
|
|
19
|
+
"data-direction": direction,
|
|
20
|
+
className: cn("inline-flex items-center gap-1 whitespace-nowrap", className),
|
|
13
21
|
children: [
|
|
14
|
-
|
|
22
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
15
23
|
"aria-hidden": "true",
|
|
16
|
-
|
|
17
|
-
}),
|
|
18
|
-
direction && /* @__PURE__ */ jsx("span", {
|
|
19
|
-
className: "sr-only",
|
|
20
|
-
children: direction
|
|
24
|
+
className: cn("size-3.5 shrink-0", direction === "up" && "text-positive", direction === "down" && "text-negative", direction === "none" && "text-muted-foreground")
|
|
21
25
|
}),
|
|
26
|
+
direction === "up" ? "+" : direction === "down" ? MINUS : "",
|
|
22
27
|
size
|
|
23
28
|
]
|
|
24
29
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/siyahi/page-hero.d.ts
|
|
3
|
+
export interface PageHeroProps {
|
|
4
|
+
/** A short line above the title in the link colour, such as a package or a section name. */
|
|
5
|
+
eyebrow?: ReactNode;
|
|
6
|
+
/** The page's h1, set large. */
|
|
7
|
+
title: ReactNode;
|
|
8
|
+
/** What the page is, in a sentence or two, in the body colour. */
|
|
9
|
+
lead?: ReactNode;
|
|
10
|
+
/** Muted detail under the lead: scope, sources, what is left out. */
|
|
11
|
+
detail?: ReactNode;
|
|
12
|
+
/** The one or two places a reader goes next, usually a primary Button and an outline one. */
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* A large decorative mark drawn faintly in the accent behind the top right, such as the product's logo as an
|
|
16
|
+
* SVG that paints with currentColor. It is hidden from assistive technology.
|
|
17
|
+
*/
|
|
18
|
+
mark?: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The introduction at the top of a front page or a long explainer: a large title, what the page is, and where to
|
|
22
|
+
* go next, on a card with an optional watermark. One per page, above everything else; a section uses PageSection.
|
|
23
|
+
*/
|
|
24
|
+
export declare function PageHero({ eyebrow, title, lead, detail, actions, mark }: PageHeroProps): import("react").JSX.Element;
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/siyahi/page-hero.tsx
|
|
3
|
+
/**
|
|
4
|
+
* The introduction at the top of a front page or a long explainer: a large title, what the page is, and where to
|
|
5
|
+
* go next, on a card with an optional watermark. One per page, above everything else; a section uses PageSection.
|
|
6
|
+
*/
|
|
7
|
+
function PageHero({ eyebrow, title, lead, detail, actions, mark }) {
|
|
8
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
9
|
+
"data-slot": "page-hero",
|
|
10
|
+
className: "relative isolate flex flex-col gap-4 overflow-hidden rounded-2xl border bg-card px-6 py-8 sm:px-10 sm:py-10",
|
|
11
|
+
children: [
|
|
12
|
+
mark && /* @__PURE__ */ jsx("div", {
|
|
13
|
+
"aria-hidden": true,
|
|
14
|
+
className: "pointer-events-none absolute -top-8 -right-10 -z-10 size-80 text-chart-1 opacity-10 sm:-top-12 sm:right-4 [&>svg]:size-full",
|
|
15
|
+
children: mark
|
|
16
|
+
}),
|
|
17
|
+
eyebrow && /* @__PURE__ */ jsx("p", {
|
|
18
|
+
className: "font-mono text-xs text-link",
|
|
19
|
+
children: eyebrow
|
|
20
|
+
}),
|
|
21
|
+
/* @__PURE__ */ jsx("h1", {
|
|
22
|
+
className: "text-5xl font-semibold tracking-tighter text-balance sm:text-6xl",
|
|
23
|
+
children: title
|
|
24
|
+
}),
|
|
25
|
+
lead && /* @__PURE__ */ jsx("div", {
|
|
26
|
+
className: "max-w-xl text-lg text-pretty",
|
|
27
|
+
children: lead
|
|
28
|
+
}),
|
|
29
|
+
detail && /* @__PURE__ */ jsx("div", {
|
|
30
|
+
className: "max-w-xl text-pretty text-muted-foreground",
|
|
31
|
+
children: detail
|
|
32
|
+
}),
|
|
33
|
+
actions && /* @__PURE__ */ jsx("div", {
|
|
34
|
+
className: "mt-2 flex flex-wrap gap-2",
|
|
35
|
+
children: actions
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { PageHero };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/components/siyahi/page-toc.d.ts
|
|
2
|
+
export interface PageTocItem {
|
|
3
|
+
/** The `id` of the PageSection it jumps to. */
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PageTocProps {
|
|
8
|
+
items: PageTocItem[];
|
|
9
|
+
/** Names the navigation for screen readers. */
|
|
10
|
+
label?: string;
|
|
11
|
+
/** Layout only, such as `top-14` under a sticky page header. */
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A long page's sections as one sticky "On this page" index. It stays at the top while the reader scrolls,
|
|
16
|
+
* underlines the section in view in the link colour, and scrolls sideways on a phone with the current link kept
|
|
17
|
+
* in sight.
|
|
18
|
+
*/
|
|
19
|
+
export declare function PageToc({ items, label, className }: PageTocProps): import("react").JSX.Element;
|
|
20
|
+
//#endregion
|