@windrun-huaiin/third-ui 29.2.1 → 30.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/fuma/mdx/cheet-table.d.ts +13 -0
  2. package/dist/fuma/mdx/cheet-table.js +295 -0
  3. package/dist/fuma/mdx/cheet-table.mjs +293 -0
  4. package/dist/fuma/mdx/index.d.ts +1 -0
  5. package/dist/fuma/mdx/index.js +2 -0
  6. package/dist/fuma/mdx/index.mjs +1 -0
  7. package/dist/fuma/server/features/widgets.js +2 -0
  8. package/dist/fuma/server/features/widgets.mjs +2 -0
  9. package/dist/lib/fuma-schema-check-util.d.ts +1 -1
  10. package/dist/main/alert-dialog/confirm-dialog.js +1 -1
  11. package/dist/main/alert-dialog/confirm-dialog.mjs +2 -2
  12. package/dist/main/alert-dialog/dialog-loading-action.js +5 -2
  13. package/dist/main/alert-dialog/dialog-loading-action.mjs +5 -2
  14. package/dist/main/alert-dialog/dialog-styles.d.ts +4 -2
  15. package/dist/main/alert-dialog/dialog-styles.js +8 -4
  16. package/dist/main/alert-dialog/dialog-styles.mjs +7 -5
  17. package/dist/main/alert-dialog/high-priority-confirm-dialog.js +5 -5
  18. package/dist/main/alert-dialog/high-priority-confirm-dialog.mjs +6 -6
  19. package/dist/main/alert-dialog/info-dialog.js +1 -1
  20. package/dist/main/alert-dialog/info-dialog.mjs +2 -2
  21. package/dist/main/alert-dialog/undoable-confirm-dialog.js +2 -2
  22. package/dist/main/alert-dialog/undoable-confirm-dialog.mjs +3 -3
  23. package/dist/main/anime/anime-beam-frame.d.ts +3 -0
  24. package/dist/main/anime/anime-beam-frame.js +63 -0
  25. package/dist/main/anime/anime-beam-frame.mjs +61 -0
  26. package/dist/main/anime/anime-spiral-loading.d.ts +10 -0
  27. package/dist/main/anime/anime-spiral-loading.js +77 -0
  28. package/dist/main/anime/anime-spiral-loading.mjs +75 -0
  29. package/dist/main/anime/index.d.ts +2 -0
  30. package/dist/main/anime/index.js +10 -0
  31. package/dist/main/anime/index.mjs +3 -0
  32. package/dist/main/beam-frame/animate.d.ts +3 -0
  33. package/dist/main/beam-frame/animate.js +63 -0
  34. package/dist/main/beam-frame/animate.mjs +61 -0
  35. package/dist/main/beam-frame/beam-frame.d.ts +4 -0
  36. package/dist/main/beam-frame/beam-frame.js +262 -0
  37. package/dist/main/beam-frame/beam-frame.mjs +258 -0
  38. package/dist/main/beam-frame/index.d.ts +4 -0
  39. package/dist/main/beam-frame/index.js +11 -0
  40. package/dist/main/beam-frame/index.mjs +3 -0
  41. package/dist/main/beam-frame/motion.d.ts +3 -0
  42. package/dist/main/beam-frame/motion.js +61 -0
  43. package/dist/main/beam-frame/motion.mjs +59 -0
  44. package/dist/main/beam-frame/share-config.d.ts +54 -0
  45. package/dist/main/beam-frame/share-config.js +161 -0
  46. package/dist/main/beam-frame/share-config.mjs +152 -0
  47. package/dist/main/beam-frame-config.d.ts +54 -0
  48. package/dist/main/beam-frame-config.js +161 -0
  49. package/dist/main/beam-frame-config.mjs +152 -0
  50. package/dist/main/calendar/random-date-range-dialog.js +177 -51
  51. package/dist/main/calendar/random-date-range-dialog.mjs +178 -52
  52. package/dist/main/cta.js +17 -1
  53. package/dist/main/cta.mjs +18 -2
  54. package/dist/main/delayed-img.d.ts +1 -1
  55. package/dist/main/delayed-img.js +8 -5
  56. package/dist/main/delayed-img.mjs +8 -5
  57. package/dist/main/info-tooltip.js +70 -9
  58. package/dist/main/info-tooltip.mjs +70 -9
  59. package/dist/main/loading-frame/index.d.ts +1 -0
  60. package/dist/main/loading.d.ts +2 -1
  61. package/dist/main/loading.js +64 -26
  62. package/dist/main/loading.mjs +64 -26
  63. package/dist/main/motion/index.d.ts +1 -0
  64. package/dist/main/motion/index.js +9 -0
  65. package/dist/main/motion/index.mjs +2 -0
  66. package/dist/main/motion/motion-beam-frame.d.ts +3 -0
  67. package/dist/main/motion/motion-beam-frame.js +61 -0
  68. package/dist/main/motion/motion-beam-frame.mjs +59 -0
  69. package/dist/main/snake-loading-frame.d.ts +7 -3
  70. package/dist/main/snake-loading-frame.js +44 -252
  71. package/dist/main/snake-loading-frame.mjs +46 -254
  72. package/package.json +16 -5
  73. package/src/fuma/mdx/cheet-table.tsx +650 -0
  74. package/src/fuma/mdx/index.ts +1 -0
  75. package/src/fuma/server/features/widgets.tsx +2 -0
  76. package/src/main/alert-dialog/confirm-dialog.tsx +2 -1
  77. package/src/main/alert-dialog/dialog-loading-action.tsx +7 -5
  78. package/src/main/alert-dialog/dialog-styles.ts +13 -3
  79. package/src/main/alert-dialog/high-priority-confirm-dialog.tsx +26 -23
  80. package/src/main/alert-dialog/info-dialog.tsx +2 -1
  81. package/src/main/alert-dialog/undoable-confirm-dialog.tsx +18 -17
  82. package/src/main/anime/anime-beam-frame.tsx +128 -0
  83. package/src/main/anime/anime-spiral-loading.tsx +123 -0
  84. package/src/main/anime/index.ts +9 -0
  85. package/src/main/beam-frame-config.tsx +341 -0
  86. package/src/main/calendar/random-date-range-dialog.tsx +225 -69
  87. package/src/main/cta.tsx +50 -21
  88. package/src/main/delayed-img.tsx +9 -4
  89. package/src/main/info-tooltip.tsx +116 -20
  90. package/src/main/loading-frame/index.ts +4 -0
  91. package/src/main/loading.tsx +75 -24
  92. package/src/main/motion/index.ts +8 -0
  93. package/src/main/motion/motion-beam-frame.tsx +137 -0
  94. package/src/main/snake-loading-frame.tsx +95 -496
  95. package/src/styles/cta.css +21 -4
  96. package/src/styles/third-ui.css +0 -20
