@datum-cloud/datum-ui 2.9.1 → 2.11.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/dist/assistant/index.mjs +133 -24
- package/dist/components/features/assistant/components/assistant-workspace.d.ts +11 -1
- package/dist/components/features/assistant/components/sidebar/history-panel.d.ts +16 -3
- package/dist/components/features/assistant/types.d.ts +5 -0
- package/dist/components/features/data-table/components/column-header.d.ts +1 -1
- package/dist/components/features/data-table/components/content.d.ts +2 -1
- package/dist/components/features/data-table/components/list-pagination.d.ts +7 -0
- package/dist/components/features/data-table/components/list-panel.d.ts +10 -0
- package/dist/components/features/data-table/data-table.d.ts +4 -0
- package/dist/components/features/data-table/index.d.ts +1 -1
- package/dist/components/features/data-table/types.d.ts +49 -0
- package/dist/data-table/index.mjs +1 -1
- package/dist/{data-table-CW3-xqkg.mjs → data-table-DrSsRKRi.mjs} +244 -27
- package/dist/grouped-table/index.mjs +1 -1
- package/dist/{grouped-table-DKUsSHpe.mjs → grouped-table-DIFPMKLL.mjs} +1 -1
- package/dist/index.mjs +2 -2
- package/dist/styles/themes/alpha.css +16 -0
- package/package.json +1 -1
package/dist/assistant/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { t as cn } from "../utils-Bu32wN-x.mjs";
|
|
2
|
+
import { t as Button } from "../button-DoMqfQLu.mjs";
|
|
2
3
|
import { t as Icon } from "../icon-wrapper-DKfJlJd0.mjs";
|
|
4
|
+
import { t as Dialog } from "../dialog-Gu70fban.mjs";
|
|
3
5
|
import { a as DropdownMenuRadioGroup, c as DropdownMenuTrigger, i as DropdownMenuLabel, o as DropdownMenuRadioItem, r as DropdownMenuContent, s as DropdownMenuSeparator, t as DropdownMenu } from "../dropdown-menu-b8zY5Cs6.mjs";
|
|
4
6
|
import { t as Tooltip } from "../tooltip-DhbN1BDK.mjs";
|
|
5
7
|
import { n as EASE, t as DURATION } from "../motion-DvGRWdsm.mjs";
|
|
6
|
-
import { ArrowDown, ArrowRight, ArrowUp, Brain, Check, ChevronDown, Copy, Download, MessagesSquare, Mic, MicOff, PanelLeft, Plus, RotateCw, Square, Trash2 } from "lucide-react";
|
|
8
|
+
import { Archive, ArchiveRestore, ArrowDown, ArrowRight, ArrowUp, Brain, Check, ChevronDown, Copy, Download, MessagesSquare, Mic, MicOff, PanelLeft, Plus, RotateCw, Square, Trash2 } from "lucide-react";
|
|
7
9
|
import { Fragment, createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
8
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
import { AnimatePresence, motion } from "motion/react";
|
|
@@ -842,12 +844,32 @@ function downloadChat(chat) {
|
|
|
842
844
|
a.click();
|
|
843
845
|
URL.revokeObjectURL(url);
|
|
844
846
|
}
|
|
845
|
-
|
|
847
|
+
const ROW_ACTION_CLASS = "text-muted-foreground/40 shrink-0 rounded p-0.5 opacity-0 transition-colors group-hover:opacity-100";
|
|
848
|
+
function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, onArchiveChat, onUnarchiveChat, confirmDelete = false, header }) {
|
|
849
|
+
const archiveEnabled = Boolean(onArchiveChat);
|
|
846
850
|
const [query, setQuery] = useState("");
|
|
851
|
+
const [showArchived, setShowArchived] = useState(false);
|
|
852
|
+
const [pendingDelete, setPendingDelete] = useState(null);
|
|
853
|
+
const inArchivedView = archiveEnabled && showArchived;
|
|
847
854
|
const filtered = useMemo(() => {
|
|
855
|
+
const inView = archiveEnabled ? chatList.filter((c) => Boolean(c.archived) === showArchived) : chatList;
|
|
848
856
|
const q = query.trim().toLowerCase();
|
|
849
|
-
return q ?
|
|
850
|
-
}, [
|
|
857
|
+
return q ? inView.filter((c) => c.title.toLowerCase().includes(q)) : inView;
|
|
858
|
+
}, [
|
|
859
|
+
archiveEnabled,
|
|
860
|
+
chatList,
|
|
861
|
+
query,
|
|
862
|
+
showArchived
|
|
863
|
+
]);
|
|
864
|
+
const emptyText = inArchivedView ? query ? "No matching archived chats" : "No archived chats" : query ? "No matching chats" : "No saved chats";
|
|
865
|
+
const handleDelete = (e, chat) => {
|
|
866
|
+
if (!confirmDelete) {
|
|
867
|
+
onDeleteChat(e, chat.id);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
e.stopPropagation();
|
|
871
|
+
setPendingDelete(chat);
|
|
872
|
+
};
|
|
851
873
|
return /* @__PURE__ */ jsxs("div", {
|
|
852
874
|
className: "bg-background flex h-full w-64 shrink-0 flex-col border-r",
|
|
853
875
|
children: [
|
|
@@ -855,36 +877,90 @@ function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, heade
|
|
|
855
877
|
className: "shrink-0 border-b px-3 py-2",
|
|
856
878
|
children: header
|
|
857
879
|
}),
|
|
858
|
-
/* @__PURE__ */
|
|
859
|
-
className: "flex h-12 items-center border-b px-2",
|
|
860
|
-
children: /* @__PURE__ */ jsx("input", {
|
|
880
|
+
/* @__PURE__ */ jsxs("div", {
|
|
881
|
+
className: "flex h-12 items-center gap-1 border-b px-2",
|
|
882
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
861
883
|
value: query,
|
|
862
884
|
onChange: (e) => setQuery(e.target.value),
|
|
863
|
-
placeholder: "Search chats…",
|
|
885
|
+
placeholder: inArchivedView ? "Search archived chats…" : "Search chats…",
|
|
864
886
|
className: "bg-muted text-foreground placeholder:text-muted-foreground/70 focus:ring-primary/40 w-full rounded-md px-2.5 py-1.5 text-xs focus:ring-1 focus:outline-none"
|
|
865
|
-
})
|
|
887
|
+
}), archiveEnabled && /* @__PURE__ */ jsx(Tooltip, {
|
|
888
|
+
message: showArchived ? "Show active chats" : "Show archived chats",
|
|
889
|
+
side: "bottom",
|
|
890
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
891
|
+
type: "button",
|
|
892
|
+
"aria-label": "Archived",
|
|
893
|
+
"aria-pressed": showArchived,
|
|
894
|
+
onClick: () => setShowArchived((v) => !v),
|
|
895
|
+
className: cn("shrink-0 rounded-md p-1.5 transition-colors", showArchived ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent"),
|
|
896
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
897
|
+
icon: Archive,
|
|
898
|
+
className: "size-3.5"
|
|
899
|
+
})
|
|
900
|
+
})
|
|
901
|
+
})]
|
|
866
902
|
}),
|
|
867
903
|
/* @__PURE__ */ jsx("div", {
|
|
868
904
|
className: "flex flex-1 flex-col gap-0.5 overflow-y-auto p-2",
|
|
869
905
|
children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
870
906
|
className: "text-muted-foreground/60 px-2 py-1 text-xs",
|
|
871
|
-
children:
|
|
907
|
+
children: emptyText
|
|
872
908
|
}) : filtered.map((chat) => /* @__PURE__ */ jsxs("button", {
|
|
873
909
|
type: "button",
|
|
874
910
|
onClick: () => onLoadChat(chat),
|
|
875
911
|
className: cn("group w-full rounded-lg px-2 py-1.5 text-left transition-colors", chat.id === currentChatId ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"),
|
|
876
912
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
877
913
|
className: "flex items-center gap-1",
|
|
878
|
-
children: [
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
914
|
+
children: [
|
|
915
|
+
/* @__PURE__ */ jsx("span", {
|
|
916
|
+
className: "min-w-0 flex-1 truncate text-xs font-medium",
|
|
917
|
+
children: chat.title
|
|
918
|
+
}),
|
|
919
|
+
archiveEnabled && !inArchivedView && /* @__PURE__ */ jsx(Tooltip, {
|
|
920
|
+
message: "Archive chat",
|
|
921
|
+
side: "top",
|
|
922
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
923
|
+
role: "button",
|
|
924
|
+
onClick: (e) => {
|
|
925
|
+
e.stopPropagation();
|
|
926
|
+
onArchiveChat?.(e, chat.id);
|
|
927
|
+
},
|
|
928
|
+
"aria-label": "Archive chat",
|
|
929
|
+
className: cn(ROW_ACTION_CLASS, "hover:text-foreground"),
|
|
930
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
931
|
+
icon: Archive,
|
|
932
|
+
className: "size-3"
|
|
933
|
+
})
|
|
934
|
+
})
|
|
935
|
+
}),
|
|
936
|
+
inArchivedView && onUnarchiveChat && /* @__PURE__ */ jsx(Tooltip, {
|
|
937
|
+
message: "Unarchive chat",
|
|
938
|
+
side: "top",
|
|
939
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
940
|
+
role: "button",
|
|
941
|
+
onClick: (e) => {
|
|
942
|
+
e.stopPropagation();
|
|
943
|
+
onUnarchiveChat(e, chat.id);
|
|
944
|
+
},
|
|
945
|
+
"aria-label": "Unarchive chat",
|
|
946
|
+
className: cn(ROW_ACTION_CLASS, "hover:text-foreground"),
|
|
947
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
948
|
+
icon: ArchiveRestore,
|
|
949
|
+
className: "size-3"
|
|
950
|
+
})
|
|
951
|
+
})
|
|
952
|
+
}),
|
|
953
|
+
/* @__PURE__ */ jsx("span", {
|
|
954
|
+
role: "button",
|
|
955
|
+
onClick: (e) => handleDelete(e, chat),
|
|
956
|
+
"aria-label": "Delete chat",
|
|
957
|
+
className: cn(ROW_ACTION_CLASS, "hover:text-destructive"),
|
|
958
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
959
|
+
icon: Trash2,
|
|
960
|
+
className: "size-3"
|
|
961
|
+
})
|
|
962
|
+
})
|
|
963
|
+
]
|
|
888
964
|
}), /* @__PURE__ */ jsxs("span", {
|
|
889
965
|
className: "flex items-center gap-1",
|
|
890
966
|
children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -900,8 +976,11 @@ function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, heade
|
|
|
900
976
|
downloadChat(chat);
|
|
901
977
|
},
|
|
902
978
|
"aria-label": "Download chat as Markdown",
|
|
903
|
-
className: "
|
|
904
|
-
children: /* @__PURE__ */ jsx(
|
|
979
|
+
className: cn(ROW_ACTION_CLASS, "hover:text-foreground"),
|
|
980
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
981
|
+
icon: Download,
|
|
982
|
+
className: "size-3"
|
|
983
|
+
})
|
|
905
984
|
})
|
|
906
985
|
})]
|
|
907
986
|
})]
|
|
@@ -910,13 +989,40 @@ function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, heade
|
|
|
910
989
|
/* @__PURE__ */ jsx("p", {
|
|
911
990
|
className: "text-muted-foreground mt-auto shrink-0 border-t px-3 py-2 text-[10px]",
|
|
912
991
|
children: "Chats are saved to your browser's local storage."
|
|
992
|
+
}),
|
|
993
|
+
confirmDelete && /* @__PURE__ */ jsx(Dialog, {
|
|
994
|
+
open: pendingDelete !== null,
|
|
995
|
+
onOpenChange: (open) => !open && setPendingDelete(null),
|
|
996
|
+
children: /* @__PURE__ */ jsxs(Dialog.Content, {
|
|
997
|
+
className: "sm:max-w-md",
|
|
998
|
+
children: [/* @__PURE__ */ jsx(Dialog.Header, {
|
|
999
|
+
title: "Delete chat?",
|
|
1000
|
+
description: pendingDelete ? `"${pendingDelete.title}" will be permanently deleted. This can't be undone.` : void 0
|
|
1001
|
+
}), /* @__PURE__ */ jsxs(Dialog.Footer, {
|
|
1002
|
+
className: "border-t-0",
|
|
1003
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
1004
|
+
type: "tertiary",
|
|
1005
|
+
theme: "outline",
|
|
1006
|
+
onClick: () => setPendingDelete(null),
|
|
1007
|
+
children: "Cancel"
|
|
1008
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1009
|
+
type: "danger",
|
|
1010
|
+
theme: "solid",
|
|
1011
|
+
onClick: (e) => {
|
|
1012
|
+
if (pendingDelete) onDeleteChat(e, pendingDelete.id);
|
|
1013
|
+
setPendingDelete(null);
|
|
1014
|
+
},
|
|
1015
|
+
children: "Delete"
|
|
1016
|
+
})]
|
|
1017
|
+
})]
|
|
1018
|
+
})
|
|
913
1019
|
})
|
|
914
1020
|
]
|
|
915
1021
|
});
|
|
916
1022
|
}
|
|
917
1023
|
//#endregion
|
|
918
1024
|
//#region src/components/features/assistant/components/assistant-workspace.tsx
|
|
919
|
-
function AssistantWorkspace({ config, userName, title, messages, status, error, isReady, chatList, currentChatId, sidebarHeader, editor, htmlByUserMsgIndex, bottomRef, containerRef, userScrolledUpRef, onSend, onStop, onRetry, onNewChat, onLoadChat, onDeleteChat, onSuggestion, modelId, effortId, onModelChange, onEffortChange, micSupported, micListening, micFrequencyData, onMicToggle, historyOpen, onToggleHistory }) {
|
|
1025
|
+
function AssistantWorkspace({ config, userName, title, messages, status, error, isReady, chatList, currentChatId, sidebarHeader, editor, htmlByUserMsgIndex, bottomRef, containerRef, userScrolledUpRef, onSend, onStop, onRetry, onNewChat, onLoadChat, onDeleteChat, onArchiveChat, onUnarchiveChat, confirmDelete, onSuggestion, modelId, effortId, onModelChange, onEffortChange, micSupported, micListening, micFrequencyData, onMicToggle, historyOpen, onToggleHistory }) {
|
|
920
1026
|
const hasMessages = messages.length > 0;
|
|
921
1027
|
const promptCard = /* @__PURE__ */ jsx(PromptCard, {
|
|
922
1028
|
editor,
|
|
@@ -961,7 +1067,10 @@ function AssistantWorkspace({ config, userName, title, messages, status, error,
|
|
|
961
1067
|
currentChatId,
|
|
962
1068
|
header: sidebarHeader,
|
|
963
1069
|
onLoadChat,
|
|
964
|
-
onDeleteChat
|
|
1070
|
+
onDeleteChat,
|
|
1071
|
+
onArchiveChat,
|
|
1072
|
+
onUnarchiveChat,
|
|
1073
|
+
confirmDelete
|
|
965
1074
|
})
|
|
966
1075
|
})
|
|
967
1076
|
}),
|
|
@@ -38,6 +38,16 @@ export interface AssistantWorkspaceProps {
|
|
|
38
38
|
onNewChat: () => void;
|
|
39
39
|
onLoadChat: (chat: ChatSummary) => void;
|
|
40
40
|
onDeleteChat: (e: ReactMouseEvent, chatId: string) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Opt-in archive support. When provided, `chatList` may mix archived and
|
|
43
|
+
* active chats; the history panel lists active chats by default and adds an
|
|
44
|
+
* "Archived" view toggle plus per-row archive actions.
|
|
45
|
+
*/
|
|
46
|
+
onArchiveChat?: (e: ReactMouseEvent, chatId: string) => void;
|
|
47
|
+
/** Restores an archived chat from the history panel's archived view. */
|
|
48
|
+
onUnarchiveChat?: (e: ReactMouseEvent, chatId: string) => void;
|
|
49
|
+
/** Confirm (deletion can't be undone) before calling `onDeleteChat`. */
|
|
50
|
+
confirmDelete?: boolean;
|
|
41
51
|
onSuggestion: (text: string) => void;
|
|
42
52
|
modelId: string;
|
|
43
53
|
effortId: EffortId;
|
|
@@ -50,4 +60,4 @@ export interface AssistantWorkspaceProps {
|
|
|
50
60
|
historyOpen: boolean;
|
|
51
61
|
onToggleHistory: () => void;
|
|
52
62
|
}
|
|
53
|
-
export declare function AssistantWorkspace({ config, userName, title, messages, status, error, isReady, chatList, currentChatId, sidebarHeader, editor, htmlByUserMsgIndex, bottomRef, containerRef, userScrolledUpRef, onSend, onStop, onRetry, onNewChat, onLoadChat, onDeleteChat, onSuggestion, modelId, effortId, onModelChange, onEffortChange, micSupported, micListening, micFrequencyData, onMicToggle, historyOpen, onToggleHistory, }: AssistantWorkspaceProps): import("react").JSX.Element;
|
|
63
|
+
export declare function AssistantWorkspace({ config, userName, title, messages, status, error, isReady, chatList, currentChatId, sidebarHeader, editor, htmlByUserMsgIndex, bottomRef, containerRef, userScrolledUpRef, onSend, onStop, onRetry, onNewChat, onLoadChat, onDeleteChat, onArchiveChat, onUnarchiveChat, confirmDelete, onSuggestion, modelId, effortId, onModelChange, onEffortChange, micSupported, micListening, micFrequencyData, onMicToggle, historyOpen, onToggleHistory, }: AssistantWorkspaceProps): import("react").JSX.Element;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { ChatSummary } from '../../types';
|
|
3
|
-
interface HistoryPanelProps {
|
|
3
|
+
export interface HistoryPanelProps {
|
|
4
4
|
chatList: ChatSummary[];
|
|
5
5
|
currentChatId: string;
|
|
6
6
|
onLoadChat: (chat: ChatSummary) => void;
|
|
7
7
|
onDeleteChat: (e: React.MouseEvent, chatId: string) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Opt-in archive support. When provided, `chatList` may mix archived and
|
|
10
|
+
* active chats: the panel lists active chats by default, adds an "Archived"
|
|
11
|
+
* view toggle next to the search input, and gives active rows an archive
|
|
12
|
+
* action. When omitted, the `archived` flag is ignored and no archive UI renders.
|
|
13
|
+
*/
|
|
14
|
+
onArchiveChat?: (e: React.MouseEvent, chatId: string) => void;
|
|
15
|
+
/** Restores an archived chat. Rendered on rows in the archived view. */
|
|
16
|
+
onUnarchiveChat?: (e: React.MouseEvent, chatId: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Ask for confirmation (deletion can't be undone) before calling
|
|
19
|
+
* `onDeleteChat`. Defaults to false, which deletes immediately.
|
|
20
|
+
*/
|
|
21
|
+
confirmDelete?: boolean;
|
|
8
22
|
/**
|
|
9
23
|
* Optional host-supplied block pinned above the search input, naming the scope
|
|
10
24
|
* the listed chats belong to — cloud-portal shows the current project. Hosts
|
|
@@ -12,5 +26,4 @@ interface HistoryPanelProps {
|
|
|
12
26
|
*/
|
|
13
27
|
header?: ReactNode;
|
|
14
28
|
}
|
|
15
|
-
export declare function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, header, }: HistoryPanelProps): import("react").JSX.Element;
|
|
16
|
-
export {};
|
|
29
|
+
export declare function HistoryPanel({ chatList, currentChatId, onLoadChat, onDeleteChat, onArchiveChat, onUnarchiveChat, confirmDelete, header, }: HistoryPanelProps): import("react").JSX.Element;
|
|
@@ -63,4 +63,9 @@ export interface ChatSummary {
|
|
|
63
63
|
title: string;
|
|
64
64
|
updatedAt: number;
|
|
65
65
|
messages: UIMessage[];
|
|
66
|
+
/**
|
|
67
|
+
* Archived (hidden but restorable). Only honoured when the host passes
|
|
68
|
+
* `onArchiveChat`; otherwise every chat is listed regardless of this flag.
|
|
69
|
+
*/
|
|
70
|
+
archived?: boolean;
|
|
66
71
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { RowData } from '@tanstack/react-table';
|
|
2
2
|
import type { ColumnHeaderProps } from '../types';
|
|
3
|
-
export declare function DataTableColumnHeader<TData extends RowData, TValue>({ column, title, className, }: ColumnHeaderProps<TData, TValue>): import("react").JSX.Element;
|
|
3
|
+
export declare function DataTableColumnHeader<TData extends RowData, TValue>({ column, title, className, density, }: ColumnHeaderProps<TData, TValue>): import("react").JSX.Element;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import type { RowData } from '@tanstack/react-table';
|
|
1
2
|
import type { ContentProps } from '../types';
|
|
2
|
-
export declare function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName, }: ContentProps): import("react").JSX.Element;
|
|
3
|
+
export declare function DataTableContent<TData extends RowData = Record<string, any>>({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName, density, onRowClick, }: ContentProps<TData>): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ListPaginationProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Compact list-page footer: `[pageSize ▾] Rows per page … 1-100 of N [resource]`
|
|
4
|
+
* + joined prev/next buttons. Pass `labels` to localize — this package ships no
|
|
5
|
+
* i18n of its own.
|
|
6
|
+
*/
|
|
7
|
+
export declare function DataTableListPagination({ pageSizes, resourceLabel, hideWhenSinglePage, labels, className, }: ListPaginationProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RowData } from '@tanstack/react-table';
|
|
2
|
+
import type { ListPanelProps } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Compact "list table" card: search row + dense sticky-header table, matching
|
|
5
|
+
* staff-portal's Figma org-list pattern. Renders inside `DataTable.Client` /
|
|
6
|
+
* `DataTable.Server`, same as every other `DataTable.*` part. Pagination is
|
|
7
|
+
* deliberately not included — render `DataTable.ListPagination` as a sibling,
|
|
8
|
+
* below the panel.
|
|
9
|
+
*/
|
|
10
|
+
export declare function DataTableListPanel<TData extends RowData = Record<string, any>>({ search, searchSlot, toolbar, emptyMessage, loading, onRowClick, className, panelClassName, }: ListPanelProps<TData>): import("react").JSX.Element;
|
|
@@ -2,6 +2,8 @@ import { DataTableBulkActions } from './components/bulk-actions';
|
|
|
2
2
|
import { DataTableColumnHeader } from './components/column-header';
|
|
3
3
|
import { DataTableContent } from './components/content';
|
|
4
4
|
import { DataTableInlineContent } from './components/inline-content';
|
|
5
|
+
import { DataTableListPagination } from './components/list-pagination';
|
|
6
|
+
import { DataTableListPanel } from './components/list-panel';
|
|
5
7
|
import { DataTableLoading } from './components/loading';
|
|
6
8
|
import { DataTablePagination } from './components/pagination';
|
|
7
9
|
import { DataTableRowActions } from './components/row-actions';
|
|
@@ -19,6 +21,8 @@ export declare const DataTable: {
|
|
|
19
21
|
readonly InlineContent: typeof DataTableInlineContent;
|
|
20
22
|
readonly ColumnHeader: typeof DataTableColumnHeader;
|
|
21
23
|
readonly Pagination: typeof DataTablePagination;
|
|
24
|
+
readonly ListPanel: typeof DataTableListPanel;
|
|
25
|
+
readonly ListPagination: typeof DataTableListPagination;
|
|
22
26
|
readonly Search: typeof DataTableSearch;
|
|
23
27
|
readonly RowActions: typeof DataTableRowActions;
|
|
24
28
|
readonly BulkActions: typeof DataTableBulkActions;
|
|
@@ -11,4 +11,4 @@ export type { SearchConfig } from './core/filter-engine';
|
|
|
11
11
|
export { createDataTableStore } from './core/store';
|
|
12
12
|
export { DataTable } from './data-table';
|
|
13
13
|
export { useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, } from './hooks/use-selectors';
|
|
14
|
-
export type { ActionItem, ActiveFiltersProps, BulkActionsProps, ClientPaginationControls, ClientPaginationState, ColumnHeaderProps, ContentProps, CreateStoreOptions, DataTableClientProps, DataTableContextValue, DataTableServerProps, DataTableState, DataTableStore, DataTableStoreState, FilterCheckboxProps, FilterDatePickerProps, FilterFn, FilterFnMap, FilterOption, FilterSelectProps, FilterStrategy, FilterValue, InlineContentEntry, InlineContentProps, InlineContentRenderParams, LoadingProps, PaginationProps, PaginationState, RowActionsProps, SearchProps, SelectionColumnOptions, ServerFetchArgs, ServerPaginationControls, ServerPaginationState, ServerTransformResult, StateAdapter, UseDataTableClientOptions, UseDataTableServerOptions, } from './types';
|
|
14
|
+
export type { ActionItem, ActiveFiltersProps, BulkActionsProps, ClientPaginationControls, ClientPaginationState, ColumnHeaderProps, ContentProps, CreateStoreOptions, DataTableClientProps, DataTableContextValue, DataTableServerProps, DataTableState, DataTableStore, DataTableStoreState, FilterCheckboxProps, FilterDatePickerProps, FilterFn, FilterFnMap, FilterOption, FilterSelectProps, FilterStrategy, FilterValue, InlineContentEntry, InlineContentProps, InlineContentRenderParams, ListPaginationProps, ListPanelProps, ListPanelSearchConfig, LoadingProps, PaginationProps, PaginationState, RowActionsProps, SearchProps, SelectionColumnOptions, ServerFetchArgs, ServerPaginationControls, ServerPaginationState, ServerTransformResult, StateAdapter, UseDataTableClientOptions, UseDataTableServerOptions, } from './types';
|
|
@@ -229,6 +229,8 @@ export interface ColumnHeaderProps<TData extends RowData, TValue> {
|
|
|
229
229
|
readonly column: Column<DataTableFeatures, TData, TValue>;
|
|
230
230
|
readonly title: string;
|
|
231
231
|
readonly className?: string;
|
|
232
|
+
/** `'compact'` swaps in the dense uppercase header label + dual-caret sort icon used by list pages. Default `'default'` renders unchanged. */
|
|
233
|
+
readonly density?: 'default' | 'compact';
|
|
232
234
|
}
|
|
233
235
|
export interface RowActionsProps<TData extends RowData> {
|
|
234
236
|
readonly row: Row<DataTableFeatures, TData>;
|
|
@@ -252,6 +254,10 @@ export interface ContentProps<TData extends RowData = Record<string, any>> {
|
|
|
252
254
|
readonly bodyClassName?: string;
|
|
253
255
|
readonly rowClassName?: string | ((row: Row<DataTableFeatures, TData>) => string);
|
|
254
256
|
readonly cellClassName?: string | ((cell: Cell<DataTableFeatures, TData, unknown>) => string);
|
|
257
|
+
/** `'compact'` applies the sticky mist header, dense rows, and borderless dividers used by list pages. Default `'default'` renders unchanged. */
|
|
258
|
+
readonly density?: 'default' | 'compact';
|
|
259
|
+
/** Fires on row click. Skipped when the click lands on a link, button, checkbox, form control, or the row-actions menu, so nested interactive elements keep working. */
|
|
260
|
+
readonly onRowClick?: (row: TData) => void;
|
|
255
261
|
}
|
|
256
262
|
export interface PaginationProps {
|
|
257
263
|
readonly pageSizes?: readonly number[];
|
|
@@ -282,6 +288,49 @@ export interface LoadingProps {
|
|
|
282
288
|
readonly columns?: number;
|
|
283
289
|
readonly className?: string;
|
|
284
290
|
}
|
|
291
|
+
/** Search-row config for {@link ListPanelProps}. */
|
|
292
|
+
export interface ListPanelSearchConfig {
|
|
293
|
+
readonly placeholder?: string;
|
|
294
|
+
/**
|
|
295
|
+
* Set together with `onChange` to drive the search input externally (server-side
|
|
296
|
+
* search). When set, the built-in store search is not read — the panel renders
|
|
297
|
+
* whatever rows the caller already scoped.
|
|
298
|
+
*/
|
|
299
|
+
readonly value?: string;
|
|
300
|
+
readonly onChange?: (value: string) => void;
|
|
301
|
+
}
|
|
302
|
+
export interface ListPanelProps<TData extends RowData = Record<string, any>> {
|
|
303
|
+
/** `false` hides the search row entirely. Defaults to the built-in (client-filtered) search. */
|
|
304
|
+
readonly search?: false | ListPanelSearchConfig;
|
|
305
|
+
/** Rendered to the right of the search input, inside the search row (e.g. a mobile filter trigger). */
|
|
306
|
+
readonly searchSlot?: ReactNode;
|
|
307
|
+
/** Rendered between the search row and the table (e.g. active-filter chips). */
|
|
308
|
+
readonly toolbar?: ReactNode;
|
|
309
|
+
readonly emptyMessage?: ReactNode;
|
|
310
|
+
/** Renders a skeleton in place of the table body. */
|
|
311
|
+
readonly loading?: boolean;
|
|
312
|
+
/** See {@link ContentProps.onRowClick}. */
|
|
313
|
+
readonly onRowClick?: (row: TData) => void;
|
|
314
|
+
/** Class on the panel's outer element. */
|
|
315
|
+
readonly className?: string;
|
|
316
|
+
/** Class on the card container (border/rounding/background) — merges with, and can override, the default chrome. */
|
|
317
|
+
readonly panelClassName?: string;
|
|
318
|
+
}
|
|
319
|
+
export interface ListPaginationProps {
|
|
320
|
+
readonly pageSizes?: readonly number[];
|
|
321
|
+
/** Appended to the row-count summary, e.g. `"1-20 of 42 organizations"`. */
|
|
322
|
+
readonly resourceLabel?: string;
|
|
323
|
+
/** Hides the whole control when there's one page or fewer. */
|
|
324
|
+
readonly hideWhenSinglePage?: boolean;
|
|
325
|
+
/** English defaults; pass translated strings to localize. */
|
|
326
|
+
readonly labels?: {
|
|
327
|
+
readonly rowsPerPage?: string;
|
|
328
|
+
readonly of?: string;
|
|
329
|
+
readonly previousPage?: string;
|
|
330
|
+
readonly nextPage?: string;
|
|
331
|
+
};
|
|
332
|
+
readonly className?: string;
|
|
333
|
+
}
|
|
285
334
|
export type FilterStrategy = 'checkbox' | 'select' | 'date-gte' | 'date-lte' | FilterFn;
|
|
286
335
|
export interface DataTableStoreState<TData extends RowData> {
|
|
287
336
|
readonly data: TData[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-
|
|
1
|
+
import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "../data-table-DrSsRKRi.mjs";
|
|
2
2
|
export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, DataTableColumnHeader, DataTableRowActions, createDataTableStore, createSelectionColumn, dataTableFeatures, resolvePath, rowMatchesSearch, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, useNuqsAdapter };
|
|
@@ -12,7 +12,7 @@ import { t as CalendarDatePicker } from "./calendar-date-picker-DW31tDSa.mjs";
|
|
|
12
12
|
import { t as ActionRow } from "./action-row-DTvs91_3.mjs";
|
|
13
13
|
import { n as useDebouncedSearchInput } from "./use-debounced-search-input-yu_F0QBt.mjs";
|
|
14
14
|
import { t as MultiSelect } from "./multi-select-CH8KERkt.mjs";
|
|
15
|
-
import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, X } from "lucide-react";
|
|
15
|
+
import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, MoreHorizontal, Search, X } from "lucide-react";
|
|
16
16
|
import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
17
17
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
18
18
|
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
|
@@ -151,13 +151,54 @@ function withSelectionColumn(columns, options = {}) {
|
|
|
151
151
|
}
|
|
152
152
|
//#endregion
|
|
153
153
|
//#region src/components/features/data-table/components/column-header.tsx
|
|
154
|
-
|
|
154
|
+
/** Figma `Datum App UI/Tables/Header/Dropdown` — two carets, one dimmed to show sort direction. */
|
|
155
|
+
function CompactSortIcon({ sorted }) {
|
|
156
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
157
|
+
className: "text-muted-foreground relative h-[15px] w-[5px] shrink-0",
|
|
158
|
+
"aria-hidden": "true",
|
|
159
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
160
|
+
viewBox: "0 0 5.28572 3.25",
|
|
161
|
+
fill: "none",
|
|
162
|
+
className: cn("absolute inset-[26.66%_6.67%_58.34%_7.62%] overflow-visible", sorted === "desc" ? "opacity-35" : "opacity-100"),
|
|
163
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
164
|
+
d: "M0.5 2.75L2.64286 0.5L4.78571 2.75",
|
|
165
|
+
stroke: "currentColor",
|
|
166
|
+
strokeLinecap: "round",
|
|
167
|
+
strokeLinejoin: "round"
|
|
168
|
+
})
|
|
169
|
+
}), /* @__PURE__ */ jsx("svg", {
|
|
170
|
+
viewBox: "0 0 5.28572 3.25",
|
|
171
|
+
fill: "none",
|
|
172
|
+
className: cn("absolute inset-[61.66%_6.67%_23.34%_7.62%] overflow-visible", sorted === "asc" ? "opacity-35" : "opacity-100"),
|
|
173
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
174
|
+
d: "M0.5 0.5L2.64286 2.75L4.78571 0.5",
|
|
175
|
+
stroke: "currentColor",
|
|
176
|
+
strokeLinecap: "round",
|
|
177
|
+
strokeLinejoin: "round"
|
|
178
|
+
})
|
|
179
|
+
})]
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function DataTableColumnHeader({ column, title, className, density = "default" }) {
|
|
183
|
+
const isCompact = density === "compact";
|
|
155
184
|
if (!column.getCanSort()) return /* @__PURE__ */ jsx("div", {
|
|
156
|
-
className: cn(className),
|
|
185
|
+
className: cn(isCompact && "text-xs leading-4 font-normal tracking-normal text-inherit uppercase", className),
|
|
157
186
|
"data-slot": "dt-column-header",
|
|
158
187
|
children: title
|
|
159
188
|
});
|
|
160
189
|
const sorted = column.getIsSorted();
|
|
190
|
+
const sortLabel = `Sort by ${title}${sorted === "asc" ? ", sorted ascending" : sorted === "desc" ? ", sorted descending" : ""}`;
|
|
191
|
+
if (isCompact) return /* @__PURE__ */ jsx("div", {
|
|
192
|
+
className: cn("flex items-center", className),
|
|
193
|
+
"data-slot": "dt-column-header",
|
|
194
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
195
|
+
type: "button",
|
|
196
|
+
className: "hover:text-foreground inline-flex h-9 cursor-pointer items-center gap-2 text-xs leading-4 font-normal tracking-normal text-inherit uppercase",
|
|
197
|
+
onClick: column.getToggleSortingHandler(),
|
|
198
|
+
"aria-label": sortLabel,
|
|
199
|
+
children: [/* @__PURE__ */ jsx("span", { children: title }), /* @__PURE__ */ jsx(CompactSortIcon, { sorted })]
|
|
200
|
+
})
|
|
201
|
+
});
|
|
161
202
|
return /* @__PURE__ */ jsx("div", {
|
|
162
203
|
className: cn("flex items-center gap-2", className),
|
|
163
204
|
"data-slot": "dt-column-header",
|
|
@@ -165,7 +206,7 @@ function DataTableColumnHeader({ column, title, className }) {
|
|
|
165
206
|
type: "button",
|
|
166
207
|
className: "flex items-center gap-1 hover:text-foreground -ml-3 h-8 px-3 cursor-pointer",
|
|
167
208
|
onClick: column.getToggleSortingHandler(),
|
|
168
|
-
"aria-label":
|
|
209
|
+
"aria-label": sortLabel,
|
|
169
210
|
children: [/* @__PURE__ */ jsx("span", { children: title }), sorted === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-4" }) : sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-4" })]
|
|
170
211
|
})
|
|
171
212
|
});
|
|
@@ -870,10 +911,33 @@ function DataTableBulkActions({ children, className }) {
|
|
|
870
911
|
}
|
|
871
912
|
//#endregion
|
|
872
913
|
//#region src/components/features/data-table/components/content.tsx
|
|
914
|
+
/**
|
|
915
|
+
* Per-slot class presets keyed by `density`. `'default'` is intentionally empty
|
|
916
|
+
* so omitting `density` (or passing `'default'`) renders identically to before
|
|
917
|
+
* this prop existed. Every preset only ever *adds* to what a caller passes via
|
|
918
|
+
* the matching `*ClassName` prop — `cn(preset, callerClassName)` below lets the
|
|
919
|
+
* caller's own classes win on conflict.
|
|
920
|
+
*/
|
|
921
|
+
const DENSITY_PRESETS = {
|
|
922
|
+
default: {},
|
|
923
|
+
compact: {
|
|
924
|
+
className: "overflow-auto [&>div]:overflow-visible",
|
|
925
|
+
headerClassName: "[&_tr]:border-0",
|
|
926
|
+
headerRowClassName: "border-0 hover:bg-transparent",
|
|
927
|
+
headerCellClassName: cn("sticky top-0 z-10 h-9 border-b border-border bg-table-header-background px-4", "text-xs leading-4 font-normal tracking-normal text-table-header-foreground uppercase"),
|
|
928
|
+
bodyClassName: "[&_tr:last-child]:border-b-0",
|
|
929
|
+
rowClassName: "border-0 hover:bg-muted/30",
|
|
930
|
+
cellClassName: "border-b border-border px-4 py-0.5 text-sm"
|
|
931
|
+
}
|
|
932
|
+
};
|
|
873
933
|
function resolveClassName(value, item) {
|
|
874
934
|
if (typeof value === "function") return value(item);
|
|
875
935
|
return value;
|
|
876
936
|
}
|
|
937
|
+
/** Clicks inside these should drive their own control, not the row's `onRowClick`. */
|
|
938
|
+
function isInteractiveTarget(target) {
|
|
939
|
+
return target instanceof HTMLElement && target.closest("a, button, [role=\"checkbox\"], input, textarea, select, [data-slot=\"dt-row-actions\"]") != null;
|
|
940
|
+
}
|
|
877
941
|
function renderInlineContentRow(entry, colSpan, rows) {
|
|
878
942
|
return /* @__PURE__ */ jsx(TableRow, {
|
|
879
943
|
"data-slot": "dt-inline-content",
|
|
@@ -888,7 +952,7 @@ function renderInlineContentRow(entry, colSpan, rows) {
|
|
|
888
952
|
})
|
|
889
953
|
}, entry.id);
|
|
890
954
|
}
|
|
891
|
-
function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName }) {
|
|
955
|
+
function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName, density = "default", onRowClick }) {
|
|
892
956
|
const { rows, headerGroups, totalColumns } = useDataTableRows();
|
|
893
957
|
const { isLoading, columnCount } = useDataTableLoading();
|
|
894
958
|
const { pageSize } = useDataTablePagination();
|
|
@@ -896,38 +960,42 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
|
|
|
896
960
|
const openInlineContents = useMemo(() => inlineContents.filter((e) => e.open), [inlineContents]);
|
|
897
961
|
const colSpan = totalColumns;
|
|
898
962
|
const skeletonColumns = totalColumns || columnCount || 5;
|
|
963
|
+
const preset = DENSITY_PRESETS[density];
|
|
899
964
|
return /* @__PURE__ */ jsx("div", {
|
|
900
|
-
className: cn("datum-ui-data-table", className),
|
|
965
|
+
className: cn("datum-ui-data-table", preset.className, className),
|
|
901
966
|
"data-slot": "dt",
|
|
902
967
|
style: { overflowX: "auto" },
|
|
903
968
|
children: /* @__PURE__ */ jsxs(Table, {
|
|
904
969
|
className: cn(tableClassName),
|
|
905
970
|
"data-slot": "dt-table",
|
|
906
971
|
children: [/* @__PURE__ */ jsx(TableHeader, {
|
|
907
|
-
className: cn(headerClassName),
|
|
972
|
+
className: cn(preset.headerClassName, headerClassName),
|
|
908
973
|
"data-slot": "dt-header",
|
|
909
974
|
children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(TableRow, {
|
|
910
|
-
className: cn(headerRowClassName),
|
|
975
|
+
className: cn(preset.headerRowClassName, headerRowClassName),
|
|
911
976
|
"data-slot": "dt-header-row",
|
|
912
977
|
children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, {
|
|
913
|
-
className: cn(headerCellClassName),
|
|
978
|
+
className: cn(preset.headerCellClassName, headerCellClassName),
|
|
914
979
|
"data-slot": "dt-header-cell",
|
|
915
980
|
children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
|
|
916
981
|
}, header.id))
|
|
917
982
|
}, headerGroup.id))
|
|
918
983
|
}), /* @__PURE__ */ jsxs(TableBody, {
|
|
919
|
-
className: cn(bodyClassName),
|
|
984
|
+
className: cn(preset.bodyClassName, bodyClassName),
|
|
920
985
|
"data-slot": "dt-body",
|
|
921
986
|
children: [openInlineContents.filter((e) => e.position === "top").map((entry) => renderInlineContentRow(entry, colSpan, rows)), rows.length > 0 ? rows.map((row) => {
|
|
922
987
|
const rowEntry = openInlineContents.find((e) => e.position === "row" && e.rowId === row.id);
|
|
923
988
|
if (rowEntry) return renderInlineContentRow(rowEntry, colSpan, rows);
|
|
924
989
|
return /* @__PURE__ */ jsx(TableRow, {
|
|
925
|
-
className: cn(resolveClassName(rowClassName, row)),
|
|
990
|
+
className: cn(preset.rowClassName, resolveClassName(rowClassName, row), onRowClick && "cursor-pointer"),
|
|
926
991
|
style: { transitionProperty: "none" },
|
|
927
992
|
"data-slot": "dt-row",
|
|
928
993
|
"data-state": row.getIsSelected() ? "selected" : void 0,
|
|
994
|
+
onClick: onRowClick ? (e) => {
|
|
995
|
+
if (!isInteractiveTarget(e.target)) onRowClick(row.original);
|
|
996
|
+
} : void 0,
|
|
929
997
|
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
|
|
930
|
-
className: cn(resolveClassName(cellClassName, cell)),
|
|
998
|
+
className: cn(preset.cellClassName, resolveClassName(cellClassName, cell)),
|
|
931
999
|
"data-slot": "dt-cell",
|
|
932
1000
|
children: flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
933
1001
|
}, cell.id))
|
|
@@ -1002,6 +1070,168 @@ function DataTableInlineContent({ position, rowId, open, onClose, className, chi
|
|
|
1002
1070
|
return null;
|
|
1003
1071
|
}
|
|
1004
1072
|
//#endregion
|
|
1073
|
+
//#region src/components/features/data-table/components/list-pagination.tsx
|
|
1074
|
+
const DEFAULT_LIST_PAGE_SIZES = [
|
|
1075
|
+
10,
|
|
1076
|
+
20,
|
|
1077
|
+
50,
|
|
1078
|
+
100
|
|
1079
|
+
];
|
|
1080
|
+
const DEFAULT_LABELS = {
|
|
1081
|
+
rowsPerPage: "Rows per page",
|
|
1082
|
+
of: "of",
|
|
1083
|
+
previousPage: "Previous page",
|
|
1084
|
+
nextPage: "Next page"
|
|
1085
|
+
};
|
|
1086
|
+
/**
|
|
1087
|
+
* Compact list-page footer: `[pageSize ▾] Rows per page … 1-100 of N [resource]`
|
|
1088
|
+
* + joined prev/next buttons. Pass `labels` to localize — this package ships no
|
|
1089
|
+
* i18n of its own.
|
|
1090
|
+
*/
|
|
1091
|
+
function DataTableListPagination({ pageSizes = DEFAULT_LIST_PAGE_SIZES, resourceLabel, hideWhenSinglePage = false, labels, className }) {
|
|
1092
|
+
const { canNextPage, canPrevPage, nextPage, prevPage, pageIndex, pageCount, pageSize, setPageSize, totalRows } = useDataTablePagination();
|
|
1093
|
+
const t = {
|
|
1094
|
+
...DEFAULT_LABELS,
|
|
1095
|
+
...labels
|
|
1096
|
+
};
|
|
1097
|
+
if (hideWhenSinglePage && pageCount <= 1) return null;
|
|
1098
|
+
const startRow = totalRows === 0 ? 0 : pageIndex * pageSize + 1;
|
|
1099
|
+
const endRow = Math.min((pageIndex + 1) * pageSize, totalRows);
|
|
1100
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1101
|
+
className: cn("flex h-7 w-full items-center justify-between gap-3", className),
|
|
1102
|
+
"data-slot": "dt-list-pagination",
|
|
1103
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1104
|
+
className: "flex items-center gap-3",
|
|
1105
|
+
children: [/* @__PURE__ */ jsxs(Select, {
|
|
1106
|
+
value: String(pageSize),
|
|
1107
|
+
onValueChange: (value) => setPageSize(Number(value)),
|
|
1108
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
1109
|
+
className: cn("border-border text-muted-foreground h-7 w-[4.5rem] gap-1.5 rounded-md px-2.5 py-0", "min-h-0 text-xs font-normal shadow-none"),
|
|
1110
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: String(pageSize) })
|
|
1111
|
+
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
1112
|
+
side: "top",
|
|
1113
|
+
children: pageSizes.map((size) => /* @__PURE__ */ jsx(SelectItem, {
|
|
1114
|
+
value: String(size),
|
|
1115
|
+
children: size
|
|
1116
|
+
}, size))
|
|
1117
|
+
})]
|
|
1118
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1119
|
+
className: "text-muted-foreground text-xs leading-4 whitespace-nowrap",
|
|
1120
|
+
children: t.rowsPerPage
|
|
1121
|
+
})]
|
|
1122
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1123
|
+
className: "flex items-center gap-3",
|
|
1124
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1125
|
+
className: "text-muted-foreground text-xs leading-4 whitespace-nowrap tabular-nums",
|
|
1126
|
+
children: `${startRow}-${endRow} ${t.of} ${totalRows}${resourceLabel ? ` ${resourceLabel}` : ""}`
|
|
1127
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1128
|
+
className: "border-border flex items-center overflow-hidden rounded-md border",
|
|
1129
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
1130
|
+
theme: "outline",
|
|
1131
|
+
size: "icon",
|
|
1132
|
+
className: "border-border size-7 rounded-none border-0 border-r shadow-none",
|
|
1133
|
+
onClick: prevPage,
|
|
1134
|
+
disabled: !canPrevPage,
|
|
1135
|
+
"aria-label": t.previousPage,
|
|
1136
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5" })
|
|
1137
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1138
|
+
theme: "outline",
|
|
1139
|
+
size: "icon",
|
|
1140
|
+
className: "size-7 rounded-none border-0 shadow-none",
|
|
1141
|
+
onClick: nextPage,
|
|
1142
|
+
disabled: !canNextPage,
|
|
1143
|
+
"aria-label": t.nextPage,
|
|
1144
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" })
|
|
1145
|
+
})]
|
|
1146
|
+
})]
|
|
1147
|
+
})]
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/components/features/data-table/components/search.tsx
|
|
1152
|
+
function DataTableSearch({ placeholder = "Search...", debounceMs = 300, className, disabled }) {
|
|
1153
|
+
const { search, setSearch } = useDataTableSearch();
|
|
1154
|
+
const [inputValue, setInputValue] = useDebouncedSearchInput(search, setSearch, debounceMs);
|
|
1155
|
+
return /* @__PURE__ */ jsx(Input, {
|
|
1156
|
+
placeholder,
|
|
1157
|
+
value: inputValue,
|
|
1158
|
+
onChange: (e) => setInputValue(e.target.value),
|
|
1159
|
+
className,
|
|
1160
|
+
disabled,
|
|
1161
|
+
"aria-label": placeholder,
|
|
1162
|
+
"data-slot": "dt-search"
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
//#endregion
|
|
1166
|
+
//#region src/components/features/data-table/components/list-panel.tsx
|
|
1167
|
+
const LOADING_SKELETON_ROWS = 8;
|
|
1168
|
+
const SEARCH_INPUT_CLASS = "h-10 border-0 bg-transparent pl-9 shadow-none focus-visible:shadow-none focus-visible:ring-0";
|
|
1169
|
+
function ListPanelSkeleton({ columnCount }) {
|
|
1170
|
+
const cols = Math.max(columnCount, 1);
|
|
1171
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1172
|
+
className: "min-h-0 flex-1 overflow-hidden",
|
|
1173
|
+
"data-slot": "dt-list-panel-loading",
|
|
1174
|
+
"aria-busy": "true",
|
|
1175
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1176
|
+
className: "border-border bg-table-header-background flex h-9 items-center border-b px-4",
|
|
1177
|
+
children: Array.from({ length: cols }, (_, i) => /* @__PURE__ */ jsx("div", {
|
|
1178
|
+
className: "min-w-0 flex-1 px-2 first:pl-0 last:pr-0",
|
|
1179
|
+
children: /* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-16" })
|
|
1180
|
+
}, i))
|
|
1181
|
+
}), Array.from({ length: LOADING_SKELETON_ROWS }, (_, row) => /* @__PURE__ */ jsx("div", {
|
|
1182
|
+
className: "border-border flex h-8 items-center border-b px-4",
|
|
1183
|
+
"data-slot": "dt-list-panel-skeleton-row",
|
|
1184
|
+
children: Array.from({ length: cols }, (_, col) => /* @__PURE__ */ jsx("div", {
|
|
1185
|
+
className: "min-w-0 flex-1 px-2 first:pl-0 last:pr-0",
|
|
1186
|
+
children: /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3", col % 3 === 0 ? "w-3/4" : col % 3 === 1 ? "w-1/2" : "w-2/3") })
|
|
1187
|
+
}, col))
|
|
1188
|
+
}, row))]
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Compact "list table" card: search row + dense sticky-header table, matching
|
|
1193
|
+
* staff-portal's Figma org-list pattern. Renders inside `DataTable.Client` /
|
|
1194
|
+
* `DataTable.Server`, same as every other `DataTable.*` part. Pagination is
|
|
1195
|
+
* deliberately not included — render `DataTable.ListPagination` as a sibling,
|
|
1196
|
+
* below the panel.
|
|
1197
|
+
*/
|
|
1198
|
+
function DataTableListPanel({ search = {}, searchSlot, toolbar, emptyMessage, loading = false, onRowClick, className, panelClassName }) {
|
|
1199
|
+
const { totalColumns } = useDataTableRows();
|
|
1200
|
+
const showSearch = search !== false;
|
|
1201
|
+
const isControlledSearch = showSearch && (search.value !== void 0 || search.onChange !== void 0);
|
|
1202
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1203
|
+
className: cn("bg-card text-card-foreground border-card-border flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl border", panelClassName, className),
|
|
1204
|
+
"data-slot": "dt-list-panel",
|
|
1205
|
+
children: [
|
|
1206
|
+
showSearch && /* @__PURE__ */ jsxs("div", {
|
|
1207
|
+
className: "flex shrink-0 items-center gap-1 border-b pr-2",
|
|
1208
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1209
|
+
className: "relative min-w-0 flex-1",
|
|
1210
|
+
children: [/* @__PURE__ */ jsx(Search, {
|
|
1211
|
+
className: "text-muted-foreground absolute top-1/2 left-3 size-4 -translate-y-1/2",
|
|
1212
|
+
"aria-hidden": "true"
|
|
1213
|
+
}), isControlledSearch ? /* @__PURE__ */ jsx(Input, {
|
|
1214
|
+
placeholder: search.placeholder,
|
|
1215
|
+
value: search.value,
|
|
1216
|
+
onChange: (e) => search.onChange?.(e.target.value),
|
|
1217
|
+
className: SEARCH_INPUT_CLASS
|
|
1218
|
+
}) : /* @__PURE__ */ jsx(DataTableSearch, {
|
|
1219
|
+
placeholder: search.placeholder,
|
|
1220
|
+
className: SEARCH_INPUT_CLASS
|
|
1221
|
+
})]
|
|
1222
|
+
}), searchSlot]
|
|
1223
|
+
}),
|
|
1224
|
+
toolbar,
|
|
1225
|
+
loading ? /* @__PURE__ */ jsx(ListPanelSkeleton, { columnCount: totalColumns }) : /* @__PURE__ */ jsx(DataTableContent, {
|
|
1226
|
+
density: "compact",
|
|
1227
|
+
emptyMessage,
|
|
1228
|
+
onRowClick,
|
|
1229
|
+
className: "min-h-0 flex-1"
|
|
1230
|
+
})
|
|
1231
|
+
]
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
1005
1235
|
//#region src/components/features/data-table/components/loading.tsx
|
|
1006
1236
|
function DataTableLoading({ rows = 5, columns = 4, className }) {
|
|
1007
1237
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -1135,21 +1365,6 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
|
|
|
1135
1365
|
});
|
|
1136
1366
|
}
|
|
1137
1367
|
//#endregion
|
|
1138
|
-
//#region src/components/features/data-table/components/search.tsx
|
|
1139
|
-
function DataTableSearch({ placeholder = "Search...", debounceMs = 300, className, disabled }) {
|
|
1140
|
-
const { search, setSearch } = useDataTableSearch();
|
|
1141
|
-
const [inputValue, setInputValue] = useDebouncedSearchInput(search, setSearch, debounceMs);
|
|
1142
|
-
return /* @__PURE__ */ jsx(Input, {
|
|
1143
|
-
placeholder,
|
|
1144
|
-
value: inputValue,
|
|
1145
|
-
onChange: (e) => setInputValue(e.target.value),
|
|
1146
|
-
className,
|
|
1147
|
-
disabled,
|
|
1148
|
-
"aria-label": placeholder,
|
|
1149
|
-
"data-slot": "dt-search"
|
|
1150
|
-
});
|
|
1151
|
-
}
|
|
1152
|
-
//#endregion
|
|
1153
1368
|
//#region src/components/features/data-table/hooks/use-data-table-client.ts
|
|
1154
1369
|
/**
|
|
1155
1370
|
* Creates a TanStack Table instance from an existing store.
|
|
@@ -1622,6 +1837,8 @@ const DataTable = {
|
|
|
1622
1837
|
InlineContent: DataTableInlineContent,
|
|
1623
1838
|
ColumnHeader: DataTableColumnHeader,
|
|
1624
1839
|
Pagination: DataTablePagination,
|
|
1840
|
+
ListPanel: DataTableListPanel,
|
|
1841
|
+
ListPagination: DataTableListPagination,
|
|
1625
1842
|
Search: DataTableSearch,
|
|
1626
1843
|
RowActions: DataTableRowActions,
|
|
1627
1844
|
BulkActions: DataTableBulkActions,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as GroupedTable } from "../grouped-table-
|
|
1
|
+
import { t as GroupedTable } from "../grouped-table-DIFPMKLL.mjs";
|
|
2
2
|
export { GroupedTable };
|
|
@@ -6,7 +6,7 @@ import { t as useControllableState } from "./use-controllable-state-Dna7u3r9.mjs
|
|
|
6
6
|
import { Skeleton } from "./skeleton/index.mjs";
|
|
7
7
|
import { TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table/index.mjs";
|
|
8
8
|
import { n as EASE } from "./motion-DvGRWdsm.mjs";
|
|
9
|
-
import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-
|
|
9
|
+
import { f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, m as DataTableRowActions, p as dataTableFeatures } from "./data-table-DrSsRKRi.mjs";
|
|
10
10
|
import { n as useDebouncedSearchInput } from "./use-debounced-search-input-yu_F0QBt.mjs";
|
|
11
11
|
import { InputWithAddons } from "./input-with-addons/index.mjs";
|
|
12
12
|
import { ChevronRight, Search, X } from "lucide-react";
|
package/dist/index.mjs
CHANGED
|
@@ -55,7 +55,7 @@ import { i as ClientOnly, n as useTheme, r as ThemeScript, t as ThemeProvider }
|
|
|
55
55
|
import { a as formatJson, c as isValidYaml, d as CodeEditor, i as CodeEditorTabs, l as jsonToYaml, n as jsonSchema, o as formatYaml, r as yamlSchema, s as isValidJson, t as createCodeEditorSchema, u as yamlToJson } from "./types-agDpVXBG.mjs";
|
|
56
56
|
import { t as toast } from "./toast-BUPlDkN3.mjs";
|
|
57
57
|
import { n as Toaster, t as useToast } from "./use-toast-Bpq0yJIT.mjs";
|
|
58
|
-
import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-
|
|
58
|
+
import { _ as useNuqsAdapter, a as useDataTablePagination, b as DEFAULT_PAGE_SIZE, c as useDataTableSelection, d as resolvePath, f as rowMatchesSearch, g as createSelectionColumn, h as DataTableColumnHeader, i as useDataTableLoading, l as useDataTableSorting, m as DataTableRowActions, n as useDataTableFilters, o as useDataTableRows, p as dataTableFeatures, r as useDataTableInlineContents, s as useDataTableSearch, t as DataTable, u as createDataTableStore, v as DEFAULT_DEBOUNCE_MS, x as DEFAULT_PAGE_SIZES, y as DEFAULT_LOADING_ROWS } from "./data-table-DrSsRKRi.mjs";
|
|
59
59
|
import { t as ActionRow } from "./action-row-DTvs91_3.mjs";
|
|
60
60
|
import { n as useDebouncedSearchInput, t as DEFAULT_SEARCH_DEBOUNCE_MS } from "./use-debounced-search-input-yu_F0QBt.mjs";
|
|
61
61
|
import { t as MultiSelect } from "./multi-select-CH8KERkt.mjs";
|
|
@@ -71,7 +71,7 @@ import { InputWithAddons } from "./input-with-addons/index.mjs";
|
|
|
71
71
|
import { t as TimePicker } from "./time-picker-BzNSUazl.mjs";
|
|
72
72
|
import { t as Transfer } from "./transfer-DsshYgIk.mjs";
|
|
73
73
|
import { a as getResponsiveValue, c as GRID_COLUMNS, d as RESPONSIVE_MAP, i as getGutter, l as GRID_PREFIX, n as Row, o as registerMediaQuery, r as RowContext, s as GRID_BREAKPOINTS, t as Col, u as RESPONSIVE_ARRAY } from "./col-BYGWTB4j.mjs";
|
|
74
|
-
import { t as GroupedTable } from "./grouped-table-
|
|
74
|
+
import { t as GroupedTable } from "./grouped-table-DIFPMKLL.mjs";
|
|
75
75
|
import { t as InputNumber } from "./input-number-TXmW-X9o.mjs";
|
|
76
76
|
import { a as logoStyles, i as LogoFlat, n as LogoStacked, r as LogoIcon, t as LogoText } from "./logo-text-Dpm5O6Kg.mjs";
|
|
77
77
|
import { t as Logo } from "./logo-C3FxD7jb.mjs";
|
|
@@ -118,6 +118,9 @@ Light-mode tokens on :root, dark-mode tokens on .dark
|
|
|
118
118
|
--table-accent: var(--glacier-mist-800);
|
|
119
119
|
--table-cell: hsl(0 0% 100%);
|
|
120
120
|
--table-cell-hover: var(--glacier-mist-700);
|
|
121
|
+
/* Sticky "mist" list-table header (DataTable.Content density="compact" / DataTable.ListPanel) */
|
|
122
|
+
--table-header-background: var(--glacier-mist-700);
|
|
123
|
+
--table-header-foreground: color-mix(in oklch, var(--midnight-fjord) 60%, transparent);
|
|
121
124
|
|
|
122
125
|
/* Badge */
|
|
123
126
|
--badge-primary: var(--primary);
|
|
@@ -320,6 +323,8 @@ Light-mode tokens on :root, dark-mode tokens on .dark
|
|
|
320
323
|
--table-accent: var(--app-dark-utility-1);
|
|
321
324
|
--table-cell: var(--midnight-fjord);
|
|
322
325
|
--table-cell-hover: var(--app-dark-utility-1);
|
|
326
|
+
--table-header-background: var(--muted);
|
|
327
|
+
--table-header-foreground: var(--muted-foreground);
|
|
323
328
|
|
|
324
329
|
/* Badge */
|
|
325
330
|
--badge-primary: var(--primary);
|
|
@@ -425,6 +430,15 @@ TAILWIND CUSTOM UTILITIES FOR THEME
|
|
|
425
430
|
/* Custom base shadow */
|
|
426
431
|
--shadow: 0 2px 4px 2px rgba(12, 29, 49, 0.03);
|
|
427
432
|
--shadow-tooltip: 0 4px 16px 0 rgba(0, 0, 0, 0.05), 1px 8px 8px 0 rgba(0, 0, 0, 0.02);
|
|
433
|
+
/**
|
|
434
|
+
* Section-card shadow (Figma org-overview cards). Aliases the base `--shadow`
|
|
435
|
+
* rather than a standalone value — applies identically in light and dark since
|
|
436
|
+
* `--shadow` itself isn't theme-split. Consumers reference it directly via
|
|
437
|
+
* `shadow-(--section-card-shadow)` (e.g. staff-portal's `SECTION_CARD_CHROME`),
|
|
438
|
+
* which previously pointed at an undefined custom property and rendered no
|
|
439
|
+
* shadow at all.
|
|
440
|
+
*/
|
|
441
|
+
--section-card-shadow: var(--shadow);
|
|
428
442
|
|
|
429
443
|
--color-tertiary: var(--tertiary);
|
|
430
444
|
--color-tertiary-foreground: var(--tertiary-foreground);
|
|
@@ -445,6 +459,8 @@ TAILWIND CUSTOM UTILITIES FOR THEME
|
|
|
445
459
|
--color-table-accent: var(--table-accent);
|
|
446
460
|
--color-table-cell: var(--table-cell);
|
|
447
461
|
--color-table-cell-hover: var(--table-cell-hover);
|
|
462
|
+
--color-table-header-background: var(--table-header-background);
|
|
463
|
+
--color-table-header-foreground: var(--table-header-foreground);
|
|
448
464
|
|
|
449
465
|
--color-dialog-overlay: var(--dialog-overlay);
|
|
450
466
|
--color-dialog-border: var(--dialog-border);
|