@comitor/ui 3.3.1 → 3.3.2
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.
|
@@ -34,8 +34,8 @@ function DataGridRowHeightMenu({
|
|
|
34
34
|
{
|
|
35
35
|
"aria-label": msg.rowHeight.label,
|
|
36
36
|
size: "sm",
|
|
37
|
-
className: cn("w-auto gap-
|
|
38
|
-
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: msg.rowHeight.label, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-
|
|
37
|
+
className: cn("w-auto gap-1.5 text-xs font-normal", triggerClassName),
|
|
38
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: msg.rowHeight.label, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
39
39
|
/* @__PURE__ */ jsx(selectedRowHeight.icon, { "aria-hidden": "true", className: "size-3.5 text-muted-foreground" }),
|
|
40
40
|
selectedRowHeight.label
|
|
41
41
|
] }) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-grid/data-grid-row-height-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Table } from \"@tanstack/react-table\";\nimport { AlignVerticalSpaceAroundIcon, ChevronsDownUpIcon, EqualIcon, MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useResolvedDataGridMessages } from \"./data-grid-locale.js\";\nimport type { DataGridLocale, DataGridMessages } from \"./messages.js\";\n\n/*\n * Bốn mức chiều cao. PHẢI dựng theo bảng chuỗi chứ không đóng băng thành hằng ở đầu module —\n * nhãn (\"Short\" / \"Thấp\") đổi theo ngôn ngữ, còn `value` và icon thì không.\n */\nfunction buildRowHeights(msg: DataGridMessages) {\n // Tuple (không phải mảng) để `rowHeights[0]` có kiểu chắc chắn — bốn mức là cố định.\n return [\n { label: msg.rowHeight.short, value: \"short\" as const, icon: MinusIcon },\n { label: msg.rowHeight.medium, value: \"medium\" as const, icon: EqualIcon },\n { label: msg.rowHeight.tall, value: \"tall\" as const, icon: AlignVerticalSpaceAroundIcon },\n { label: msg.rowHeight.extraTall, value: \"extra-tall\" as const, icon: ChevronsDownUpIcon }\n ] as const;\n}\n\ninterface DataGridRowHeightMenuProps<TData> extends React.ComponentProps<typeof SelectContent> {\n table: Table<TData>;\n disabled?: boolean;\n /** className cho nút mở menu (mặc định cao 8 như các nút khác trên thanh công cụ) */\n triggerClassName?: string;\n /**\n * Ngôn ngữ của menu: `\"en\"` · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn.\n *\n * ⚠ Thanh công cụ nằm NGOÀI `<DataGrid>`, nên `locale` truyền cho lưới không với tới đây. Bỏ\n * trống thì menu theo `DataGridLocaleProvider`, rồi ngôn ngữ của `ComitorLocaleProvider`, rồi\n * `\"en\"` — app đã đặt `ComitorLocaleProvider` ở root thì không cần làm gì. Prop thắng provider.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\n/** Chọn chiều cao dòng của lưới (thấp/vừa/cao/rất cao) — đặt trên thanh công cụ. */\nexport function DataGridRowHeightMenu<TData>({\n table,\n disabled,\n triggerClassName,\n locale,\n ...props\n}: DataGridRowHeightMenuProps<TData>) {\n const msg = useResolvedDataGridMessages(locale);\n const rowHeights = React.useMemo(() => buildRowHeights(msg), [msg]);\n const rowHeight = table.options.meta?.rowHeight;\n const onRowHeightChange = table.options.meta?.onRowHeightChange;\n\n const selectedRowHeight = React.useMemo(\n () => rowHeights.find((option) => option.value === rowHeight) ?? rowHeights[0],\n [rowHeight, rowHeights]\n );\n\n return (\n <Select value={rowHeight} onValueChange={onRowHeightChange} disabled={disabled}>\n <SelectTrigger\n aria-label={msg.rowHeight.label}\n size=\"sm\"\n className={cn(\"w-auto gap-
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/data-grid-row-height-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Table } from \"@tanstack/react-table\";\nimport { AlignVerticalSpaceAroundIcon, ChevronsDownUpIcon, EqualIcon, MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useResolvedDataGridMessages } from \"./data-grid-locale.js\";\nimport type { DataGridLocale, DataGridMessages } from \"./messages.js\";\n\n/*\n * Bốn mức chiều cao. PHẢI dựng theo bảng chuỗi chứ không đóng băng thành hằng ở đầu module —\n * nhãn (\"Short\" / \"Thấp\") đổi theo ngôn ngữ, còn `value` và icon thì không.\n */\nfunction buildRowHeights(msg: DataGridMessages) {\n // Tuple (không phải mảng) để `rowHeights[0]` có kiểu chắc chắn — bốn mức là cố định.\n return [\n { label: msg.rowHeight.short, value: \"short\" as const, icon: MinusIcon },\n { label: msg.rowHeight.medium, value: \"medium\" as const, icon: EqualIcon },\n { label: msg.rowHeight.tall, value: \"tall\" as const, icon: AlignVerticalSpaceAroundIcon },\n { label: msg.rowHeight.extraTall, value: \"extra-tall\" as const, icon: ChevronsDownUpIcon }\n ] as const;\n}\n\ninterface DataGridRowHeightMenuProps<TData> extends React.ComponentProps<typeof SelectContent> {\n table: Table<TData>;\n disabled?: boolean;\n /** className cho nút mở menu (mặc định cao 8 như các nút khác trên thanh công cụ) */\n triggerClassName?: string;\n /**\n * Ngôn ngữ của menu: `\"en\"` · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn.\n *\n * ⚠ Thanh công cụ nằm NGOÀI `<DataGrid>`, nên `locale` truyền cho lưới không với tới đây. Bỏ\n * trống thì menu theo `DataGridLocaleProvider`, rồi ngôn ngữ của `ComitorLocaleProvider`, rồi\n * `\"en\"` — app đã đặt `ComitorLocaleProvider` ở root thì không cần làm gì. Prop thắng provider.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\n/** Chọn chiều cao dòng của lưới (thấp/vừa/cao/rất cao) — đặt trên thanh công cụ. */\nexport function DataGridRowHeightMenu<TData>({\n table,\n disabled,\n triggerClassName,\n locale,\n ...props\n}: DataGridRowHeightMenuProps<TData>) {\n const msg = useResolvedDataGridMessages(locale);\n const rowHeights = React.useMemo(() => buildRowHeights(msg), [msg]);\n const rowHeight = table.options.meta?.rowHeight;\n const onRowHeightChange = table.options.meta?.onRowHeightChange;\n\n const selectedRowHeight = React.useMemo(\n () => rowHeights.find((option) => option.value === rowHeight) ?? rowHeights[0],\n [rowHeight, rowHeights]\n );\n\n return (\n <Select value={rowHeight} onValueChange={onRowHeightChange} disabled={disabled}>\n {/*\n * ⚠ Khác bản gốc (3.3.2): nút mở mang `text-xs` + `gap-1.5`, đúng bậc chữ của các nút khác\n * trên thanh công cụ (`Button size=\"sm\"` = `h-8 px-3 text-xs gap-1.5`). Bậc `sm` của\n * `SelectTrigger` chỉ hạ CHIỀU CAO (`data-[size=sm]:h-8`), chữ giữ `text-sm` — nên trước bản\n * này nút này là chữ 14px (dòng 20px, icon–chữ 8px) đứng giữa một hàng 12px (dòng 16px,\n * icon–chữ 6px). Đổi ở ĐÂY chứ không đổi bậc `sm` của `SelectTrigger`: đó là giao diện của\n * mọi `Select size=\"sm\"` trong app, và trong gói thì đây là chỗ DUY NHẤT dùng bậc đó.\n */}\n <SelectTrigger\n aria-label={msg.rowHeight.label}\n size=\"sm\"\n className={cn(\"w-auto gap-1.5 text-xs font-normal\", triggerClassName)}\n >\n <SelectValue placeholder={msg.rowHeight.label}>\n <span className=\"flex items-center gap-1.5\">\n <selectedRowHeight.icon aria-hidden=\"true\" className=\"size-3.5 text-muted-foreground\" />\n {selectedRowHeight.label}\n </span>\n </SelectValue>\n </SelectTrigger>\n <SelectContent {...props}>\n <SelectGroup>\n {rowHeights.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <span className=\"flex items-center gap-2\">\n <option.icon aria-hidden=\"true\" className=\"size-4 text-muted-foreground\" />\n {option.label}\n </span>\n </SelectItem>\n ))}\n </SelectGroup>\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":";AAyEU,SACE,KADF;AAtEV,SAAS,8BAA8B,oBAAoB,WAAW,iBAAiB;AACvF,YAAY,WAAW;AACvB,SAAS,QAAQ,eAAe,aAAa,YAAY,eAAe,mBAAmB;AAC3F,SAAS,UAAU;AACnB,SAAS,mCAAmC;AAO5C,SAAS,gBAAgB,KAAuB;AAE9C,SAAO;AAAA,IACL,EAAE,OAAO,IAAI,UAAU,OAAO,OAAO,SAAkB,MAAM,UAAU;AAAA,IACvE,EAAE,OAAO,IAAI,UAAU,QAAQ,OAAO,UAAmB,MAAM,UAAU;AAAA,IACzE,EAAE,OAAO,IAAI,UAAU,MAAM,OAAO,QAAiB,MAAM,6BAA6B;AAAA,IACxF,EAAE,OAAO,IAAI,UAAU,WAAW,OAAO,cAAuB,MAAM,mBAAmB;AAAA,EAC3F;AACF;AAkBO,SAAS,sBAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsC;AACpC,QAAM,MAAM,4BAA4B,MAAM;AAC9C,QAAM,aAAa,MAAM,QAAQ,MAAM,gBAAgB,GAAG,GAAG,CAAC,GAAG,CAAC;AAClE,QAAM,YAAY,MAAM,QAAQ,MAAM;AACtC,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAE9C,QAAM,oBAAoB,MAAM;AAAA,IAC9B,MAAM,WAAW,KAAK,CAAC,WAAW,OAAO,UAAU,SAAS,KAAK,WAAW,CAAC;AAAA,IAC7E,CAAC,WAAW,UAAU;AAAA,EACxB;AAEA,SACE,qBAAC,UAAO,OAAO,WAAW,eAAe,mBAAmB,UAS1D;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,cAAY,IAAI,UAAU;AAAA,QAC1B,MAAK;AAAA,QACL,WAAW,GAAG,sCAAsC,gBAAgB;AAAA,QAEpE,8BAAC,eAAY,aAAa,IAAI,UAAU,OACtC,+BAAC,UAAK,WAAU,6BACd;AAAA,8BAAC,kBAAkB,MAAlB,EAAuB,eAAY,QAAO,WAAU,kCAAiC;AAAA,UACrF,kBAAkB;AAAA,WACrB,GACF;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,iBAAe,GAAG,OACjB,8BAAC,eACE,qBAAW,IAAI,CAAC,WACf,oBAAC,cAA8B,OAAO,OAAO,OAC3C,+BAAC,UAAK,WAAU,2BACd;AAAA,0BAAC,OAAO,MAAP,EAAY,eAAY,QAAO,WAAU,gCAA+B;AAAA,MACxE,OAAO;AAAA,OACV,KAJe,OAAO,KAKxB,CACD,GACH,GACF;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/data-grid/utils.js
CHANGED
|
@@ -92,7 +92,6 @@ function getColumnPinningStyle(params) {
|
|
|
92
92
|
boxShadow: withBorder ? isLastLeftPinnedColumn ? isRtl ? "4px 0 4px -4px var(--border) inset" : "-4px 0 4px -4px var(--border) inset" : isFirstRightPinnedColumn ? isRtl ? "-4px 0 4px -4px var(--border) inset" : "4px 0 4px -4px var(--border) inset" : void 0 : void 0,
|
|
93
93
|
left: isRtl ? rightPosition : leftPosition,
|
|
94
94
|
right: isRtl ? leftPosition : rightPosition,
|
|
95
|
-
opacity: isPinned ? 0.97 : 1,
|
|
96
95
|
position: isPinned ? "sticky" : "relative",
|
|
97
96
|
background: "var(--card)",
|
|
98
97
|
width: column.getSize(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/data-grid/utils.ts"],"sourcesContent":["import type { Column, Table } from \"@tanstack/react-table\";\nimport { type Locale as DateFnsLocale, format, isValid, parse } from \"date-fns\";\nimport {\n BaselineIcon,\n CalendarIcon,\n CheckSquareIcon,\n File,\n FileArchive,\n FileAudio,\n FileIcon,\n FileImage,\n FileSpreadsheet,\n FileText,\n FileVideo,\n HashIcon,\n LinkIcon,\n ListChecksIcon,\n ListIcon,\n Presentation,\n WrapTextIcon\n} from \"lucide-react\";\nimport type * as React from \"react\";\nimport { type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport type { CellOpts, CellPosition, Direction, FileCellData, RowHeightValue } from \"./types.js\";\n\nexport function flexRender<TProps extends object>(\n Comp: ((props: TProps) => React.ReactNode) | string | undefined,\n props: TProps\n): React.ReactNode {\n if (typeof Comp === \"string\") {\n return Comp;\n }\n return Comp?.(props);\n}\n\nexport function getIsFileCellData(item: unknown): item is FileCellData {\n return !!item && typeof item === \"object\" && \"id\" in item && \"name\" in item && \"size\" in item && \"type\" in item;\n}\n\nexport function matchSelectOption(value: string, options: { value: string; label: string }[]): string | undefined {\n return options.find(\n (o) =>\n o.value === value ||\n o.value.toLowerCase() === value.toLowerCase() ||\n o.label.toLowerCase() === value.toLowerCase()\n )?.value;\n}\n\nexport function getCellKey(rowIndex: number, columnId: string) {\n return `${rowIndex}:${columnId}`;\n}\n\nexport function parseCellKey(cellKey: string): Required<CellPosition> {\n const parts = cellKey.split(\":\");\n const rowIndexStr = parts[0];\n const columnId = parts[1];\n if (rowIndexStr && columnId) {\n const rowIndex = parseInt(rowIndexStr, 10);\n if (!Number.isNaN(rowIndex)) {\n return { rowIndex, columnId };\n }\n }\n return { rowIndex: 0, columnId: \"\" };\n}\n\nexport function getRowHeightValue(rowHeight: RowHeightValue): number {\n const rowHeightMap: Record<RowHeightValue, number> = {\n short: 36,\n medium: 56,\n tall: 76,\n \"extra-tall\": 96\n };\n\n return rowHeightMap[rowHeight];\n}\n\nexport function getLineCount(rowHeight: RowHeightValue): number {\n const lineCountMap: Record<RowHeightValue, number> = {\n short: 1,\n medium: 2,\n tall: 3,\n \"extra-tall\": 4\n };\n\n return lineCountMap[rowHeight];\n}\n\nexport function getColumnBorderVisibility<TData>(params: {\n column: Column<TData>;\n nextColumn?: Column<TData>;\n isLastColumn: boolean;\n}): {\n showEndBorder: boolean;\n showStartBorder: boolean;\n} {\n const { column, nextColumn, isLastColumn } = params;\n\n const isPinned = column.getIsPinned();\n const isFirstRightPinnedColumn = isPinned === \"right\" && column.getIsFirstColumn(\"right\");\n const isLastRightPinnedColumn = isPinned === \"right\" && column.getIsLastColumn(\"right\");\n\n const nextIsPinned = nextColumn?.getIsPinned();\n const isBeforeRightPinned = nextIsPinned === \"right\" && nextColumn?.getIsFirstColumn(\"right\");\n\n const showEndBorder = !isBeforeRightPinned && (isLastColumn || !isLastRightPinnedColumn);\n\n const showStartBorder = isFirstRightPinnedColumn;\n\n return {\n showEndBorder,\n showStartBorder\n };\n}\n\nexport function getColumnPinningStyle<TData>(params: {\n column: Column<TData>;\n withBorder?: boolean;\n dir?: Direction;\n}): React.CSSProperties {\n const { column, dir = \"ltr\", withBorder = false } = params;\n\n const isPinned = column.getIsPinned();\n const isLastLeftPinnedColumn = isPinned === \"left\" && column.getIsLastColumn(\"left\");\n const isFirstRightPinnedColumn = isPinned === \"right\" && column.getIsFirstColumn(\"right\");\n\n const isRtl = dir === \"rtl\";\n\n const leftPosition = isPinned === \"left\" ? `${column.getStart(\"left\")}px` : undefined;\n const rightPosition = isPinned === \"right\" ? `${column.getAfter(\"right\")}px` : undefined;\n\n /*\n * ⚠ Token của gói này là MÀU ĐẦY ĐỦ (`--card: #ffffff`), KHÔNG phải bộ 3 kênh HSL — nên viết\n * `var(--card)` trần, đừng bọc `hsl()`. Bản gốc port từ một hệ token kiểu `\"150 20% 98%\"` nên\n * ở đó `hsl(var(--card))` mới đúng; bê nguyên sang đây thì `hsl(#ffffff)` là giá trị KHÔNG\n * HỢP LỆ, và hệ quả là cột ghim trong suốt — nội dung cuộn phía dưới lộ xuyên qua. Không có\n * lỗi nào được ném ra: CSS bỏ qua khai báo hỏng rồi rơi về `background: transparent`.\n *\n * Nền lấy theo `--card` vì hộp lưới dùng `bg-card` như mọi bảng khác của gói.\n */\n return {\n boxShadow: withBorder\n ? isLastLeftPinnedColumn\n ? isRtl\n ? \"4px 0 4px -4px var(--border) inset\"\n : \"-4px 0 4px -4px var(--border) inset\"\n : isFirstRightPinnedColumn\n ? isRtl\n ? \"-4px 0 4px -4px var(--border) inset\"\n : \"4px 0 4px -4px var(--border) inset\"\n : undefined\n : undefined,\n left: isRtl ? rightPosition : leftPosition,\n right: isRtl ? leftPosition : rightPosition,\n opacity: isPinned ? 0.97 : 1,\n position: isPinned ? \"sticky\" : \"relative\",\n background: \"var(--card)\",\n width: column.getSize(),\n zIndex: isPinned ? 1 : undefined\n };\n}\n\nexport function getScrollDirection(direction: string): \"left\" | \"right\" | \"home\" | \"end\" | undefined {\n if (direction === \"left\" || direction === \"right\" || direction === \"home\" || direction === \"end\") {\n return direction as \"left\" | \"right\" | \"home\" | \"end\";\n }\n if (direction === \"pageleft\") return \"left\";\n if (direction === \"pageright\") return \"right\";\n return undefined;\n}\n\nexport function scrollCellIntoView<TData>(params: {\n container: HTMLDivElement;\n targetCell: HTMLDivElement;\n tableRef: React.RefObject<Table<TData> | null>;\n viewportOffset: number;\n direction?: \"left\" | \"right\" | \"home\" | \"end\";\n isRtl: boolean;\n}): void {\n const { container, targetCell, tableRef, direction, viewportOffset, isRtl } = params;\n\n const containerRect = container.getBoundingClientRect();\n const cellRect = targetCell.getBoundingClientRect();\n\n const hasNegativeScroll = container.scrollLeft < 0;\n const isActuallyRtl = isRtl || hasNegativeScroll;\n\n const currentTable = tableRef.current;\n const leftPinnedColumns = currentTable?.getLeftVisibleLeafColumns() ?? [];\n const rightPinnedColumns = currentTable?.getRightVisibleLeafColumns() ?? [];\n\n const leftPinnedWidth = leftPinnedColumns.reduce((sum, c) => sum + c.getSize(), 0);\n const rightPinnedWidth = rightPinnedColumns.reduce((sum, c) => sum + c.getSize(), 0);\n\n const viewportLeft = isActuallyRtl\n ? containerRect.left + rightPinnedWidth + viewportOffset\n : containerRect.left + leftPinnedWidth + viewportOffset;\n const viewportRight = isActuallyRtl\n ? containerRect.right - leftPinnedWidth - viewportOffset\n : containerRect.right - rightPinnedWidth - viewportOffset;\n\n const isFullyVisible = cellRect.left >= viewportLeft && cellRect.right <= viewportRight;\n\n if (isFullyVisible) return;\n\n const isClippedLeft = cellRect.left < viewportLeft;\n const isClippedRight = cellRect.right > viewportRight;\n\n let scrollDelta = 0;\n\n if (!direction) {\n if (isClippedRight) {\n scrollDelta = cellRect.right - viewportRight;\n } else if (isClippedLeft) {\n scrollDelta = -(viewportLeft - cellRect.left);\n }\n } else {\n const shouldScrollRight = isActuallyRtl\n ? direction === \"right\" || direction === \"home\"\n : direction === \"right\" || direction === \"end\";\n\n if (shouldScrollRight) {\n scrollDelta = cellRect.right - viewportRight;\n } else {\n scrollDelta = -(viewportLeft - cellRect.left);\n }\n }\n\n container.scrollLeft += scrollDelta;\n}\n\nfunction countTabs(s: string): number {\n let n = 0;\n for (let i = 0; i < s.length; i++) if (s[i] === \"\\t\") n++;\n return n;\n}\n\nexport function parseTsv(text: string, fallbackColumnCount: number): string[][] {\n if (text.startsWith('\"') || text.includes('\\t\"')) {\n const rows: string[][] = [];\n let currentRow: string[] = [];\n let currentField = \"\";\n let inQuotes = false;\n let i = 0;\n\n while (i < text.length) {\n const char = text[i];\n const nextChar = text[i + 1];\n\n if (inQuotes) {\n if (char === '\"' && nextChar === '\"') {\n currentField += '\"';\n i += 2;\n } else if (char === '\"') {\n inQuotes = false;\n i++;\n } else {\n currentField += char;\n i++;\n }\n } else {\n if (char === '\"' && currentField === \"\") {\n inQuotes = true;\n i++;\n } else if (char === \"\\t\") {\n currentRow.push(currentField);\n currentField = \"\";\n i++;\n } else if (char === \"\\n\") {\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n currentRow = [];\n currentField = \"\";\n i++;\n } else if (char === \"\\r\" && nextChar === \"\\n\") {\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n currentRow = [];\n currentField = \"\";\n i += 2;\n } else {\n currentField += char;\n i++;\n }\n }\n }\n\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n\n return rows;\n }\n\n const lines = text.split(\"\\n\").map((l) => l.replace(/\\r$/, \"\"));\n let maxTabCount = 0;\n for (const line of lines) {\n const n = countTabs(line);\n if (n > maxTabCount) maxTabCount = n;\n }\n const columnCount = maxTabCount > 0 ? maxTabCount + 1 : fallbackColumnCount;\n if (columnCount <= 0) return [];\n\n const expectedTabCount = columnCount - 1;\n const rows: string[][] = [];\n let buf = \"\";\n let bufTabCount = 0;\n\n for (const line of lines) {\n const tc = countTabs(line);\n\n if (tc === expectedTabCount) {\n if (buf && bufTabCount === expectedTabCount) rows.push(buf.split(\"\\t\"));\n buf = \"\";\n bufTabCount = 0;\n rows.push(line.split(\"\\t\"));\n } else {\n buf = buf ? `${buf}\\n${line}` : line;\n bufTabCount += tc;\n if (bufTabCount === expectedTabCount) {\n rows.push(buf.split(\"\\t\"));\n buf = \"\";\n bufTabCount = 0;\n }\n }\n }\n\n if (buf && bufTabCount === expectedTabCount) rows.push(buf.split(\"\\t\"));\n\n return rows.length > 0 ? rows : lines.filter((l) => l.length > 0).map((l) => l.split(\"\\t\"));\n}\n\n/**\n * Phần tử có đang nằm trong một lớp nổi không? Dùng để lưới KHÔNG coi thao tác\n * trong popover/menu/hộp thoại là \"bấm ra ngoài\" (sẽ mất focus ô đang sửa).\n *\n * Primitive của package không gắn `data-slot`, nên bắt theo wrapper của Radix Popper\n * (bao Popover/DropdownMenu/Select/Tooltip) và theo role — kể cả Select \"item-aligned\".\n *\n * ⚠ Thêm ở `3.1.0`: `ActionBar` của gói (`data-slot=\"action-bar\"`). Thanh thao tác hàng loạt là bạn\n * đi kèm của lưới — nó mở khi có dòng được tick — nhưng nó là `role=\"toolbar\"` nằm NGOÀI lưới,\n * nên trước đây một cú bấm vào nó bị tính là \"bấm ra ngoài\": `mousedown` xoá lựa chọn dòng, thanh\n * đóng lại TRƯỚC khi `click` tới nơi, và hành động không bao giờ chạy. Đo trên comitor-ds: tick hai\n * dòng, bấm \"Mark as done\" → thanh biến mất, không dòng nào đổi. Cùng lý do, focus bàn phím đi\n * vào thanh cũng bị lưới kéo ngược về ô.\n */\nexport function getIsInPopover(element: unknown): boolean {\n if (!(element instanceof Element)) return false;\n\n return (\n element.closest(\"[data-grid-cell-editor]\") !== null ||\n element.closest(\"[data-grid-popover]\") !== null ||\n element.closest(\"[data-radix-popper-content-wrapper]\") !== null ||\n element.closest(\"[data-slot='action-bar']\") !== null ||\n element.closest(\"[role='dialog']\") !== null ||\n // `alertdialog` (3.1.0): `ConfirmDialog` của gói là `role=\"alertdialog\"` — thiếu dòng này thì\n // bấm Huỷ trong hộp xác nhận xoá các dòng đang tick, dù hộp mở ra chính vì các dòng đó.\n element.closest(\"[role='alertdialog']\") !== null ||\n element.closest(\"[role='menu']\") !== null ||\n element.closest(\"[role='listbox']\") !== null\n );\n}\n\n/**\n * Icon + tên kiểu dữ liệu của một cột (hiện trong tooltip ở tiêu đề cột).\n *\n * `messages` bỏ trống = bảng mặc định (tiếng Anh), giữ đúng chữ ký của `1.11.0`. Component nào\n * vẽ nhãn này cho người dùng thì PHẢI truyền bảng đang có hiệu lực vào — quên là tooltip nói\n * tiếng Anh giữa một lưới tiếng Việt.\n */\nexport function getColumnVariant(\n variant?: CellOpts[\"variant\"],\n messages?: DataGridMessages\n): {\n icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n label: string;\n} | null {\n const msg = messages ?? getDataGridMessages();\n switch (variant) {\n case \"short-text\":\n return { label: msg.column.variant.shortText, icon: BaselineIcon };\n case \"long-text\":\n return { label: msg.column.variant.longText, icon: WrapTextIcon };\n case \"number\":\n return { label: msg.column.variant.number, icon: HashIcon };\n case \"url\":\n return { label: msg.column.variant.url, icon: LinkIcon };\n case \"checkbox\":\n return { label: msg.column.variant.checkbox, icon: CheckSquareIcon };\n case \"select\":\n return { label: msg.column.variant.select, icon: ListIcon };\n case \"multi-select\":\n return { label: msg.column.variant.multiSelect, icon: ListChecksIcon };\n case \"date\":\n return { label: msg.column.variant.date, icon: CalendarIcon };\n case \"file\":\n return { label: msg.column.variant.file, icon: FileIcon };\n default:\n return null;\n }\n}\n\nexport function getEmptyCellValue(variant: CellOpts[\"variant\"] | undefined): unknown {\n if (variant === \"multi-select\" || variant === \"file\") return [];\n if (variant === \"number\" || variant === \"date\" || variant === \"select\") return null;\n if (variant === \"checkbox\") return false;\n return \"\";\n}\n\nexport function getUrlHref(urlString: string): string {\n if (!urlString || urlString.trim() === \"\") return \"\";\n\n const trimmed = urlString.trim();\n\n // Reject dangerous protocols (extra safety, though our http:// prefix would neutralize them)\n if (/^(javascript|data|vbscript|file):/i.test(trimmed)) {\n return \"\";\n }\n\n if (trimmed.startsWith(\"http://\") || trimmed.startsWith(\"https://\")) {\n return trimmed;\n }\n\n return `http://${trimmed}`;\n}\n\nexport function parseLocalDate(dateStr: unknown): Date | null {\n if (!dateStr) return null;\n if (dateStr instanceof Date) return dateStr;\n if (typeof dateStr !== \"string\") return null;\n const [year, month, day] = dateStr.split(\"-\").map(Number);\n if (!year || !month || !day) return null;\n const date = new Date(year, month - 1, day);\n // Verify date wasn't auto-corrected (e.g. Feb 30 -> Mar 1)\n if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {\n return null;\n }\n return date;\n}\n\nexport function formatDateToString(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Mẫu định dạng ngày của lưới: ngày NGẮN theo locale date-fns — `en-US` ra `12/25/2024`,\n * `vi` / `en-GB` ra `25/12/2024`. Một mẫu cho cả hiển thị lẫn phép đọc lại ({@link parseDateInput}),\n * để thứ người dùng THẤY cũng là thứ dán vào được.\n */\nconst DATE_DISPLAY_FORMAT = \"P\";\n\n/**\n * Hiển thị ngày dạng ngắn theo `locale` (dùng cho ô ngày, bộ lọc ngày).\n *\n * Hàm THUẦN nên không đọc được ngôn ngữ của lưới: component truyền locale đang có hiệu lực vào\n * (`useDataGridDateLocale()`). Bỏ trống `locale` = locale mặc định của date-fns (`enUS`, trừ khi\n * app đã đổi bằng `setDefaultOptions`).\n *\n * ⚠ Trước `3.0.0` hàm này luôn ra `dd/MM/yyyy` bất kể ngôn ngữ. Cần lại đúng chuỗi cũ thì truyền\n * `vi` (`import { vi } from \"date-fns/locale\"`).\n */\nexport function formatDate(date: Date | string | number | null | undefined, locale?: DateFnsLocale): string {\n if (!date) return \"\";\n const value = date instanceof Date ? date : new Date(date);\n if (Number.isNaN(value.getTime())) return \"\";\n return format(value, DATE_DISPLAY_FORMAT, { locale });\n}\n\n/** Như {@link formatDate}, cho giá trị thô của ô ngày (`yyyy-MM-dd` hoặc `Date`). */\nexport function formatDateForDisplay(dateStr: unknown, locale?: DateFnsLocale): string {\n if (!dateStr) return \"\";\n const date = parseLocalDate(dateStr);\n if (!date) return typeof dateStr === \"string\" ? dateStr : \"\";\n return formatDate(date, locale);\n}\n\n/**\n * Đọc chuỗi ngày DÁN vào ô ngày. Thử dạng ngắn của `locale` trước — đúng dạng lưới đang hiển\n * thị, nên `25/12/2024` dán vào lưới tiếng Việt là 25 tháng 12, không phải lỗi — rồi mới tới\n * `new Date()` (ISO `yyyy-MM-dd`, và những gì JS tự hiểu). `null` khi không đọc được.\n *\n * Năm phải đủ 4 chữ số: mẫu `\"P\"` đọc `25/12/24` thành năm 24 sau Công nguyên, một ngày \"hợp lệ\"\n * mà không ai muốn — chuỗi như thế rơi xuống `new Date()` như trước.\n */\nexport function parseDateInput(text: string, locale?: DateFnsLocale): Date | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n const localized = parse(trimmed, DATE_DISPLAY_FORMAT, new Date(), { locale });\n if (isValid(localized) && localized.getFullYear() >= 1000) return localized;\n const date = new Date(trimmed);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\n/**\n * Id cho tệp đính kèm. `crypto.randomUUID` chỉ có ở secure context (https/localhost) —\n * app nội bộ chạy http trong mạng bệnh viện sẽ không có, nên phải có đường lui.\n */\nexport function generateFileId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Chép text vào clipboard. `navigator.clipboard` cũng chỉ có ở secure context,\n * nên rơi về `document.execCommand(\"copy\")` (đã deprecated nhưng vẫn chạy mọi trình duyệt).\n * Ném lỗi khi cả hai đường đều không dùng được để phía gọi báo toast.\n */\nexport async function writeClipboardText(text: string): Promise<void> {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n try {\n await navigator.clipboard.writeText(text);\n return;\n } catch {\n // Bị chặn quyền → thử đường lui bên dưới\n }\n }\n\n if (!copyWithExecCommand(text)) {\n throw new Error(\"Clipboard không khả dụng\");\n }\n}\n\nfunction copyWithExecCommand(text: string): boolean {\n if (typeof document === \"undefined\") return false;\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.top = \"0\";\n textarea.style.insetInlineStart = \"-9999px\";\n document.body.appendChild(textarea);\n\n const previouslyFocused = document.activeElement;\n textarea.select();\n\n let copied = false;\n try {\n copied = document.execCommand(\"copy\");\n } catch {\n copied = false;\n }\n\n document.body.removeChild(textarea);\n if (previouslyFocused instanceof HTMLElement) {\n previouslyFocused.focus();\n }\n\n return copied;\n}\n\n/** Đọc clipboard khi có quyền; trả `null` nếu trình duyệt/ngữ cảnh không cho đọc. */\nexport async function readClipboardText(): Promise<string | null> {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.readText) return null;\n try {\n return await navigator.clipboard.readText();\n } catch {\n return null;\n }\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes <= 0 || !Number.isFinite(bytes)) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.min(sizes.length - 1, Math.floor(Math.log(bytes) / Math.log(k)));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n}\n\nexport function getFileIcon(type: string): React.ComponentType<React.SVGProps<SVGSVGElement>> {\n if (type.startsWith(\"image/\")) return FileImage;\n if (type.startsWith(\"video/\")) return FileVideo;\n if (type.startsWith(\"audio/\")) return FileAudio;\n if (type.includes(\"pdf\")) return FileText;\n if (type.includes(\"zip\") || type.includes(\"rar\")) return FileArchive;\n if (type.includes(\"word\") || type.includes(\"document\") || type.includes(\"doc\")) return FileText;\n if (type.includes(\"sheet\") || type.includes(\"excel\") || type.includes(\"xls\")) return FileSpreadsheet;\n if (type.includes(\"presentation\") || type.includes(\"powerpoint\") || type.includes(\"ppt\")) return Presentation;\n return File;\n}\n"],"mappings":"AACA,SAAuC,QAAQ,SAAS,aAAa;AACrE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAgC,2BAA2B;AAGpD,SAAS,WACd,MACA,OACiB;AACjB,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO;AAAA,EACT;AACA,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,kBAAkB,MAAqC;AACrE,SAAO,CAAC,CAAC,QAAQ,OAAO,SAAS,YAAY,QAAQ,QAAQ,UAAU,QAAQ,UAAU,QAAQ,UAAU;AAC7G;AAEO,SAAS,kBAAkB,OAAe,SAAiE;AAChH,SAAO,QAAQ;AAAA,IACb,CAAC,MACC,EAAE,UAAU,SACZ,EAAE,MAAM,YAAY,MAAM,MAAM,YAAY,KAC5C,EAAE,MAAM,YAAY,MAAM,MAAM,YAAY;AAAA,EAChD,GAAG;AACL;AAEO,SAAS,WAAW,UAAkB,UAAkB;AAC7D,SAAO,GAAG,QAAQ,IAAI,QAAQ;AAChC;AAEO,SAAS,aAAa,SAAyC;AACpE,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,QAAM,cAAc,MAAM,CAAC;AAC3B,QAAM,WAAW,MAAM,CAAC;AACxB,MAAI,eAAe,UAAU;AAC3B,UAAM,WAAW,SAAS,aAAa,EAAE;AACzC,QAAI,CAAC,OAAO,MAAM,QAAQ,GAAG;AAC3B,aAAO,EAAE,UAAU,SAAS;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,EAAE,UAAU,GAAG,UAAU,GAAG;AACrC;AAEO,SAAS,kBAAkB,WAAmC;AACnE,QAAM,eAA+C;AAAA,IACnD,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAEA,SAAO,aAAa,SAAS;AAC/B;AAEO,SAAS,aAAa,WAAmC;AAC9D,QAAM,eAA+C;AAAA,IACnD,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAEA,SAAO,aAAa,SAAS;AAC/B;AAEO,SAAS,0BAAiC,QAO/C;AACA,QAAM,EAAE,QAAQ,YAAY,aAAa,IAAI;AAE7C,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,2BAA2B,aAAa,WAAW,OAAO,iBAAiB,OAAO;AACxF,QAAM,0BAA0B,aAAa,WAAW,OAAO,gBAAgB,OAAO;AAEtF,QAAM,eAAe,YAAY,YAAY;AAC7C,QAAM,sBAAsB,iBAAiB,WAAW,YAAY,iBAAiB,OAAO;AAE5F,QAAM,gBAAgB,CAAC,wBAAwB,gBAAgB,CAAC;AAEhE,QAAM,kBAAkB;AAExB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,sBAA6B,QAIrB;AACtB,QAAM,EAAE,QAAQ,MAAM,OAAO,aAAa,MAAM,IAAI;AAEpD,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,yBAAyB,aAAa,UAAU,OAAO,gBAAgB,MAAM;AACnF,QAAM,2BAA2B,aAAa,WAAW,OAAO,iBAAiB,OAAO;AAExF,QAAM,QAAQ,QAAQ;AAEtB,QAAM,eAAe,aAAa,SAAS,GAAG,OAAO,SAAS,MAAM,CAAC,OAAO;AAC5E,QAAM,gBAAgB,aAAa,UAAU,GAAG,OAAO,SAAS,OAAO,CAAC,OAAO;AAW/E,SAAO;AAAA,IACL,WAAW,aACP,yBACE,QACE,uCACA,wCACF,2BACE,QACE,wCACA,uCACF,SACJ;AAAA,IACJ,MAAM,QAAQ,gBAAgB;AAAA,IAC9B,OAAO,QAAQ,eAAe;AAAA,IAC9B,SAAS,WAAW,OAAO;AAAA,IAC3B,UAAU,WAAW,WAAW;AAAA,IAChC,YAAY;AAAA,IACZ,OAAO,OAAO,QAAQ;AAAA,IACtB,QAAQ,WAAW,IAAI;AAAA,EACzB;AACF;AAEO,SAAS,mBAAmB,WAAkE;AACnG,MAAI,cAAc,UAAU,cAAc,WAAW,cAAc,UAAU,cAAc,OAAO;AAChG,WAAO;AAAA,EACT;AACA,MAAI,cAAc,WAAY,QAAO;AACrC,MAAI,cAAc,YAAa,QAAO;AACtC,SAAO;AACT;AAEO,SAAS,mBAA0B,QAOjC;AACP,QAAM,EAAE,WAAW,YAAY,UAAU,WAAW,gBAAgB,MAAM,IAAI;AAE9E,QAAM,gBAAgB,UAAU,sBAAsB;AACtD,QAAM,WAAW,WAAW,sBAAsB;AAElD,QAAM,oBAAoB,UAAU,aAAa;AACjD,QAAM,gBAAgB,SAAS;AAE/B,QAAM,eAAe,SAAS;AAC9B,QAAM,oBAAoB,cAAc,0BAA0B,KAAK,CAAC;AACxE,QAAM,qBAAqB,cAAc,2BAA2B,KAAK,CAAC;AAE1E,QAAM,kBAAkB,kBAAkB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,GAAG,CAAC;AACjF,QAAM,mBAAmB,mBAAmB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,GAAG,CAAC;AAEnF,QAAM,eAAe,gBACjB,cAAc,OAAO,mBAAmB,iBACxC,cAAc,OAAO,kBAAkB;AAC3C,QAAM,gBAAgB,gBAClB,cAAc,QAAQ,kBAAkB,iBACxC,cAAc,QAAQ,mBAAmB;AAE7C,QAAM,iBAAiB,SAAS,QAAQ,gBAAgB,SAAS,SAAS;AAE1E,MAAI,eAAgB;AAEpB,QAAM,gBAAgB,SAAS,OAAO;AACtC,QAAM,iBAAiB,SAAS,QAAQ;AAExC,MAAI,cAAc;AAElB,MAAI,CAAC,WAAW;AACd,QAAI,gBAAgB;AAClB,oBAAc,SAAS,QAAQ;AAAA,IACjC,WAAW,eAAe;AACxB,oBAAc,EAAE,eAAe,SAAS;AAAA,IAC1C;AAAA,EACF,OAAO;AACL,UAAM,oBAAoB,gBACtB,cAAc,WAAW,cAAc,SACvC,cAAc,WAAW,cAAc;AAE3C,QAAI,mBAAmB;AACrB,oBAAc,SAAS,QAAQ;AAAA,IACjC,OAAO;AACL,oBAAc,EAAE,eAAe,SAAS;AAAA,IAC1C;AAAA,EACF;AAEA,YAAU,cAAc;AAC1B;AAEA,SAAS,UAAU,GAAmB;AACpC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,KAAI,EAAE,CAAC,MAAM,IAAM;AACtD,SAAO;AACT;AAEO,SAAS,SAAS,MAAc,qBAAyC;AAC9E,MAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,IAAK,GAAG;AAChD,UAAMA,QAAmB,CAAC;AAC1B,QAAI,aAAuB,CAAC;AAC5B,QAAI,eAAe;AACnB,QAAI,WAAW;AACf,QAAI,IAAI;AAER,WAAO,IAAI,KAAK,QAAQ;AACtB,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,WAAW,KAAK,IAAI,CAAC;AAE3B,UAAI,UAAU;AACZ,YAAI,SAAS,OAAO,aAAa,KAAK;AACpC,0BAAgB;AAChB,eAAK;AAAA,QACP,WAAW,SAAS,KAAK;AACvB,qBAAW;AACX;AAAA,QACF,OAAO;AACL,0BAAgB;AAChB;AAAA,QACF;AAAA,MACF,OAAO;AACL,YAAI,SAAS,OAAO,iBAAiB,IAAI;AACvC,qBAAW;AACX;AAAA,QACF,WAAW,SAAS,KAAM;AACxB,qBAAW,KAAK,YAAY;AAC5B,yBAAe;AACf;AAAA,QACF,WAAW,SAAS,MAAM;AACxB,qBAAW,KAAK,YAAY;AAC5B,cAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,YAAAA,MAAK,KAAK,UAAU;AAAA,UACtB;AACA,uBAAa,CAAC;AACd,yBAAe;AACf;AAAA,QACF,WAAW,SAAS,QAAQ,aAAa,MAAM;AAC7C,qBAAW,KAAK,YAAY;AAC5B,cAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,YAAAA,MAAK,KAAK,UAAU;AAAA,UACtB;AACA,uBAAa,CAAC;AACd,yBAAe;AACf,eAAK;AAAA,QACP,OAAO;AACL,0BAAgB;AAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,eAAW,KAAK,YAAY;AAC5B,QAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,MAAAA,MAAK,KAAK,UAAU;AAAA,IACtB;AAEA,WAAOA;AAAA,EACT;AAEA,QAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,CAAC;AAC9D,MAAI,cAAc;AAClB,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,UAAU,IAAI;AACxB,QAAI,IAAI,YAAa,eAAc;AAAA,EACrC;AACA,QAAM,cAAc,cAAc,IAAI,cAAc,IAAI;AACxD,MAAI,eAAe,EAAG,QAAO,CAAC;AAE9B,QAAM,mBAAmB,cAAc;AACvC,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAM;AACV,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,UAAU,IAAI;AAEzB,QAAI,OAAO,kBAAkB;AAC3B,UAAI,OAAO,gBAAgB,iBAAkB,MAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AACtE,YAAM;AACN,oBAAc;AACd,WAAK,KAAK,KAAK,MAAM,GAAI,CAAC;AAAA,IAC5B,OAAO;AACL,YAAM,MAAM,GAAG,GAAG;AAAA,EAAK,IAAI,KAAK;AAChC,qBAAe;AACf,UAAI,gBAAgB,kBAAkB;AACpC,aAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AACzB,cAAM;AACN,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,gBAAgB,iBAAkB,MAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AAEtE,SAAO,KAAK,SAAS,IAAI,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAI,CAAC;AAC5F;AAgBO,SAAS,eAAe,SAA2B;AACxD,MAAI,EAAE,mBAAmB,SAAU,QAAO;AAE1C,SACE,QAAQ,QAAQ,yBAAyB,MAAM,QAC/C,QAAQ,QAAQ,qBAAqB,MAAM,QAC3C,QAAQ,QAAQ,qCAAqC,MAAM,QAC3D,QAAQ,QAAQ,0BAA0B,MAAM,QAChD,QAAQ,QAAQ,iBAAiB,MAAM;AAAA;AAAA,EAGvC,QAAQ,QAAQ,sBAAsB,MAAM,QAC5C,QAAQ,QAAQ,eAAe,MAAM,QACrC,QAAQ,QAAQ,kBAAkB,MAAM;AAE5C;AASO,SAAS,iBACd,SACA,UAIO;AACP,QAAM,MAAM,YAAY,oBAAoB;AAC5C,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,WAAW,MAAM,aAAa;AAAA,IACnE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,aAAa;AAAA,IAClE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,QAAQ,MAAM,SAAS;AAAA,IAC5D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,KAAK,MAAM,SAAS;AAAA,IACzD,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,gBAAgB;AAAA,IACrE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,QAAQ,MAAM,SAAS;AAAA,IAC5D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,aAAa,MAAM,eAAe;AAAA,IACvE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,MAAM,MAAM,aAAa;AAAA,IAC9D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,MAAM,MAAM,SAAS;AAAA,IAC1D;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,kBAAkB,SAAmD;AACnF,MAAI,YAAY,kBAAkB,YAAY,OAAQ,QAAO,CAAC;AAC9D,MAAI,YAAY,YAAY,YAAY,UAAU,YAAY,SAAU,QAAO;AAC/E,MAAI,YAAY,WAAY,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,WAAW,WAA2B;AACpD,MAAI,CAAC,aAAa,UAAU,KAAK,MAAM,GAAI,QAAO;AAElD,QAAM,UAAU,UAAU,KAAK;AAG/B,MAAI,qCAAqC,KAAK,OAAO,GAAG;AACtD,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,UAAU,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,OAAO;AAC1B;AAEO,SAAS,eAAe,SAA+B;AAC5D,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,mBAAmB,KAAM,QAAO;AACpC,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,QAAM,CAAC,MAAM,OAAO,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AACxD,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAK,QAAO;AACpC,QAAM,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AAE1C,MAAI,KAAK,YAAY,MAAM,QAAQ,KAAK,SAAS,MAAM,QAAQ,KAAK,KAAK,QAAQ,MAAM,KAAK;AAC1F,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,MAAoB;AACrD,QAAM,OAAO,KAAK,YAAY;AAC9B,QAAM,QAAQ,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AACzD,QAAM,MAAM,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AAClD,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAChC;AAOA,MAAM,sBAAsB;AAYrB,SAAS,WAAW,MAAiD,QAAgC;AAC1G,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,gBAAgB,OAAO,OAAO,IAAI,KAAK,IAAI;AACzD,MAAI,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC1C,SAAO,OAAO,OAAO,qBAAqB,EAAE,OAAO,CAAC;AACtD;AAGO,SAAS,qBAAqB,SAAkB,QAAgC;AACrF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,eAAe,OAAO;AACnC,MAAI,CAAC,KAAM,QAAO,OAAO,YAAY,WAAW,UAAU;AAC1D,SAAO,WAAW,MAAM,MAAM;AAChC;AAUO,SAAS,eAAe,MAAc,QAAqC;AAChF,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,YAAY,MAAM,SAAS,qBAAqB,oBAAI,KAAK,GAAG,EAAE,OAAO,CAAC;AAC5E,MAAI,QAAQ,SAAS,KAAK,UAAU,YAAY,KAAK,IAAM,QAAO;AAClE,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAMO,SAAS,iBAAyB;AACvC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACnF;AAOA,eAAsB,mBAAmB,MAA6B;AACpE,MAAI,OAAO,cAAc,eAAe,UAAU,WAAW,WAAW;AACtE,QAAI;AACF,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,UAAM,IAAI,MAAM,uCAA0B;AAAA,EAC5C;AACF;AAEA,SAAS,oBAAoB,MAAuB;AAClD,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,aAAa,YAAY,EAAE;AACpC,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,MAAM;AACrB,WAAS,MAAM,mBAAmB;AAClC,WAAS,KAAK,YAAY,QAAQ;AAElC,QAAM,oBAAoB,SAAS;AACnC,WAAS,OAAO;AAEhB,MAAI,SAAS;AACb,MAAI;AACF,aAAS,SAAS,YAAY,MAAM;AAAA,EACtC,QAAQ;AACN,aAAS;AAAA,EACX;AAEA,WAAS,KAAK,YAAY,QAAQ;AAClC,MAAI,6BAA6B,aAAa;AAC5C,sBAAkB,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;AAGA,eAAsB,oBAA4C;AAChE,MAAI,OAAO,cAAc,eAAe,CAAC,UAAU,WAAW,SAAU,QAAO;AAC/E,MAAI;AACF,WAAO,MAAM,UAAU,UAAU,SAAS;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,OAAuB;AACpD,MAAI,SAAS,KAAK,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAClD,QAAM,IAAI;AACV,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI;AACpC,QAAM,IAAI,KAAK,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;AAC9E,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AACtE;AAEO,SAAS,YAAY,MAAkE;AAC5F,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,SAAS,KAAK,EAAG,QAAO;AACjC,MAAI,KAAK,SAAS,KAAK,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACzD,MAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,UAAU,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACvF,MAAI,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACrF,MAAI,KAAK,SAAS,cAAc,KAAK,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACjG,SAAO;AACT;","names":["rows"]}
|
|
1
|
+
{"version":3,"sources":["../../src/data-grid/utils.ts"],"sourcesContent":["import type { Column, Table } from \"@tanstack/react-table\";\nimport { type Locale as DateFnsLocale, format, isValid, parse } from \"date-fns\";\nimport {\n BaselineIcon,\n CalendarIcon,\n CheckSquareIcon,\n File,\n FileArchive,\n FileAudio,\n FileIcon,\n FileImage,\n FileSpreadsheet,\n FileText,\n FileVideo,\n HashIcon,\n LinkIcon,\n ListChecksIcon,\n ListIcon,\n Presentation,\n WrapTextIcon\n} from \"lucide-react\";\nimport type * as React from \"react\";\nimport { type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport type { CellOpts, CellPosition, Direction, FileCellData, RowHeightValue } from \"./types.js\";\n\nexport function flexRender<TProps extends object>(\n Comp: ((props: TProps) => React.ReactNode) | string | undefined,\n props: TProps\n): React.ReactNode {\n if (typeof Comp === \"string\") {\n return Comp;\n }\n return Comp?.(props);\n}\n\nexport function getIsFileCellData(item: unknown): item is FileCellData {\n return !!item && typeof item === \"object\" && \"id\" in item && \"name\" in item && \"size\" in item && \"type\" in item;\n}\n\nexport function matchSelectOption(value: string, options: { value: string; label: string }[]): string | undefined {\n return options.find(\n (o) =>\n o.value === value ||\n o.value.toLowerCase() === value.toLowerCase() ||\n o.label.toLowerCase() === value.toLowerCase()\n )?.value;\n}\n\nexport function getCellKey(rowIndex: number, columnId: string) {\n return `${rowIndex}:${columnId}`;\n}\n\nexport function parseCellKey(cellKey: string): Required<CellPosition> {\n const parts = cellKey.split(\":\");\n const rowIndexStr = parts[0];\n const columnId = parts[1];\n if (rowIndexStr && columnId) {\n const rowIndex = parseInt(rowIndexStr, 10);\n if (!Number.isNaN(rowIndex)) {\n return { rowIndex, columnId };\n }\n }\n return { rowIndex: 0, columnId: \"\" };\n}\n\nexport function getRowHeightValue(rowHeight: RowHeightValue): number {\n const rowHeightMap: Record<RowHeightValue, number> = {\n short: 36,\n medium: 56,\n tall: 76,\n \"extra-tall\": 96\n };\n\n return rowHeightMap[rowHeight];\n}\n\nexport function getLineCount(rowHeight: RowHeightValue): number {\n const lineCountMap: Record<RowHeightValue, number> = {\n short: 1,\n medium: 2,\n tall: 3,\n \"extra-tall\": 4\n };\n\n return lineCountMap[rowHeight];\n}\n\nexport function getColumnBorderVisibility<TData>(params: {\n column: Column<TData>;\n nextColumn?: Column<TData>;\n isLastColumn: boolean;\n}): {\n showEndBorder: boolean;\n showStartBorder: boolean;\n} {\n const { column, nextColumn, isLastColumn } = params;\n\n const isPinned = column.getIsPinned();\n const isFirstRightPinnedColumn = isPinned === \"right\" && column.getIsFirstColumn(\"right\");\n const isLastRightPinnedColumn = isPinned === \"right\" && column.getIsLastColumn(\"right\");\n\n const nextIsPinned = nextColumn?.getIsPinned();\n const isBeforeRightPinned = nextIsPinned === \"right\" && nextColumn?.getIsFirstColumn(\"right\");\n\n const showEndBorder = !isBeforeRightPinned && (isLastColumn || !isLastRightPinnedColumn);\n\n const showStartBorder = isFirstRightPinnedColumn;\n\n return {\n showEndBorder,\n showStartBorder\n };\n}\n\nexport function getColumnPinningStyle<TData>(params: {\n column: Column<TData>;\n withBorder?: boolean;\n dir?: Direction;\n}): React.CSSProperties {\n const { column, dir = \"ltr\", withBorder = false } = params;\n\n const isPinned = column.getIsPinned();\n const isLastLeftPinnedColumn = isPinned === \"left\" && column.getIsLastColumn(\"left\");\n const isFirstRightPinnedColumn = isPinned === \"right\" && column.getIsFirstColumn(\"right\");\n\n const isRtl = dir === \"rtl\";\n\n const leftPosition = isPinned === \"left\" ? `${column.getStart(\"left\")}px` : undefined;\n const rightPosition = isPinned === \"right\" ? `${column.getAfter(\"right\")}px` : undefined;\n\n /*\n * ⚠ Token của gói này là MÀU ĐẦY ĐỦ (`--card: #ffffff`), KHÔNG phải bộ 3 kênh HSL — nên viết\n * `var(--card)` trần, đừng bọc `hsl()`. Bản gốc port từ một hệ token kiểu `\"150 20% 98%\"` nên\n * ở đó `hsl(var(--card))` mới đúng; bê nguyên sang đây thì `hsl(#ffffff)` là giá trị KHÔNG\n * HỢP LỆ, và hệ quả là cột ghim trong suốt — nội dung cuộn phía dưới lộ xuyên qua. Không có\n * lỗi nào được ném ra: CSS bỏ qua khai báo hỏng rồi rơi về `background: transparent`.\n *\n * Nền lấy theo `--card` vì hộp lưới dùng `bg-card` như mọi bảng khác của gói.\n *\n * ⚠ Khác bản gốc (3.3.2): KHÔNG có `opacity`. Bản gốc đặt `opacity: 0.97` cho ô ghim — nền đã\n * đục nhưng cả Ô mờ 3%, nên khi cuộn ngang, chữ của cột đang trôi phía dưới lộ thành vệt mờ sau\n * cột ghim. Cùng một kết cục với lỗi `hsl()` ngay trên (cột ghim để lộ nội dung cuộn qua), chỉ\n * nhạt hơn — và cũng không cổng nào thấy, vì `opacity` nằm ở `style` inline. `test:grid-render`\n * nay canh cả chuyện này. Ranh giới vùng ghim ⇄ vùng cuộn đã có viền cuối cột lo.\n */\n return {\n boxShadow: withBorder\n ? isLastLeftPinnedColumn\n ? isRtl\n ? \"4px 0 4px -4px var(--border) inset\"\n : \"-4px 0 4px -4px var(--border) inset\"\n : isFirstRightPinnedColumn\n ? isRtl\n ? \"-4px 0 4px -4px var(--border) inset\"\n : \"4px 0 4px -4px var(--border) inset\"\n : undefined\n : undefined,\n left: isRtl ? rightPosition : leftPosition,\n right: isRtl ? leftPosition : rightPosition,\n position: isPinned ? \"sticky\" : \"relative\",\n background: \"var(--card)\",\n width: column.getSize(),\n zIndex: isPinned ? 1 : undefined\n };\n}\n\nexport function getScrollDirection(direction: string): \"left\" | \"right\" | \"home\" | \"end\" | undefined {\n if (direction === \"left\" || direction === \"right\" || direction === \"home\" || direction === \"end\") {\n return direction as \"left\" | \"right\" | \"home\" | \"end\";\n }\n if (direction === \"pageleft\") return \"left\";\n if (direction === \"pageright\") return \"right\";\n return undefined;\n}\n\nexport function scrollCellIntoView<TData>(params: {\n container: HTMLDivElement;\n targetCell: HTMLDivElement;\n tableRef: React.RefObject<Table<TData> | null>;\n viewportOffset: number;\n direction?: \"left\" | \"right\" | \"home\" | \"end\";\n isRtl: boolean;\n}): void {\n const { container, targetCell, tableRef, direction, viewportOffset, isRtl } = params;\n\n const containerRect = container.getBoundingClientRect();\n const cellRect = targetCell.getBoundingClientRect();\n\n const hasNegativeScroll = container.scrollLeft < 0;\n const isActuallyRtl = isRtl || hasNegativeScroll;\n\n const currentTable = tableRef.current;\n const leftPinnedColumns = currentTable?.getLeftVisibleLeafColumns() ?? [];\n const rightPinnedColumns = currentTable?.getRightVisibleLeafColumns() ?? [];\n\n const leftPinnedWidth = leftPinnedColumns.reduce((sum, c) => sum + c.getSize(), 0);\n const rightPinnedWidth = rightPinnedColumns.reduce((sum, c) => sum + c.getSize(), 0);\n\n const viewportLeft = isActuallyRtl\n ? containerRect.left + rightPinnedWidth + viewportOffset\n : containerRect.left + leftPinnedWidth + viewportOffset;\n const viewportRight = isActuallyRtl\n ? containerRect.right - leftPinnedWidth - viewportOffset\n : containerRect.right - rightPinnedWidth - viewportOffset;\n\n const isFullyVisible = cellRect.left >= viewportLeft && cellRect.right <= viewportRight;\n\n if (isFullyVisible) return;\n\n const isClippedLeft = cellRect.left < viewportLeft;\n const isClippedRight = cellRect.right > viewportRight;\n\n let scrollDelta = 0;\n\n if (!direction) {\n if (isClippedRight) {\n scrollDelta = cellRect.right - viewportRight;\n } else if (isClippedLeft) {\n scrollDelta = -(viewportLeft - cellRect.left);\n }\n } else {\n const shouldScrollRight = isActuallyRtl\n ? direction === \"right\" || direction === \"home\"\n : direction === \"right\" || direction === \"end\";\n\n if (shouldScrollRight) {\n scrollDelta = cellRect.right - viewportRight;\n } else {\n scrollDelta = -(viewportLeft - cellRect.left);\n }\n }\n\n container.scrollLeft += scrollDelta;\n}\n\nfunction countTabs(s: string): number {\n let n = 0;\n for (let i = 0; i < s.length; i++) if (s[i] === \"\\t\") n++;\n return n;\n}\n\nexport function parseTsv(text: string, fallbackColumnCount: number): string[][] {\n if (text.startsWith('\"') || text.includes('\\t\"')) {\n const rows: string[][] = [];\n let currentRow: string[] = [];\n let currentField = \"\";\n let inQuotes = false;\n let i = 0;\n\n while (i < text.length) {\n const char = text[i];\n const nextChar = text[i + 1];\n\n if (inQuotes) {\n if (char === '\"' && nextChar === '\"') {\n currentField += '\"';\n i += 2;\n } else if (char === '\"') {\n inQuotes = false;\n i++;\n } else {\n currentField += char;\n i++;\n }\n } else {\n if (char === '\"' && currentField === \"\") {\n inQuotes = true;\n i++;\n } else if (char === \"\\t\") {\n currentRow.push(currentField);\n currentField = \"\";\n i++;\n } else if (char === \"\\n\") {\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n currentRow = [];\n currentField = \"\";\n i++;\n } else if (char === \"\\r\" && nextChar === \"\\n\") {\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n currentRow = [];\n currentField = \"\";\n i += 2;\n } else {\n currentField += char;\n i++;\n }\n }\n }\n\n currentRow.push(currentField);\n if (currentRow.length > 1 || currentRow.some((f) => f.length > 0)) {\n rows.push(currentRow);\n }\n\n return rows;\n }\n\n const lines = text.split(\"\\n\").map((l) => l.replace(/\\r$/, \"\"));\n let maxTabCount = 0;\n for (const line of lines) {\n const n = countTabs(line);\n if (n > maxTabCount) maxTabCount = n;\n }\n const columnCount = maxTabCount > 0 ? maxTabCount + 1 : fallbackColumnCount;\n if (columnCount <= 0) return [];\n\n const expectedTabCount = columnCount - 1;\n const rows: string[][] = [];\n let buf = \"\";\n let bufTabCount = 0;\n\n for (const line of lines) {\n const tc = countTabs(line);\n\n if (tc === expectedTabCount) {\n if (buf && bufTabCount === expectedTabCount) rows.push(buf.split(\"\\t\"));\n buf = \"\";\n bufTabCount = 0;\n rows.push(line.split(\"\\t\"));\n } else {\n buf = buf ? `${buf}\\n${line}` : line;\n bufTabCount += tc;\n if (bufTabCount === expectedTabCount) {\n rows.push(buf.split(\"\\t\"));\n buf = \"\";\n bufTabCount = 0;\n }\n }\n }\n\n if (buf && bufTabCount === expectedTabCount) rows.push(buf.split(\"\\t\"));\n\n return rows.length > 0 ? rows : lines.filter((l) => l.length > 0).map((l) => l.split(\"\\t\"));\n}\n\n/**\n * Phần tử có đang nằm trong một lớp nổi không? Dùng để lưới KHÔNG coi thao tác\n * trong popover/menu/hộp thoại là \"bấm ra ngoài\" (sẽ mất focus ô đang sửa).\n *\n * Primitive của package không gắn `data-slot`, nên bắt theo wrapper của Radix Popper\n * (bao Popover/DropdownMenu/Select/Tooltip) và theo role — kể cả Select \"item-aligned\".\n *\n * ⚠ Thêm ở `3.1.0`: `ActionBar` của gói (`data-slot=\"action-bar\"`). Thanh thao tác hàng loạt là bạn\n * đi kèm của lưới — nó mở khi có dòng được tick — nhưng nó là `role=\"toolbar\"` nằm NGOÀI lưới,\n * nên trước đây một cú bấm vào nó bị tính là \"bấm ra ngoài\": `mousedown` xoá lựa chọn dòng, thanh\n * đóng lại TRƯỚC khi `click` tới nơi, và hành động không bao giờ chạy. Đo trên comitor-ds: tick hai\n * dòng, bấm \"Mark as done\" → thanh biến mất, không dòng nào đổi. Cùng lý do, focus bàn phím đi\n * vào thanh cũng bị lưới kéo ngược về ô.\n */\nexport function getIsInPopover(element: unknown): boolean {\n if (!(element instanceof Element)) return false;\n\n return (\n element.closest(\"[data-grid-cell-editor]\") !== null ||\n element.closest(\"[data-grid-popover]\") !== null ||\n element.closest(\"[data-radix-popper-content-wrapper]\") !== null ||\n element.closest(\"[data-slot='action-bar']\") !== null ||\n element.closest(\"[role='dialog']\") !== null ||\n // `alertdialog` (3.1.0): `ConfirmDialog` của gói là `role=\"alertdialog\"` — thiếu dòng này thì\n // bấm Huỷ trong hộp xác nhận xoá các dòng đang tick, dù hộp mở ra chính vì các dòng đó.\n element.closest(\"[role='alertdialog']\") !== null ||\n element.closest(\"[role='menu']\") !== null ||\n element.closest(\"[role='listbox']\") !== null\n );\n}\n\n/**\n * Icon + tên kiểu dữ liệu của một cột (hiện trong tooltip ở tiêu đề cột).\n *\n * `messages` bỏ trống = bảng mặc định (tiếng Anh), giữ đúng chữ ký của `1.11.0`. Component nào\n * vẽ nhãn này cho người dùng thì PHẢI truyền bảng đang có hiệu lực vào — quên là tooltip nói\n * tiếng Anh giữa một lưới tiếng Việt.\n */\nexport function getColumnVariant(\n variant?: CellOpts[\"variant\"],\n messages?: DataGridMessages\n): {\n icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n label: string;\n} | null {\n const msg = messages ?? getDataGridMessages();\n switch (variant) {\n case \"short-text\":\n return { label: msg.column.variant.shortText, icon: BaselineIcon };\n case \"long-text\":\n return { label: msg.column.variant.longText, icon: WrapTextIcon };\n case \"number\":\n return { label: msg.column.variant.number, icon: HashIcon };\n case \"url\":\n return { label: msg.column.variant.url, icon: LinkIcon };\n case \"checkbox\":\n return { label: msg.column.variant.checkbox, icon: CheckSquareIcon };\n case \"select\":\n return { label: msg.column.variant.select, icon: ListIcon };\n case \"multi-select\":\n return { label: msg.column.variant.multiSelect, icon: ListChecksIcon };\n case \"date\":\n return { label: msg.column.variant.date, icon: CalendarIcon };\n case \"file\":\n return { label: msg.column.variant.file, icon: FileIcon };\n default:\n return null;\n }\n}\n\nexport function getEmptyCellValue(variant: CellOpts[\"variant\"] | undefined): unknown {\n if (variant === \"multi-select\" || variant === \"file\") return [];\n if (variant === \"number\" || variant === \"date\" || variant === \"select\") return null;\n if (variant === \"checkbox\") return false;\n return \"\";\n}\n\nexport function getUrlHref(urlString: string): string {\n if (!urlString || urlString.trim() === \"\") return \"\";\n\n const trimmed = urlString.trim();\n\n // Reject dangerous protocols (extra safety, though our http:// prefix would neutralize them)\n if (/^(javascript|data|vbscript|file):/i.test(trimmed)) {\n return \"\";\n }\n\n if (trimmed.startsWith(\"http://\") || trimmed.startsWith(\"https://\")) {\n return trimmed;\n }\n\n return `http://${trimmed}`;\n}\n\nexport function parseLocalDate(dateStr: unknown): Date | null {\n if (!dateStr) return null;\n if (dateStr instanceof Date) return dateStr;\n if (typeof dateStr !== \"string\") return null;\n const [year, month, day] = dateStr.split(\"-\").map(Number);\n if (!year || !month || !day) return null;\n const date = new Date(year, month - 1, day);\n // Verify date wasn't auto-corrected (e.g. Feb 30 -> Mar 1)\n if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {\n return null;\n }\n return date;\n}\n\nexport function formatDateToString(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Mẫu định dạng ngày của lưới: ngày NGẮN theo locale date-fns — `en-US` ra `12/25/2024`,\n * `vi` / `en-GB` ra `25/12/2024`. Một mẫu cho cả hiển thị lẫn phép đọc lại ({@link parseDateInput}),\n * để thứ người dùng THẤY cũng là thứ dán vào được.\n */\nconst DATE_DISPLAY_FORMAT = \"P\";\n\n/**\n * Hiển thị ngày dạng ngắn theo `locale` (dùng cho ô ngày, bộ lọc ngày).\n *\n * Hàm THUẦN nên không đọc được ngôn ngữ của lưới: component truyền locale đang có hiệu lực vào\n * (`useDataGridDateLocale()`). Bỏ trống `locale` = locale mặc định của date-fns (`enUS`, trừ khi\n * app đã đổi bằng `setDefaultOptions`).\n *\n * ⚠ Trước `3.0.0` hàm này luôn ra `dd/MM/yyyy` bất kể ngôn ngữ. Cần lại đúng chuỗi cũ thì truyền\n * `vi` (`import { vi } from \"date-fns/locale\"`).\n */\nexport function formatDate(date: Date | string | number | null | undefined, locale?: DateFnsLocale): string {\n if (!date) return \"\";\n const value = date instanceof Date ? date : new Date(date);\n if (Number.isNaN(value.getTime())) return \"\";\n return format(value, DATE_DISPLAY_FORMAT, { locale });\n}\n\n/** Như {@link formatDate}, cho giá trị thô của ô ngày (`yyyy-MM-dd` hoặc `Date`). */\nexport function formatDateForDisplay(dateStr: unknown, locale?: DateFnsLocale): string {\n if (!dateStr) return \"\";\n const date = parseLocalDate(dateStr);\n if (!date) return typeof dateStr === \"string\" ? dateStr : \"\";\n return formatDate(date, locale);\n}\n\n/**\n * Đọc chuỗi ngày DÁN vào ô ngày. Thử dạng ngắn của `locale` trước — đúng dạng lưới đang hiển\n * thị, nên `25/12/2024` dán vào lưới tiếng Việt là 25 tháng 12, không phải lỗi — rồi mới tới\n * `new Date()` (ISO `yyyy-MM-dd`, và những gì JS tự hiểu). `null` khi không đọc được.\n *\n * Năm phải đủ 4 chữ số: mẫu `\"P\"` đọc `25/12/24` thành năm 24 sau Công nguyên, một ngày \"hợp lệ\"\n * mà không ai muốn — chuỗi như thế rơi xuống `new Date()` như trước.\n */\nexport function parseDateInput(text: string, locale?: DateFnsLocale): Date | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n const localized = parse(trimmed, DATE_DISPLAY_FORMAT, new Date(), { locale });\n if (isValid(localized) && localized.getFullYear() >= 1000) return localized;\n const date = new Date(trimmed);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\n/**\n * Id cho tệp đính kèm. `crypto.randomUUID` chỉ có ở secure context (https/localhost) —\n * app nội bộ chạy http trong mạng bệnh viện sẽ không có, nên phải có đường lui.\n */\nexport function generateFileId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `file-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Chép text vào clipboard. `navigator.clipboard` cũng chỉ có ở secure context,\n * nên rơi về `document.execCommand(\"copy\")` (đã deprecated nhưng vẫn chạy mọi trình duyệt).\n * Ném lỗi khi cả hai đường đều không dùng được để phía gọi báo toast.\n */\nexport async function writeClipboardText(text: string): Promise<void> {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n try {\n await navigator.clipboard.writeText(text);\n return;\n } catch {\n // Bị chặn quyền → thử đường lui bên dưới\n }\n }\n\n if (!copyWithExecCommand(text)) {\n throw new Error(\"Clipboard không khả dụng\");\n }\n}\n\nfunction copyWithExecCommand(text: string): boolean {\n if (typeof document === \"undefined\") return false;\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.top = \"0\";\n textarea.style.insetInlineStart = \"-9999px\";\n document.body.appendChild(textarea);\n\n const previouslyFocused = document.activeElement;\n textarea.select();\n\n let copied = false;\n try {\n copied = document.execCommand(\"copy\");\n } catch {\n copied = false;\n }\n\n document.body.removeChild(textarea);\n if (previouslyFocused instanceof HTMLElement) {\n previouslyFocused.focus();\n }\n\n return copied;\n}\n\n/** Đọc clipboard khi có quyền; trả `null` nếu trình duyệt/ngữ cảnh không cho đọc. */\nexport async function readClipboardText(): Promise<string | null> {\n if (typeof navigator === \"undefined\" || !navigator.clipboard?.readText) return null;\n try {\n return await navigator.clipboard.readText();\n } catch {\n return null;\n }\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes <= 0 || !Number.isFinite(bytes)) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.min(sizes.length - 1, Math.floor(Math.log(bytes) / Math.log(k)));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n}\n\nexport function getFileIcon(type: string): React.ComponentType<React.SVGProps<SVGSVGElement>> {\n if (type.startsWith(\"image/\")) return FileImage;\n if (type.startsWith(\"video/\")) return FileVideo;\n if (type.startsWith(\"audio/\")) return FileAudio;\n if (type.includes(\"pdf\")) return FileText;\n if (type.includes(\"zip\") || type.includes(\"rar\")) return FileArchive;\n if (type.includes(\"word\") || type.includes(\"document\") || type.includes(\"doc\")) return FileText;\n if (type.includes(\"sheet\") || type.includes(\"excel\") || type.includes(\"xls\")) return FileSpreadsheet;\n if (type.includes(\"presentation\") || type.includes(\"powerpoint\") || type.includes(\"ppt\")) return Presentation;\n return File;\n}\n"],"mappings":"AACA,SAAuC,QAAQ,SAAS,aAAa;AACrE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAgC,2BAA2B;AAGpD,SAAS,WACd,MACA,OACiB;AACjB,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO;AAAA,EACT;AACA,SAAO,OAAO,KAAK;AACrB;AAEO,SAAS,kBAAkB,MAAqC;AACrE,SAAO,CAAC,CAAC,QAAQ,OAAO,SAAS,YAAY,QAAQ,QAAQ,UAAU,QAAQ,UAAU,QAAQ,UAAU;AAC7G;AAEO,SAAS,kBAAkB,OAAe,SAAiE;AAChH,SAAO,QAAQ;AAAA,IACb,CAAC,MACC,EAAE,UAAU,SACZ,EAAE,MAAM,YAAY,MAAM,MAAM,YAAY,KAC5C,EAAE,MAAM,YAAY,MAAM,MAAM,YAAY;AAAA,EAChD,GAAG;AACL;AAEO,SAAS,WAAW,UAAkB,UAAkB;AAC7D,SAAO,GAAG,QAAQ,IAAI,QAAQ;AAChC;AAEO,SAAS,aAAa,SAAyC;AACpE,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,QAAM,cAAc,MAAM,CAAC;AAC3B,QAAM,WAAW,MAAM,CAAC;AACxB,MAAI,eAAe,UAAU;AAC3B,UAAM,WAAW,SAAS,aAAa,EAAE;AACzC,QAAI,CAAC,OAAO,MAAM,QAAQ,GAAG;AAC3B,aAAO,EAAE,UAAU,SAAS;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,EAAE,UAAU,GAAG,UAAU,GAAG;AACrC;AAEO,SAAS,kBAAkB,WAAmC;AACnE,QAAM,eAA+C;AAAA,IACnD,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAEA,SAAO,aAAa,SAAS;AAC/B;AAEO,SAAS,aAAa,WAAmC;AAC9D,QAAM,eAA+C;AAAA,IACnD,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAEA,SAAO,aAAa,SAAS;AAC/B;AAEO,SAAS,0BAAiC,QAO/C;AACA,QAAM,EAAE,QAAQ,YAAY,aAAa,IAAI;AAE7C,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,2BAA2B,aAAa,WAAW,OAAO,iBAAiB,OAAO;AACxF,QAAM,0BAA0B,aAAa,WAAW,OAAO,gBAAgB,OAAO;AAEtF,QAAM,eAAe,YAAY,YAAY;AAC7C,QAAM,sBAAsB,iBAAiB,WAAW,YAAY,iBAAiB,OAAO;AAE5F,QAAM,gBAAgB,CAAC,wBAAwB,gBAAgB,CAAC;AAEhE,QAAM,kBAAkB;AAExB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,sBAA6B,QAIrB;AACtB,QAAM,EAAE,QAAQ,MAAM,OAAO,aAAa,MAAM,IAAI;AAEpD,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,yBAAyB,aAAa,UAAU,OAAO,gBAAgB,MAAM;AACnF,QAAM,2BAA2B,aAAa,WAAW,OAAO,iBAAiB,OAAO;AAExF,QAAM,QAAQ,QAAQ;AAEtB,QAAM,eAAe,aAAa,SAAS,GAAG,OAAO,SAAS,MAAM,CAAC,OAAO;AAC5E,QAAM,gBAAgB,aAAa,UAAU,GAAG,OAAO,SAAS,OAAO,CAAC,OAAO;AAiB/E,SAAO;AAAA,IACL,WAAW,aACP,yBACE,QACE,uCACA,wCACF,2BACE,QACE,wCACA,uCACF,SACJ;AAAA,IACJ,MAAM,QAAQ,gBAAgB;AAAA,IAC9B,OAAO,QAAQ,eAAe;AAAA,IAC9B,UAAU,WAAW,WAAW;AAAA,IAChC,YAAY;AAAA,IACZ,OAAO,OAAO,QAAQ;AAAA,IACtB,QAAQ,WAAW,IAAI;AAAA,EACzB;AACF;AAEO,SAAS,mBAAmB,WAAkE;AACnG,MAAI,cAAc,UAAU,cAAc,WAAW,cAAc,UAAU,cAAc,OAAO;AAChG,WAAO;AAAA,EACT;AACA,MAAI,cAAc,WAAY,QAAO;AACrC,MAAI,cAAc,YAAa,QAAO;AACtC,SAAO;AACT;AAEO,SAAS,mBAA0B,QAOjC;AACP,QAAM,EAAE,WAAW,YAAY,UAAU,WAAW,gBAAgB,MAAM,IAAI;AAE9E,QAAM,gBAAgB,UAAU,sBAAsB;AACtD,QAAM,WAAW,WAAW,sBAAsB;AAElD,QAAM,oBAAoB,UAAU,aAAa;AACjD,QAAM,gBAAgB,SAAS;AAE/B,QAAM,eAAe,SAAS;AAC9B,QAAM,oBAAoB,cAAc,0BAA0B,KAAK,CAAC;AACxE,QAAM,qBAAqB,cAAc,2BAA2B,KAAK,CAAC;AAE1E,QAAM,kBAAkB,kBAAkB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,GAAG,CAAC;AACjF,QAAM,mBAAmB,mBAAmB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,GAAG,CAAC;AAEnF,QAAM,eAAe,gBACjB,cAAc,OAAO,mBAAmB,iBACxC,cAAc,OAAO,kBAAkB;AAC3C,QAAM,gBAAgB,gBAClB,cAAc,QAAQ,kBAAkB,iBACxC,cAAc,QAAQ,mBAAmB;AAE7C,QAAM,iBAAiB,SAAS,QAAQ,gBAAgB,SAAS,SAAS;AAE1E,MAAI,eAAgB;AAEpB,QAAM,gBAAgB,SAAS,OAAO;AACtC,QAAM,iBAAiB,SAAS,QAAQ;AAExC,MAAI,cAAc;AAElB,MAAI,CAAC,WAAW;AACd,QAAI,gBAAgB;AAClB,oBAAc,SAAS,QAAQ;AAAA,IACjC,WAAW,eAAe;AACxB,oBAAc,EAAE,eAAe,SAAS;AAAA,IAC1C;AAAA,EACF,OAAO;AACL,UAAM,oBAAoB,gBACtB,cAAc,WAAW,cAAc,SACvC,cAAc,WAAW,cAAc;AAE3C,QAAI,mBAAmB;AACrB,oBAAc,SAAS,QAAQ;AAAA,IACjC,OAAO;AACL,oBAAc,EAAE,eAAe,SAAS;AAAA,IAC1C;AAAA,EACF;AAEA,YAAU,cAAc;AAC1B;AAEA,SAAS,UAAU,GAAmB;AACpC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,KAAI,EAAE,CAAC,MAAM,IAAM;AACtD,SAAO;AACT;AAEO,SAAS,SAAS,MAAc,qBAAyC;AAC9E,MAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,IAAK,GAAG;AAChD,UAAMA,QAAmB,CAAC;AAC1B,QAAI,aAAuB,CAAC;AAC5B,QAAI,eAAe;AACnB,QAAI,WAAW;AACf,QAAI,IAAI;AAER,WAAO,IAAI,KAAK,QAAQ;AACtB,YAAM,OAAO,KAAK,CAAC;AACnB,YAAM,WAAW,KAAK,IAAI,CAAC;AAE3B,UAAI,UAAU;AACZ,YAAI,SAAS,OAAO,aAAa,KAAK;AACpC,0BAAgB;AAChB,eAAK;AAAA,QACP,WAAW,SAAS,KAAK;AACvB,qBAAW;AACX;AAAA,QACF,OAAO;AACL,0BAAgB;AAChB;AAAA,QACF;AAAA,MACF,OAAO;AACL,YAAI,SAAS,OAAO,iBAAiB,IAAI;AACvC,qBAAW;AACX;AAAA,QACF,WAAW,SAAS,KAAM;AACxB,qBAAW,KAAK,YAAY;AAC5B,yBAAe;AACf;AAAA,QACF,WAAW,SAAS,MAAM;AACxB,qBAAW,KAAK,YAAY;AAC5B,cAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,YAAAA,MAAK,KAAK,UAAU;AAAA,UACtB;AACA,uBAAa,CAAC;AACd,yBAAe;AACf;AAAA,QACF,WAAW,SAAS,QAAQ,aAAa,MAAM;AAC7C,qBAAW,KAAK,YAAY;AAC5B,cAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,YAAAA,MAAK,KAAK,UAAU;AAAA,UACtB;AACA,uBAAa,CAAC;AACd,yBAAe;AACf,eAAK;AAAA,QACP,OAAO;AACL,0BAAgB;AAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,eAAW,KAAK,YAAY;AAC5B,QAAI,WAAW,SAAS,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACjE,MAAAA,MAAK,KAAK,UAAU;AAAA,IACtB;AAEA,WAAOA;AAAA,EACT;AAEA,QAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,CAAC;AAC9D,MAAI,cAAc;AAClB,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,UAAU,IAAI;AACxB,QAAI,IAAI,YAAa,eAAc;AAAA,EACrC;AACA,QAAM,cAAc,cAAc,IAAI,cAAc,IAAI;AACxD,MAAI,eAAe,EAAG,QAAO,CAAC;AAE9B,QAAM,mBAAmB,cAAc;AACvC,QAAM,OAAmB,CAAC;AAC1B,MAAI,MAAM;AACV,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,UAAU,IAAI;AAEzB,QAAI,OAAO,kBAAkB;AAC3B,UAAI,OAAO,gBAAgB,iBAAkB,MAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AACtE,YAAM;AACN,oBAAc;AACd,WAAK,KAAK,KAAK,MAAM,GAAI,CAAC;AAAA,IAC5B,OAAO;AACL,YAAM,MAAM,GAAG,GAAG;AAAA,EAAK,IAAI,KAAK;AAChC,qBAAe;AACf,UAAI,gBAAgB,kBAAkB;AACpC,aAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AACzB,cAAM;AACN,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,gBAAgB,iBAAkB,MAAK,KAAK,IAAI,MAAM,GAAI,CAAC;AAEtE,SAAO,KAAK,SAAS,IAAI,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAI,CAAC;AAC5F;AAgBO,SAAS,eAAe,SAA2B;AACxD,MAAI,EAAE,mBAAmB,SAAU,QAAO;AAE1C,SACE,QAAQ,QAAQ,yBAAyB,MAAM,QAC/C,QAAQ,QAAQ,qBAAqB,MAAM,QAC3C,QAAQ,QAAQ,qCAAqC,MAAM,QAC3D,QAAQ,QAAQ,0BAA0B,MAAM,QAChD,QAAQ,QAAQ,iBAAiB,MAAM;AAAA;AAAA,EAGvC,QAAQ,QAAQ,sBAAsB,MAAM,QAC5C,QAAQ,QAAQ,eAAe,MAAM,QACrC,QAAQ,QAAQ,kBAAkB,MAAM;AAE5C;AASO,SAAS,iBACd,SACA,UAIO;AACP,QAAM,MAAM,YAAY,oBAAoB;AAC5C,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,WAAW,MAAM,aAAa;AAAA,IACnE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,aAAa;AAAA,IAClE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,QAAQ,MAAM,SAAS;AAAA,IAC5D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,KAAK,MAAM,SAAS;AAAA,IACzD,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,gBAAgB;AAAA,IACrE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,QAAQ,MAAM,SAAS;AAAA,IAC5D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,aAAa,MAAM,eAAe;AAAA,IACvE,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,MAAM,MAAM,aAAa;AAAA,IAC9D,KAAK;AACH,aAAO,EAAE,OAAO,IAAI,OAAO,QAAQ,MAAM,MAAM,SAAS;AAAA,IAC1D;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,kBAAkB,SAAmD;AACnF,MAAI,YAAY,kBAAkB,YAAY,OAAQ,QAAO,CAAC;AAC9D,MAAI,YAAY,YAAY,YAAY,UAAU,YAAY,SAAU,QAAO;AAC/E,MAAI,YAAY,WAAY,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,WAAW,WAA2B;AACpD,MAAI,CAAC,aAAa,UAAU,KAAK,MAAM,GAAI,QAAO;AAElD,QAAM,UAAU,UAAU,KAAK;AAG/B,MAAI,qCAAqC,KAAK,OAAO,GAAG;AACtD,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,UAAU,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,OAAO;AAC1B;AAEO,SAAS,eAAe,SAA+B;AAC5D,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,mBAAmB,KAAM,QAAO;AACpC,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,QAAM,CAAC,MAAM,OAAO,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AACxD,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAK,QAAO;AACpC,QAAM,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AAE1C,MAAI,KAAK,YAAY,MAAM,QAAQ,KAAK,SAAS,MAAM,QAAQ,KAAK,KAAK,QAAQ,MAAM,KAAK;AAC1F,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,MAAoB;AACrD,QAAM,OAAO,KAAK,YAAY;AAC9B,QAAM,QAAQ,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AACzD,QAAM,MAAM,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AAClD,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAChC;AAOA,MAAM,sBAAsB;AAYrB,SAAS,WAAW,MAAiD,QAAgC;AAC1G,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,gBAAgB,OAAO,OAAO,IAAI,KAAK,IAAI;AACzD,MAAI,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC1C,SAAO,OAAO,OAAO,qBAAqB,EAAE,OAAO,CAAC;AACtD;AAGO,SAAS,qBAAqB,SAAkB,QAAgC;AACrF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,eAAe,OAAO;AACnC,MAAI,CAAC,KAAM,QAAO,OAAO,YAAY,WAAW,UAAU;AAC1D,SAAO,WAAW,MAAM,MAAM;AAChC;AAUO,SAAS,eAAe,MAAc,QAAqC;AAChF,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,YAAY,MAAM,SAAS,qBAAqB,oBAAI,KAAK,GAAG,EAAE,OAAO,CAAC;AAC5E,MAAI,QAAQ,SAAS,KAAK,UAAU,YAAY,KAAK,IAAM,QAAO;AAClE,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAMO,SAAS,iBAAyB;AACvC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACnF;AAOA,eAAsB,mBAAmB,MAA6B;AACpE,MAAI,OAAO,cAAc,eAAe,UAAU,WAAW,WAAW;AACtE,QAAI;AACF,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,UAAM,IAAI,MAAM,uCAA0B;AAAA,EAC5C;AACF;AAEA,SAAS,oBAAoB,MAAuB;AAClD,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,aAAa,YAAY,EAAE;AACpC,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,MAAM;AACrB,WAAS,MAAM,mBAAmB;AAClC,WAAS,KAAK,YAAY,QAAQ;AAElC,QAAM,oBAAoB,SAAS;AACnC,WAAS,OAAO;AAEhB,MAAI,SAAS;AACb,MAAI;AACF,aAAS,SAAS,YAAY,MAAM;AAAA,EACtC,QAAQ;AACN,aAAS;AAAA,EACX;AAEA,WAAS,KAAK,YAAY,QAAQ;AAClC,MAAI,6BAA6B,aAAa;AAC5C,sBAAkB,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;AAGA,eAAsB,oBAA4C;AAChE,MAAI,OAAO,cAAc,eAAe,CAAC,UAAU,WAAW,SAAU,QAAO;AAC/E,MAAI;AACF,WAAO,MAAM,UAAU,UAAU,SAAS;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,OAAuB;AACpD,MAAI,SAAS,KAAK,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAClD,QAAM,IAAI;AACV,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI;AACpC,QAAM,IAAI,KAAK,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;AAC9E,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AACtE;AAEO,SAAS,YAAY,MAAkE;AAC5F,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,WAAW,QAAQ,EAAG,QAAO;AACtC,MAAI,KAAK,SAAS,KAAK,EAAG,QAAO;AACjC,MAAI,KAAK,SAAS,KAAK,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACzD,MAAI,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,UAAU,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACvF,MAAI,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACrF,MAAI,KAAK,SAAS,cAAc,KAAK,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AACjG,SAAO;AACT;","names":["rows"]}
|
package/package.json
CHANGED