@@ -0,0 +1,13 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ export type CheetTableProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
3
+ title?: ReactNode;
4
+ description?: string;
5
+ copyableColumns?: string[];
6
+ defaultOpen?: boolean;
7
+ collapsible?: boolean;
8
+ striped?: boolean;
9
+ stickyHeader?: boolean;
10
+ emptyText?: string;
11
+ children: ReactNode;
12
+ };
13
+ export declare function CheetTable({ title, description, copyableColumns, defaultOpen, collapsible, striped, stickyHeader, emptyText, className, children, style, ...props }: CheetTableProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,295 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var tslib = require('tslib');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var icons = require('@windrun-huaiin/base-ui/icons');
7
+ var lib = require('@windrun-huaiin/base-ui/lib');
8
+ var utils = require('@windrun-huaiin/lib/utils');
9
+ var React = require('react');
10
+ var infoTooltip = require('../../main/info-tooltip.js');
11
+
12
+ const DEFAULT_COLUMN_WIDTH = 180;
13
+ const MIN_COLUMN_WIDTH = 140;
14
+ const CHEET_TABLE_BORDER_COLOR = `color-mix(in srgb, ${lib.themeSvgIconColor} 35%, transparent)`;
15
+ const CHEET_TABLE_BORDER_CLASS = 'border-[color:var(--cheet-table-border-color)]';
16
+ function parseCheetCell(raw) {
17
+ let value = raw.trim();
18
+ let force = false;
19
+ if (value.startsWith('!!')) {
20
+ force = true;
21
+ value = value.slice(2).trimStart();
22
+ }
23
+ const descriptionIndex = value.indexOf('??');
24
+ if (descriptionIndex === -1) {
25
+ return {
26
+ text: value,
27
+ force,
28
+ };
29
+ }
30
+ const description = value.slice(descriptionIndex + 2).trim();
31
+ return {
32
+ text: value.slice(0, descriptionIndex).trimEnd(),
33
+ description: description ? description : undefined,
34
+ force,
35
+ };
36
+ }
37
+ function normalizeText(value) {
38
+ return value.replace(/\s+/g, ' ').trim();
39
+ }
40
+ function collectText(node) {
41
+ if (typeof node === 'string' || typeof node === 'number') {
42
+ return String(node);
43
+ }
44
+ if (Array.isArray(node)) {
45
+ return node.map(collectText).join('');
46
+ }
47
+ if (React.isValidElement(node)) {
48
+ return collectText(node.props.children);
49
+ }
50
+ return '';
51
+ }
52
+ function getElementChildren(element) {
53
+ return React.Children.toArray(element.props.children);
54
+ }
55
+ function isElementType(node, type) {
56
+ return React.isValidElement(node) && node.type === type;
57
+ }
58
+ function findFirstElement(node, type) {
59
+ if (isElementType(node, type)) {
60
+ return node;
61
+ }
62
+ if (Array.isArray(node)) {
63
+ for (const child of node) {
64
+ const match = findFirstElement(child, type);
65
+ if (match) {
66
+ return match;
67
+ }
68
+ }
69
+ }
70
+ if (React.isValidElement(node)) {
71
+ return findFirstElement(node.props.children, type);
72
+ }
73
+ return null;
74
+ }
75
+ function collectRowElements(node) {
76
+ if (isElementType(node, 'tr')) {
77
+ return [node];
78
+ }
79
+ if (Array.isArray(node)) {
80
+ return node.flatMap(collectRowElements);
81
+ }
82
+ if (React.isValidElement(node)) {
83
+ return collectRowElements(node.props.children);
84
+ }
85
+ return [];
86
+ }
87
+ function parseRow(row) {
88
+ return getElementChildren(row)
89
+ .filter(React.isValidElement)
90
+ .map((cell) => {
91
+ const rawText = normalizeText(collectText(cell.props.children));
92
+ return Object.assign({ rawText }, parseCheetCell(rawText));
93
+ });
94
+ }
95
+ function parseCheetTable(children) {
96
+ var _a;
97
+ const rows = collectRowElements(children);
98
+ if (rows.length > 0) {
99
+ const headerRow = rows[0];
100
+ const dataRows = rows.slice(1);
101
+ const headers = parseRow(headerRow);
102
+ if (headers.length === 0) {
103
+ return null;
104
+ }
105
+ return {
106
+ headers,
107
+ rows: dataRows.map(parseRow).filter((row) => row.length > 0),
108
+ };
109
+ }
110
+ const table = findFirstElement(children, 'table');
111
+ if (!table) {
112
+ return null;
113
+ }
114
+ const thead = findFirstElement(table.props.children, 'thead');
115
+ const tbody = findFirstElement(table.props.children, 'tbody');
116
+ const headRows = thead ? collectRowElements(thead.props.children) : [];
117
+ const bodyRows = tbody ? collectRowElements(tbody.props.children) : [];
118
+ const fallbackRows = !thead && !tbody ? collectRowElements(table.props.children) : [];
119
+ const headerRow = (_a = headRows[0]) !== null && _a !== void 0 ? _a : fallbackRows[0];
120
+ const dataRows = bodyRows.length > 0 ? bodyRows : fallbackRows.slice(1);
121
+ if (!headerRow) {
122
+ return null;
123
+ }
124
+ const headers = parseRow(headerRow);
125
+ if (headers.length === 0) {
126
+ return null;
127
+ }
128
+ return {
129
+ headers,
130
+ rows: dataRows.map(parseRow).filter((row) => row.length > 0),
131
+ };
132
+ }
133
+ function parseCheetTableElement(element) {
134
+ const table = element === null || element === void 0 ? void 0 : element.querySelector('table');
135
+ if (!table) {
136
+ return null;
137
+ }
138
+ const headRow = table.querySelector('thead tr');
139
+ const bodyRows = Array.from(table.querySelectorAll('tbody tr'));
140
+ const fallbackRows = Array.from(table.querySelectorAll('tr'));
141
+ const headerRow = headRow !== null && headRow !== void 0 ? headRow : fallbackRows[0];
142
+ const dataRows = bodyRows.length > 0
143
+ ? bodyRows
144
+ : fallbackRows.slice(headerRow ? 1 : 0);
145
+ if (!headerRow) {
146
+ return null;
147
+ }
148
+ const parseDomRow = (row) => Array.from(row.querySelectorAll('th,td')).map((cell) => {
149
+ var _a;
150
+ const rawText = normalizeText((_a = cell.textContent) !== null && _a !== void 0 ? _a : '');
151
+ return Object.assign({ rawText }, parseCheetCell(rawText));
152
+ });
153
+ const headers = parseDomRow(headerRow);
154
+ if (headers.length === 0) {
155
+ return null;
156
+ }
157
+ return {
158
+ headers,
159
+ rows: dataRows.map(parseDomRow).filter((row) => row.length > 0),
160
+ };
161
+ }
162
+ function createCopyableColumnSet(headers, copyableColumns) {
163
+ const names = copyableColumns !== null && copyableColumns !== void 0 ? copyableColumns : [];
164
+ return new Set(names.map((name) => {
165
+ const header = headers.find((item) => item.text === name || item.rawText === name);
166
+ return header ? header.text : name;
167
+ }));
168
+ }
169
+ function CheetCellContent({ cell, force, copied, copyable, onCopy, }) {
170
+ return (jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-0.5", children: [jsxRuntime.jsx("span", { className: utils.cn('block min-w-0 truncate', force && utils.cn('font-semibold', lib.themeIconColor)), children: cell.text }), cell.description ? (jsxRuntime.jsx(infoTooltip.InfoTooltip, { content: cell.description, className: "not-prose -mx-0.5", desktopSide: "bottom" })) : null, copyable ? (jsxRuntime.jsx("button", { type: "button", "aria-label": copied ? 'Copied' : 'Copy cell content', className: utils.cn('not-prose inline-flex size-6 shrink-0 touch-manipulation items-center justify-center rounded text-fd-muted-foreground transition', 'hover:bg-fd-accent hover:text-fd-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950', copied && lib.themeIconColor, lib.themeRingColor), onClick: (event) => {
171
+ event.preventDefault();
172
+ event.stopPropagation();
173
+ onCopy === null || onCopy === void 0 ? void 0 : onCopy();
174
+ }, onPointerDown: (event) => {
175
+ event.stopPropagation();
176
+ }, children: copied ? jsxRuntime.jsx(icons.CheckIcon, { className: "size-3.5" }) : jsxRuntime.jsx(icons.CopyIcon, { className: "size-3.5" }) })) : null] }));
177
+ }
178
+ function CheetTable(_a) {
179
+ var _b;
180
+ var { title, description, copyableColumns, defaultOpen = true, collapsible = true, striped = true, stickyHeader = false, emptyText = 'No data', className, children, style } = _a, props = tslib.__rest(_a, ["title", "description", "copyableColumns", "defaultOpen", "collapsible", "striped", "stickyHeader", "emptyText", "className", "children", "style"]);
181
+ const [mounted, setMounted] = React.useState(false);
182
+ const [model, setModel] = React.useState(null);
183
+ const [parsed, setParsed] = React.useState(false);
184
+ const [open, setOpen] = React.useState(defaultOpen);
185
+ const [copiedCell, setCopiedCell] = React.useState(null);
186
+ const [columnWidths, setColumnWidths] = React.useState([]);
187
+ const sourceRef = React.useRef(null);
188
+ const resizeStartRef = React.useRef(null);
189
+ const headerCount = (_b = model === null || model === void 0 ? void 0 : model.headers.length) !== null && _b !== void 0 ? _b : 0;
190
+ const copyableColumnSet = React.useMemo(() => { var _a; return createCopyableColumnSet((_a = model === null || model === void 0 ? void 0 : model.headers) !== null && _a !== void 0 ? _a : [], copyableColumns); }, [copyableColumns, model === null || model === void 0 ? void 0 : model.headers]);
191
+ React.useEffect(() => {
192
+ var _a;
193
+ setMounted(true);
194
+ setModel((_a = parseCheetTableElement(sourceRef.current)) !== null && _a !== void 0 ? _a : parseCheetTable(children));
195
+ setParsed(true);
196
+ }, [children]);
197
+ React.useEffect(() => {
198
+ if (headerCount === 0) {
199
+ setColumnWidths([]);
200
+ return;
201
+ }
202
+ setColumnWidths((current) => Array.from({ length: headerCount }, (_, index) => { var _a; return Math.max((_a = current[index]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH, MIN_COLUMN_WIDTH); }));
203
+ }, [headerCount]);
204
+ React.useEffect(() => {
205
+ function handlePointerMove(event) {
206
+ const start = resizeStartRef.current;
207
+ if (!start) {
208
+ return;
209
+ }
210
+ const nextWidth = Math.max(MIN_COLUMN_WIDTH, start.startWidth + event.clientX - start.startX);
211
+ setColumnWidths((current) => {
212
+ const next = [...current];
213
+ next[start.columnIndex] = nextWidth;
214
+ return next;
215
+ });
216
+ }
217
+ function handlePointerUp() {
218
+ resizeStartRef.current = null;
219
+ document.body.style.removeProperty('cursor');
220
+ document.body.style.removeProperty('user-select');
221
+ }
222
+ window.addEventListener('pointermove', handlePointerMove);
223
+ window.addEventListener('pointerup', handlePointerUp);
224
+ return () => {
225
+ window.removeEventListener('pointermove', handlePointerMove);
226
+ window.removeEventListener('pointerup', handlePointerUp);
227
+ document.body.style.removeProperty('cursor');
228
+ document.body.style.removeProperty('user-select');
229
+ };
230
+ }, []);
231
+ const startResize = (event, columnIndex) => {
232
+ var _a;
233
+ event.preventDefault();
234
+ event.stopPropagation();
235
+ resizeStartRef.current = {
236
+ columnIndex,
237
+ startX: event.clientX,
238
+ startWidth: (_a = columnWidths[columnIndex]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH,
239
+ };
240
+ document.body.style.cursor = 'col-resize';
241
+ document.body.style.userSelect = 'none';
242
+ };
243
+ const handleCopy = (text, cellId) => tslib.__awaiter(this, void 0, void 0, function* () {
244
+ if (!text || !navigator.clipboard) {
245
+ return;
246
+ }
247
+ try {
248
+ yield navigator.clipboard.writeText(text);
249
+ setCopiedCell(cellId);
250
+ window.setTimeout(() => setCopiedCell(null), 1200);
251
+ }
252
+ catch (_a) {
253
+ // Keep MDX reading uninterrupted when clipboard permission is unavailable.
254
+ }
255
+ });
256
+ const shellClassName = utils.cn('not-prose my-6 overflow-hidden rounded-lg border bg-fd-card text-fd-card-foreground shadow-sm', CHEET_TABLE_BORDER_CLASS, className);
257
+ const shellStyle = Object.assign({ '--cheet-table-border-color': CHEET_TABLE_BORDER_COLOR }, style);
258
+ const titleBar = title || description || collapsible ? (jsxRuntime.jsxs("div", { className: "grid min-w-0 grid-cols-[minmax(0,90%)_minmax(2rem,10%)] items-center bg-fd-muted/35 px-4 py-2", children: [jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center justify-center gap-0.5", children: [title ? (jsxRuntime.jsx("h3", { className: "truncate text-center text-sm font-semibold leading-6 text-fd-foreground", children: title })) : null, description ? (jsxRuntime.jsx(infoTooltip.InfoTooltip, { content: description, className: "not-prose -mx-0.5", desktopSide: "bottom" })) : null] }), collapsible ? (jsxRuntime.jsx("button", { type: "button", className: utils.cn('justify-self-end inline-flex size-8 shrink-0 items-center justify-center rounded-md text-fd-muted-foreground transition', 'hover:text-fd-accent-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950', open && lib.themeIconColor, lib.themeRingColor), "aria-expanded": open, onClick: () => setOpen((value) => !value), children: open ? jsxRuntime.jsx(icons.EyeIcon, { className: "size-4" }) : jsxRuntime.jsx(icons.EyeOffIcon, { className: "size-4" }) })) : null] })) : null;
259
+ if (!mounted) {
260
+ return (jsxRuntime.jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsxRuntime.jsx("div", { ref: sourceRef, hidden: true, children: children })] })));
261
+ }
262
+ if (!model) {
263
+ return (jsxRuntime.jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsxRuntime.jsx("div", { ref: sourceRef, hidden: true, children: children }), open && parsed ? (jsxRuntime.jsx("div", { className: "px-4 py-6 text-center text-sm text-fd-muted-foreground", children: emptyText })) : null] })));
264
+ }
265
+ const content = model.rows.length > 0 ? (jsxRuntime.jsx("div", { className: "overflow-x-auto", children: jsxRuntime.jsxs("table", { className: "table-fixed border-separate border-spacing-0 text-sm", style: {
266
+ width: columnWidths.length
267
+ ? columnWidths.reduce((total, width) => total + width, 0)
268
+ : '100%',
269
+ minWidth: '100%',
270
+ }, children: [jsxRuntime.jsx("colgroup", { children: model.headers.map((header, index) => {
271
+ var _a;
272
+ return (jsxRuntime.jsx("col", { style: {
273
+ width: (_a = columnWidths[index]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH,
274
+ minWidth: MIN_COLUMN_WIDTH,
275
+ } }, `${header.text}-${index}-col`));
276
+ }) }), jsxRuntime.jsx("thead", { className: utils.cn(stickyHeader && 'sticky top-0 z-10'), children: jsxRuntime.jsx("tr", { children: model.headers.map((header, index) => (jsxRuntime.jsxs("th", { className: utils.cn('relative border-t border-b bg-fd-muted/80 px-3 py-2.5 align-middle text-xs font-semibold uppercase tracking-normal text-fd-muted-foreground first:pl-4 last:pr-4', index > 0 && 'border-l', CHEET_TABLE_BORDER_CLASS, 'text-left'), scope: "col", children: [jsxRuntime.jsx(CheetCellContent, { cell: header, force: header.force }), index < model.headers.length - 1 ? (jsxRuntime.jsx("button", { type: "button", "aria-label": "Resize column", className: utils.cn('absolute top-0 right-0 h-full w-2 cursor-col-resize touch-none select-none', 'after:absolute after:top-2 after:right-0 after:bottom-2 after:w-px after:bg-transparent after:transition-colors', 'hover:after:bg-fd-muted-foreground/40'), onPointerDown: (event) => startResize(event, index) })) : null] }, `${header.text}-${index}`))) }) }), jsxRuntime.jsx("tbody", { children: model.rows.map((row, rowIndex) => (jsxRuntime.jsx("tr", { className: utils.cn('transition-colors hover:bg-fd-accent/45', striped && rowIndex % 2 === 1 && 'bg-fd-muted/30'), children: model.headers.map((header, columnIndex) => {
277
+ var _a;
278
+ const cell = (_a = row[columnIndex]) !== null && _a !== void 0 ? _a : {
279
+ rawText: '',
280
+ text: '',
281
+ force: false,
282
+ };
283
+ const cellId = `${rowIndex}:${columnIndex}`;
284
+ const copyable = copyableColumnSet.has(header.text);
285
+ return (jsxRuntime.jsx("td", { className: utils.cn('group px-3 py-2.5 align-top text-fd-foreground first:pl-4 last:pr-4', rowIndex < model.rows.length - 1 && 'border-b', columnIndex > 0 && 'border-l', 'text-left', CHEET_TABLE_BORDER_CLASS), onDoubleClick: (event) => {
286
+ event.preventDefault();
287
+ event.stopPropagation();
288
+ }, children: jsxRuntime.jsx(CheetCellContent, { cell: cell, force: header.force || cell.force, copyable: copyable, copied: copiedCell === cellId, onCopy: copyable
289
+ ? () => handleCopy(cell.text, cellId)
290
+ : undefined }) }, cellId));
291
+ }) }, rowIndex))) })] }) })) : (jsxRuntime.jsx("div", { className: "px-4 py-6 text-center text-sm text-fd-muted-foreground", children: emptyText }));
292
+ return (jsxRuntime.jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsxRuntime.jsx("div", { ref: sourceRef, hidden: true, children: children }), open ? content : null] })));
293
+ }
294
+
295
+ exports.CheetTable = CheetTable;
@@ -0,0 +1,293 @@
1
+ "use client";
2
+ import { __rest, __awaiter } from 'tslib';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { EyeIcon, EyeOffIcon, CheckIcon, CopyIcon } from '@windrun-huaiin/base-ui/icons';
5
+ import { themeSvgIconColor, themeIconColor, themeRingColor } from '@windrun-huaiin/base-ui/lib';
6
+ import { cn } from '@windrun-huaiin/lib/utils';
7
+ import { useState, useRef, useMemo, useEffect, isValidElement, Children } from 'react';
8
+ import { InfoTooltip } from '../../main/info-tooltip.mjs';
9
+
10
+ const DEFAULT_COLUMN_WIDTH = 180;
11
+ const MIN_COLUMN_WIDTH = 140;
12
+ const CHEET_TABLE_BORDER_COLOR = `color-mix(in srgb, ${themeSvgIconColor} 35%, transparent)`;
13
+ const CHEET_TABLE_BORDER_CLASS = 'border-[color:var(--cheet-table-border-color)]';
14
+ function parseCheetCell(raw) {
15
+ let value = raw.trim();
16
+ let force = false;
17
+ if (value.startsWith('!!')) {
18
+ force = true;
19
+ value = value.slice(2).trimStart();
20
+ }
21
+ const descriptionIndex = value.indexOf('??');
22
+ if (descriptionIndex === -1) {
23
+ return {
24
+ text: value,
25
+ force,
26
+ };
27
+ }
28
+ const description = value.slice(descriptionIndex + 2).trim();
29
+ return {
30
+ text: value.slice(0, descriptionIndex).trimEnd(),
31
+ description: description ? description : undefined,
32
+ force,
33
+ };
34
+ }
35
+ function normalizeText(value) {
36
+ return value.replace(/\s+/g, ' ').trim();
37
+ }
38
+ function collectText(node) {
39
+ if (typeof node === 'string' || typeof node === 'number') {
40
+ return String(node);
41
+ }
42
+ if (Array.isArray(node)) {
43
+ return node.map(collectText).join('');
44
+ }
45
+ if (isValidElement(node)) {
46
+ return collectText(node.props.children);
47
+ }
48
+ return '';
49
+ }
50
+ function getElementChildren(element) {
51
+ return Children.toArray(element.props.children);
52
+ }
53
+ function isElementType(node, type) {
54
+ return isValidElement(node) && node.type === type;
55
+ }
56
+ function findFirstElement(node, type) {
57
+ if (isElementType(node, type)) {
58
+ return node;
59
+ }
60
+ if (Array.isArray(node)) {
61
+ for (const child of node) {
62
+ const match = findFirstElement(child, type);
63
+ if (match) {
64
+ return match;
65
+ }
66
+ }
67
+ }
68
+ if (isValidElement(node)) {
69
+ return findFirstElement(node.props.children, type);
70
+ }
71
+ return null;
72
+ }
73
+ function collectRowElements(node) {
74
+ if (isElementType(node, 'tr')) {
75
+ return [node];
76
+ }
77
+ if (Array.isArray(node)) {
78
+ return node.flatMap(collectRowElements);
79
+ }
80
+ if (isValidElement(node)) {
81
+ return collectRowElements(node.props.children);
82
+ }
83
+ return [];
84
+ }
85
+ function parseRow(row) {
86
+ return getElementChildren(row)
87
+ .filter(isValidElement)
88
+ .map((cell) => {
89
+ const rawText = normalizeText(collectText(cell.props.children));
90
+ return Object.assign({ rawText }, parseCheetCell(rawText));
91
+ });
92
+ }
93
+ function parseCheetTable(children) {
94
+ var _a;
95
+ const rows = collectRowElements(children);
96
+ if (rows.length > 0) {
97
+ const headerRow = rows[0];
98
+ const dataRows = rows.slice(1);
99
+ const headers = parseRow(headerRow);
100
+ if (headers.length === 0) {
101
+ return null;
102
+ }
103
+ return {
104
+ headers,
105
+ rows: dataRows.map(parseRow).filter((row) => row.length > 0),
106
+ };
107
+ }
108
+ const table = findFirstElement(children, 'table');
109
+ if (!table) {
110
+ return null;
111
+ }
112
+ const thead = findFirstElement(table.props.children, 'thead');
113
+ const tbody = findFirstElement(table.props.children, 'tbody');
114
+ const headRows = thead ? collectRowElements(thead.props.children) : [];
115
+ const bodyRows = tbody ? collectRowElements(tbody.props.children) : [];
116
+ const fallbackRows = !thead && !tbody ? collectRowElements(table.props.children) : [];
117
+ const headerRow = (_a = headRows[0]) !== null && _a !== void 0 ? _a : fallbackRows[0];
118
+ const dataRows = bodyRows.length > 0 ? bodyRows : fallbackRows.slice(1);
119
+ if (!headerRow) {
120
+ return null;
121
+ }
122
+ const headers = parseRow(headerRow);
123
+ if (headers.length === 0) {
124
+ return null;
125
+ }
126
+ return {
127
+ headers,
128
+ rows: dataRows.map(parseRow).filter((row) => row.length > 0),
129
+ };
130
+ }
131
+ function parseCheetTableElement(element) {
132
+ const table = element === null || element === void 0 ? void 0 : element.querySelector('table');
133
+ if (!table) {
134
+ return null;
135
+ }
136
+ const headRow = table.querySelector('thead tr');
137
+ const bodyRows = Array.from(table.querySelectorAll('tbody tr'));
138
+ const fallbackRows = Array.from(table.querySelectorAll('tr'));
139
+ const headerRow = headRow !== null && headRow !== void 0 ? headRow : fallbackRows[0];
140
+ const dataRows = bodyRows.length > 0
141
+ ? bodyRows
142
+ : fallbackRows.slice(headerRow ? 1 : 0);
143
+ if (!headerRow) {
144
+ return null;
145
+ }
146
+ const parseDomRow = (row) => Array.from(row.querySelectorAll('th,td')).map((cell) => {
147
+ var _a;
148
+ const rawText = normalizeText((_a = cell.textContent) !== null && _a !== void 0 ? _a : '');
149
+ return Object.assign({ rawText }, parseCheetCell(rawText));
150
+ });
151
+ const headers = parseDomRow(headerRow);
152
+ if (headers.length === 0) {
153
+ return null;
154
+ }
155
+ return {
156
+ headers,
157
+ rows: dataRows.map(parseDomRow).filter((row) => row.length > 0),
158
+ };
159
+ }
160
+ function createCopyableColumnSet(headers, copyableColumns) {
161
+ const names = copyableColumns !== null && copyableColumns !== void 0 ? copyableColumns : [];
162
+ return new Set(names.map((name) => {
163
+ const header = headers.find((item) => item.text === name || item.rawText === name);
164
+ return header ? header.text : name;
165
+ }));
166
+ }
167
+ function CheetCellContent({ cell, force, copied, copyable, onCopy, }) {
168
+ return (jsxs("span", { className: "flex min-w-0 items-center gap-0.5", children: [jsx("span", { className: cn('block min-w-0 truncate', force && cn('font-semibold', themeIconColor)), children: cell.text }), cell.description ? (jsx(InfoTooltip, { content: cell.description, className: "not-prose -mx-0.5", desktopSide: "bottom" })) : null, copyable ? (jsx("button", { type: "button", "aria-label": copied ? 'Copied' : 'Copy cell content', className: cn('not-prose inline-flex size-6 shrink-0 touch-manipulation items-center justify-center rounded text-fd-muted-foreground transition', 'hover:bg-fd-accent hover:text-fd-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950', copied && themeIconColor, themeRingColor), onClick: (event) => {
169
+ event.preventDefault();
170
+ event.stopPropagation();
171
+ onCopy === null || onCopy === void 0 ? void 0 : onCopy();
172
+ }, onPointerDown: (event) => {
173
+ event.stopPropagation();
174
+ }, children: copied ? jsx(CheckIcon, { className: "size-3.5" }) : jsx(CopyIcon, { className: "size-3.5" }) })) : null] }));
175
+ }
176
+ function CheetTable(_a) {
177
+ var _b;
178
+ var { title, description, copyableColumns, defaultOpen = true, collapsible = true, striped = true, stickyHeader = false, emptyText = 'No data', className, children, style } = _a, props = __rest(_a, ["title", "description", "copyableColumns", "defaultOpen", "collapsible", "striped", "stickyHeader", "emptyText", "className", "children", "style"]);
179
+ const [mounted, setMounted] = useState(false);
180
+ const [model, setModel] = useState(null);
181
+ const [parsed, setParsed] = useState(false);
182
+ const [open, setOpen] = useState(defaultOpen);
183
+ const [copiedCell, setCopiedCell] = useState(null);
184
+ const [columnWidths, setColumnWidths] = useState([]);
185
+ const sourceRef = useRef(null);
186
+ const resizeStartRef = useRef(null);
187
+ const headerCount = (_b = model === null || model === void 0 ? void 0 : model.headers.length) !== null && _b !== void 0 ? _b : 0;
188
+ const copyableColumnSet = useMemo(() => { var _a; return createCopyableColumnSet((_a = model === null || model === void 0 ? void 0 : model.headers) !== null && _a !== void 0 ? _a : [], copyableColumns); }, [copyableColumns, model === null || model === void 0 ? void 0 : model.headers]);
189
+ useEffect(() => {
190
+ var _a;
191
+ setMounted(true);
192
+ setModel((_a = parseCheetTableElement(sourceRef.current)) !== null && _a !== void 0 ? _a : parseCheetTable(children));
193
+ setParsed(true);
194
+ }, [children]);
195
+ useEffect(() => {
196
+ if (headerCount === 0) {
197
+ setColumnWidths([]);
198
+ return;
199
+ }
200
+ setColumnWidths((current) => Array.from({ length: headerCount }, (_, index) => { var _a; return Math.max((_a = current[index]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH, MIN_COLUMN_WIDTH); }));
201
+ }, [headerCount]);
202
+ useEffect(() => {
203
+ function handlePointerMove(event) {
204
+ const start = resizeStartRef.current;
205
+ if (!start) {
206
+ return;
207
+ }
208
+ const nextWidth = Math.max(MIN_COLUMN_WIDTH, start.startWidth + event.clientX - start.startX);
209
+ setColumnWidths((current) => {
210
+ const next = [...current];
211
+ next[start.columnIndex] = nextWidth;
212
+ return next;
213
+ });
214
+ }
215
+ function handlePointerUp() {
216
+ resizeStartRef.current = null;
217
+ document.body.style.removeProperty('cursor');
218
+ document.body.style.removeProperty('user-select');
219
+ }
220
+ window.addEventListener('pointermove', handlePointerMove);
221
+ window.addEventListener('pointerup', handlePointerUp);
222
+ return () => {
223
+ window.removeEventListener('pointermove', handlePointerMove);
224
+ window.removeEventListener('pointerup', handlePointerUp);
225
+ document.body.style.removeProperty('cursor');
226
+ document.body.style.removeProperty('user-select');
227
+ };
228
+ }, []);
229
+ const startResize = (event, columnIndex) => {
230
+ var _a;
231
+ event.preventDefault();
232
+ event.stopPropagation();
233
+ resizeStartRef.current = {
234
+ columnIndex,
235
+ startX: event.clientX,
236
+ startWidth: (_a = columnWidths[columnIndex]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH,
237
+ };
238
+ document.body.style.cursor = 'col-resize';
239
+ document.body.style.userSelect = 'none';
240
+ };
241
+ const handleCopy = (text, cellId) => __awaiter(this, void 0, void 0, function* () {
242
+ if (!text || !navigator.clipboard) {
243
+ return;
244
+ }
245
+ try {
246
+ yield navigator.clipboard.writeText(text);
247
+ setCopiedCell(cellId);
248
+ window.setTimeout(() => setCopiedCell(null), 1200);
249
+ }
250
+ catch (_a) {
251
+ // Keep MDX reading uninterrupted when clipboard permission is unavailable.
252
+ }
253
+ });
254
+ const shellClassName = cn('not-prose my-6 overflow-hidden rounded-lg border bg-fd-card text-fd-card-foreground shadow-sm', CHEET_TABLE_BORDER_CLASS, className);
255
+ const shellStyle = Object.assign({ '--cheet-table-border-color': CHEET_TABLE_BORDER_COLOR }, style);
256
+ const titleBar = title || description || collapsible ? (jsxs("div", { className: "grid min-w-0 grid-cols-[minmax(0,90%)_minmax(2rem,10%)] items-center bg-fd-muted/35 px-4 py-2", children: [jsxs("div", { className: "flex min-w-0 items-center justify-center gap-0.5", children: [title ? (jsx("h3", { className: "truncate text-center text-sm font-semibold leading-6 text-fd-foreground", children: title })) : null, description ? (jsx(InfoTooltip, { content: description, className: "not-prose -mx-0.5", desktopSide: "bottom" })) : null] }), collapsible ? (jsx("button", { type: "button", className: cn('justify-self-end inline-flex size-8 shrink-0 items-center justify-center rounded-md text-fd-muted-foreground transition', 'hover:text-fd-accent-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950', open && themeIconColor, themeRingColor), "aria-expanded": open, onClick: () => setOpen((value) => !value), children: open ? jsx(EyeIcon, { className: "size-4" }) : jsx(EyeOffIcon, { className: "size-4" }) })) : null] })) : null;
257
+ if (!mounted) {
258
+ return (jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsx("div", { ref: sourceRef, hidden: true, children: children })] })));
259
+ }
260
+ if (!model) {
261
+ return (jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsx("div", { ref: sourceRef, hidden: true, children: children }), open && parsed ? (jsx("div", { className: "px-4 py-6 text-center text-sm text-fd-muted-foreground", children: emptyText })) : null] })));
262
+ }
263
+ const content = model.rows.length > 0 ? (jsx("div", { className: "overflow-x-auto", children: jsxs("table", { className: "table-fixed border-separate border-spacing-0 text-sm", style: {
264
+ width: columnWidths.length
265
+ ? columnWidths.reduce((total, width) => total + width, 0)
266
+ : '100%',
267
+ minWidth: '100%',
268
+ }, children: [jsx("colgroup", { children: model.headers.map((header, index) => {
269
+ var _a;
270
+ return (jsx("col", { style: {
271
+ width: (_a = columnWidths[index]) !== null && _a !== void 0 ? _a : DEFAULT_COLUMN_WIDTH,
272
+ minWidth: MIN_COLUMN_WIDTH,
273
+ } }, `${header.text}-${index}-col`));
274
+ }) }), jsx("thead", { className: cn(stickyHeader && 'sticky top-0 z-10'), children: jsx("tr", { children: model.headers.map((header, index) => (jsxs("th", { className: cn('relative border-t border-b bg-fd-muted/80 px-3 py-2.5 align-middle text-xs font-semibold uppercase tracking-normal text-fd-muted-foreground first:pl-4 last:pr-4', index > 0 && 'border-l', CHEET_TABLE_BORDER_CLASS, 'text-left'), scope: "col", children: [jsx(CheetCellContent, { cell: header, force: header.force }), index < model.headers.length - 1 ? (jsx("button", { type: "button", "aria-label": "Resize column", className: cn('absolute top-0 right-0 h-full w-2 cursor-col-resize touch-none select-none', 'after:absolute after:top-2 after:right-0 after:bottom-2 after:w-px after:bg-transparent after:transition-colors', 'hover:after:bg-fd-muted-foreground/40'), onPointerDown: (event) => startResize(event, index) })) : null] }, `${header.text}-${index}`))) }) }), jsx("tbody", { children: model.rows.map((row, rowIndex) => (jsx("tr", { className: cn('transition-colors hover:bg-fd-accent/45', striped && rowIndex % 2 === 1 && 'bg-fd-muted/30'), children: model.headers.map((header, columnIndex) => {
275
+ var _a;
276
+ const cell = (_a = row[columnIndex]) !== null && _a !== void 0 ? _a : {
277
+ rawText: '',
278
+ text: '',
279
+ force: false,
280
+ };
281
+ const cellId = `${rowIndex}:${columnIndex}`;
282
+ const copyable = copyableColumnSet.has(header.text);
283
+ return (jsx("td", { className: cn('group px-3 py-2.5 align-top text-fd-foreground first:pl-4 last:pr-4', rowIndex < model.rows.length - 1 && 'border-b', columnIndex > 0 && 'border-l', 'text-left', CHEET_TABLE_BORDER_CLASS), onDoubleClick: (event) => {
284
+ event.preventDefault();
285
+ event.stopPropagation();
286
+ }, children: jsx(CheetCellContent, { cell: cell, force: header.force || cell.force, copyable: copyable, copied: copiedCell === cellId, onCopy: copyable
287
+ ? () => handleCopy(cell.text, cellId)
288
+ : undefined }) }, cellId));
289
+ }) }, rowIndex))) })] }) })) : (jsx("div", { className: "px-4 py-6 text-center text-sm text-fd-muted-foreground", children: emptyText }));
290
+ return (jsxs("section", Object.assign({ "data-cheet-table": true, className: shellClassName, style: shellStyle }, props, { children: [titleBar, jsx("div", { ref: sourceRef, hidden: true, children: children }), open ? content : null] })));
291
+ }
292
+
293
+ export { CheetTable };
@@ -7,3 +7,4 @@ export * from './toc-footer-wrapper';
7
7
  export * from './toc-clerk-portable';
8
8
  export * from './banner';
9
9
  export * from './suno-embed';
10
+ export * from './cheet-table';
@@ -10,6 +10,7 @@ var tocFooterWrapper = require('./toc-footer-wrapper.js');
10
10
  var tocClerkPortable = require('./toc-clerk-portable.js');
11
11
  var banner = require('./banner.js');
12
12
  var sunoEmbed = require('./suno-embed.js');
13
+ var cheetTable = require('./cheet-table.js');
13
14
 
14
15
 
15
16
 
@@ -29,3 +30,4 @@ exports.PortableClerkTOCScrollArea = tocClerkPortable.PortableClerkTOCScrollArea
29
30
  exports.PortableClerkTOCTitle = tocClerkPortable.PortableClerkTOCTitle;
30
31
  exports.Banner = banner.Banner;
31
32
  exports.SunoEmbed = sunoEmbed.SunoEmbed;
33
+ exports.CheetTable = cheetTable.CheetTable;
@@ -8,3 +8,4 @@ export { TocFooterWrapper } from './toc-footer-wrapper.mjs';
8
8
  export { PortableClerkTOC, PortableClerkTOCItems, PortableClerkTOCPopover, PortableClerkTOCScrollArea, PortableClerkTOCTitle } from './toc-clerk-portable.mjs';
9
9
  export { Banner } from './banner.mjs';
10
10
  export { SunoEmbed } from './suno-embed.mjs';
11
+ export { CheetTable } from './cheet-table.mjs';
@@ -11,6 +11,7 @@ require('@windrun-huaiin/base-ui/lib');
11
11
  require('@windrun-huaiin/lib/utils');
12
12
  var ziaFile = require('../../mdx/zia-file.js');
13
13
  var sunoEmbed = require('../../mdx/suno-embed.js');
14
+ var cheetTable = require('../../mdx/cheet-table.js');
14
15
 
15
16
  const ImageGrid = React.lazy(() => Promise.resolve().then(function () { return require('../../heavy/image-grid.js'); }).then((mod) => ({ default: mod.ImageGrid })));
16
17
  const ImageZoom = React.lazy(() => Promise.resolve().then(function () { return require('../../heavy/image-zoom.js'); }).then((mod) => ({ default: mod.ImageZoom })));
@@ -22,6 +23,7 @@ function createWidgetMdxComponents(cdnBaseUrl, imageFallbackSrc) {
22
23
  ZiaFile: ziaFile.ZiaFile,
23
24
  ZiaFolder: ziaFile.ZiaFolder,
24
25
  SunoEmbed: sunoEmbed.SunoEmbed,
26
+ CheetTable: cheetTable.CheetTable,
25
27
  ImageGrid: (props) => (jsxRuntime.jsx(ImageGrid, Object.assign({}, props, { cdnBaseUrl: cdnBaseUrl }))),
26
28
  ImageZoom: (props) => (jsxRuntime.jsx(ImageZoom, Object.assign({}, props, { fallbackSrc: imageFallbackSrc }))),
27
29
  };