@tollerud/ui
Advanced tools
| 'use client'; | ||
| import { Skeleton } from './chunk-PCFRRG2S.js'; | ||
| import { Segmented } from './chunk-YREZRV73.js'; | ||
| import { Select } from './chunk-BDFXBUZ3.js'; | ||
| import { Pagination } from './chunk-YMAYHKPG.js'; | ||
| import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuItem } from './chunk-5QNHCMEX.js'; | ||
| import { Checkbox } from './chunk-BCNAGDLP.js'; | ||
| import { Combobox } from './chunk-KDRWILS3.js'; | ||
| import { Badge } from './chunk-MMCPZVBB.js'; | ||
| import { ButtonGroup } from './chunk-FSRS5IWZ.js'; | ||
| import { Button } from './chunk-WZVSFANP.js'; | ||
| import { cn } from './chunk-62DSK4IE.js'; | ||
| import { forwardRef, useMemo, useState } from 'react'; | ||
| import { Search, X, MoreHorizontal, ChevronDown } from 'lucide-react'; | ||
| import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; | ||
| function normalizeColumns(columns) { | ||
| return columns.map(({ header, label, ...col }) => ({ | ||
| ...col, | ||
| label: label ?? header ?? col.key | ||
| })); | ||
| } | ||
| function columnReactKey(col, index) { | ||
| return `${col.key}-${index}`; | ||
| } | ||
| function DataTableInner({ | ||
| columns, | ||
| data, | ||
| rows, | ||
| rowKey, | ||
| onRowClick, | ||
| className, | ||
| emptyMessage = "No data", | ||
| searchable = false, | ||
| searchKeys, | ||
| searchPlaceholder = "Search\u2026", | ||
| filter, | ||
| selectable = false, | ||
| pageSize, | ||
| pageSizeOptions, | ||
| bulkActions = [], | ||
| rowMenu, | ||
| toolbarRight, | ||
| emptyState, | ||
| loading = false, | ||
| skeletonRows = 5, | ||
| striped = false, | ||
| footer, | ||
| pinColumns, | ||
| forwardedRef | ||
| }) { | ||
| const normalizedColumns = useMemo(() => normalizeColumns(columns), [columns]); | ||
| const tableData = useMemo(() => rows ?? data ?? [], [rows, data]); | ||
| const pageSizeOptionsResolved = useMemo( | ||
| () => [...new Set(pageSizeOptions ?? [])].filter((n) => n > 0).sort((a, b) => a - b), | ||
| [pageSizeOptions] | ||
| ); | ||
| const allowsPageSizeChange = pageSizeOptionsResolved.length > 0; | ||
| const [activePageSize, setActivePageSize] = useState( | ||
| pageSize ?? pageSizeOptionsResolved[0] ?? 10 | ||
| ); | ||
| const effectivePageSize = allowsPageSizeChange ? activePageSize : pageSize; | ||
| const isRich = searchable || filter || selectable || pageSize !== void 0 || allowsPageSizeChange || !!rowMenu || !!toolbarRight || bulkActions.length > 0 || !!emptyState || loading; | ||
| const pinEdges = pinColumns ?? isRich; | ||
| const tableMinWidth = Math.max(640, normalizedColumns.length * 120 + (selectable ? 40 : 0) + (rowMenu ? 44 : 0)); | ||
| const [sortKey, setSortKey] = useState(null); | ||
| const [sortDir, setSortDir] = useState("asc"); | ||
| const [filters, setFilters] = useState({}); | ||
| const [query, setQuery] = useState(""); | ||
| const [filterValue, setFilterValue] = useState("all"); | ||
| const [selected, setSelected] = useState([]); | ||
| const [page, setPage] = useState(1); | ||
| const getRowKey = (row, i) => { | ||
| if (typeof rowKey === "function") return rowKey(row); | ||
| if (rowKey) return row[rowKey]; | ||
| return row.id ?? row.key ?? i; | ||
| }; | ||
| const toggleSort = (key) => { | ||
| if (sortKey === key) { | ||
| setSortDir((d) => d === "asc" ? "desc" : "asc"); | ||
| } else { | ||
| setSortKey(key); | ||
| setSortDir("asc"); | ||
| } | ||
| }; | ||
| const updateFilter = (key, value) => { | ||
| setFilters((prev) => { | ||
| const next = { ...prev }; | ||
| if (value.trim() === "") { | ||
| delete next[key]; | ||
| } else { | ||
| next[key] = value; | ||
| } | ||
| return next; | ||
| }); | ||
| }; | ||
| const searchKeysResolved = useMemo( | ||
| () => searchKeys ?? normalizedColumns.map((c) => c.key), | ||
| [searchKeys, normalizedColumns] | ||
| ); | ||
| const filterSelectOptions = useMemo(() => { | ||
| if (!filter) return []; | ||
| const options = filter.options ?? Array.from(new Set(tableData.map((r) => String(r[filter.key] ?? "")))); | ||
| return [ | ||
| { value: "all", label: filter.allLabel ?? "All" }, | ||
| ...options.map((opt) => ({ value: opt, label: opt })) | ||
| ]; | ||
| }, [filter, tableData]); | ||
| const filtered = useMemo(() => { | ||
| let result = tableData; | ||
| if (isRich && filter && filterValue !== "all") { | ||
| result = result.filter((row) => String(row[filter.key] ?? "") === filterValue); | ||
| } | ||
| if (isRich && searchable && query.trim()) { | ||
| const q = query.trim().toLowerCase(); | ||
| result = result.filter( | ||
| (row) => searchKeysResolved.some((key) => String(row[key] ?? "").toLowerCase().includes(q)) | ||
| ); | ||
| } | ||
| if (!isRich) { | ||
| const activeFilters = Object.entries(filters).filter(([, v]) => v.trim() !== ""); | ||
| if (activeFilters.length > 0) { | ||
| result = result.filter( | ||
| (row) => activeFilters.every(([key, filterVal]) => { | ||
| const cellValue = String(row[key] ?? "").toLowerCase(); | ||
| return cellValue.includes(filterVal.toLowerCase()); | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| return result; | ||
| }, [tableData, filters, isRich, filter, filterValue, searchable, query, searchKeysResolved]); | ||
| const sorted = useMemo(() => { | ||
| if (!sortKey) return filtered; | ||
| const col = normalizedColumns.find((c) => c.key === sortKey); | ||
| if (!col?.sortable) return filtered; | ||
| return [...filtered].sort((a, b) => { | ||
| const aVal = a[sortKey]; | ||
| const bVal = b[sortKey]; | ||
| if (typeof aVal === "number" && typeof bVal === "number") { | ||
| return sortDir === "asc" ? aVal - bVal : bVal - aVal; | ||
| } | ||
| const aStr = String(aVal ?? ""); | ||
| const bStr = String(bVal ?? ""); | ||
| const cmp = aStr.localeCompare(bStr, "nb", { numeric: true }); | ||
| return sortDir === "asc" ? cmp : -cmp; | ||
| }); | ||
| }, [filtered, sortKey, sortDir, normalizedColumns]); | ||
| const pageCount = effectivePageSize ? Math.max(1, Math.ceil(sorted.length / effectivePageSize)) : 1; | ||
| const currentPage = Math.min(page, pageCount); | ||
| const pageRows = effectivePageSize ? sorted.slice((currentPage - 1) * effectivePageSize, currentPage * effectivePageSize) : sorted; | ||
| const clearSelection = () => setSelected([]); | ||
| const allOnPageSelected = pageRows.length > 0 && pageRows.every((row, i) => selected.includes(getRowKey(row, i))); | ||
| const someOnPageSelected = !allOnPageSelected && pageRows.some((row, i) => selected.includes(getRowKey(row, i))); | ||
| const toggleAllOnPage = () => { | ||
| const pageIds = pageRows.map((row, i) => getRowKey(row, i)); | ||
| if (allOnPageSelected) { | ||
| setSelected((prev) => prev.filter((id) => !pageIds.includes(id))); | ||
| } else { | ||
| setSelected((prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...pageIds]))); | ||
| } | ||
| }; | ||
| const toggleRow = (id) => { | ||
| setSelected((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]); | ||
| }; | ||
| const hasActiveFilters = Object.values(filters).some((v) => v.trim() !== ""); | ||
| const filterableColumns = normalizedColumns.filter((c) => c.filterable); | ||
| const colSpan = normalizedColumns.length + (selectable ? 1 : 0) + (rowMenu ? 1 : 0); | ||
| const rowHoverable = Boolean(onRowClick || isRich); | ||
| const selectedRowBg = "bg-[color-mix(in_srgb,var(--tollerud-yellow)_7%,var(--tollerud-noir-900))]"; | ||
| const selectedRowHoverBg = "group-hover/tr:bg-[color-mix(in_srgb,var(--tollerud-yellow)_12%,var(--tollerud-noir-800))]"; | ||
| const cellSurface = (rowIndex, isSelected) => cn( | ||
| "transition-colors duration-fast", | ||
| rowHoverable && (isSelected ? selectedRowHoverBg : "group-hover/tr:bg-tollerud-noir-800"), | ||
| isSelected && selectedRowBg, | ||
| !isSelected && striped && rowIndex % 2 === 0 && "bg-tollerud-noir-950", | ||
| !isSelected && !(striped && rowIndex % 2 === 0) && isRich && "bg-tollerud-noir-900" | ||
| ); | ||
| const stickyPosition = (edge) => pinEdges && cn( | ||
| "sticky z-[2]", | ||
| edge === "check" && "left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", | ||
| edge === "first" && cn("left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", selectable && "left-10"), | ||
| edge === "actions" && "right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]" | ||
| ); | ||
| const stickyHead = (edge) => pinEdges && cn( | ||
| "sticky z-[3] bg-tollerud-noir-900", | ||
| edge === "check" && "left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", | ||
| edge === "first" && cn("left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", selectable && "left-10"), | ||
| edge === "actions" && "right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]" | ||
| ); | ||
| const stickyBody = (edge) => stickyPosition(edge); | ||
| const sortIndicator = (key) => { | ||
| const active = sortKey === key; | ||
| return /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 12, | ||
| className: cn( | ||
| "shrink-0 transition-transform duration-fast", | ||
| active ? "text-tollerud-yellow opacity-100" : "opacity-25", | ||
| active && sortDir === "desc" && "rotate-180" | ||
| ), | ||
| "aria-hidden": true | ||
| } | ||
| ); | ||
| }; | ||
| const headerCell = (col, columnIndex, reactKey) => { | ||
| const content = /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| col.label, | ||
| col.sortable && (isRich ? sortIndicator(col.key) : /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| sortKey === col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-accent", children: sortDir === "asc" ? "\u2191" : "\u2193" }), | ||
| sortKey !== col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-muted/30", children: "\u2195" }) | ||
| ] })), | ||
| !isRich && col.filterable && /* @__PURE__ */ jsx( | ||
| "svg", | ||
| { | ||
| width: "12", | ||
| height: "12", | ||
| viewBox: "0 0 24 24", | ||
| fill: "none", | ||
| stroke: "currentColor", | ||
| strokeWidth: "2", | ||
| strokeLinecap: "round", | ||
| strokeLinejoin: "round", | ||
| className: cn("opacity-40", filters[col.key] && "opacity-100 text-tollerud-yellow"), | ||
| "aria-hidden": true, | ||
| children: /* @__PURE__ */ jsx("path", { d: "M4 4h16v2.172a2 2 0 0 1-.586 1.414L15 12v7l-6 2v-8.5L4.52 7.53A2 2 0 0 1 4 6.16V4z" }) | ||
| } | ||
| ) | ||
| ] }); | ||
| const contentAlignment = cn( | ||
| "inline-flex items-center gap-1", | ||
| col.align === "right" && "justify-end w-full", | ||
| col.align === "center" && "justify-center w-full" | ||
| ); | ||
| return /* @__PURE__ */ jsx( | ||
| "th", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 text-left text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| col.align === "center" && "text-center", | ||
| columnIndex === 0 && stickyHead("first") | ||
| ), | ||
| style: col.width ? { width: col.width, minWidth: col.width } : void 0, | ||
| "aria-sort": col.sortable ? sortKey === col.key ? sortDir === "asc" ? "ascending" : "descending" : "none" : void 0, | ||
| children: col.sortable ? /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| onClick: () => toggleSort(col.key), | ||
| className: cn( | ||
| contentAlignment, | ||
| "cursor-pointer select-none uppercase tracking-wider hover:text-tollerud-text-primary transition-colors tollerud-focus-ring rounded-sm" | ||
| ), | ||
| children: content | ||
| } | ||
| ) : /* @__PURE__ */ jsx("span", { className: contentAlignment, children: content }) | ||
| }, | ||
| reactKey | ||
| ); | ||
| }; | ||
| const tableBody = () => { | ||
| if (loading) { | ||
| return /* @__PURE__ */ jsx("tbody", { children: Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-tollerud-border/20", children: [ | ||
| selectable && /* @__PURE__ */ jsx("td", { className: cn("px-3 py-2.5 w-10 min-w-10", cellSurface(i, false), stickyBody("check")), children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded" }) }), | ||
| normalizedColumns.map((col, j) => /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| cellSurface(i, false), | ||
| j === 0 && stickyBody("first") | ||
| ), | ||
| children: /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3", j === 0 ? "w-[70%]" : "w-[55%]") }) | ||
| }, | ||
| columnReactKey(col, j) | ||
| )), | ||
| rowMenu && /* @__PURE__ */ jsx("td", { className: cn("px-3 py-2.5 w-11 min-w-11", cellSurface(i, false), stickyBody("actions")), children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded" }) }) | ||
| ] }, i)) }); | ||
| } | ||
| if (sorted.length === 0) { | ||
| if (isRich && emptyState) { | ||
| return null; | ||
| } | ||
| return /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, className: "px-3 py-8 text-center text-sm text-tollerud-text-muted", children: hasActiveFilters || query || filterValue !== "all" ? "No matching rows" : emptyMessage }) }) }); | ||
| } | ||
| return /* @__PURE__ */ jsx("tbody", { children: pageRows.map((row, i) => { | ||
| const id = getRowKey(row, i); | ||
| const isSelected = selected.includes(id); | ||
| return /* @__PURE__ */ jsxs( | ||
| "tr", | ||
| { | ||
| className: cn( | ||
| "group/tr border-b border-tollerud-border/20", | ||
| onRowClick && "cursor-pointer" | ||
| ), | ||
| onClick: () => onRowClick?.(row), | ||
| children: [ | ||
| selectable && /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn("px-3 py-2.5 w-10 min-w-10", cellSurface(i, isSelected), stickyBody("check")), | ||
| onClick: (e) => e.stopPropagation(), | ||
| children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(id), "aria-label": `Select row ${id}` }) | ||
| } | ||
| ), | ||
| normalizedColumns.map((col, columnIndex) => { | ||
| const value = row[col.key]; | ||
| return /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 text-tollerud-text-secondary whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| col.align === "center" && "text-center", | ||
| !col.render && "font-mono text-xs", | ||
| cellSurface(i, isSelected), | ||
| columnIndex === 0 && stickyBody("first") | ||
| ), | ||
| style: col.width ? { width: col.width, minWidth: col.width } : void 0, | ||
| children: col.render ? col.render(value, row) : String(value ?? "\u2014") | ||
| }, | ||
| columnReactKey(col, columnIndex) | ||
| ); | ||
| }), | ||
| rowMenu && /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn("px-2 py-2 w-11 min-w-11", cellSurface(i, isSelected), stickyBody("actions")), | ||
| onClick: (e) => e.stopPropagation(), | ||
| children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [ | ||
| /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-tollerud-text-muted hover:text-tollerud-text-primary hover:bg-tollerud-surface-raised transition-colors tollerud-focus-ring", | ||
| "aria-label": "Row actions", | ||
| children: /* @__PURE__ */ jsx(MoreHorizontal, { size: 15 }) | ||
| } | ||
| ) }), | ||
| /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: rowMenu(row).map((item, index) => { | ||
| if (item.sep) return /* @__PURE__ */ jsx(DropdownMenuSeparator, {}, index); | ||
| if (item.heading) return /* @__PURE__ */ jsx(DropdownMenuLabel, { children: item.label }, index); | ||
| return /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => item.onSelect?.(), children: [ | ||
| item.icon ? /* @__PURE__ */ jsx("span", { className: "mr-2 inline-flex", children: item.icon }) : null, | ||
| item.label | ||
| ] }, index); | ||
| }) }) | ||
| ] }) | ||
| } | ||
| ) | ||
| ] | ||
| }, | ||
| id | ||
| ); | ||
| }) }); | ||
| }; | ||
| const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", style: { minWidth: tableMinWidth }, children: [ | ||
| /* @__PURE__ */ jsxs("thead", { children: [ | ||
| /* @__PURE__ */ jsxs("tr", { className: "border-b border-tollerud-border/30 bg-tollerud-noir-900", children: [ | ||
| selectable && /* @__PURE__ */ jsx("th", { className: cn("px-3 py-2.5 w-10 min-w-10", stickyHead("check")), children: /* @__PURE__ */ jsx( | ||
| Checkbox, | ||
| { | ||
| checked: allOnPageSelected, | ||
| indeterminate: someOnPageSelected, | ||
| onChange: toggleAllOnPage, | ||
| "aria-label": "Select all rows on page" | ||
| } | ||
| ) }), | ||
| normalizedColumns.map( | ||
| (col, columnIndex) => headerCell(col, columnIndex, columnReactKey(col, columnIndex)) | ||
| ), | ||
| rowMenu && /* @__PURE__ */ jsx("th", { className: cn("w-11 min-w-11", stickyHead("actions")), "aria-label": "Row actions" }) | ||
| ] }), | ||
| !isRich && filterableColumns.length > 0 && /* @__PURE__ */ jsx("tr", { className: "border-b border-tollerud-border/20", children: normalizedColumns.map((col, columnIndex) => /* @__PURE__ */ jsx( | ||
| "th", | ||
| { | ||
| className: cn("px-1.5 py-1", col.align === "right" && "text-right", col.align === "center" && "text-center"), | ||
| children: col.filterable ? /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| type: "text", | ||
| placeholder: `Filter ${col.label.toLowerCase()}\u2026`, | ||
| value: filters[col.key] ?? "", | ||
| onChange: (e) => updateFilter(col.key, e.target.value), | ||
| className: cn( | ||
| "w-full px-2 py-1 text-xs rounded border transition-colors outline-none", | ||
| "bg-tollerud-noir-900/50 border-tollerud-border/20 text-tollerud-text-primary placeholder:text-tollerud-text-muted/40", | ||
| "focus:border-tollerud-yellow/40 focus:ring-1 focus:ring-tollerud-yellow/20" | ||
| ) | ||
| } | ||
| ) : null | ||
| }, | ||
| columnReactKey(col, columnIndex) | ||
| )) }) | ||
| ] }), | ||
| tableBody() | ||
| ] }); | ||
| const bulkActionButtons = bulkActions.map((action, index) => /* @__PURE__ */ jsxs( | ||
| Button, | ||
| { | ||
| variant: action.variant ?? "ghost", | ||
| size: "sm", | ||
| onClick: () => action.onRun(selected, clearSelection), | ||
| children: [ | ||
| action.icon ? /* @__PURE__ */ jsx("span", { className: "mr-1.5 inline-flex shrink-0", children: action.icon }) : null, | ||
| action.label | ||
| ] | ||
| }, | ||
| index | ||
| )); | ||
| const tableScroll = /* @__PURE__ */ jsx( | ||
| "div", | ||
| { | ||
| className: "tollerud-datatable-scroll min-w-0 w-full overflow-x-auto overscroll-x-contain", | ||
| role: "region", | ||
| "aria-label": "Scrollable table", | ||
| tabIndex: pinEdges ? 0 : void 0, | ||
| children: table | ||
| } | ||
| ); | ||
| if (!isRich) { | ||
| return /* @__PURE__ */ jsx("div", { ref: forwardedRef, className: cn("min-w-0 w-full rounded-lg border border-tollerud-border/30", className), children: tableScroll }); | ||
| } | ||
| const showEmpty = !loading && sorted.length === 0; | ||
| return /* @__PURE__ */ jsxs( | ||
| "div", | ||
| { | ||
| ref: forwardedRef, | ||
| className: cn( | ||
| "min-w-0 w-full rounded-lg border border-tollerud-border/30 bg-tollerud-noir-900", | ||
| className | ||
| ), | ||
| children: [ | ||
| (searchable || filter || toolbarRight) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5 border-b border-tollerud-border/30 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between", children: [ | ||
| searchable && /* @__PURE__ */ jsxs("div", { className: "flex h-9 w-full items-center gap-2 rounded-lg border border-tollerud-border/30 bg-tollerud-noir-950 px-3 sm:w-[230px] sm:shrink-0", children: [ | ||
| /* @__PURE__ */ jsx(Search, { size: 15, className: "shrink-0 text-tollerud-text-muted", "aria-hidden": true }), | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| value: query, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setPage(1); | ||
| }, | ||
| placeholder: searchPlaceholder, | ||
| className: "min-w-0 flex-1 border-none bg-transparent text-sm text-tollerud-text-primary outline-none placeholder:text-tollerud-text-muted" | ||
| } | ||
| ), | ||
| query && /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| onClick: () => setQuery(""), | ||
| className: "text-tollerud-text-muted hover:text-tollerud-text-primary", | ||
| "aria-label": "Clear search", | ||
| children: /* @__PURE__ */ jsx(X, { size: 13 }) | ||
| } | ||
| ) | ||
| ] }), | ||
| (filter || toolbarRight) && /* @__PURE__ */ jsxs( | ||
| "div", | ||
| { | ||
| className: cn( | ||
| "flex min-w-0 items-center gap-2", | ||
| filter ? "justify-between" : "justify-end", | ||
| "sm:justify-end" | ||
| ), | ||
| children: [ | ||
| filter && (filter.variant === "combobox" ? /* @__PURE__ */ jsx( | ||
| Combobox, | ||
| { | ||
| value: filterValue, | ||
| onChange: (value) => { | ||
| setFilterValue(value); | ||
| setPage(1); | ||
| }, | ||
| options: filterSelectOptions, | ||
| placeholder: filter.placeholder ?? filter.allLabel ?? "Filter\u2026", | ||
| className: "min-w-0 flex-1 sm:flex-none sm:w-[200px]" | ||
| } | ||
| ) : /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 overflow-x-auto sm:flex-none", children: /* @__PURE__ */ jsx( | ||
| Segmented, | ||
| { | ||
| size: "sm", | ||
| value: filterValue, | ||
| onChange: (value) => { | ||
| setFilterValue(value); | ||
| setPage(1); | ||
| }, | ||
| options: filterSelectOptions | ||
| } | ||
| ) })), | ||
| toolbarRight ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: toolbarRight }) : null | ||
| ] | ||
| } | ||
| ) | ||
| ] }), | ||
| selectable && selected.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-tollerud-border/30 bg-tollerud-yellow/[0.07] px-4 py-2.5", children: [ | ||
| /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm text-tollerud-text-primary", children: [ | ||
| /* @__PURE__ */ jsx(Badge, { variant: "accent", children: selected.length }), | ||
| "selected" | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: bulkActions.length > 1 ? /* @__PURE__ */ jsx(ButtonGroup, { size: "sm", children: bulkActionButtons }) : bulkActionButtons }) | ||
| ] }), | ||
| showEmpty ? /* @__PURE__ */ jsx("div", { className: "p-2", children: emptyState ?? /* @__PURE__ */ jsx("div", { className: "py-12 text-center text-sm text-tollerud-text-muted", children: "No results." }) }) : tableScroll, | ||
| !loading && sorted.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative z-[4] flex flex-wrap items-center justify-between gap-3 border-t border-tollerud-border/30 px-4 py-2", children: [ | ||
| /* @__PURE__ */ jsx("span", { className: "text-xs text-tollerud-text-muted", children: effectivePageSize ? `Showing ${(currentPage - 1) * effectivePageSize + 1}\u2013${(currentPage - 1) * effectivePageSize + pageRows.length} of ${sorted.length}` : `${sorted.length} row${sorted.length === 1 ? "" : "s"}` }), | ||
| /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [ | ||
| footer, | ||
| allowsPageSizeChange && /* @__PURE__ */ jsx( | ||
| Select, | ||
| { | ||
| label: "Rows", | ||
| layout: "inline", | ||
| size: "sm", | ||
| value: String(activePageSize), | ||
| onChange: (value) => { | ||
| setActivePageSize(Number(value)); | ||
| setPage(1); | ||
| }, | ||
| options: pageSizeOptionsResolved.map((size) => ({ | ||
| value: String(size), | ||
| label: String(size) | ||
| })), | ||
| className: "w-[72px]" | ||
| } | ||
| ), | ||
| effectivePageSize && pageCount > 1 && /* @__PURE__ */ jsx(Pagination, { page: currentPage, pageCount, onChange: setPage, siblingCount: 1 }) | ||
| ] }) | ||
| ] }) | ||
| ] | ||
| } | ||
| ); | ||
| } | ||
| var DataTable = forwardRef((props, ref) => /* @__PURE__ */ jsx(DataTableInner, { ...props, forwardedRef: ref })); | ||
| DataTable.displayName = "DataTable"; | ||
| export { DataTable }; | ||
| //# sourceMappingURL=chunk-5ZNSG4BJ.js.map | ||
| //# sourceMappingURL=chunk-5ZNSG4BJ.js.map |
| {"version":3,"sources":["../components/DataTable.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;AAkDA,SAAS,iBAAoB,OAAA,EAA6C;AACxE,EAAA,OAAO,OAAA,CAAQ,IAAI,CAAC,EAAE,QAAQ,KAAA,EAAO,GAAG,KAAI,MAAO;AAAA,IACjD,GAAG,GAAA;AAAA,IACH,KAAA,EAAO,KAAA,IAAS,MAAA,IAAU,GAAA,CAAI;AAAA,GAChC,CAAE,CAAA;AACJ;AAEA,SAAS,cAAA,CAAe,KAAsB,KAAA,EAAe;AAC3D,EAAA,OAAO,CAAA,EAAG,GAAA,CAAI,GAAG,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAC5B;AAkEA,SAAS,cAAA,CAAkD;AAAA,EACzD,OAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA,GAAe,SAAA;AAAA,EACf,UAAA,GAAa,KAAA;AAAA,EACb,UAAA;AAAA,EACA,iBAAA,GAAoB,cAAA;AAAA,EACpB,MAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EACb,QAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAc,EAAC;AAAA,EACf,OAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA,GAAU,KAAA;AAAA,EACV,YAAA,GAAe,CAAA;AAAA,EACf,OAAA,GAAU,KAAA;AAAA,EACV,MAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,iBAAA,GAAoB,QAAQ,MAAM,gBAAA,CAAiB,OAAO,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAC5E,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,MAAM,IAAA,IAAQ,IAAA,IAAQ,EAAC,EAAG,CAAC,IAAA,EAAM,IAAI,CAAC,CAAA;AAEhE,EAAA,MAAM,uBAAA,GAA0B,OAAA;AAAA,IAC9B,MAAM,CAAC,GAAG,IAAI,IAAI,eAAA,IAAmB,EAAE,CAAC,CAAA,CAAE,OAAO,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAAA,IACnF,CAAC,eAAe;AAAA,GAClB;AACA,EAAA,MAAM,oBAAA,GAAuB,wBAAwB,MAAA,GAAS,CAAA;AAC9D,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,QAAA;AAAA,IAC1C,QAAA,IAAY,uBAAA,CAAwB,CAAC,CAAA,IAAK;AAAA,GAC5C;AACA,EAAA,MAAM,iBAAA,GAAoB,uBAAuB,cAAA,GAAiB,QAAA;AAElE,EAAA,MAAM,SACJ,UAAA,IACA,MAAA,IACA,cACA,QAAA,KAAa,MAAA,IACb,wBACA,CAAC,CAAC,OAAA,IACF,CAAC,CAAC,YAAA,IACF,WAAA,CAAY,SAAS,CAAA,IACrB,CAAC,CAAC,UAAA,IACF,OAAA;AAEF,EAAA,MAAM,WAAW,UAAA,IAAc,MAAA;AAC/B,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,iBAAA,CAAkB,MAAA,GAAS,GAAA,IAAO,UAAA,GAAa,EAAA,GAAK,CAAA,CAAA,IAAM,OAAA,GAAU,EAAA,GAAK,CAAA,CAAE,CAAA;AAE/G,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAwB,IAAI,CAAA;AAC1D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAyB,KAAK,CAAA;AAC5D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAiC,EAAE,CAAA;AACjE,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,EAAE,CAAA;AACrC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,QAAA,CAA8B,EAAE,CAAA;AAChE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,CAAC,CAAA;AAElC,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,EAAQ,CAAA,KAA+B;AACxD,IAAA,IAAI,OAAO,MAAA,KAAW,UAAA,EAAY,OAAO,OAAO,GAAG,CAAA;AACnD,IAAA,IAAI,MAAA,EAAQ,OAAO,GAAA,CAAI,MAAM,CAAA;AAC7B,IAAA,OAAQ,GAAA,CAAI,EAAA,IAAM,GAAA,CAAI,GAAA,IAAO,CAAA;AAAA,EAC/B,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB;AAClC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,UAAA,CAAW,CAAC,CAAA,KAAO,CAAA,KAAM,KAAA,GAAQ,SAAS,KAAM,CAAA;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,UAAA,CAAW,GAAG,CAAA;AACd,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,GAAA,EAAa,KAAA,KAAkB;AACnD,IAAA,UAAA,CAAW,CAAC,IAAA,KAAS;AACnB,MAAA,MAAM,IAAA,GAAO,EAAE,GAAG,IAAA,EAAK;AACvB,MAAA,IAAI,KAAA,CAAM,IAAA,EAAK,KAAM,EAAA,EAAI;AACvB,QAAA,OAAO,KAAK,GAAG,CAAA;AAAA,MACjB,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA;AAAA,MACd;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,kBAAA,GAAqB,OAAA;AAAA,IACzB,MAAM,UAAA,IAAc,iBAAA,CAAkB,IAAI,CAAC,CAAA,KAAM,EAAE,GAAG,CAAA;AAAA,IACtD,CAAC,YAAY,iBAAiB;AAAA,GAChC;AACA,EAAA,MAAM,mBAAA,GAAsB,QAAQ,MAAM;AACxC,IAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,EAAC;AACrB,IAAA,MAAM,UACJ,MAAA,CAAO,OAAA,IAAW,MAAM,IAAA,CAAK,IAAI,IAAI,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,MAAA,CAAO,EAAE,MAAA,CAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;AACzF,IAAA,OAAO;AAAA,MACL,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,CAAO,YAAY,KAAA,EAAM;AAAA,MAChD,GAAG,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,MAAS,EAAE,KAAA,EAAO,GAAA,EAAK,KAAA,EAAO,GAAA,EAAI,CAAE;AAAA,KACtD;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,SAAS,CAAC,CAAA;AAEtB,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAM;AAC7B,IAAA,IAAI,MAAA,GAAS,SAAA;AAEb,IAAA,IAAI,MAAA,IAAU,MAAA,IAAU,WAAA,KAAgB,KAAA,EAAO;AAC7C,MAAA,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,CAAC,GAAA,KAAQ,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,IAAK,EAAE,CAAA,KAAM,WAAW,CAAA;AAAA,IAC/E;AAEA,IAAA,IAAI,MAAA,IAAU,UAAA,IAAc,KAAA,CAAM,IAAA,EAAK,EAAG;AACxC,MAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AACnC,MAAA,MAAA,GAAS,MAAA,CAAO,MAAA;AAAA,QAAO,CAAC,GAAA,KACtB,kBAAA,CAAmB,IAAA,CAAK,CAAC,QAAQ,MAAA,CAAO,GAAA,CAAI,GAAc,CAAA,IAAK,EAAE,CAAA,CAAE,WAAA,EAAY,CAAE,QAAA,CAAS,CAAC,CAAC;AAAA,OAC9F;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,OAAA,CAAQ,OAAO,EAAE,MAAA,CAAO,CAAC,GAAG,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,OAAW,EAAE,CAAA;AAC/E,MAAA,IAAI,aAAA,CAAc,SAAS,CAAA,EAAG;AAC5B,QAAA,MAAA,GAAS,MAAA,CAAO,MAAA;AAAA,UAAO,CAAC,QACtB,aAAA,CAAc,KAAA,CAAM,CAAC,CAAC,GAAA,EAAK,SAAS,CAAA,KAAM;AACxC,YAAA,MAAM,YAAY,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,IAAK,EAAE,EAAE,WAAA,EAAY;AACrD,YAAA,OAAO,SAAA,CAAU,QAAA,CAAS,SAAA,CAAU,WAAA,EAAa,CAAA;AAAA,UACnD,CAAC;AAAA,SACH;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA,EAAG,CAAC,SAAA,EAAW,OAAA,EAAS,MAAA,EAAQ,QAAQ,WAAA,EAAa,UAAA,EAAY,KAAA,EAAO,kBAAkB,CAAC,CAAA;AAE3F,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAM;AAC3B,IAAA,IAAI,CAAC,SAAS,OAAO,QAAA;AACrB,IAAA,MAAM,MAAM,iBAAA,CAAkB,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,OAAO,CAAA;AAC3D,IAAA,IAAI,CAAC,GAAA,EAAK,QAAA,EAAU,OAAO,QAAA;AAE3B,IAAA,OAAO,CAAC,GAAG,QAAQ,EAAE,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AAClC,MAAA,MAAM,IAAA,GAAO,EAAE,OAAO,CAAA;AACtB,MAAA,MAAM,IAAA,GAAO,EAAE,OAAO,CAAA;AAEtB,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,SAAS,QAAA,EAAU;AACxD,QAAA,OAAO,OAAA,KAAY,KAAA,GAAQ,IAAA,GAAO,IAAA,GAAO,IAAA,GAAO,IAAA;AAAA,MAClD;AAEA,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,IAAQ,EAAE,CAAA;AAC9B,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,IAAQ,EAAE,CAAA;AAC9B,MAAA,MAAM,GAAA,GAAM,KAAK,aAAA,CAAc,IAAA,EAAM,MAAM,EAAE,OAAA,EAAS,MAAM,CAAA;AAC5D,MAAA,OAAO,OAAA,KAAY,KAAA,GAAQ,GAAA,GAAM,CAAC,GAAA;AAAA,IACpC,CAAC,CAAA;AAAA,EACH,GAAG,CAAC,QAAA,EAAU,OAAA,EAAS,OAAA,EAAS,iBAAiB,CAAC,CAAA;AAElD,EAAA,MAAM,SAAA,GAAY,iBAAA,GAAoB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,iBAAiB,CAAC,CAAA,GAAI,CAAA;AAClG,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,SAAS,CAAA;AAC5C,EAAA,MAAM,QAAA,GAAW,oBACb,MAAA,CAAO,KAAA,CAAA,CAAO,cAAc,CAAA,IAAK,iBAAA,EAAmB,WAAA,GAAc,iBAAiB,CAAA,GACnF,MAAA;AAEJ,EAAA,MAAM,cAAA,GAAiB,MAAM,WAAA,CAAY,EAAE,CAAA;AAC3C,EAAA,MAAM,iBAAA,GACJ,QAAA,CAAS,MAAA,GAAS,CAAA,IAAK,SAAS,KAAA,CAAM,CAAC,GAAA,EAAK,CAAA,KAAM,SAAS,QAAA,CAAS,SAAA,CAAU,GAAA,EAAK,CAAC,CAAC,CAAC,CAAA;AACxF,EAAA,MAAM,kBAAA,GACJ,CAAC,iBAAA,IAAqB,QAAA,CAAS,KAAK,CAAC,GAAA,EAAK,CAAA,KAAM,QAAA,CAAS,QAAA,CAAS,SAAA,CAAU,GAAA,EAAK,CAAC,CAAC,CAAC,CAAA;AAEtF,EAAA,MAAM,kBAAkB,MAAM;AAC5B,IAAA,MAAM,OAAA,GAAU,SAAS,GAAA,CAAI,CAAC,KAAK,CAAA,KAAM,SAAA,CAAU,GAAA,EAAK,CAAC,CAAC,CAAA;AAC1D,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,WAAA,CAAY,CAAC,IAAA,KAAS,IAAA,CAAK,MAAA,CAAO,CAAC,EAAA,KAAO,CAAC,OAAA,CAAQ,QAAA,CAAS,EAAE,CAAC,CAAC,CAAA;AAAA,IAClE,CAAA,MAAO;AACL,MAAA,WAAA,CAAY,CAAC,IAAA,KAAS,KAAA,CAAM,IAAA,iBAAK,IAAI,GAAA,CAAI,CAAC,GAAG,IAAA,EAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAAA,IAClE;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,EAAA,KAAwB;AACzC,IAAA,WAAA,CAAY,CAAC,IAAA,KAAU,IAAA,CAAK,QAAA,CAAS,EAAE,IAAI,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,MAAM,EAAE,CAAA,GAAI,CAAC,GAAG,IAAA,EAAM,EAAE,CAAE,CAAA;AAAA,EAC1F,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAK,KAAM,EAAE,CAAA;AAC3E,EAAA,MAAM,oBAAoB,iBAAA,CAAkB,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,UAAU,CAAA;AACtE,EAAA,MAAM,UAAU,iBAAA,CAAkB,MAAA,IAAU,aAAa,CAAA,GAAI,CAAA,CAAA,IAAM,UAAU,CAAA,GAAI,CAAA,CAAA;AAEjF,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,UAAA,IAAc,MAAM,CAAA;AAEjD,EAAA,MAAM,aAAA,GAAgB,4EAAA;AACtB,EAAA,MAAM,kBAAA,GACJ,4FAAA;AAEF,EAAA,MAAM,WAAA,GAAc,CAAC,QAAA,EAAkB,UAAA,KACrC,EAAA;AAAA,IACE,iCAAA;AAAA,IACA,YAAA,KAAiB,aAAa,kBAAA,GAAqB,qCAAA,CAAA;AAAA,IACnD,UAAA,IAAc,aAAA;AAAA,IACd,CAAC,UAAA,IAAc,OAAA,IAAW,QAAA,GAAW,MAAM,CAAA,IAAK,sBAAA;AAAA,IAChD,CAAC,UAAA,IAAc,EAAE,WAAW,QAAA,GAAW,CAAA,KAAM,MAAM,MAAA,IAAU;AAAA,GAC/D;AAEF,EAAA,MAAM,cAAA,GAAiB,CAAC,IAAA,KACtB,QAAA,IACA,EAAA;AAAA,IACE,cAAA;AAAA,IACA,SAAS,OAAA,IAAW,kDAAA;AAAA,IACpB,IAAA,KAAS,OAAA,IAAW,EAAA,CAAG,kDAAA,EAAoD,cAAc,SAAS,CAAA;AAAA,IAClG,SAAS,SAAA,IAAa;AAAA,GACxB;AAEF,EAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAClB,QAAA,IACA,EAAA;AAAA,IACE,mCAAA;AAAA,IACA,SAAS,OAAA,IAAW,kDAAA;AAAA,IACpB,IAAA,KAAS,OAAA,IAAW,EAAA,CAAG,kDAAA,EAAoD,cAAc,SAAS,CAAA;AAAA,IAClG,SAAS,SAAA,IAAa;AAAA,GACxB;AAEF,EAAA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAwC,cAAA,CAAe,IAAI,CAAA;AAE/E,EAAA,MAAM,aAAA,GAAgB,CAAC,GAAA,KAAgB;AACrC,IAAA,MAAM,SAAS,OAAA,KAAY,GAAA;AAC3B,IAAA,uBACE,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,EAAA;AAAA,QACN,SAAA,EAAW,EAAA;AAAA,UACT,6CAAA;AAAA,UACA,SAAS,kCAAA,GAAqC,YAAA;AAAA,UAC9C,MAAA,IAAU,YAAY,MAAA,IAAU;AAAA,SAClC;AAAA,QACA,aAAA,EAAW;AAAA;AAAA,KACb;AAAA,EAEJ,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,CAAC,GAAA,EAA0B,WAAA,EAAqB,QAAA,KAAqB;AACtF,IAAA,MAAM,0BACJ,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,GAAA,CAAI,KAAA;AAAA,MACJ,IAAI,QAAA,KAAa,MAAA,GAAS,cAAc,GAAA,CAAI,GAAG,oBAC9C,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,QAAA,OAAA,KAAY,GAAA,CAAI,uBACf,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,sBAAA,EAAwB,QAAA,EAAA,OAAA,KAAY,KAAA,GAAQ,QAAA,GAAM,QAAA,EAAI,CAAA;AAAA,QAEvE,YAAY,GAAA,CAAI,GAAA,wBAAQ,MAAA,EAAA,EAAK,SAAA,EAAU,+BAA8B,QAAA,EAAA,QAAA,EAAC;AAAA,OAAA,EACzE,CAAA,CAAA;AAAA,MAED,CAAC,MAAA,IAAU,GAAA,CAAI,UAAA,oBACd,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAM,IAAA;AAAA,UACN,MAAA,EAAO,IAAA;AAAA,UACP,OAAA,EAAQ,WAAA;AAAA,UACR,IAAA,EAAK,MAAA;AAAA,UACL,MAAA,EAAO,cAAA;AAAA,UACP,WAAA,EAAY,GAAA;AAAA,UACZ,aAAA,EAAc,OAAA;AAAA,UACd,cAAA,EAAe,OAAA;AAAA,UACf,WAAW,EAAA,CAAG,YAAA,EAAc,QAAQ,GAAA,CAAI,GAAG,KAAK,kCAAkC,CAAA;AAAA,UAClF,aAAA,EAAW,IAAA;AAAA,UAEX,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,CAAA,EAAE,oFAAA,EAAqF;AAAA;AAAA;AAC/F,KAAA,EAEJ,CAAA;AAEF,IAAA,MAAM,gBAAA,GAAmB,EAAA;AAAA,MACvB,gCAAA;AAAA,MACA,GAAA,CAAI,UAAU,OAAA,IAAW,oBAAA;AAAA,MACzB,GAAA,CAAI,UAAU,QAAA,IAAY;AAAA,KAC5B;AACA,IAAA,uBACE,GAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QAEC,SAAA,EAAW,EAAA;AAAA,UACT,iHAAA;AAAA,UACA,GAAA,CAAI,UAAU,OAAA,IAAW,YAAA;AAAA,UACzB,GAAA,CAAI,UAAU,QAAA,IAAY,aAAA;AAAA,UAC1B,WAAA,KAAgB,CAAA,IAAK,UAAA,CAAW,OAAO;AAAA,SACzC;AAAA,QACA,KAAA,EAAO,GAAA,CAAI,KAAA,GAAQ,EAAE,KAAA,EAAO,IAAI,KAAA,EAAO,QAAA,EAAU,GAAA,CAAI,KAAA,EAAM,GAAI,MAAA;AAAA,QAC/D,WAAA,EAAW,GAAA,CAAI,QAAA,GAAY,OAAA,KAAY,GAAA,CAAI,MAAO,OAAA,KAAY,KAAA,GAAQ,WAAA,GAAc,YAAA,GAAgB,MAAA,GAAU,MAAA;AAAA,QAE7G,cAAI,QAAA,mBACH,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,MAAM,UAAA,CAAW,GAAA,CAAI,GAAG,CAAA;AAAA,YACjC,SAAA,EAAW,EAAA;AAAA,cACT,gBAAA;AAAA,cACA;AAAA,aACF;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA,SACH,mBAEA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,kBAAmB,QAAA,EAAA,OAAA,EAAQ;AAAA,OAAA;AAAA,MAtBzC;AAAA,KAwBP;AAAA,EAEJ,CAAA;AAEA,EAAA,MAAM,YAAY,MAAM;AACtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,2BACG,OAAA,EAAA,EACE,QAAA,EAAA,KAAA,CAAM,IAAA,CAAK,EAAE,QAAQ,YAAA,EAAc,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,qBAC5C,IAAA,CAAC,IAAA,EAAA,EAAW,WAAU,oCAAA,EACnB,QAAA,EAAA;AAAA,QAAA,UAAA,wBACE,IAAA,EAAA,EAAG,SAAA,EAAW,EAAA,CAAG,2BAAA,EAA6B,YAAY,CAAA,EAAG,KAAK,CAAA,EAAG,UAAA,CAAW,OAAO,CAAC,CAAA,EACvF,8BAAC,QAAA,EAAA,EAAS,SAAA,EAAU,mBAAkB,CAAA,EACxC,CAAA;AAAA,QAED,iBAAA,CAAkB,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,qBAC3B,GAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YAEC,SAAA,EAAW,EAAA;AAAA,cACT,+BAAA;AAAA,cACA,GAAA,CAAI,UAAU,OAAA,IAAW,YAAA;AAAA,cACzB,WAAA,CAAY,GAAG,KAAK,CAAA;AAAA,cACpB,CAAA,KAAM,CAAA,IAAK,UAAA,CAAW,OAAO;AAAA,aAC/B;AAAA,YAEA,QAAA,kBAAA,GAAA,CAAC,YAAS,SAAA,EAAW,EAAA,CAAG,OAAO,CAAA,KAAM,CAAA,GAAI,SAAA,GAAY,SAAS,CAAA,EAAG;AAAA,WAAA;AAAA,UAR5D,cAAA,CAAe,KAAK,CAAC;AAAA,SAU7B,CAAA;AAAA,QACA,2BACC,GAAA,CAAC,IAAA,EAAA,EAAG,WAAW,EAAA,CAAG,2BAAA,EAA6B,YAAY,CAAA,EAAG,KAAK,CAAA,EAAG,UAAA,CAAW,SAAS,CAAC,CAAA,EACzF,8BAAC,QAAA,EAAA,EAAS,SAAA,EAAU,mBAAkB,CAAA,EACxC;AAAA,OAAA,EAAA,EAtBK,CAwBT,CACD,CAAA,EACH,CAAA;AAAA,IAEJ;AAEA,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,IAAI,UAAU,UAAA,EAAY;AACxB,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,2BACG,OAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,QAAG,OAAA,EAAkB,SAAA,EAAU,wDAAA,EAC7B,QAAA,EAAA,gBAAA,IAAoB,SAAS,WAAA,KAAgB,KAAA,GAAQ,kBAAA,GAAqB,YAAA,EAC7E,GACF,CAAA,EACF,CAAA;AAAA,IAEJ;AAEA,IAAA,2BACG,OAAA,EAAA,EACE,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,KAAK,CAAA,KAAM;AACxB,MAAA,MAAM,EAAA,GAAK,SAAA,CAAU,GAAA,EAAK,CAAC,CAAA;AAC3B,MAAA,MAAM,UAAA,GAAa,QAAA,CAAS,QAAA,CAAS,EAAE,CAAA;AACvC,MAAA,uBACE,IAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAW,EAAA;AAAA,YACT,6CAAA;AAAA,YACA,UAAA,IAAc;AAAA,WAChB;AAAA,UACA,OAAA,EAAS,MAAM,UAAA,GAAa,GAAG,CAAA;AAAA,UAE9B,QAAA,EAAA;AAAA,YAAA,UAAA,oBACC,GAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,GAAG,2BAAA,EAA6B,WAAA,CAAY,GAAG,UAAU,CAAA,EAAG,UAAA,CAAW,OAAO,CAAC,CAAA;AAAA,gBAC1F,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,gBAElC,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAS,OAAA,EAAS,UAAA,EAAY,QAAA,EAAU,MAAM,SAAA,CAAU,EAAE,CAAA,EAAG,YAAA,EAAY,CAAA,WAAA,EAAc,EAAE,CAAA,CAAA,EAAI;AAAA;AAAA,aAChG;AAAA,YAED,iBAAA,CAAkB,GAAA,CAAI,CAAC,GAAA,EAAK,WAAA,KAAgB;AAC3C,cAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AACzB,cAAA,uBACE,GAAA;AAAA,gBAAC,IAAA;AAAA,gBAAA;AAAA,kBAEC,SAAA,EAAW,EAAA;AAAA,oBACT,4DAAA;AAAA,oBACA,GAAA,CAAI,UAAU,OAAA,IAAW,YAAA;AAAA,oBACzB,GAAA,CAAI,UAAU,QAAA,IAAY,aAAA;AAAA,oBAC1B,CAAC,IAAI,MAAA,IAAU,mBAAA;AAAA,oBACf,WAAA,CAAY,GAAG,UAAU,CAAA;AAAA,oBACzB,WAAA,KAAgB,CAAA,IAAK,UAAA,CAAW,OAAO;AAAA,mBACzC;AAAA,kBACA,KAAA,EAAO,GAAA,CAAI,KAAA,GAAQ,EAAE,KAAA,EAAO,IAAI,KAAA,EAAO,QAAA,EAAU,GAAA,CAAI,KAAA,EAAM,GAAI,MAAA;AAAA,kBAE9D,QAAA,EAAA,GAAA,CAAI,SAAS,GAAA,CAAI,MAAA,CAAO,OAAO,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,QAAG;AAAA,iBAAA;AAAA,gBAXrD,cAAA,CAAe,KAAK,WAAW;AAAA,eAYtC;AAAA,YAEJ,CAAC,CAAA;AAAA,YACA,OAAA,oBACC,GAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,GAAG,yBAAA,EAA2B,WAAA,CAAY,GAAG,UAAU,CAAA,EAAG,UAAA,CAAW,SAAS,CAAC,CAAA;AAAA,gBAC1F,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,gBAElC,+BAAC,YAAA,EAAA,EACC,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,mBAAA,EAAA,EAAoB,SAAO,IAAA,EAC1B,QAAA,kBAAA,GAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,IAAA,EAAK,QAAA;AAAA,sBACL,SAAA,EAAU,6LAAA;AAAA,sBACV,YAAA,EAAW,aAAA;AAAA,sBAEX,QAAA,kBAAA,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA,mBAC5B,EACF,CAAA;AAAA,kCACA,GAAA,CAAC,mBAAA,EAAA,EAAoB,KAAA,EAAM,KAAA,EACxB,QAAA,EAAA,OAAA,CAAQ,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,KAAU;AACjC,oBAAA,IAAI,IAAA,CAAK,GAAA,EAAK,uBAAO,GAAA,CAAC,2BAA2B,KAAO,CAAA;AACxD,oBAAA,IAAI,KAAK,OAAA,EAAS,2BAAQ,iBAAA,EAAA,EAA+B,QAAA,EAAA,IAAA,CAAK,SAAb,KAAmB,CAAA;AACpE,oBAAA,4BACG,gBAAA,EAAA,EAA6B,QAAA,EAAU,MAAM,IAAA,CAAK,YAAW,EAC3D,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAK,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,kBAAA,EAAoB,QAAA,EAAA,IAAA,CAAK,MAAK,CAAA,GAAU,IAAA;AAAA,sBACpE,IAAA,CAAK;AAAA,qBAAA,EAAA,EAFe,KAGvB,CAAA;AAAA,kBAEJ,CAAC,CAAA,EACH;AAAA,iBAAA,EACF;AAAA;AAAA;AACF;AAAA,SAAA;AAAA,QA9DG;AAAA,OAgEP;AAAA,IAEJ,CAAC,CAAA,EACH,CAAA;AAAA,EAEJ,CAAA;AAEA,EAAA,MAAM,KAAA,wBACH,OAAA,EAAA,EAAM,SAAA,EAAU,kBAAiB,KAAA,EAAO,EAAE,QAAA,EAAU,aAAA,EAAc,EACjE,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,OAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,IAAA,EAAA,EAAG,WAAU,yDAAA,EACX,QAAA,EAAA;AAAA,QAAA,UAAA,oBACC,GAAA,CAAC,QAAG,SAAA,EAAW,EAAA,CAAG,6BAA6B,UAAA,CAAW,OAAO,CAAC,CAAA,EAChE,QAAA,kBAAA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAS,iBAAA;AAAA,YACT,aAAA,EAAe,kBAAA;AAAA,YACf,QAAA,EAAU,eAAA;AAAA,YACV,YAAA,EAAW;AAAA;AAAA,SACb,EACF,CAAA;AAAA,QAED,iBAAA,CAAkB,GAAA;AAAA,UAAI,CAAC,KAAK,WAAA,KAC3B,UAAA,CAAW,KAAK,WAAA,EAAa,cAAA,CAAe,GAAA,EAAK,WAAW,CAAC;AAAA,SAC/D;AAAA,QACC,OAAA,oBAAW,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAW,EAAA,CAAG,eAAA,EAAiB,UAAA,CAAW,SAAS,CAAC,CAAA,EAAG,YAAA,EAAW,aAAA,EAAc;AAAA,OAAA,EAClG,CAAA;AAAA,MACC,CAAC,MAAA,IAAU,iBAAA,CAAkB,MAAA,GAAS,CAAA,oBACrC,GAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,oCAAA,EACX,QAAA,EAAA,iBAAA,CAAkB,GAAA,CAAI,CAAC,KAAK,WAAA,qBAC3B,GAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAW,EAAA,CAAG,aAAA,EAAe,GAAA,CAAI,KAAA,KAAU,WAAW,YAAA,EAAc,GAAA,CAAI,KAAA,KAAU,QAAA,IAAY,aAAa,CAAA;AAAA,UAE1G,cAAI,UAAA,mBACH,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,MAAA;AAAA,cACL,WAAA,EAAa,CAAA,OAAA,EAAU,GAAA,CAAI,KAAA,CAAM,aAAa,CAAA,MAAA,CAAA;AAAA,cAC9C,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,IAAK,EAAA;AAAA,cAC3B,QAAA,EAAU,CAAC,CAAA,KAAM,YAAA,CAAa,IAAI,GAAA,EAAK,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,cACrD,SAAA,EAAW,EAAA;AAAA,gBACT,wEAAA;AAAA,gBACA,sHAAA;AAAA,gBACA;AAAA;AACF;AAAA,WACF,GACE;AAAA,SAAA;AAAA,QAfC,cAAA,CAAe,KAAK,WAAW;AAAA,OAiBvC,CAAA,EACH;AAAA,KAAA,EAEJ,CAAA;AAAA,IACC,SAAA;AAAU,GAAA,EACb,CAAA;AAGF,EAAA,MAAM,iBAAA,GAAoB,WAAA,CAAY,GAAA,CAAI,CAAC,QAAQ,KAAA,qBACjD,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MAEC,OAAA,EAAS,OAAO,OAAA,IAAW,OAAA;AAAA,MAC3B,IAAA,EAAK,IAAA;AAAA,MACL,OAAA,EAAS,MAAM,MAAA,CAAO,KAAA,CAAM,UAAU,cAAc,CAAA;AAAA,MAEnD,QAAA,EAAA;AAAA,QAAA,MAAA,CAAO,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,6BAAA,EAA+B,QAAA,EAAA,MAAA,CAAO,MAAK,CAAA,GAAU,IAAA;AAAA,QACnF,MAAA,CAAO;AAAA;AAAA,KAAA;AAAA,IANH;AAAA,GAQR,CAAA;AAED,EAAA,MAAM,WAAA,mBACJ,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,+EAAA;AAAA,MACV,IAAA,EAAK,QAAA;AAAA,MACL,YAAA,EAAW,kBAAA;AAAA,MACX,QAAA,EAAU,WAAW,CAAA,GAAI,MAAA;AAAA,MAExB,QAAA,EAAA;AAAA;AAAA,GACH;AAGF,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,uBACE,GAAA,CAAC,SAAI,GAAA,EAAK,YAAA,EAAc,WAAW,EAAA,CAAG,4DAAA,EAA8D,SAAS,CAAA,EAC1G,QAAA,EAAA,WAAA,EACH,CAAA;AAAA,EAEJ;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,OAAA,IAAW,MAAA,CAAO,MAAA,KAAW,CAAA;AAEhD,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,YAAA;AAAA,MACL,SAAA,EAAW,EAAA;AAAA,QACT,iFAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEE,QAAA,EAAA;AAAA,QAAA,CAAA,UAAA,IAAc,MAAA,IAAU,YAAA,qBACxB,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,qHAAA,EACZ,QAAA,EAAA;AAAA,UAAA,UAAA,oBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mIAAA,EACb,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,UAAO,IAAA,EAAM,EAAA,EAAI,SAAA,EAAU,mCAAA,EAAoC,eAAW,IAAA,EAAC,CAAA;AAAA,4BAC5E,GAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBACC,KAAA,EAAO,KAAA;AAAA,gBACP,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,kBAAA,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AACvB,kBAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,gBACX,CAAA;AAAA,gBACA,WAAA,EAAa,iBAAA;AAAA,gBACb,SAAA,EAAU;AAAA;AAAA,aACZ;AAAA,YACC,KAAA,oBACC,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,OAAA,EAAS,MAAM,QAAA,CAAS,EAAE,CAAA;AAAA,gBAC1B,SAAA,EAAU,2DAAA;AAAA,gBACV,YAAA,EAAW,cAAA;AAAA,gBAEX,QAAA,kBAAA,GAAA,CAAC,CAAA,EAAA,EAAE,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA;AACf,WAAA,EAEJ,CAAA;AAAA,UAAA,CAEA,UAAU,YAAA,qBACV,IAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,EAAA;AAAA,gBACT,iCAAA;AAAA,gBACA,SAAS,iBAAA,GAAoB,aAAA;AAAA,gBAC7B;AAAA,eACF;AAAA,cAEC,QAAA,EAAA;AAAA,gBAAA,MAAA,KACE,MAAA,CAAO,YAAY,UAAA,mBAClB,GAAA;AAAA,kBAAC,QAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO,WAAA;AAAA,oBACP,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,sBAAA,cAAA,CAAe,KAAK,CAAA;AACpB,sBAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,oBACX,CAAA;AAAA,oBACA,OAAA,EAAS,mBAAA;AAAA,oBACT,WAAA,EAAa,MAAA,CAAO,WAAA,IAAe,MAAA,CAAO,QAAA,IAAY,cAAA;AAAA,oBACtD,SAAA,EAAU;AAAA;AAAA,iBACZ,mBAEA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6CAAA,EACb,QAAA,kBAAA,GAAA;AAAA,kBAAC,SAAA;AAAA,kBAAA;AAAA,oBACC,IAAA,EAAK,IAAA;AAAA,oBACL,KAAA,EAAO,WAAA;AAAA,oBACP,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,sBAAA,cAAA,CAAe,KAAK,CAAA;AACpB,sBAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,oBACX,CAAA;AAAA,oBACA,OAAA,EAAS;AAAA;AAAA,iBACX,EACF,CAAA,CAAA;AAAA,gBAEH,+BAAe,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,UAAA,EAAY,wBAAa,CAAA,GAAS;AAAA;AAAA;AAAA;AACnE,SAAA,EAEJ,CAAA;AAAA,QAGD,cAAc,QAAA,CAAS,MAAA,GAAS,qBAC/B,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,4HAAA,EACb,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,MAAA,EAAA,EAAK,WAAU,4DAAA,EACd,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,KAAA,EAAA,EAAM,OAAA,EAAQ,QAAA,EAAU,QAAA,EAAA,QAAA,CAAS,MAAA,EAAO,CAAA;AAAA,YAAQ;AAAA,WAAA,EAEnD,CAAA;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mCAAA,EACZ,QAAA,EAAA,WAAA,CAAY,MAAA,GAAS,CAAA,mBACpB,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAK,IAAA,EAAM,QAAA,EAAA,iBAAA,EAAkB,IAE1C,iBAAA,EAEJ;AAAA,SAAA,EACF,CAAA;AAAA,QAGD,SAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,KAAA,EAAO,QAAA,EAAA,UAAA,oBAAc,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oDAAA,EAAqD,QAAA,EAAA,aAAA,EAAW,CAAA,EAAO,CAAA,GAE1H,WAAA;AAAA,QAGD,CAAC,WAAW,MAAA,CAAO,MAAA,GAAS,qBAC3B,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,+GAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,kCAAA,EACb,QAAA,EAAA,iBAAA,GACG,CAAA,QAAA,EAAA,CAAY,WAAA,GAAc,CAAA,IAAK,iBAAA,GAAoB,CAAC,CAAA,MAAA,EAAA,CAAK,WAAA,GAAc,CAAA,IAAK,iBAAA,GAAoB,QAAA,CAAS,MAAM,CAAA,IAAA,EAAO,MAAA,CAAO,MAAM,CAAA,CAAA,GACnI,CAAA,EAAG,MAAA,CAAO,MAAM,CAAA,IAAA,EAAO,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,EAAA,GAAK,GAAG,CAAA,CAAA,EAC3D,CAAA;AAAA,0BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mCAAA,EACZ,QAAA,EAAA;AAAA,YAAA,MAAA;AAAA,YACA,oBAAA,oBACC,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,KAAA,EAAM,MAAA;AAAA,gBACN,MAAA,EAAO,QAAA;AAAA,gBACP,IAAA,EAAK,IAAA;AAAA,gBACL,KAAA,EAAO,OAAO,cAAc,CAAA;AAAA,gBAC5B,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,kBAAA,iBAAA,CAAkB,MAAA,CAAO,KAAK,CAAC,CAAA;AAC/B,kBAAA,OAAA,CAAQ,CAAC,CAAA;AAAA,gBACX,CAAA;AAAA,gBACA,OAAA,EAAS,uBAAA,CAAwB,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,kBAC9C,KAAA,EAAO,OAAO,IAAI,CAAA;AAAA,kBAClB,KAAA,EAAO,OAAO,IAAI;AAAA,iBACpB,CAAE,CAAA;AAAA,gBACF,SAAA,EAAU;AAAA;AAAA,aACZ;AAAA,YAED,iBAAA,IAAqB,SAAA,GAAY,CAAA,oBAChC,GAAA,CAAC,UAAA,EAAA,EAAW,IAAA,EAAM,WAAA,EAAa,SAAA,EAAsB,QAAA,EAAU,OAAA,EAAS,YAAA,EAAc,CAAA,EAAG;AAAA,WAAA,EAE7F;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GAEJ;AAEJ;AAEA,IAAM,SAAA,GAAY,UAAA,CAAW,CAC3B,KAAA,EACA,GAAA,qBACG,GAAA,CAAC,cAAA,EAAA,EAAgB,GAAG,KAAA,EAAO,YAAA,EAAc,GAAA,EAAK,CAAE;AAErD,SAAA,CAAU,WAAA,GAAc,WAAA","file":"chunk-5ZNSG4BJ.js","sourcesContent":["'use client'\n\nimport { useState, useMemo, forwardRef, type ReactNode } from 'react'\nimport { ChevronDown, MoreHorizontal, Search, X } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from './Badge'\nimport { Button } from './Button'\nimport { ButtonGroup } from './ButtonGroup'\nimport { Checkbox } from './Checkbox'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './DropdownMenu'\nimport { Pagination } from './Pagination'\nimport { Segmented } from './Segmented'\nimport { Combobox } from './Combobox'\nimport { Select } from './Select'\nimport { Skeleton } from './Skeleton'\n\n/* ──────────────────── Sortable & Filterable Data Table ──────────────────── */\n\n/**\n * Cell renderer — always `(value, row)`. `value` is `row[key]`; ignore it\n * (`(_value, row) => …`) when you only need the row.\n * Before 4.8.40 a single-parameter `(row) => …` form was auto-detected from\n * `fn.length`; that variant was removed because default/rest parameters made\n * detection unreliable.\n */\nexport type ColumnRender<T> = (value: unknown, row: T) => ReactNode\n\nexport interface Column<T> {\n key: string\n /** Column heading — use `label` or `header` (alias). */\n label?: string\n header?: string\n sortable?: boolean\n filterable?: boolean\n align?: 'left' | 'center' | 'right'\n width?: string\n render?: ColumnRender<T>\n}\n\ntype NormalizedColumn<T> = Omit<Column<T>, 'label' | 'header'> & {\n label: string\n}\n\nfunction normalizeColumns<T>(columns: Column<T>[]): NormalizedColumn<T>[] {\n return columns.map(({ header, label, ...col }) => ({\n ...col,\n label: label ?? header ?? col.key,\n }))\n}\n\nfunction columnReactKey(col: { key: string }, index: number) {\n return `${col.key}-${index}`\n}\n\nexport interface DataTableFilter {\n key: string\n options?: string[]\n allLabel?: string\n /** Toolbar control — `segmented` (default) or searchable `combobox`. */\n variant?: 'segmented' | 'combobox'\n /** Combobox placeholder when `variant` is `combobox`. */\n placeholder?: string\n}\n\nexport interface DataTableBulkAction {\n label: string\n variant?: 'primary' | 'secondary' | 'ghost' | 'ghost-destructive' | 'ghost-success' | 'ghost-warning' | 'ghost-info' | 'destructive' | 'terminal'\n icon?: ReactNode\n onRun: (selectedIds: (string | number)[], clearSelection: () => void) => void\n}\n\nexport interface DataTableRowMenuItem {\n label: string\n onSelect?: () => void\n icon?: ReactNode\n sep?: boolean\n heading?: boolean\n}\n\nexport interface DataTableProps<T extends Record<string, unknown>> {\n columns: Column<T>[]\n /** Row data — use `data` or `rows` (alias). */\n data?: T[]\n rows?: T[]\n /** Row key extractor — defaults to `row.id` or `row.key` */\n rowKey?: keyof T | ((row: T) => string | number)\n onRowClick?: (row: T) => void\n className?: string\n emptyMessage?: string\n /** Global search across `searchKeys` (or all column keys). */\n searchable?: boolean\n searchKeys?: (keyof T | string)[]\n searchPlaceholder?: string\n /** Column filter in rich mode — segmented tabs or searchable combobox. */\n filter?: DataTableFilter\n selectable?: boolean\n /** Rows per page — fixed unless `pageSizeOptions` is set. */\n pageSize?: number\n /** Lets users change rows per page from the footer (initial value from `pageSize` or first option). */\n pageSizeOptions?: number[]\n bulkActions?: DataTableBulkAction[]\n rowMenu?: (row: T) => DataTableRowMenuItem[]\n toolbarRight?: ReactNode\n emptyState?: ReactNode\n loading?: boolean\n skeletonRows?: number\n /** Alternating row backgrounds in rich mode. */\n striped?: boolean\n /** Pin the first column and row-menu column during horizontal scroll (default: on in rich mode). */\n pinColumns?: boolean\n /** Extra content in the table footer bar (right of the row count). */\n footer?: ReactNode\n}\n\ninterface DataTableInnerProps<T extends Record<string, unknown>> extends DataTableProps<T> {\n forwardedRef?: React.Ref<HTMLDivElement>\n}\n\nfunction DataTableInner<T extends Record<string, unknown>>({\n columns,\n data,\n rows,\n rowKey,\n onRowClick,\n className,\n emptyMessage = 'No data',\n searchable = false,\n searchKeys,\n searchPlaceholder = 'Search…',\n filter,\n selectable = false,\n pageSize,\n pageSizeOptions,\n bulkActions = [],\n rowMenu,\n toolbarRight,\n emptyState,\n loading = false,\n skeletonRows = 5,\n striped = false,\n footer,\n pinColumns,\n forwardedRef,\n}: DataTableInnerProps<T>) {\n const normalizedColumns = useMemo(() => normalizeColumns(columns), [columns])\n const tableData = useMemo(() => rows ?? data ?? [], [rows, data])\n\n const pageSizeOptionsResolved = useMemo(\n () => [...new Set(pageSizeOptions ?? [])].filter((n) => n > 0).sort((a, b) => a - b),\n [pageSizeOptions],\n )\n const allowsPageSizeChange = pageSizeOptionsResolved.length > 0\n const [activePageSize, setActivePageSize] = useState(\n pageSize ?? pageSizeOptionsResolved[0] ?? 10,\n )\n const effectivePageSize = allowsPageSizeChange ? activePageSize : pageSize\n\n const isRich =\n searchable ||\n filter ||\n selectable ||\n pageSize !== undefined ||\n allowsPageSizeChange ||\n !!rowMenu ||\n !!toolbarRight ||\n bulkActions.length > 0 ||\n !!emptyState ||\n loading\n\n const pinEdges = pinColumns ?? isRich\n const tableMinWidth = Math.max(640, normalizedColumns.length * 120 + (selectable ? 40 : 0) + (rowMenu ? 44 : 0))\n\n const [sortKey, setSortKey] = useState<string | null>(null)\n const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc')\n const [filters, setFilters] = useState<Record<string, string>>({})\n const [query, setQuery] = useState('')\n const [filterValue, setFilterValue] = useState('all')\n const [selected, setSelected] = useState<(string | number)[]>([])\n const [page, setPage] = useState(1)\n\n const getRowKey = (row: T, i: number): string | number => {\n if (typeof rowKey === 'function') return rowKey(row)\n if (rowKey) return row[rowKey] as string | number\n return (row.id ?? row.key ?? i) as string | number\n }\n\n const toggleSort = (key: string) => {\n if (sortKey === key) {\n setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))\n } else {\n setSortKey(key)\n setSortDir('asc')\n }\n }\n\n const updateFilter = (key: string, value: string) => {\n setFilters((prev) => {\n const next = { ...prev }\n if (value.trim() === '') {\n delete next[key]\n } else {\n next[key] = value\n }\n return next\n })\n }\n\n const searchKeysResolved = useMemo(\n () => searchKeys ?? normalizedColumns.map((c) => c.key),\n [searchKeys, normalizedColumns],\n )\n const filterSelectOptions = useMemo(() => {\n if (!filter) return []\n const options =\n filter.options ?? Array.from(new Set(tableData.map((r) => String(r[filter.key] ?? ''))))\n return [\n { value: 'all', label: filter.allLabel ?? 'All' },\n ...options.map((opt) => ({ value: opt, label: opt })),\n ]\n }, [filter, tableData])\n\n const filtered = useMemo(() => {\n let result = tableData\n\n if (isRich && filter && filterValue !== 'all') {\n result = result.filter((row) => String(row[filter.key] ?? '') === filterValue)\n }\n\n if (isRich && searchable && query.trim()) {\n const q = query.trim().toLowerCase()\n result = result.filter((row) =>\n searchKeysResolved.some((key) => String(row[key as keyof T] ?? '').toLowerCase().includes(q))\n )\n }\n\n if (!isRich) {\n const activeFilters = Object.entries(filters).filter(([, v]) => v.trim() !== '')\n if (activeFilters.length > 0) {\n result = result.filter((row) =>\n activeFilters.every(([key, filterVal]) => {\n const cellValue = String(row[key] ?? '').toLowerCase()\n return cellValue.includes(filterVal.toLowerCase())\n })\n )\n }\n }\n\n return result\n }, [tableData, filters, isRich, filter, filterValue, searchable, query, searchKeysResolved])\n\n const sorted = useMemo(() => {\n if (!sortKey) return filtered\n const col = normalizedColumns.find((c) => c.key === sortKey)\n if (!col?.sortable) return filtered\n\n return [...filtered].sort((a, b) => {\n const aVal = a[sortKey]\n const bVal = b[sortKey]\n\n if (typeof aVal === 'number' && typeof bVal === 'number') {\n return sortDir === 'asc' ? aVal - bVal : bVal - aVal\n }\n\n const aStr = String(aVal ?? '')\n const bStr = String(bVal ?? '')\n const cmp = aStr.localeCompare(bStr, 'nb', { numeric: true })\n return sortDir === 'asc' ? cmp : -cmp\n })\n }, [filtered, sortKey, sortDir, normalizedColumns])\n\n const pageCount = effectivePageSize ? Math.max(1, Math.ceil(sorted.length / effectivePageSize)) : 1\n const currentPage = Math.min(page, pageCount)\n const pageRows = effectivePageSize\n ? sorted.slice((currentPage - 1) * effectivePageSize, currentPage * effectivePageSize)\n : sorted\n\n const clearSelection = () => setSelected([])\n const allOnPageSelected =\n pageRows.length > 0 && pageRows.every((row, i) => selected.includes(getRowKey(row, i)))\n const someOnPageSelected =\n !allOnPageSelected && pageRows.some((row, i) => selected.includes(getRowKey(row, i)))\n\n const toggleAllOnPage = () => {\n const pageIds = pageRows.map((row, i) => getRowKey(row, i))\n if (allOnPageSelected) {\n setSelected((prev) => prev.filter((id) => !pageIds.includes(id)))\n } else {\n setSelected((prev) => Array.from(new Set([...prev, ...pageIds])))\n }\n }\n\n const toggleRow = (id: string | number) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]))\n }\n\n const hasActiveFilters = Object.values(filters).some((v) => v.trim() !== '')\n const filterableColumns = normalizedColumns.filter((c) => c.filterable)\n const colSpan = normalizedColumns.length + (selectable ? 1 : 0) + (rowMenu ? 1 : 0)\n\n const rowHoverable = Boolean(onRowClick || isRich)\n\n const selectedRowBg = 'bg-[color-mix(in_srgb,var(--tollerud-yellow)_7%,var(--tollerud-noir-900))]'\n const selectedRowHoverBg =\n 'group-hover/tr:bg-[color-mix(in_srgb,var(--tollerud-yellow)_12%,var(--tollerud-noir-800))]'\n\n const cellSurface = (rowIndex: number, isSelected: boolean) =>\n cn(\n 'transition-colors duration-fast',\n rowHoverable && (isSelected ? selectedRowHoverBg : 'group-hover/tr:bg-tollerud-noir-800'),\n isSelected && selectedRowBg,\n !isSelected && striped && rowIndex % 2 === 0 && 'bg-tollerud-noir-950',\n !isSelected && !(striped && rowIndex % 2 === 0) && isRich && 'bg-tollerud-noir-900',\n )\n\n const stickyPosition = (edge: 'check' | 'first' | 'actions') =>\n pinEdges &&\n cn(\n 'sticky z-[2]',\n edge === 'check' && 'left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n edge === 'first' && cn('left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]', selectable && 'left-10'),\n edge === 'actions' && 'right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n )\n\n const stickyHead = (edge: 'check' | 'first' | 'actions') =>\n pinEdges &&\n cn(\n 'sticky z-[3] bg-tollerud-noir-900',\n edge === 'check' && 'left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n edge === 'first' && cn('left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]', selectable && 'left-10'),\n edge === 'actions' && 'right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n )\n\n const stickyBody = (edge: 'check' | 'first' | 'actions') => stickyPosition(edge)\n\n const sortIndicator = (key: string) => {\n const active = sortKey === key\n return (\n <ChevronDown\n size={12}\n className={cn(\n 'shrink-0 transition-transform duration-fast',\n active ? 'text-tollerud-yellow opacity-100' : 'opacity-25',\n active && sortDir === 'desc' && 'rotate-180'\n )}\n aria-hidden\n />\n )\n }\n\n const headerCell = (col: NormalizedColumn<T>, columnIndex: number, reactKey: string) => {\n const content = (\n <>\n {col.label}\n {col.sortable && (isRich ? sortIndicator(col.key) : (\n <>\n {sortKey === col.key && (\n <span className=\"text-tollerud-accent\">{sortDir === 'asc' ? '↑' : '↓'}</span>\n )}\n {sortKey !== col.key && <span className=\"text-tollerud-text-muted/30\">↕</span>}\n </>\n ))}\n {!isRich && col.filterable && (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn('opacity-40', filters[col.key] && 'opacity-100 text-tollerud-yellow')}\n aria-hidden\n >\n <path d=\"M4 4h16v2.172a2 2 0 0 1-.586 1.414L15 12v7l-6 2v-8.5L4.52 7.53A2 2 0 0 1 4 6.16V4z\" />\n </svg>\n )}\n </>\n )\n const contentAlignment = cn(\n 'inline-flex items-center gap-1',\n col.align === 'right' && 'justify-end w-full',\n col.align === 'center' && 'justify-center w-full',\n )\n return (\n <th\n key={reactKey}\n className={cn(\n 'px-3 py-2.5 text-left text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider whitespace-nowrap',\n col.align === 'right' && 'text-right',\n col.align === 'center' && 'text-center',\n columnIndex === 0 && stickyHead('first'),\n )}\n style={col.width ? { width: col.width, minWidth: col.width } : undefined}\n aria-sort={col.sortable ? (sortKey === col.key ? (sortDir === 'asc' ? 'ascending' : 'descending') : 'none') : undefined}\n >\n {col.sortable ? (\n <button\n type=\"button\"\n onClick={() => toggleSort(col.key)}\n className={cn(\n contentAlignment,\n 'cursor-pointer select-none uppercase tracking-wider hover:text-tollerud-text-primary transition-colors tollerud-focus-ring rounded-sm',\n )}\n >\n {content}\n </button>\n ) : (\n <span className={contentAlignment}>{content}</span>\n )}\n </th>\n )\n }\n\n const tableBody = () => {\n if (loading) {\n return (\n <tbody>\n {Array.from({ length: skeletonRows }).map((_, i) => (\n <tr key={i} className=\"border-b border-tollerud-border/20\">\n {selectable && (\n <td className={cn('px-3 py-2.5 w-10 min-w-10', cellSurface(i, false), stickyBody('check'))}>\n <Skeleton className=\"h-4 w-4 rounded\" />\n </td>\n )}\n {normalizedColumns.map((col, j) => (\n <td\n key={columnReactKey(col, j)}\n className={cn(\n 'px-3 py-2.5 whitespace-nowrap',\n col.align === 'right' && 'text-right',\n cellSurface(i, false),\n j === 0 && stickyBody('first'),\n )}\n >\n <Skeleton className={cn('h-3', j === 0 ? 'w-[70%]' : 'w-[55%]')} />\n </td>\n ))}\n {rowMenu && (\n <td className={cn('px-3 py-2.5 w-11 min-w-11', cellSurface(i, false), stickyBody('actions'))}>\n <Skeleton className=\"h-4 w-4 rounded\" />\n </td>\n )}\n </tr>\n ))}\n </tbody>\n )\n }\n\n if (sorted.length === 0) {\n if (isRich && emptyState) {\n return null\n }\n return (\n <tbody>\n <tr>\n <td colSpan={colSpan} className=\"px-3 py-8 text-center text-sm text-tollerud-text-muted\">\n {hasActiveFilters || query || filterValue !== 'all' ? 'No matching rows' : emptyMessage}\n </td>\n </tr>\n </tbody>\n )\n }\n\n return (\n <tbody>\n {pageRows.map((row, i) => {\n const id = getRowKey(row, i)\n const isSelected = selected.includes(id)\n return (\n <tr\n key={id}\n className={cn(\n 'group/tr border-b border-tollerud-border/20',\n onRowClick && 'cursor-pointer',\n )}\n onClick={() => onRowClick?.(row)}\n >\n {selectable && (\n <td\n className={cn('px-3 py-2.5 w-10 min-w-10', cellSurface(i, isSelected), stickyBody('check'))}\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox checked={isSelected} onChange={() => toggleRow(id)} aria-label={`Select row ${id}`} />\n </td>\n )}\n {normalizedColumns.map((col, columnIndex) => {\n const value = row[col.key]\n return (\n <td\n key={columnReactKey(col, columnIndex)}\n className={cn(\n 'px-3 py-2.5 text-tollerud-text-secondary whitespace-nowrap',\n col.align === 'right' && 'text-right',\n col.align === 'center' && 'text-center',\n !col.render && 'font-mono text-xs',\n cellSurface(i, isSelected),\n columnIndex === 0 && stickyBody('first'),\n )}\n style={col.width ? { width: col.width, minWidth: col.width } : undefined}\n >\n {col.render ? col.render(value, row) : String(value ?? '—')}\n </td>\n )\n })}\n {rowMenu && (\n <td\n className={cn('px-2 py-2 w-11 min-w-11', cellSurface(i, isSelected), stickyBody('actions'))}\n onClick={(e) => e.stopPropagation()}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-tollerud-text-muted hover:text-tollerud-text-primary hover:bg-tollerud-surface-raised transition-colors tollerud-focus-ring\"\n aria-label=\"Row actions\"\n >\n <MoreHorizontal size={15} />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {rowMenu(row).map((item, index) => {\n if (item.sep) return <DropdownMenuSeparator key={index} />\n if (item.heading) return <DropdownMenuLabel key={index}>{item.label}</DropdownMenuLabel>\n return (\n <DropdownMenuItem key={index} onSelect={() => item.onSelect?.()}>\n {item.icon ? <span className=\"mr-2 inline-flex\">{item.icon}</span> : null}\n {item.label}\n </DropdownMenuItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n </td>\n )}\n </tr>\n )\n })}\n </tbody>\n )\n }\n\n const table = (\n <table className=\"w-full text-sm\" style={{ minWidth: tableMinWidth }}>\n <thead>\n <tr className=\"border-b border-tollerud-border/30 bg-tollerud-noir-900\">\n {selectable && (\n <th className={cn('px-3 py-2.5 w-10 min-w-10', stickyHead('check'))}>\n <Checkbox\n checked={allOnPageSelected}\n indeterminate={someOnPageSelected}\n onChange={toggleAllOnPage}\n aria-label=\"Select all rows on page\"\n />\n </th>\n )}\n {normalizedColumns.map((col, columnIndex) =>\n headerCell(col, columnIndex, columnReactKey(col, columnIndex))\n )}\n {rowMenu && <th className={cn('w-11 min-w-11', stickyHead('actions'))} aria-label=\"Row actions\" />}\n </tr>\n {!isRich && filterableColumns.length > 0 && (\n <tr className=\"border-b border-tollerud-border/20\">\n {normalizedColumns.map((col, columnIndex) => (\n <th\n key={columnReactKey(col, columnIndex)}\n className={cn('px-1.5 py-1', col.align === 'right' && 'text-right', col.align === 'center' && 'text-center')}\n >\n {col.filterable ? (\n <input\n type=\"text\"\n placeholder={`Filter ${col.label.toLowerCase()}…`}\n value={filters[col.key] ?? ''}\n onChange={(e) => updateFilter(col.key, e.target.value)}\n className={cn(\n 'w-full px-2 py-1 text-xs rounded border transition-colors outline-none',\n 'bg-tollerud-noir-900/50 border-tollerud-border/20 text-tollerud-text-primary placeholder:text-tollerud-text-muted/40',\n 'focus:border-tollerud-yellow/40 focus:ring-1 focus:ring-tollerud-yellow/20',\n )}\n />\n ) : null}\n </th>\n ))}\n </tr>\n )}\n </thead>\n {tableBody()}\n </table>\n )\n\n const bulkActionButtons = bulkActions.map((action, index) => (\n <Button\n key={index}\n variant={action.variant ?? 'ghost'}\n size=\"sm\"\n onClick={() => action.onRun(selected, clearSelection)}\n >\n {action.icon ? <span className=\"mr-1.5 inline-flex shrink-0\">{action.icon}</span> : null}\n {action.label}\n </Button>\n ))\n\n const tableScroll = (\n <div\n className=\"tollerud-datatable-scroll min-w-0 w-full overflow-x-auto overscroll-x-contain\"\n role=\"region\"\n aria-label=\"Scrollable table\"\n tabIndex={pinEdges ? 0 : undefined} // eslint-disable-line jsx-a11y/no-noninteractive-tabindex -- keyboard horizontal scroll\n >\n {table}\n </div>\n )\n\n if (!isRich) {\n return (\n <div ref={forwardedRef} className={cn('min-w-0 w-full rounded-lg border border-tollerud-border/30', className)}>\n {tableScroll}\n </div>\n )\n }\n\n const showEmpty = !loading && sorted.length === 0\n\n return (\n <div\n ref={forwardedRef}\n className={cn(\n 'min-w-0 w-full rounded-lg border border-tollerud-border/30 bg-tollerud-noir-900',\n className,\n )}\n >\n {(searchable || filter || toolbarRight) && (\n <div className=\"flex flex-col gap-2.5 border-b border-tollerud-border/30 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between\">\n {searchable && (\n <div className=\"flex h-9 w-full items-center gap-2 rounded-lg border border-tollerud-border/30 bg-tollerud-noir-950 px-3 sm:w-[230px] sm:shrink-0\">\n <Search size={15} className=\"shrink-0 text-tollerud-text-muted\" aria-hidden />\n <input\n value={query}\n onChange={(e) => {\n setQuery(e.target.value)\n setPage(1)\n }}\n placeholder={searchPlaceholder}\n className=\"min-w-0 flex-1 border-none bg-transparent text-sm text-tollerud-text-primary outline-none placeholder:text-tollerud-text-muted\"\n />\n {query && (\n <button\n type=\"button\"\n onClick={() => setQuery('')}\n className=\"text-tollerud-text-muted hover:text-tollerud-text-primary\"\n aria-label=\"Clear search\"\n >\n <X size={13} />\n </button>\n )}\n </div>\n )}\n {(filter || toolbarRight) && (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-2',\n filter ? 'justify-between' : 'justify-end',\n 'sm:justify-end',\n )}\n >\n {filter &&\n (filter.variant === 'combobox' ? (\n <Combobox\n value={filterValue}\n onChange={(value) => {\n setFilterValue(value)\n setPage(1)\n }}\n options={filterSelectOptions}\n placeholder={filter.placeholder ?? filter.allLabel ?? 'Filter…'}\n className=\"min-w-0 flex-1 sm:flex-none sm:w-[200px]\"\n />\n ) : (\n <div className=\"min-w-0 flex-1 overflow-x-auto sm:flex-none\">\n <Segmented\n size=\"sm\"\n value={filterValue}\n onChange={(value) => {\n setFilterValue(value)\n setPage(1)\n }}\n options={filterSelectOptions}\n />\n </div>\n ))}\n {toolbarRight ? <div className=\"shrink-0\">{toolbarRight}</div> : null}\n </div>\n )}\n </div>\n )}\n\n {selectable && selected.length > 0 && (\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-b border-tollerud-border/30 bg-tollerud-yellow/[0.07] px-4 py-2.5\">\n <span className=\"flex items-center gap-2 text-sm text-tollerud-text-primary\">\n <Badge variant=\"accent\">{selected.length}</Badge>\n selected\n </span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {bulkActions.length > 1 ? (\n <ButtonGroup size=\"sm\">{bulkActionButtons}</ButtonGroup>\n ) : (\n bulkActionButtons\n )}\n </div>\n </div>\n )}\n\n {showEmpty ? (\n <div className=\"p-2\">{emptyState ?? <div className=\"py-12 text-center text-sm text-tollerud-text-muted\">No results.</div>}</div>\n ) : (\n tableScroll\n )}\n\n {!loading && sorted.length > 0 && (\n <div className=\"relative z-[4] flex flex-wrap items-center justify-between gap-3 border-t border-tollerud-border/30 px-4 py-2\">\n <span className=\"text-xs text-tollerud-text-muted\">\n {effectivePageSize\n ? `Showing ${(currentPage - 1) * effectivePageSize + 1}–${(currentPage - 1) * effectivePageSize + pageRows.length} of ${sorted.length}`\n : `${sorted.length} row${sorted.length === 1 ? '' : 's'}`}\n </span>\n <div className=\"flex flex-wrap items-center gap-3\">\n {footer}\n {allowsPageSizeChange && (\n <Select\n label=\"Rows\"\n layout=\"inline\"\n size=\"sm\"\n value={String(activePageSize)}\n onChange={(value) => {\n setActivePageSize(Number(value))\n setPage(1)\n }}\n options={pageSizeOptionsResolved.map((size) => ({\n value: String(size),\n label: String(size),\n }))}\n className=\"w-[72px]\"\n />\n )}\n {effectivePageSize && pageCount > 1 && (\n <Pagination page={currentPage} pageCount={pageCount} onChange={setPage} siblingCount={1} />\n )}\n </div>\n </div>\n )}\n </div>\n )\n}\n\nconst DataTable = forwardRef(<T extends Record<string, unknown>>(\n props: DataTableProps<T>,\n ref: React.Ref<HTMLDivElement>\n) => <DataTableInner {...props} forwardedRef={ref} />)\n\nDataTable.displayName = 'DataTable'\n\nexport { DataTable }\n"]} |
| 'use client'; | ||
| import { FloatingDropdownPortal } from './chunk-BFC62UF6.js'; | ||
| import { cn } from './chunk-62DSK4IE.js'; | ||
| import { forwardRef, useId, useRef, useState, useMemo, useCallback, useEffect } from 'react'; | ||
| import { Check, Plus, ChevronDown, Search } from 'lucide-react'; | ||
| import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; | ||
| var defaultFilter = (option, query) => option.label.toLowerCase().includes(query.toLowerCase()); | ||
| function flattenOptions(options, groups) { | ||
| if (groups && groups.length > 0) { | ||
| return groups.flatMap((group) => group.options); | ||
| } | ||
| return options; | ||
| } | ||
| function filterGroups(groups, query, filter) { | ||
| if (!query) return groups; | ||
| return groups.map((group) => ({ | ||
| ...group, | ||
| options: group.options.filter((option) => filter(option, query)) | ||
| })).filter((group) => group.options.length > 0); | ||
| } | ||
| var Combobox = forwardRef(function Combobox2({ | ||
| options = [], | ||
| groups, | ||
| value: valueProp, | ||
| defaultValue, | ||
| onChange, | ||
| placeholder = "Search\u2026", | ||
| label, | ||
| error, | ||
| filter = defaultFilter, | ||
| searchPlacement = "trigger", | ||
| onCreateOption, | ||
| createOptionLabel = (query) => `Create "${query}"`, | ||
| className, | ||
| disabled, | ||
| required | ||
| }, ref) { | ||
| const id = useId(); | ||
| const autoErrorId = useId(); | ||
| const errorId = error ? autoErrorId : void 0; | ||
| const internalRootRef = useRef(null); | ||
| const anchorRef = useRef(null); | ||
| const listRef = useRef(null); | ||
| const isControlled = valueProp !== void 0; | ||
| const [internalValue, setInternalValue] = useState(defaultValue); | ||
| const value = isControlled ? valueProp : internalValue; | ||
| const [open, setOpen] = useState(false); | ||
| const [query, setQuery] = useState(""); | ||
| const [activeIndex, setActiveIndex] = useState(0); | ||
| const [createdOptions, setCreatedOptions] = useState([]); | ||
| const isGrouped = Boolean(groups && groups.length > 0); | ||
| const allOptions = useMemo(() => flattenOptions(options, groups), [options, groups]); | ||
| const filteredGroups = useMemo(() => { | ||
| if (!isGrouped || !groups) return []; | ||
| return filterGroups(groups, query, filter); | ||
| }, [groups, isGrouped, query, filter]); | ||
| const filtered = useMemo(() => { | ||
| if (isGrouped) return filteredGroups.flatMap((group) => group.options); | ||
| if (!query) return options; | ||
| return options.filter((option) => filter(option, query)); | ||
| }, [filteredGroups, filter, isGrouped, options, query]); | ||
| const lookupOptions = useMemo(() => { | ||
| if (createdOptions.length === 0) return allOptions; | ||
| return [...allOptions, ...createdOptions.filter((c) => !allOptions.some((o) => o.value === c.value))]; | ||
| }, [allOptions, createdOptions]); | ||
| const trimmedQuery = query.trim(); | ||
| const showCreateOption = Boolean(onCreateOption) && trimmedQuery !== "" && !lookupOptions.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase()); | ||
| const rowCount = filtered.length + (showCreateOption ? 1 : 0); | ||
| const createRowIndex = filtered.length; | ||
| const highlightedIndex = rowCount === 0 ? 0 : Math.min(activeIndex, rowCount - 1); | ||
| const activeOptionId = open && rowCount > 0 ? highlightedIndex === createRowIndex && showCreateOption ? `${id}-option-create` : `${id}-option-${highlightedIndex}` : void 0; | ||
| const selected = lookupOptions.find((option) => option.value === value); | ||
| const setRootRef = useCallback( | ||
| (node) => { | ||
| internalRootRef.current = node; | ||
| if (typeof ref === "function") ref(node); | ||
| else if (ref) ref.current = node; | ||
| }, | ||
| [ref] | ||
| ); | ||
| useEffect(() => { | ||
| if (!open) return; | ||
| function onClickOutside(e) { | ||
| const target = e.target; | ||
| if (internalRootRef.current?.contains(target)) return; | ||
| if (listRef.current?.contains(target)) return; | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| document.addEventListener("mousedown", onClickOutside); | ||
| return () => { | ||
| document.removeEventListener("mousedown", onClickOutside); | ||
| }; | ||
| }, [open]); | ||
| useEffect(() => { | ||
| if (!activeOptionId) return; | ||
| document.getElementById(activeOptionId)?.scrollIntoView({ block: "nearest" }); | ||
| }, [activeOptionId]); | ||
| const dropdownSearchCallbackRef = useCallback((node) => { | ||
| if (node && searchPlacement === "dropdown") { | ||
| queueMicrotask(() => node.focus()); | ||
| } | ||
| }, [searchPlacement]); | ||
| const commit = (option) => { | ||
| if (option.disabled) return; | ||
| if (!isControlled) setInternalValue(option.value); | ||
| onChange?.(option.value); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| }; | ||
| const commitCreate = () => { | ||
| const label2 = trimmedQuery; | ||
| if (!label2) return; | ||
| const returned = onCreateOption?.(label2); | ||
| const newValue = typeof returned === "string" && returned ? returned : label2; | ||
| setCreatedOptions( | ||
| (prev) => prev.some((o) => o.value === newValue) ? prev : [...prev, { value: newValue, label: label2 }] | ||
| ); | ||
| if (!isControlled) setInternalValue(newValue); | ||
| onChange?.(newValue); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| }; | ||
| const onKeyDown = (e) => { | ||
| if (e.key === "ArrowDown") { | ||
| e.preventDefault(); | ||
| setOpen(true); | ||
| setActiveIndex((index) => Math.min(index + 1, rowCount - 1)); | ||
| } else if (e.key === "ArrowUp") { | ||
| e.preventDefault(); | ||
| setActiveIndex((index) => Math.max(index - 1, 0)); | ||
| } else if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| if (showCreateOption && highlightedIndex === createRowIndex) { | ||
| commitCreate(); | ||
| return; | ||
| } | ||
| const option = filtered[highlightedIndex]; | ||
| if (option) commit(option); | ||
| } else if (e.key === "Escape") { | ||
| if (open) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| } | ||
| }; | ||
| let flatIndex = 0; | ||
| const optionList = /* @__PURE__ */ jsxs("ul", { className: "m-0 list-none p-0", children: [ | ||
| filtered.length === 0 && !showCreateOption && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-tollerud-text-muted", children: "No results" }), | ||
| isGrouped ? filteredGroups.map((group) => /* @__PURE__ */ jsxs("li", { role: "presentation", children: [ | ||
| /* @__PURE__ */ jsx("div", { className: "px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-[0.06em] text-tollerud-text-muted", children: group.label }), | ||
| /* @__PURE__ */ jsx("ul", { role: "group", "aria-label": group.label, children: group.options.map((option) => { | ||
| const index = flatIndex++; | ||
| const isSelected = option.value === value; | ||
| return /* @__PURE__ */ jsxs( | ||
| "li", | ||
| { | ||
| id: `${id}-option-${index}`, | ||
| role: "option", | ||
| "aria-selected": isSelected, | ||
| onMouseDown: (e) => { | ||
| e.preventDefault(); | ||
| commit(option); | ||
| }, | ||
| onMouseEnter: () => setActiveIndex(index), | ||
| className: cn( | ||
| "flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer", | ||
| index === highlightedIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary", | ||
| option.disabled && "opacity-40 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| option.label, | ||
| isSelected && /* @__PURE__ */ jsx(Check, { size: 14, className: "text-tollerud-yellow" }) | ||
| ] | ||
| }, | ||
| option.value | ||
| ); | ||
| }) }) | ||
| ] }, group.label)) : filtered.map((option, index) => { | ||
| const isSelected = option.value === value; | ||
| return /* @__PURE__ */ jsxs( | ||
| "li", | ||
| { | ||
| id: `${id}-option-${index}`, | ||
| role: "option", | ||
| "aria-selected": isSelected, | ||
| onMouseDown: (e) => { | ||
| e.preventDefault(); | ||
| commit(option); | ||
| }, | ||
| onMouseEnter: () => setActiveIndex(index), | ||
| className: cn( | ||
| "flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer", | ||
| index === highlightedIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary", | ||
| option.disabled && "opacity-40 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| option.label, | ||
| isSelected && /* @__PURE__ */ jsx(Check, { size: 14, className: "text-tollerud-yellow" }) | ||
| ] | ||
| }, | ||
| option.value | ||
| ); | ||
| }), | ||
| showCreateOption && /* @__PURE__ */ jsxs( | ||
| "li", | ||
| { | ||
| id: `${id}-option-create`, | ||
| role: "option", | ||
| "aria-selected": false, | ||
| onMouseDown: (e) => { | ||
| e.preventDefault(); | ||
| commitCreate(); | ||
| }, | ||
| onMouseEnter: () => setActiveIndex(createRowIndex), | ||
| className: cn( | ||
| "flex items-center gap-2 px-3 py-2 text-sm cursor-pointer", | ||
| createRowIndex === highlightedIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary" | ||
| ), | ||
| children: [ | ||
| /* @__PURE__ */ jsx(Plus, { size: 14, className: "shrink-0 text-tollerud-yellow" }), | ||
| createOptionLabel(trimmedQuery) | ||
| ] | ||
| } | ||
| ) | ||
| ] }); | ||
| return /* @__PURE__ */ jsxs("div", { ref: setRootRef, className: cn("relative flex flex-col gap-1", className), children: [ | ||
| label && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: [ | ||
| label, | ||
| required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-tollerud-error", children: "*" }) | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { ref: anchorRef, className: "relative", children: searchPlacement === "dropdown" ? /* @__PURE__ */ jsxs( | ||
| "button", | ||
| { | ||
| id, | ||
| type: "button", | ||
| role: "combobox", | ||
| "aria-expanded": open, | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-haspopup": "listbox", | ||
| "aria-required": required || void 0, | ||
| "aria-invalid": error ? true : void 0, | ||
| "aria-describedby": errorId, | ||
| disabled, | ||
| onClick: () => { | ||
| if (open) { | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } else { | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| } | ||
| }, | ||
| onKeyDown: (e) => { | ||
| if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") { | ||
| e.preventDefault(); | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| } else if (e.key === "Escape") { | ||
| if (open) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| } | ||
| }, | ||
| className: cn( | ||
| "w-full font-sans text-base px-3 py-2.5 rounded text-left flex items-center justify-between gap-2", | ||
| "bg-tollerud-surface-raised border", | ||
| "transition-[border-color] duration-fast", | ||
| "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]", | ||
| error ? "border-tollerud-error" : "border-tollerud-border", | ||
| disabled && "opacity-50 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| /* @__PURE__ */ jsx("span", { className: selected ? "text-tollerud-text-primary" : "text-tollerud-text-muted", children: selected?.label ?? placeholder }), | ||
| /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 15, | ||
| className: cn( | ||
| "shrink-0 text-tollerud-text-muted transition-transform duration-fast", | ||
| open && "rotate-180" | ||
| ) | ||
| } | ||
| ) | ||
| ] | ||
| } | ||
| ) : /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| id, | ||
| role: "combobox", | ||
| "aria-expanded": open, | ||
| "aria-autocomplete": "list", | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-activedescendant": activeOptionId, | ||
| "aria-required": required || void 0, | ||
| "aria-invalid": error ? true : void 0, | ||
| "aria-describedby": errorId, | ||
| disabled, | ||
| value: open ? query : selected?.label ?? "", | ||
| placeholder: selected ? selected.label : placeholder, | ||
| onFocus: () => { | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| }, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setActiveIndex(0); | ||
| if (!open) setOpen(true); | ||
| }, | ||
| onKeyDown, | ||
| className: cn( | ||
| "w-full font-sans text-base px-3 py-2.5 pr-9 rounded", | ||
| "bg-tollerud-surface-raised border", | ||
| "text-tollerud-text-primary placeholder:text-tollerud-text-muted", | ||
| "transition-[border-color] duration-fast", | ||
| "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]", | ||
| error ? "border-tollerud-error" : "border-tollerud-border", | ||
| disabled && "opacity-50 pointer-events-none" | ||
| ) | ||
| } | ||
| ), | ||
| /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 15, | ||
| className: cn( | ||
| "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-fast", | ||
| open && "rotate-180" | ||
| ) | ||
| } | ||
| ) | ||
| ] }) }), | ||
| /* @__PURE__ */ jsx( | ||
| FloatingDropdownPortal, | ||
| { | ||
| open, | ||
| anchorRef, | ||
| popoverRef: listRef, | ||
| id: `${id}-listbox`, | ||
| role: "listbox", | ||
| placementOptions: { maxHeight: 320 }, | ||
| className: cn( | ||
| "rounded-lg border border-tollerud-border bg-tollerud-surface-overlay", | ||
| searchPlacement === "dropdown" ? "overflow-hidden" : "overflow-auto py-1" | ||
| ), | ||
| children: searchPlacement === "dropdown" ? /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-tollerud-border px-3 py-2", children: [ | ||
| /* @__PURE__ */ jsx(Search, { size: 13, className: "shrink-0 text-tollerud-text-muted" }), | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| ref: dropdownSearchCallbackRef, | ||
| type: "text", | ||
| role: "combobox", | ||
| "aria-expanded": true, | ||
| "aria-autocomplete": "list", | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-activedescendant": activeOptionId, | ||
| placeholder, | ||
| value: query, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setActiveIndex(0); | ||
| }, | ||
| onKeyDown, | ||
| className: "w-full bg-transparent text-sm pointer-coarse:text-base text-tollerud-text-primary placeholder:text-tollerud-text-muted focus:outline-none" | ||
| } | ||
| ) | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { className: "overflow-auto py-1", style: { maxHeight: 256 }, children: optionList }) | ||
| ] }) : optionList | ||
| } | ||
| ), | ||
| error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-xs text-tollerud-error mt-0.5", children: error }) | ||
| ] }); | ||
| }); | ||
| Combobox.displayName = "Combobox"; | ||
| export { Combobox }; | ||
| //# sourceMappingURL=chunk-KDRWILS3.js.map | ||
| //# sourceMappingURL=chunk-KDRWILS3.js.map |
| {"version":3,"sources":["../components/Combobox.tsx"],"names":["Combobox","label"],"mappings":"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","file":"chunk-KDRWILS3.js","sourcesContent":["'use client'\n\nimport { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { Check, ChevronDown, Plus, Search } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { FloatingDropdownPortal } from '@/lib/floating-dropdown'\n\nexport interface ComboboxOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface ComboboxGroup {\n /** Section title shown above options in the dropdown */\n label: string\n options: ComboboxOption[]\n}\n\nexport interface ComboboxProps {\n /** Flat option list — use when sections are not needed */\n options?: ComboboxOption[]\n /** Grouped options with section titles — takes precedence over `options` when provided */\n groups?: ComboboxGroup[]\n value?: string\n defaultValue?: string\n onChange?: (value: string) => void\n placeholder?: string\n label?: string\n error?: string\n /** Filter predicate, defaults to a case-insensitive substring match on the label */\n filter?: (option: ComboboxOption, query: string) => boolean\n /**\n * Where the search input lives.\n * - `'trigger'` (default) — the trigger input doubles as the search field, matching the original behaviour.\n * - `'dropdown'` — the trigger is a button showing the selected value; the search input appears at the top of the dropdown panel.\n */\n searchPlacement?: 'trigger' | 'dropdown'\n /**\n * Enables \"create a new option\" — when set, a `Create \"<query>\"` row appears at the\n * end of the list whenever the search text has no exact (case-insensitive) label match,\n * so users can add an option that doesn't exist yet without leaving the field. Called\n * with the trimmed query when that row is chosen; return a string to use as the new\n * option's value (e.g. a generated id), or return nothing to use the typed text as both\n * label and value.\n */\n onCreateOption?: (label: string) => string | void\n /** Customizes the create row's label. Defaults to `Create \"<query>\"`. */\n createOptionLabel?: (query: string) => string\n className?: string\n disabled?: boolean\n required?: boolean\n}\n\nconst defaultFilter = (option: ComboboxOption, query: string) =>\n option.label.toLowerCase().includes(query.toLowerCase())\n\nfunction flattenOptions(options: ComboboxOption[], groups?: ComboboxGroup[]) {\n if (groups && groups.length > 0) {\n return groups.flatMap((group) => group.options)\n }\n return options\n}\n\nfunction filterGroups(\n groups: ComboboxGroup[],\n query: string,\n filter: (option: ComboboxOption, query: string) => boolean\n): ComboboxGroup[] {\n if (!query) return groups\n return groups\n .map((group) => ({\n ...group,\n options: group.options.filter((option) => filter(option, query)),\n }))\n .filter((group) => group.options.length > 0)\n}\n\nconst Combobox = forwardRef<HTMLDivElement, ComboboxProps>(function Combobox(\n {\n options = [],\n groups,\n value: valueProp,\n defaultValue,\n onChange,\n placeholder = 'Search…',\n label,\n error,\n filter = defaultFilter,\n searchPlacement = 'trigger',\n onCreateOption,\n createOptionLabel = (query) => `Create \"${query}\"`,\n className,\n disabled,\n required,\n },\n ref\n) {\n const id = useId()\n const autoErrorId = useId()\n const errorId = error ? autoErrorId : undefined\n const internalRootRef = useRef<HTMLDivElement>(null)\n const anchorRef = useRef<HTMLDivElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n const isControlled = valueProp !== undefined\n const [internalValue, setInternalValue] = useState(defaultValue)\n const value = isControlled ? valueProp : internalValue\n\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [activeIndex, setActiveIndex] = useState(0)\n const [createdOptions, setCreatedOptions] = useState<ComboboxOption[]>([])\n\n const isGrouped = Boolean(groups && groups.length > 0)\n const allOptions = useMemo(() => flattenOptions(options, groups), [options, groups])\n\n const filteredGroups = useMemo(() => {\n if (!isGrouped || !groups) return []\n return filterGroups(groups, query, filter)\n }, [groups, isGrouped, query, filter])\n\n const filtered = useMemo(() => {\n if (isGrouped) return filteredGroups.flatMap((group) => group.options)\n if (!query) return options\n return options.filter((option) => filter(option, query))\n }, [filteredGroups, filter, isGrouped, options, query])\n\n const lookupOptions = useMemo(() => {\n if (createdOptions.length === 0) return allOptions\n return [...allOptions, ...createdOptions.filter((c) => !allOptions.some((o) => o.value === c.value))]\n }, [allOptions, createdOptions])\n\n const trimmedQuery = query.trim()\n const showCreateOption =\n Boolean(onCreateOption) &&\n trimmedQuery !== '' &&\n !lookupOptions.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase())\n const rowCount = filtered.length + (showCreateOption ? 1 : 0)\n const createRowIndex = filtered.length\n\n const highlightedIndex = rowCount === 0 ? 0 : Math.min(activeIndex, rowCount - 1)\n const activeOptionId =\n open && rowCount > 0\n ? highlightedIndex === createRowIndex && showCreateOption\n ? `${id}-option-create`\n : `${id}-option-${highlightedIndex}`\n : undefined\n\n const selected = lookupOptions.find((option) => option.value === value)\n\n const setRootRef = useCallback(\n (node: HTMLDivElement | null) => {\n internalRootRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n },\n [ref]\n )\n\n useEffect(() => {\n if (!open) return\n function onClickOutside(e: MouseEvent) {\n const target = e.target as Node\n if (internalRootRef.current?.contains(target)) return\n if (listRef.current?.contains(target)) return\n setOpen(false)\n setQuery('')\n }\n document.addEventListener('mousedown', onClickOutside)\n return () => {\n document.removeEventListener('mousedown', onClickOutside)\n }\n }, [open])\n\n // Keep the highlighted option visible while arrowing through a list taller\n // than the dropdown. The options live in a portal, so resolve them by id.\n useEffect(() => {\n if (!activeOptionId) return\n document.getElementById(activeOptionId)?.scrollIntoView({ block: 'nearest' })\n }, [activeOptionId])\n\n // Focus the search input as soon as it mounts (the portal renders null until\n // coords are computed, so a dep-effect on `open` always fires one render too\n // early). queueMicrotask defers until after React's layout effects, ensuring\n // FloatingDropdownPortal's useDialogEscapeHatch (useLayoutEffect) has already\n // attached its focusin stopPropagation before the focus event fires.\n const dropdownSearchCallbackRef = useCallback((node: HTMLInputElement | null) => {\n if (node && searchPlacement === 'dropdown') {\n queueMicrotask(() => node.focus())\n }\n }, [searchPlacement])\n\n const commit = (option: ComboboxOption) => {\n if (option.disabled) return\n if (!isControlled) setInternalValue(option.value)\n onChange?.(option.value)\n setOpen(false)\n setQuery('')\n }\n\n const commitCreate = () => {\n const label = trimmedQuery\n if (!label) return\n const returned = onCreateOption?.(label)\n const newValue = typeof returned === 'string' && returned ? returned : label\n setCreatedOptions((prev) =>\n prev.some((o) => o.value === newValue) ? prev : [...prev, { value: newValue, label }]\n )\n if (!isControlled) setInternalValue(newValue)\n onChange?.(newValue)\n setOpen(false)\n setQuery('')\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n setActiveIndex((index) => Math.min(index + 1, rowCount - 1))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setActiveIndex((index) => Math.max(index - 1, 0))\n } else if (e.key === 'Enter') {\n e.preventDefault()\n if (showCreateOption && highlightedIndex === createRowIndex) {\n commitCreate()\n return\n }\n const option = filtered[highlightedIndex]\n if (option) commit(option)\n } else if (e.key === 'Escape') {\n // Close only the innermost layer: consume the event while the dropdown\n // is open so a surrounding Dialog stays open; let it through otherwise.\n if (open) {\n e.preventDefault()\n e.stopPropagation()\n setOpen(false)\n setQuery('')\n }\n }\n }\n\n let flatIndex = 0\n\n const optionList = (\n <ul className=\"m-0 list-none p-0\">\n {filtered.length === 0 && !showCreateOption && (\n <li className=\"px-3 py-2 text-sm text-tollerud-text-muted\">No results</li>\n )}\n\n {isGrouped\n ? filteredGroups.map((group) => (\n <li key={group.label} role=\"presentation\">\n <div className=\"px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-[0.06em] text-tollerud-text-muted\">\n {group.label}\n </div>\n <ul role=\"group\" aria-label={group.label}>\n {group.options.map((option) => {\n const index = flatIndex++\n const isSelected = option.value === value\n return (\n <li\n key={option.value}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => {\n e.preventDefault()\n commit(option)\n }}\n onMouseEnter={() => setActiveIndex(index)}\n className={cn(\n 'flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer',\n index === highlightedIndex\n ? 'bg-tollerud-surface-hover text-tollerud-text-primary'\n : 'text-tollerud-text-secondary',\n option.disabled && 'opacity-40 pointer-events-none'\n )}\n >\n {option.label}\n {isSelected && <Check size={14} className=\"text-tollerud-yellow\" />}\n </li>\n )\n })}\n </ul>\n </li>\n ))\n : filtered.map((option, index) => {\n const isSelected = option.value === value\n return (\n <li\n key={option.value}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => {\n e.preventDefault()\n commit(option)\n }}\n onMouseEnter={() => setActiveIndex(index)}\n className={cn(\n 'flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer',\n index === highlightedIndex\n ? 'bg-tollerud-surface-hover text-tollerud-text-primary'\n : 'text-tollerud-text-secondary',\n option.disabled && 'opacity-40 pointer-events-none'\n )}\n >\n {option.label}\n {isSelected && <Check size={14} className=\"text-tollerud-yellow\" />}\n </li>\n )\n })}\n\n {showCreateOption && (\n <li\n id={`${id}-option-create`}\n role=\"option\"\n aria-selected={false}\n onMouseDown={(e) => {\n e.preventDefault()\n commitCreate()\n }}\n onMouseEnter={() => setActiveIndex(createRowIndex)}\n className={cn(\n 'flex items-center gap-2 px-3 py-2 text-sm cursor-pointer',\n createRowIndex === highlightedIndex\n ? 'bg-tollerud-surface-hover text-tollerud-text-primary'\n : 'text-tollerud-text-secondary'\n )}\n >\n <Plus size={14} className=\"shrink-0 text-tollerud-yellow\" />\n {createOptionLabel(trimmedQuery)}\n </li>\n )}\n </ul>\n )\n\n return (\n <div ref={setRootRef} className={cn('relative flex flex-col gap-1', className)}>\n {label && (\n <label htmlFor={id} className=\"text-xs font-medium text-tollerud-text-muted\">\n {label}\n {required && <span aria-hidden=\"true\" className=\"ml-0.5 text-tollerud-error\">*</span>}\n </label>\n )}\n <div ref={anchorRef} className=\"relative\">\n {searchPlacement === 'dropdown' ? (\n <button\n id={id}\n type=\"button\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-haspopup=\"listbox\"\n aria-required={required || undefined}\n aria-invalid={error ? true : undefined}\n aria-describedby={errorId}\n disabled={disabled}\n onClick={() => {\n if (open) {\n setOpen(false)\n setQuery('')\n } else {\n setOpen(true)\n setActiveIndex(0)\n }\n }}\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setOpen(true)\n setActiveIndex(0)\n } else if (e.key === 'Escape') {\n if (open) {\n e.preventDefault()\n e.stopPropagation()\n setOpen(false)\n setQuery('')\n }\n }\n }}\n className={cn(\n 'w-full font-sans text-base px-3 py-2.5 rounded text-left flex items-center justify-between gap-2',\n 'bg-tollerud-surface-raised border',\n 'transition-[border-color] duration-fast',\n 'focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]',\n error ? 'border-tollerud-error' : 'border-tollerud-border',\n disabled && 'opacity-50 pointer-events-none'\n )}\n >\n <span className={selected ? 'text-tollerud-text-primary' : 'text-tollerud-text-muted'}>\n {selected?.label ?? placeholder}\n </span>\n <ChevronDown\n size={15}\n className={cn(\n 'shrink-0 text-tollerud-text-muted transition-transform duration-fast',\n open && 'rotate-180'\n )}\n />\n </button>\n ) : (\n <>\n <input\n id={id}\n role=\"combobox\"\n aria-expanded={open}\n aria-autocomplete=\"list\"\n aria-controls={`${id}-listbox`}\n aria-activedescendant={activeOptionId}\n aria-required={required || undefined}\n aria-invalid={error ? true : undefined}\n aria-describedby={errorId}\n disabled={disabled}\n value={open ? query : selected?.label ?? ''}\n placeholder={selected ? selected.label : placeholder}\n onFocus={() => {\n setOpen(true)\n setActiveIndex(0)\n }}\n onChange={(e) => {\n setQuery(e.target.value)\n setActiveIndex(0)\n if (!open) setOpen(true)\n }}\n onKeyDown={onKeyDown}\n className={cn(\n 'w-full font-sans text-base px-3 py-2.5 pr-9 rounded',\n 'bg-tollerud-surface-raised border',\n 'text-tollerud-text-primary placeholder:text-tollerud-text-muted',\n 'transition-[border-color] duration-fast',\n 'focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]',\n error ? 'border-tollerud-error' : 'border-tollerud-border',\n disabled && 'opacity-50 pointer-events-none'\n )}\n />\n <ChevronDown\n size={15}\n className={cn(\n 'pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-fast',\n open && 'rotate-180'\n )}\n />\n </>\n )}\n </div>\n\n <FloatingDropdownPortal\n open={open}\n anchorRef={anchorRef}\n popoverRef={listRef}\n id={`${id}-listbox`}\n role=\"listbox\"\n placementOptions={{ maxHeight: 320 }}\n className={cn(\n 'rounded-lg border border-tollerud-border bg-tollerud-surface-overlay',\n searchPlacement === 'dropdown' ? 'overflow-hidden' : 'overflow-auto py-1'\n )}\n >\n {searchPlacement === 'dropdown' ? (\n <>\n <div className=\"flex items-center gap-2 border-b border-tollerud-border px-3 py-2\">\n <Search size={13} className=\"shrink-0 text-tollerud-text-muted\" />\n <input\n ref={dropdownSearchCallbackRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={true}\n aria-autocomplete=\"list\"\n aria-controls={`${id}-listbox`}\n aria-activedescendant={activeOptionId}\n placeholder={placeholder}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value)\n setActiveIndex(0)\n }}\n onKeyDown={onKeyDown}\n // pointer-coarse:text-base forces ≥16px on touch devices so iOS\n // Safari does not auto-zoom (and trigger a scroll) when the field\n // is focused on open. Desktop keeps the 14px (text-sm) sizing.\n className=\"w-full bg-transparent text-sm pointer-coarse:text-base text-tollerud-text-primary placeholder:text-tollerud-text-muted focus:outline-none\"\n />\n </div>\n <div className=\"overflow-auto py-1\" style={{ maxHeight: 256 }}>\n {optionList}\n </div>\n </>\n ) : (\n optionList\n )}\n </FloatingDropdownPortal>\n\n {error && <p id={errorId} className=\"text-xs text-tollerud-error mt-0.5\">{error}</p>}\n </div>\n )\n})\nCombobox.displayName = 'Combobox'\n\nexport { Combobox }\n"]} |
+17
-0
@@ -754,2 +754,19 @@ # Tollerud User Interface — AI Agent Guide | ||
| #### Combobox onCreateOption (≥ 4.14.0) | ||
| Opt-in "create a new option" row for cases like a category field where the value a user wants may not exist yet. Set `onCreateOption` and a `Create "<query>"` row appears at the end of the list whenever the search text has no exact (case-insensitive) label match — shown alongside partial matches, not only when the list is fully empty. Selecting it (click or Enter) calls `onCreateOption(label)` with the trimmed query; return a string to use as the new option's value (e.g. an id from your backend), or return nothing to use the typed text as both label and value. `createOptionLabel?: (query: string) => string` customizes the row's text (default `Create "<query>"`). The component tracks created options locally so the label displays correctly even before `options`/`groups` is updated to include it. | ||
| ```tsx | ||
| <Combobox | ||
| label="Category" | ||
| value={category} | ||
| onChange={setCategory} | ||
| options={categoryOptions} | ||
| onCreateOption={(label) => { | ||
| const id = createCategory(label) // persist however you like | ||
| return id // optional — omit to use the typed label as the value | ||
| }} | ||
| /> | ||
| ``` | ||
| #### Combobox searchPlacement (≥ 4.8.25) | ||
@@ -756,0 +773,0 @@ |
+11
-0
@@ -32,2 +32,13 @@ import * as react from 'react'; | ||
| searchPlacement?: 'trigger' | 'dropdown'; | ||
| /** | ||
| * Enables "create a new option" — when set, a `Create "<query>"` row appears at the | ||
| * end of the list whenever the search text has no exact (case-insensitive) label match, | ||
| * so users can add an option that doesn't exist yet without leaving the field. Called | ||
| * with the trimmed query when that row is chosen; return a string to use as the new | ||
| * option's value (e.g. a generated id), or return nothing to use the typed text as both | ||
| * label and value. | ||
| */ | ||
| onCreateOption?: (label: string) => string | void; | ||
| /** Customizes the create row's label. Defaults to `Create "<query>"`. */ | ||
| createOptionLabel?: (query: string) => string; | ||
| className?: string; | ||
@@ -34,0 +45,0 @@ disabled?: boolean; |
+1
-1
| 'use client'; | ||
| export { Combobox } from './chunk-X3TTOIGW.js'; | ||
| export { Combobox } from './chunk-KDRWILS3.js'; | ||
| import './chunk-BFC62UF6.js'; | ||
@@ -4,0 +4,0 @@ import './chunk-WCCKWQI7.js'; |
| 'use client'; | ||
| export { DataTable } from './chunk-LGWT2GLC.js'; | ||
| export { DataTable } from './chunk-5ZNSG4BJ.js'; | ||
| import './chunk-PCFRRG2S.js'; | ||
@@ -9,3 +9,3 @@ import './chunk-YREZRV73.js'; | ||
| import './chunk-BCNAGDLP.js'; | ||
| import './chunk-X3TTOIGW.js'; | ||
| import './chunk-KDRWILS3.js'; | ||
| import './chunk-BFC62UF6.js'; | ||
@@ -12,0 +12,0 @@ import './chunk-WCCKWQI7.js'; |
+2
-2
@@ -61,3 +61,3 @@ 'use client'; | ||
| export { Monogram } from './chunk-KGFOWDPH.js'; | ||
| export { DataTable } from './chunk-LGWT2GLC.js'; | ||
| export { DataTable } from './chunk-5ZNSG4BJ.js'; | ||
| export { Skeleton } from './chunk-PCFRRG2S.js'; | ||
@@ -80,3 +80,3 @@ export { Segmented } from './chunk-YREZRV73.js'; | ||
| export { CodeBlock } from './chunk-23OEB4WL.js'; | ||
| export { Combobox } from './chunk-X3TTOIGW.js'; | ||
| export { Combobox } from './chunk-KDRWILS3.js'; | ||
| import './chunk-BFC62UF6.js'; | ||
@@ -83,0 +83,0 @@ import './chunk-WCCKWQI7.js'; |
+1
-1
| { | ||
| "name": "@tollerud/ui", | ||
| "version": "4.13.3", | ||
| "version": "4.14.0", | ||
| "description": "Tollerud User Interface — dark, monochrome + yellow accent. Noir aesthetic meets modern utility.", | ||
@@ -5,0 +5,0 @@ "private": false, |
| 'use client'; | ||
| import { Skeleton } from './chunk-PCFRRG2S.js'; | ||
| import { Segmented } from './chunk-YREZRV73.js'; | ||
| import { Select } from './chunk-BDFXBUZ3.js'; | ||
| import { Pagination } from './chunk-YMAYHKPG.js'; | ||
| import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuItem } from './chunk-5QNHCMEX.js'; | ||
| import { Checkbox } from './chunk-BCNAGDLP.js'; | ||
| import { Combobox } from './chunk-X3TTOIGW.js'; | ||
| import { Badge } from './chunk-MMCPZVBB.js'; | ||
| import { ButtonGroup } from './chunk-FSRS5IWZ.js'; | ||
| import { Button } from './chunk-WZVSFANP.js'; | ||
| import { cn } from './chunk-62DSK4IE.js'; | ||
| import { forwardRef, useMemo, useState } from 'react'; | ||
| import { Search, X, MoreHorizontal, ChevronDown } from 'lucide-react'; | ||
| import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; | ||
| function normalizeColumns(columns) { | ||
| return columns.map(({ header, label, ...col }) => ({ | ||
| ...col, | ||
| label: label ?? header ?? col.key | ||
| })); | ||
| } | ||
| function columnReactKey(col, index) { | ||
| return `${col.key}-${index}`; | ||
| } | ||
| function DataTableInner({ | ||
| columns, | ||
| data, | ||
| rows, | ||
| rowKey, | ||
| onRowClick, | ||
| className, | ||
| emptyMessage = "No data", | ||
| searchable = false, | ||
| searchKeys, | ||
| searchPlaceholder = "Search\u2026", | ||
| filter, | ||
| selectable = false, | ||
| pageSize, | ||
| pageSizeOptions, | ||
| bulkActions = [], | ||
| rowMenu, | ||
| toolbarRight, | ||
| emptyState, | ||
| loading = false, | ||
| skeletonRows = 5, | ||
| striped = false, | ||
| footer, | ||
| pinColumns, | ||
| forwardedRef | ||
| }) { | ||
| const normalizedColumns = useMemo(() => normalizeColumns(columns), [columns]); | ||
| const tableData = useMemo(() => rows ?? data ?? [], [rows, data]); | ||
| const pageSizeOptionsResolved = useMemo( | ||
| () => [...new Set(pageSizeOptions ?? [])].filter((n) => n > 0).sort((a, b) => a - b), | ||
| [pageSizeOptions] | ||
| ); | ||
| const allowsPageSizeChange = pageSizeOptionsResolved.length > 0; | ||
| const [activePageSize, setActivePageSize] = useState( | ||
| pageSize ?? pageSizeOptionsResolved[0] ?? 10 | ||
| ); | ||
| const effectivePageSize = allowsPageSizeChange ? activePageSize : pageSize; | ||
| const isRich = searchable || filter || selectable || pageSize !== void 0 || allowsPageSizeChange || !!rowMenu || !!toolbarRight || bulkActions.length > 0 || !!emptyState || loading; | ||
| const pinEdges = pinColumns ?? isRich; | ||
| const tableMinWidth = Math.max(640, normalizedColumns.length * 120 + (selectable ? 40 : 0) + (rowMenu ? 44 : 0)); | ||
| const [sortKey, setSortKey] = useState(null); | ||
| const [sortDir, setSortDir] = useState("asc"); | ||
| const [filters, setFilters] = useState({}); | ||
| const [query, setQuery] = useState(""); | ||
| const [filterValue, setFilterValue] = useState("all"); | ||
| const [selected, setSelected] = useState([]); | ||
| const [page, setPage] = useState(1); | ||
| const getRowKey = (row, i) => { | ||
| if (typeof rowKey === "function") return rowKey(row); | ||
| if (rowKey) return row[rowKey]; | ||
| return row.id ?? row.key ?? i; | ||
| }; | ||
| const toggleSort = (key) => { | ||
| if (sortKey === key) { | ||
| setSortDir((d) => d === "asc" ? "desc" : "asc"); | ||
| } else { | ||
| setSortKey(key); | ||
| setSortDir("asc"); | ||
| } | ||
| }; | ||
| const updateFilter = (key, value) => { | ||
| setFilters((prev) => { | ||
| const next = { ...prev }; | ||
| if (value.trim() === "") { | ||
| delete next[key]; | ||
| } else { | ||
| next[key] = value; | ||
| } | ||
| return next; | ||
| }); | ||
| }; | ||
| const searchKeysResolved = useMemo( | ||
| () => searchKeys ?? normalizedColumns.map((c) => c.key), | ||
| [searchKeys, normalizedColumns] | ||
| ); | ||
| const filterSelectOptions = useMemo(() => { | ||
| if (!filter) return []; | ||
| const options = filter.options ?? Array.from(new Set(tableData.map((r) => String(r[filter.key] ?? "")))); | ||
| return [ | ||
| { value: "all", label: filter.allLabel ?? "All" }, | ||
| ...options.map((opt) => ({ value: opt, label: opt })) | ||
| ]; | ||
| }, [filter, tableData]); | ||
| const filtered = useMemo(() => { | ||
| let result = tableData; | ||
| if (isRich && filter && filterValue !== "all") { | ||
| result = result.filter((row) => String(row[filter.key] ?? "") === filterValue); | ||
| } | ||
| if (isRich && searchable && query.trim()) { | ||
| const q = query.trim().toLowerCase(); | ||
| result = result.filter( | ||
| (row) => searchKeysResolved.some((key) => String(row[key] ?? "").toLowerCase().includes(q)) | ||
| ); | ||
| } | ||
| if (!isRich) { | ||
| const activeFilters = Object.entries(filters).filter(([, v]) => v.trim() !== ""); | ||
| if (activeFilters.length > 0) { | ||
| result = result.filter( | ||
| (row) => activeFilters.every(([key, filterVal]) => { | ||
| const cellValue = String(row[key] ?? "").toLowerCase(); | ||
| return cellValue.includes(filterVal.toLowerCase()); | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| return result; | ||
| }, [tableData, filters, isRich, filter, filterValue, searchable, query, searchKeysResolved]); | ||
| const sorted = useMemo(() => { | ||
| if (!sortKey) return filtered; | ||
| const col = normalizedColumns.find((c) => c.key === sortKey); | ||
| if (!col?.sortable) return filtered; | ||
| return [...filtered].sort((a, b) => { | ||
| const aVal = a[sortKey]; | ||
| const bVal = b[sortKey]; | ||
| if (typeof aVal === "number" && typeof bVal === "number") { | ||
| return sortDir === "asc" ? aVal - bVal : bVal - aVal; | ||
| } | ||
| const aStr = String(aVal ?? ""); | ||
| const bStr = String(bVal ?? ""); | ||
| const cmp = aStr.localeCompare(bStr, "nb", { numeric: true }); | ||
| return sortDir === "asc" ? cmp : -cmp; | ||
| }); | ||
| }, [filtered, sortKey, sortDir, normalizedColumns]); | ||
| const pageCount = effectivePageSize ? Math.max(1, Math.ceil(sorted.length / effectivePageSize)) : 1; | ||
| const currentPage = Math.min(page, pageCount); | ||
| const pageRows = effectivePageSize ? sorted.slice((currentPage - 1) * effectivePageSize, currentPage * effectivePageSize) : sorted; | ||
| const clearSelection = () => setSelected([]); | ||
| const allOnPageSelected = pageRows.length > 0 && pageRows.every((row, i) => selected.includes(getRowKey(row, i))); | ||
| const someOnPageSelected = !allOnPageSelected && pageRows.some((row, i) => selected.includes(getRowKey(row, i))); | ||
| const toggleAllOnPage = () => { | ||
| const pageIds = pageRows.map((row, i) => getRowKey(row, i)); | ||
| if (allOnPageSelected) { | ||
| setSelected((prev) => prev.filter((id) => !pageIds.includes(id))); | ||
| } else { | ||
| setSelected((prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...pageIds]))); | ||
| } | ||
| }; | ||
| const toggleRow = (id) => { | ||
| setSelected((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]); | ||
| }; | ||
| const hasActiveFilters = Object.values(filters).some((v) => v.trim() !== ""); | ||
| const filterableColumns = normalizedColumns.filter((c) => c.filterable); | ||
| const colSpan = normalizedColumns.length + (selectable ? 1 : 0) + (rowMenu ? 1 : 0); | ||
| const rowHoverable = Boolean(onRowClick || isRich); | ||
| const selectedRowBg = "bg-[color-mix(in_srgb,var(--tollerud-yellow)_7%,var(--tollerud-noir-900))]"; | ||
| const selectedRowHoverBg = "group-hover/tr:bg-[color-mix(in_srgb,var(--tollerud-yellow)_12%,var(--tollerud-noir-800))]"; | ||
| const cellSurface = (rowIndex, isSelected) => cn( | ||
| "transition-colors duration-fast", | ||
| rowHoverable && (isSelected ? selectedRowHoverBg : "group-hover/tr:bg-tollerud-noir-800"), | ||
| isSelected && selectedRowBg, | ||
| !isSelected && striped && rowIndex % 2 === 0 && "bg-tollerud-noir-950", | ||
| !isSelected && !(striped && rowIndex % 2 === 0) && isRich && "bg-tollerud-noir-900" | ||
| ); | ||
| const stickyPosition = (edge) => pinEdges && cn( | ||
| "sticky z-[2]", | ||
| edge === "check" && "left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", | ||
| edge === "first" && cn("left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", selectable && "left-10"), | ||
| edge === "actions" && "right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]" | ||
| ); | ||
| const stickyHead = (edge) => pinEdges && cn( | ||
| "sticky z-[3] bg-tollerud-noir-900", | ||
| edge === "check" && "left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", | ||
| edge === "first" && cn("left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]", selectable && "left-10"), | ||
| edge === "actions" && "right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]" | ||
| ); | ||
| const stickyBody = (edge) => stickyPosition(edge); | ||
| const sortIndicator = (key) => { | ||
| const active = sortKey === key; | ||
| return /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 12, | ||
| className: cn( | ||
| "shrink-0 transition-transform duration-fast", | ||
| active ? "text-tollerud-yellow opacity-100" : "opacity-25", | ||
| active && sortDir === "desc" && "rotate-180" | ||
| ), | ||
| "aria-hidden": true | ||
| } | ||
| ); | ||
| }; | ||
| const headerCell = (col, columnIndex, reactKey) => { | ||
| const content = /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| col.label, | ||
| col.sortable && (isRich ? sortIndicator(col.key) : /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| sortKey === col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-accent", children: sortDir === "asc" ? "\u2191" : "\u2193" }), | ||
| sortKey !== col.key && /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-muted/30", children: "\u2195" }) | ||
| ] })), | ||
| !isRich && col.filterable && /* @__PURE__ */ jsx( | ||
| "svg", | ||
| { | ||
| width: "12", | ||
| height: "12", | ||
| viewBox: "0 0 24 24", | ||
| fill: "none", | ||
| stroke: "currentColor", | ||
| strokeWidth: "2", | ||
| strokeLinecap: "round", | ||
| strokeLinejoin: "round", | ||
| className: cn("opacity-40", filters[col.key] && "opacity-100 text-tollerud-yellow"), | ||
| "aria-hidden": true, | ||
| children: /* @__PURE__ */ jsx("path", { d: "M4 4h16v2.172a2 2 0 0 1-.586 1.414L15 12v7l-6 2v-8.5L4.52 7.53A2 2 0 0 1 4 6.16V4z" }) | ||
| } | ||
| ) | ||
| ] }); | ||
| const contentAlignment = cn( | ||
| "inline-flex items-center gap-1", | ||
| col.align === "right" && "justify-end w-full", | ||
| col.align === "center" && "justify-center w-full" | ||
| ); | ||
| return /* @__PURE__ */ jsx( | ||
| "th", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 text-left text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| col.align === "center" && "text-center", | ||
| columnIndex === 0 && stickyHead("first") | ||
| ), | ||
| style: col.width ? { width: col.width, minWidth: col.width } : void 0, | ||
| "aria-sort": col.sortable ? sortKey === col.key ? sortDir === "asc" ? "ascending" : "descending" : "none" : void 0, | ||
| children: col.sortable ? /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| onClick: () => toggleSort(col.key), | ||
| className: cn( | ||
| contentAlignment, | ||
| "cursor-pointer select-none uppercase tracking-wider hover:text-tollerud-text-primary transition-colors tollerud-focus-ring rounded-sm" | ||
| ), | ||
| children: content | ||
| } | ||
| ) : /* @__PURE__ */ jsx("span", { className: contentAlignment, children: content }) | ||
| }, | ||
| reactKey | ||
| ); | ||
| }; | ||
| const tableBody = () => { | ||
| if (loading) { | ||
| return /* @__PURE__ */ jsx("tbody", { children: Array.from({ length: skeletonRows }).map((_, i) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-tollerud-border/20", children: [ | ||
| selectable && /* @__PURE__ */ jsx("td", { className: cn("px-3 py-2.5 w-10 min-w-10", cellSurface(i, false), stickyBody("check")), children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded" }) }), | ||
| normalizedColumns.map((col, j) => /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| cellSurface(i, false), | ||
| j === 0 && stickyBody("first") | ||
| ), | ||
| children: /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3", j === 0 ? "w-[70%]" : "w-[55%]") }) | ||
| }, | ||
| columnReactKey(col, j) | ||
| )), | ||
| rowMenu && /* @__PURE__ */ jsx("td", { className: cn("px-3 py-2.5 w-11 min-w-11", cellSurface(i, false), stickyBody("actions")), children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded" }) }) | ||
| ] }, i)) }); | ||
| } | ||
| if (sorted.length === 0) { | ||
| if (isRich && emptyState) { | ||
| return null; | ||
| } | ||
| return /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, className: "px-3 py-8 text-center text-sm text-tollerud-text-muted", children: hasActiveFilters || query || filterValue !== "all" ? "No matching rows" : emptyMessage }) }) }); | ||
| } | ||
| return /* @__PURE__ */ jsx("tbody", { children: pageRows.map((row, i) => { | ||
| const id = getRowKey(row, i); | ||
| const isSelected = selected.includes(id); | ||
| return /* @__PURE__ */ jsxs( | ||
| "tr", | ||
| { | ||
| className: cn( | ||
| "group/tr border-b border-tollerud-border/20", | ||
| onRowClick && "cursor-pointer" | ||
| ), | ||
| onClick: () => onRowClick?.(row), | ||
| children: [ | ||
| selectable && /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn("px-3 py-2.5 w-10 min-w-10", cellSurface(i, isSelected), stickyBody("check")), | ||
| onClick: (e) => e.stopPropagation(), | ||
| children: /* @__PURE__ */ jsx(Checkbox, { checked: isSelected, onChange: () => toggleRow(id), "aria-label": `Select row ${id}` }) | ||
| } | ||
| ), | ||
| normalizedColumns.map((col, columnIndex) => { | ||
| const value = row[col.key]; | ||
| return /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn( | ||
| "px-3 py-2.5 text-tollerud-text-secondary whitespace-nowrap", | ||
| col.align === "right" && "text-right", | ||
| col.align === "center" && "text-center", | ||
| !col.render && "font-mono text-xs", | ||
| cellSurface(i, isSelected), | ||
| columnIndex === 0 && stickyBody("first") | ||
| ), | ||
| style: col.width ? { width: col.width, minWidth: col.width } : void 0, | ||
| children: col.render ? col.render(value, row) : String(value ?? "\u2014") | ||
| }, | ||
| columnReactKey(col, columnIndex) | ||
| ); | ||
| }), | ||
| rowMenu && /* @__PURE__ */ jsx( | ||
| "td", | ||
| { | ||
| className: cn("px-2 py-2 w-11 min-w-11", cellSurface(i, isSelected), stickyBody("actions")), | ||
| onClick: (e) => e.stopPropagation(), | ||
| children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [ | ||
| /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-tollerud-text-muted hover:text-tollerud-text-primary hover:bg-tollerud-surface-raised transition-colors tollerud-focus-ring", | ||
| "aria-label": "Row actions", | ||
| children: /* @__PURE__ */ jsx(MoreHorizontal, { size: 15 }) | ||
| } | ||
| ) }), | ||
| /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: rowMenu(row).map((item, index) => { | ||
| if (item.sep) return /* @__PURE__ */ jsx(DropdownMenuSeparator, {}, index); | ||
| if (item.heading) return /* @__PURE__ */ jsx(DropdownMenuLabel, { children: item.label }, index); | ||
| return /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => item.onSelect?.(), children: [ | ||
| item.icon ? /* @__PURE__ */ jsx("span", { className: "mr-2 inline-flex", children: item.icon }) : null, | ||
| item.label | ||
| ] }, index); | ||
| }) }) | ||
| ] }) | ||
| } | ||
| ) | ||
| ] | ||
| }, | ||
| id | ||
| ); | ||
| }) }); | ||
| }; | ||
| const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", style: { minWidth: tableMinWidth }, children: [ | ||
| /* @__PURE__ */ jsxs("thead", { children: [ | ||
| /* @__PURE__ */ jsxs("tr", { className: "border-b border-tollerud-border/30 bg-tollerud-noir-900", children: [ | ||
| selectable && /* @__PURE__ */ jsx("th", { className: cn("px-3 py-2.5 w-10 min-w-10", stickyHead("check")), children: /* @__PURE__ */ jsx( | ||
| Checkbox, | ||
| { | ||
| checked: allOnPageSelected, | ||
| indeterminate: someOnPageSelected, | ||
| onChange: toggleAllOnPage, | ||
| "aria-label": "Select all rows on page" | ||
| } | ||
| ) }), | ||
| normalizedColumns.map( | ||
| (col, columnIndex) => headerCell(col, columnIndex, columnReactKey(col, columnIndex)) | ||
| ), | ||
| rowMenu && /* @__PURE__ */ jsx("th", { className: cn("w-11 min-w-11", stickyHead("actions")), "aria-label": "Row actions" }) | ||
| ] }), | ||
| !isRich && filterableColumns.length > 0 && /* @__PURE__ */ jsx("tr", { className: "border-b border-tollerud-border/20", children: normalizedColumns.map((col, columnIndex) => /* @__PURE__ */ jsx( | ||
| "th", | ||
| { | ||
| className: cn("px-1.5 py-1", col.align === "right" && "text-right", col.align === "center" && "text-center"), | ||
| children: col.filterable ? /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| type: "text", | ||
| placeholder: `Filter ${col.label.toLowerCase()}\u2026`, | ||
| value: filters[col.key] ?? "", | ||
| onChange: (e) => updateFilter(col.key, e.target.value), | ||
| className: cn( | ||
| "w-full px-2 py-1 text-xs rounded border transition-colors outline-none", | ||
| "bg-tollerud-noir-900/50 border-tollerud-border/20 text-tollerud-text-primary placeholder:text-tollerud-text-muted/40", | ||
| "focus:border-tollerud-yellow/40 focus:ring-1 focus:ring-tollerud-yellow/20" | ||
| ) | ||
| } | ||
| ) : null | ||
| }, | ||
| columnReactKey(col, columnIndex) | ||
| )) }) | ||
| ] }), | ||
| tableBody() | ||
| ] }); | ||
| const bulkActionButtons = bulkActions.map((action, index) => /* @__PURE__ */ jsxs( | ||
| Button, | ||
| { | ||
| variant: action.variant ?? "ghost", | ||
| size: "sm", | ||
| onClick: () => action.onRun(selected, clearSelection), | ||
| children: [ | ||
| action.icon ? /* @__PURE__ */ jsx("span", { className: "mr-1.5 inline-flex shrink-0", children: action.icon }) : null, | ||
| action.label | ||
| ] | ||
| }, | ||
| index | ||
| )); | ||
| const tableScroll = /* @__PURE__ */ jsx( | ||
| "div", | ||
| { | ||
| className: "tollerud-datatable-scroll min-w-0 w-full overflow-x-auto overscroll-x-contain", | ||
| role: "region", | ||
| "aria-label": "Scrollable table", | ||
| tabIndex: pinEdges ? 0 : void 0, | ||
| children: table | ||
| } | ||
| ); | ||
| if (!isRich) { | ||
| return /* @__PURE__ */ jsx("div", { ref: forwardedRef, className: cn("min-w-0 w-full rounded-lg border border-tollerud-border/30", className), children: tableScroll }); | ||
| } | ||
| const showEmpty = !loading && sorted.length === 0; | ||
| return /* @__PURE__ */ jsxs( | ||
| "div", | ||
| { | ||
| ref: forwardedRef, | ||
| className: cn( | ||
| "min-w-0 w-full rounded-lg border border-tollerud-border/30 bg-tollerud-noir-900", | ||
| className | ||
| ), | ||
| children: [ | ||
| (searchable || filter || toolbarRight) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2.5 border-b border-tollerud-border/30 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between", children: [ | ||
| searchable && /* @__PURE__ */ jsxs("div", { className: "flex h-9 w-full items-center gap-2 rounded-lg border border-tollerud-border/30 bg-tollerud-noir-950 px-3 sm:w-[230px] sm:shrink-0", children: [ | ||
| /* @__PURE__ */ jsx(Search, { size: 15, className: "shrink-0 text-tollerud-text-muted", "aria-hidden": true }), | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| value: query, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setPage(1); | ||
| }, | ||
| placeholder: searchPlaceholder, | ||
| className: "min-w-0 flex-1 border-none bg-transparent text-sm text-tollerud-text-primary outline-none placeholder:text-tollerud-text-muted" | ||
| } | ||
| ), | ||
| query && /* @__PURE__ */ jsx( | ||
| "button", | ||
| { | ||
| type: "button", | ||
| onClick: () => setQuery(""), | ||
| className: "text-tollerud-text-muted hover:text-tollerud-text-primary", | ||
| "aria-label": "Clear search", | ||
| children: /* @__PURE__ */ jsx(X, { size: 13 }) | ||
| } | ||
| ) | ||
| ] }), | ||
| (filter || toolbarRight) && /* @__PURE__ */ jsxs( | ||
| "div", | ||
| { | ||
| className: cn( | ||
| "flex min-w-0 items-center gap-2", | ||
| filter ? "justify-between" : "justify-end", | ||
| "sm:justify-end" | ||
| ), | ||
| children: [ | ||
| filter && (filter.variant === "combobox" ? /* @__PURE__ */ jsx( | ||
| Combobox, | ||
| { | ||
| value: filterValue, | ||
| onChange: (value) => { | ||
| setFilterValue(value); | ||
| setPage(1); | ||
| }, | ||
| options: filterSelectOptions, | ||
| placeholder: filter.placeholder ?? filter.allLabel ?? "Filter\u2026", | ||
| className: "min-w-0 flex-1 sm:flex-none sm:w-[200px]" | ||
| } | ||
| ) : /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 overflow-x-auto sm:flex-none", children: /* @__PURE__ */ jsx( | ||
| Segmented, | ||
| { | ||
| size: "sm", | ||
| value: filterValue, | ||
| onChange: (value) => { | ||
| setFilterValue(value); | ||
| setPage(1); | ||
| }, | ||
| options: filterSelectOptions | ||
| } | ||
| ) })), | ||
| toolbarRight ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: toolbarRight }) : null | ||
| ] | ||
| } | ||
| ) | ||
| ] }), | ||
| selectable && selected.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-tollerud-border/30 bg-tollerud-yellow/[0.07] px-4 py-2.5", children: [ | ||
| /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm text-tollerud-text-primary", children: [ | ||
| /* @__PURE__ */ jsx(Badge, { variant: "accent", children: selected.length }), | ||
| "selected" | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: bulkActions.length > 1 ? /* @__PURE__ */ jsx(ButtonGroup, { size: "sm", children: bulkActionButtons }) : bulkActionButtons }) | ||
| ] }), | ||
| showEmpty ? /* @__PURE__ */ jsx("div", { className: "p-2", children: emptyState ?? /* @__PURE__ */ jsx("div", { className: "py-12 text-center text-sm text-tollerud-text-muted", children: "No results." }) }) : tableScroll, | ||
| !loading && sorted.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative z-[4] flex flex-wrap items-center justify-between gap-3 border-t border-tollerud-border/30 px-4 py-2", children: [ | ||
| /* @__PURE__ */ jsx("span", { className: "text-xs text-tollerud-text-muted", children: effectivePageSize ? `Showing ${(currentPage - 1) * effectivePageSize + 1}\u2013${(currentPage - 1) * effectivePageSize + pageRows.length} of ${sorted.length}` : `${sorted.length} row${sorted.length === 1 ? "" : "s"}` }), | ||
| /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [ | ||
| footer, | ||
| allowsPageSizeChange && /* @__PURE__ */ jsx( | ||
| Select, | ||
| { | ||
| label: "Rows", | ||
| layout: "inline", | ||
| size: "sm", | ||
| value: String(activePageSize), | ||
| onChange: (value) => { | ||
| setActivePageSize(Number(value)); | ||
| setPage(1); | ||
| }, | ||
| options: pageSizeOptionsResolved.map((size) => ({ | ||
| value: String(size), | ||
| label: String(size) | ||
| })), | ||
| className: "w-[72px]" | ||
| } | ||
| ), | ||
| effectivePageSize && pageCount > 1 && /* @__PURE__ */ jsx(Pagination, { page: currentPage, pageCount, onChange: setPage, siblingCount: 1 }) | ||
| ] }) | ||
| ] }) | ||
| ] | ||
| } | ||
| ); | ||
| } | ||
| var DataTable = forwardRef((props, ref) => /* @__PURE__ */ jsx(DataTableInner, { ...props, forwardedRef: ref })); | ||
| DataTable.displayName = "DataTable"; | ||
| export { DataTable }; | ||
| //# sourceMappingURL=chunk-LGWT2GLC.js.map | ||
| //# sourceMappingURL=chunk-LGWT2GLC.js.map |
| {"version":3,"sources":["../components/DataTable.tsx"],"names":[],"mappings":"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","file":"chunk-LGWT2GLC.js","sourcesContent":["'use client'\n\nimport { useState, useMemo, forwardRef, type ReactNode } from 'react'\nimport { ChevronDown, MoreHorizontal, Search, X } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from './Badge'\nimport { Button } from './Button'\nimport { ButtonGroup } from './ButtonGroup'\nimport { Checkbox } from './Checkbox'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './DropdownMenu'\nimport { Pagination } from './Pagination'\nimport { Segmented } from './Segmented'\nimport { Combobox } from './Combobox'\nimport { Select } from './Select'\nimport { Skeleton } from './Skeleton'\n\n/* ──────────────────── Sortable & Filterable Data Table ──────────────────── */\n\n/**\n * Cell renderer — always `(value, row)`. `value` is `row[key]`; ignore it\n * (`(_value, row) => …`) when you only need the row.\n * Before 4.8.40 a single-parameter `(row) => …` form was auto-detected from\n * `fn.length`; that variant was removed because default/rest parameters made\n * detection unreliable.\n */\nexport type ColumnRender<T> = (value: unknown, row: T) => ReactNode\n\nexport interface Column<T> {\n key: string\n /** Column heading — use `label` or `header` (alias). */\n label?: string\n header?: string\n sortable?: boolean\n filterable?: boolean\n align?: 'left' | 'center' | 'right'\n width?: string\n render?: ColumnRender<T>\n}\n\ntype NormalizedColumn<T> = Omit<Column<T>, 'label' | 'header'> & {\n label: string\n}\n\nfunction normalizeColumns<T>(columns: Column<T>[]): NormalizedColumn<T>[] {\n return columns.map(({ header, label, ...col }) => ({\n ...col,\n label: label ?? header ?? col.key,\n }))\n}\n\nfunction columnReactKey(col: { key: string }, index: number) {\n return `${col.key}-${index}`\n}\n\nexport interface DataTableFilter {\n key: string\n options?: string[]\n allLabel?: string\n /** Toolbar control — `segmented` (default) or searchable `combobox`. */\n variant?: 'segmented' | 'combobox'\n /** Combobox placeholder when `variant` is `combobox`. */\n placeholder?: string\n}\n\nexport interface DataTableBulkAction {\n label: string\n variant?: 'primary' | 'secondary' | 'ghost' | 'ghost-destructive' | 'ghost-success' | 'ghost-warning' | 'ghost-info' | 'destructive' | 'terminal'\n icon?: ReactNode\n onRun: (selectedIds: (string | number)[], clearSelection: () => void) => void\n}\n\nexport interface DataTableRowMenuItem {\n label: string\n onSelect?: () => void\n icon?: ReactNode\n sep?: boolean\n heading?: boolean\n}\n\nexport interface DataTableProps<T extends Record<string, unknown>> {\n columns: Column<T>[]\n /** Row data — use `data` or `rows` (alias). */\n data?: T[]\n rows?: T[]\n /** Row key extractor — defaults to `row.id` or `row.key` */\n rowKey?: keyof T | ((row: T) => string | number)\n onRowClick?: (row: T) => void\n className?: string\n emptyMessage?: string\n /** Global search across `searchKeys` (or all column keys). */\n searchable?: boolean\n searchKeys?: (keyof T | string)[]\n searchPlaceholder?: string\n /** Column filter in rich mode — segmented tabs or searchable combobox. */\n filter?: DataTableFilter\n selectable?: boolean\n /** Rows per page — fixed unless `pageSizeOptions` is set. */\n pageSize?: number\n /** Lets users change rows per page from the footer (initial value from `pageSize` or first option). */\n pageSizeOptions?: number[]\n bulkActions?: DataTableBulkAction[]\n rowMenu?: (row: T) => DataTableRowMenuItem[]\n toolbarRight?: ReactNode\n emptyState?: ReactNode\n loading?: boolean\n skeletonRows?: number\n /** Alternating row backgrounds in rich mode. */\n striped?: boolean\n /** Pin the first column and row-menu column during horizontal scroll (default: on in rich mode). */\n pinColumns?: boolean\n /** Extra content in the table footer bar (right of the row count). */\n footer?: ReactNode\n}\n\ninterface DataTableInnerProps<T extends Record<string, unknown>> extends DataTableProps<T> {\n forwardedRef?: React.Ref<HTMLDivElement>\n}\n\nfunction DataTableInner<T extends Record<string, unknown>>({\n columns,\n data,\n rows,\n rowKey,\n onRowClick,\n className,\n emptyMessage = 'No data',\n searchable = false,\n searchKeys,\n searchPlaceholder = 'Search…',\n filter,\n selectable = false,\n pageSize,\n pageSizeOptions,\n bulkActions = [],\n rowMenu,\n toolbarRight,\n emptyState,\n loading = false,\n skeletonRows = 5,\n striped = false,\n footer,\n pinColumns,\n forwardedRef,\n}: DataTableInnerProps<T>) {\n const normalizedColumns = useMemo(() => normalizeColumns(columns), [columns])\n const tableData = useMemo(() => rows ?? data ?? [], [rows, data])\n\n const pageSizeOptionsResolved = useMemo(\n () => [...new Set(pageSizeOptions ?? [])].filter((n) => n > 0).sort((a, b) => a - b),\n [pageSizeOptions],\n )\n const allowsPageSizeChange = pageSizeOptionsResolved.length > 0\n const [activePageSize, setActivePageSize] = useState(\n pageSize ?? pageSizeOptionsResolved[0] ?? 10,\n )\n const effectivePageSize = allowsPageSizeChange ? activePageSize : pageSize\n\n const isRich =\n searchable ||\n filter ||\n selectable ||\n pageSize !== undefined ||\n allowsPageSizeChange ||\n !!rowMenu ||\n !!toolbarRight ||\n bulkActions.length > 0 ||\n !!emptyState ||\n loading\n\n const pinEdges = pinColumns ?? isRich\n const tableMinWidth = Math.max(640, normalizedColumns.length * 120 + (selectable ? 40 : 0) + (rowMenu ? 44 : 0))\n\n const [sortKey, setSortKey] = useState<string | null>(null)\n const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc')\n const [filters, setFilters] = useState<Record<string, string>>({})\n const [query, setQuery] = useState('')\n const [filterValue, setFilterValue] = useState('all')\n const [selected, setSelected] = useState<(string | number)[]>([])\n const [page, setPage] = useState(1)\n\n const getRowKey = (row: T, i: number): string | number => {\n if (typeof rowKey === 'function') return rowKey(row)\n if (rowKey) return row[rowKey] as string | number\n return (row.id ?? row.key ?? i) as string | number\n }\n\n const toggleSort = (key: string) => {\n if (sortKey === key) {\n setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))\n } else {\n setSortKey(key)\n setSortDir('asc')\n }\n }\n\n const updateFilter = (key: string, value: string) => {\n setFilters((prev) => {\n const next = { ...prev }\n if (value.trim() === '') {\n delete next[key]\n } else {\n next[key] = value\n }\n return next\n })\n }\n\n const searchKeysResolved = useMemo(\n () => searchKeys ?? normalizedColumns.map((c) => c.key),\n [searchKeys, normalizedColumns],\n )\n const filterSelectOptions = useMemo(() => {\n if (!filter) return []\n const options =\n filter.options ?? Array.from(new Set(tableData.map((r) => String(r[filter.key] ?? ''))))\n return [\n { value: 'all', label: filter.allLabel ?? 'All' },\n ...options.map((opt) => ({ value: opt, label: opt })),\n ]\n }, [filter, tableData])\n\n const filtered = useMemo(() => {\n let result = tableData\n\n if (isRich && filter && filterValue !== 'all') {\n result = result.filter((row) => String(row[filter.key] ?? '') === filterValue)\n }\n\n if (isRich && searchable && query.trim()) {\n const q = query.trim().toLowerCase()\n result = result.filter((row) =>\n searchKeysResolved.some((key) => String(row[key as keyof T] ?? '').toLowerCase().includes(q))\n )\n }\n\n if (!isRich) {\n const activeFilters = Object.entries(filters).filter(([, v]) => v.trim() !== '')\n if (activeFilters.length > 0) {\n result = result.filter((row) =>\n activeFilters.every(([key, filterVal]) => {\n const cellValue = String(row[key] ?? '').toLowerCase()\n return cellValue.includes(filterVal.toLowerCase())\n })\n )\n }\n }\n\n return result\n }, [tableData, filters, isRich, filter, filterValue, searchable, query, searchKeysResolved])\n\n const sorted = useMemo(() => {\n if (!sortKey) return filtered\n const col = normalizedColumns.find((c) => c.key === sortKey)\n if (!col?.sortable) return filtered\n\n return [...filtered].sort((a, b) => {\n const aVal = a[sortKey]\n const bVal = b[sortKey]\n\n if (typeof aVal === 'number' && typeof bVal === 'number') {\n return sortDir === 'asc' ? aVal - bVal : bVal - aVal\n }\n\n const aStr = String(aVal ?? '')\n const bStr = String(bVal ?? '')\n const cmp = aStr.localeCompare(bStr, 'nb', { numeric: true })\n return sortDir === 'asc' ? cmp : -cmp\n })\n }, [filtered, sortKey, sortDir, normalizedColumns])\n\n const pageCount = effectivePageSize ? Math.max(1, Math.ceil(sorted.length / effectivePageSize)) : 1\n const currentPage = Math.min(page, pageCount)\n const pageRows = effectivePageSize\n ? sorted.slice((currentPage - 1) * effectivePageSize, currentPage * effectivePageSize)\n : sorted\n\n const clearSelection = () => setSelected([])\n const allOnPageSelected =\n pageRows.length > 0 && pageRows.every((row, i) => selected.includes(getRowKey(row, i)))\n const someOnPageSelected =\n !allOnPageSelected && pageRows.some((row, i) => selected.includes(getRowKey(row, i)))\n\n const toggleAllOnPage = () => {\n const pageIds = pageRows.map((row, i) => getRowKey(row, i))\n if (allOnPageSelected) {\n setSelected((prev) => prev.filter((id) => !pageIds.includes(id)))\n } else {\n setSelected((prev) => Array.from(new Set([...prev, ...pageIds])))\n }\n }\n\n const toggleRow = (id: string | number) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]))\n }\n\n const hasActiveFilters = Object.values(filters).some((v) => v.trim() !== '')\n const filterableColumns = normalizedColumns.filter((c) => c.filterable)\n const colSpan = normalizedColumns.length + (selectable ? 1 : 0) + (rowMenu ? 1 : 0)\n\n const rowHoverable = Boolean(onRowClick || isRich)\n\n const selectedRowBg = 'bg-[color-mix(in_srgb,var(--tollerud-yellow)_7%,var(--tollerud-noir-900))]'\n const selectedRowHoverBg =\n 'group-hover/tr:bg-[color-mix(in_srgb,var(--tollerud-yellow)_12%,var(--tollerud-noir-800))]'\n\n const cellSurface = (rowIndex: number, isSelected: boolean) =>\n cn(\n 'transition-colors duration-fast',\n rowHoverable && (isSelected ? selectedRowHoverBg : 'group-hover/tr:bg-tollerud-noir-800'),\n isSelected && selectedRowBg,\n !isSelected && striped && rowIndex % 2 === 0 && 'bg-tollerud-noir-950',\n !isSelected && !(striped && rowIndex % 2 === 0) && isRich && 'bg-tollerud-noir-900',\n )\n\n const stickyPosition = (edge: 'check' | 'first' | 'actions') =>\n pinEdges &&\n cn(\n 'sticky z-[2]',\n edge === 'check' && 'left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n edge === 'first' && cn('left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]', selectable && 'left-10'),\n edge === 'actions' && 'right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n )\n\n const stickyHead = (edge: 'check' | 'first' | 'actions') =>\n pinEdges &&\n cn(\n 'sticky z-[3] bg-tollerud-noir-900',\n edge === 'check' && 'left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n edge === 'first' && cn('left-0 shadow-[4px_0_12px_-6px_rgba(0,0,0,0.55)]', selectable && 'left-10'),\n edge === 'actions' && 'right-0 shadow-[-4px_0_12px_-6px_rgba(0,0,0,0.55)]',\n )\n\n const stickyBody = (edge: 'check' | 'first' | 'actions') => stickyPosition(edge)\n\n const sortIndicator = (key: string) => {\n const active = sortKey === key\n return (\n <ChevronDown\n size={12}\n className={cn(\n 'shrink-0 transition-transform duration-fast',\n active ? 'text-tollerud-yellow opacity-100' : 'opacity-25',\n active && sortDir === 'desc' && 'rotate-180'\n )}\n aria-hidden\n />\n )\n }\n\n const headerCell = (col: NormalizedColumn<T>, columnIndex: number, reactKey: string) => {\n const content = (\n <>\n {col.label}\n {col.sortable && (isRich ? sortIndicator(col.key) : (\n <>\n {sortKey === col.key && (\n <span className=\"text-tollerud-accent\">{sortDir === 'asc' ? '↑' : '↓'}</span>\n )}\n {sortKey !== col.key && <span className=\"text-tollerud-text-muted/30\">↕</span>}\n </>\n ))}\n {!isRich && col.filterable && (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn('opacity-40', filters[col.key] && 'opacity-100 text-tollerud-yellow')}\n aria-hidden\n >\n <path d=\"M4 4h16v2.172a2 2 0 0 1-.586 1.414L15 12v7l-6 2v-8.5L4.52 7.53A2 2 0 0 1 4 6.16V4z\" />\n </svg>\n )}\n </>\n )\n const contentAlignment = cn(\n 'inline-flex items-center gap-1',\n col.align === 'right' && 'justify-end w-full',\n col.align === 'center' && 'justify-center w-full',\n )\n return (\n <th\n key={reactKey}\n className={cn(\n 'px-3 py-2.5 text-left text-xs font-semibold text-tollerud-text-muted uppercase tracking-wider whitespace-nowrap',\n col.align === 'right' && 'text-right',\n col.align === 'center' && 'text-center',\n columnIndex === 0 && stickyHead('first'),\n )}\n style={col.width ? { width: col.width, minWidth: col.width } : undefined}\n aria-sort={col.sortable ? (sortKey === col.key ? (sortDir === 'asc' ? 'ascending' : 'descending') : 'none') : undefined}\n >\n {col.sortable ? (\n <button\n type=\"button\"\n onClick={() => toggleSort(col.key)}\n className={cn(\n contentAlignment,\n 'cursor-pointer select-none uppercase tracking-wider hover:text-tollerud-text-primary transition-colors tollerud-focus-ring rounded-sm',\n )}\n >\n {content}\n </button>\n ) : (\n <span className={contentAlignment}>{content}</span>\n )}\n </th>\n )\n }\n\n const tableBody = () => {\n if (loading) {\n return (\n <tbody>\n {Array.from({ length: skeletonRows }).map((_, i) => (\n <tr key={i} className=\"border-b border-tollerud-border/20\">\n {selectable && (\n <td className={cn('px-3 py-2.5 w-10 min-w-10', cellSurface(i, false), stickyBody('check'))}>\n <Skeleton className=\"h-4 w-4 rounded\" />\n </td>\n )}\n {normalizedColumns.map((col, j) => (\n <td\n key={columnReactKey(col, j)}\n className={cn(\n 'px-3 py-2.5 whitespace-nowrap',\n col.align === 'right' && 'text-right',\n cellSurface(i, false),\n j === 0 && stickyBody('first'),\n )}\n >\n <Skeleton className={cn('h-3', j === 0 ? 'w-[70%]' : 'w-[55%]')} />\n </td>\n ))}\n {rowMenu && (\n <td className={cn('px-3 py-2.5 w-11 min-w-11', cellSurface(i, false), stickyBody('actions'))}>\n <Skeleton className=\"h-4 w-4 rounded\" />\n </td>\n )}\n </tr>\n ))}\n </tbody>\n )\n }\n\n if (sorted.length === 0) {\n if (isRich && emptyState) {\n return null\n }\n return (\n <tbody>\n <tr>\n <td colSpan={colSpan} className=\"px-3 py-8 text-center text-sm text-tollerud-text-muted\">\n {hasActiveFilters || query || filterValue !== 'all' ? 'No matching rows' : emptyMessage}\n </td>\n </tr>\n </tbody>\n )\n }\n\n return (\n <tbody>\n {pageRows.map((row, i) => {\n const id = getRowKey(row, i)\n const isSelected = selected.includes(id)\n return (\n <tr\n key={id}\n className={cn(\n 'group/tr border-b border-tollerud-border/20',\n onRowClick && 'cursor-pointer',\n )}\n onClick={() => onRowClick?.(row)}\n >\n {selectable && (\n <td\n className={cn('px-3 py-2.5 w-10 min-w-10', cellSurface(i, isSelected), stickyBody('check'))}\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox checked={isSelected} onChange={() => toggleRow(id)} aria-label={`Select row ${id}`} />\n </td>\n )}\n {normalizedColumns.map((col, columnIndex) => {\n const value = row[col.key]\n return (\n <td\n key={columnReactKey(col, columnIndex)}\n className={cn(\n 'px-3 py-2.5 text-tollerud-text-secondary whitespace-nowrap',\n col.align === 'right' && 'text-right',\n col.align === 'center' && 'text-center',\n !col.render && 'font-mono text-xs',\n cellSurface(i, isSelected),\n columnIndex === 0 && stickyBody('first'),\n )}\n style={col.width ? { width: col.width, minWidth: col.width } : undefined}\n >\n {col.render ? col.render(value, row) : String(value ?? '—')}\n </td>\n )\n })}\n {rowMenu && (\n <td\n className={cn('px-2 py-2 w-11 min-w-11', cellSurface(i, isSelected), stickyBody('actions'))}\n onClick={(e) => e.stopPropagation()}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-tollerud-text-muted hover:text-tollerud-text-primary hover:bg-tollerud-surface-raised transition-colors tollerud-focus-ring\"\n aria-label=\"Row actions\"\n >\n <MoreHorizontal size={15} />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {rowMenu(row).map((item, index) => {\n if (item.sep) return <DropdownMenuSeparator key={index} />\n if (item.heading) return <DropdownMenuLabel key={index}>{item.label}</DropdownMenuLabel>\n return (\n <DropdownMenuItem key={index} onSelect={() => item.onSelect?.()}>\n {item.icon ? <span className=\"mr-2 inline-flex\">{item.icon}</span> : null}\n {item.label}\n </DropdownMenuItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n </td>\n )}\n </tr>\n )\n })}\n </tbody>\n )\n }\n\n const table = (\n <table className=\"w-full text-sm\" style={{ minWidth: tableMinWidth }}>\n <thead>\n <tr className=\"border-b border-tollerud-border/30 bg-tollerud-noir-900\">\n {selectable && (\n <th className={cn('px-3 py-2.5 w-10 min-w-10', stickyHead('check'))}>\n <Checkbox\n checked={allOnPageSelected}\n indeterminate={someOnPageSelected}\n onChange={toggleAllOnPage}\n aria-label=\"Select all rows on page\"\n />\n </th>\n )}\n {normalizedColumns.map((col, columnIndex) =>\n headerCell(col, columnIndex, columnReactKey(col, columnIndex))\n )}\n {rowMenu && <th className={cn('w-11 min-w-11', stickyHead('actions'))} aria-label=\"Row actions\" />}\n </tr>\n {!isRich && filterableColumns.length > 0 && (\n <tr className=\"border-b border-tollerud-border/20\">\n {normalizedColumns.map((col, columnIndex) => (\n <th\n key={columnReactKey(col, columnIndex)}\n className={cn('px-1.5 py-1', col.align === 'right' && 'text-right', col.align === 'center' && 'text-center')}\n >\n {col.filterable ? (\n <input\n type=\"text\"\n placeholder={`Filter ${col.label.toLowerCase()}…`}\n value={filters[col.key] ?? ''}\n onChange={(e) => updateFilter(col.key, e.target.value)}\n className={cn(\n 'w-full px-2 py-1 text-xs rounded border transition-colors outline-none',\n 'bg-tollerud-noir-900/50 border-tollerud-border/20 text-tollerud-text-primary placeholder:text-tollerud-text-muted/40',\n 'focus:border-tollerud-yellow/40 focus:ring-1 focus:ring-tollerud-yellow/20',\n )}\n />\n ) : null}\n </th>\n ))}\n </tr>\n )}\n </thead>\n {tableBody()}\n </table>\n )\n\n const bulkActionButtons = bulkActions.map((action, index) => (\n <Button\n key={index}\n variant={action.variant ?? 'ghost'}\n size=\"sm\"\n onClick={() => action.onRun(selected, clearSelection)}\n >\n {action.icon ? <span className=\"mr-1.5 inline-flex shrink-0\">{action.icon}</span> : null}\n {action.label}\n </Button>\n ))\n\n const tableScroll = (\n <div\n className=\"tollerud-datatable-scroll min-w-0 w-full overflow-x-auto overscroll-x-contain\"\n role=\"region\"\n aria-label=\"Scrollable table\"\n tabIndex={pinEdges ? 0 : undefined} // eslint-disable-line jsx-a11y/no-noninteractive-tabindex -- keyboard horizontal scroll\n >\n {table}\n </div>\n )\n\n if (!isRich) {\n return (\n <div ref={forwardedRef} className={cn('min-w-0 w-full rounded-lg border border-tollerud-border/30', className)}>\n {tableScroll}\n </div>\n )\n }\n\n const showEmpty = !loading && sorted.length === 0\n\n return (\n <div\n ref={forwardedRef}\n className={cn(\n 'min-w-0 w-full rounded-lg border border-tollerud-border/30 bg-tollerud-noir-900',\n className,\n )}\n >\n {(searchable || filter || toolbarRight) && (\n <div className=\"flex flex-col gap-2.5 border-b border-tollerud-border/30 px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between\">\n {searchable && (\n <div className=\"flex h-9 w-full items-center gap-2 rounded-lg border border-tollerud-border/30 bg-tollerud-noir-950 px-3 sm:w-[230px] sm:shrink-0\">\n <Search size={15} className=\"shrink-0 text-tollerud-text-muted\" aria-hidden />\n <input\n value={query}\n onChange={(e) => {\n setQuery(e.target.value)\n setPage(1)\n }}\n placeholder={searchPlaceholder}\n className=\"min-w-0 flex-1 border-none bg-transparent text-sm text-tollerud-text-primary outline-none placeholder:text-tollerud-text-muted\"\n />\n {query && (\n <button\n type=\"button\"\n onClick={() => setQuery('')}\n className=\"text-tollerud-text-muted hover:text-tollerud-text-primary\"\n aria-label=\"Clear search\"\n >\n <X size={13} />\n </button>\n )}\n </div>\n )}\n {(filter || toolbarRight) && (\n <div\n className={cn(\n 'flex min-w-0 items-center gap-2',\n filter ? 'justify-between' : 'justify-end',\n 'sm:justify-end',\n )}\n >\n {filter &&\n (filter.variant === 'combobox' ? (\n <Combobox\n value={filterValue}\n onChange={(value) => {\n setFilterValue(value)\n setPage(1)\n }}\n options={filterSelectOptions}\n placeholder={filter.placeholder ?? filter.allLabel ?? 'Filter…'}\n className=\"min-w-0 flex-1 sm:flex-none sm:w-[200px]\"\n />\n ) : (\n <div className=\"min-w-0 flex-1 overflow-x-auto sm:flex-none\">\n <Segmented\n size=\"sm\"\n value={filterValue}\n onChange={(value) => {\n setFilterValue(value)\n setPage(1)\n }}\n options={filterSelectOptions}\n />\n </div>\n ))}\n {toolbarRight ? <div className=\"shrink-0\">{toolbarRight}</div> : null}\n </div>\n )}\n </div>\n )}\n\n {selectable && selected.length > 0 && (\n <div className=\"flex flex-wrap items-center justify-between gap-3 border-b border-tollerud-border/30 bg-tollerud-yellow/[0.07] px-4 py-2.5\">\n <span className=\"flex items-center gap-2 text-sm text-tollerud-text-primary\">\n <Badge variant=\"accent\">{selected.length}</Badge>\n selected\n </span>\n <div className=\"flex flex-wrap items-center gap-2\">\n {bulkActions.length > 1 ? (\n <ButtonGroup size=\"sm\">{bulkActionButtons}</ButtonGroup>\n ) : (\n bulkActionButtons\n )}\n </div>\n </div>\n )}\n\n {showEmpty ? (\n <div className=\"p-2\">{emptyState ?? <div className=\"py-12 text-center text-sm text-tollerud-text-muted\">No results.</div>}</div>\n ) : (\n tableScroll\n )}\n\n {!loading && sorted.length > 0 && (\n <div className=\"relative z-[4] flex flex-wrap items-center justify-between gap-3 border-t border-tollerud-border/30 px-4 py-2\">\n <span className=\"text-xs text-tollerud-text-muted\">\n {effectivePageSize\n ? `Showing ${(currentPage - 1) * effectivePageSize + 1}–${(currentPage - 1) * effectivePageSize + pageRows.length} of ${sorted.length}`\n : `${sorted.length} row${sorted.length === 1 ? '' : 's'}`}\n </span>\n <div className=\"flex flex-wrap items-center gap-3\">\n {footer}\n {allowsPageSizeChange && (\n <Select\n label=\"Rows\"\n layout=\"inline\"\n size=\"sm\"\n value={String(activePageSize)}\n onChange={(value) => {\n setActivePageSize(Number(value))\n setPage(1)\n }}\n options={pageSizeOptionsResolved.map((size) => ({\n value: String(size),\n label: String(size),\n }))}\n className=\"w-[72px]\"\n />\n )}\n {effectivePageSize && pageCount > 1 && (\n <Pagination page={currentPage} pageCount={pageCount} onChange={setPage} siblingCount={1} />\n )}\n </div>\n </div>\n )}\n </div>\n )\n}\n\nconst DataTable = forwardRef(<T extends Record<string, unknown>>(\n props: DataTableProps<T>,\n ref: React.Ref<HTMLDivElement>\n) => <DataTableInner {...props} forwardedRef={ref} />)\n\nDataTable.displayName = 'DataTable'\n\nexport { DataTable }\n"]} |
| 'use client'; | ||
| import { FloatingDropdownPortal } from './chunk-BFC62UF6.js'; | ||
| import { cn } from './chunk-62DSK4IE.js'; | ||
| import { forwardRef, useId, useRef, useState, useMemo, useCallback, useEffect } from 'react'; | ||
| import { Check, ChevronDown, Search } from 'lucide-react'; | ||
| import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; | ||
| var defaultFilter = (option, query) => option.label.toLowerCase().includes(query.toLowerCase()); | ||
| function flattenOptions(options, groups) { | ||
| if (groups && groups.length > 0) { | ||
| return groups.flatMap((group) => group.options); | ||
| } | ||
| return options; | ||
| } | ||
| function filterGroups(groups, query, filter) { | ||
| if (!query) return groups; | ||
| return groups.map((group) => ({ | ||
| ...group, | ||
| options: group.options.filter((option) => filter(option, query)) | ||
| })).filter((group) => group.options.length > 0); | ||
| } | ||
| var Combobox = forwardRef(function Combobox2({ | ||
| options = [], | ||
| groups, | ||
| value: valueProp, | ||
| defaultValue, | ||
| onChange, | ||
| placeholder = "Search\u2026", | ||
| label, | ||
| error, | ||
| filter = defaultFilter, | ||
| searchPlacement = "trigger", | ||
| className, | ||
| disabled, | ||
| required | ||
| }, ref) { | ||
| const id = useId(); | ||
| const autoErrorId = useId(); | ||
| const errorId = error ? autoErrorId : void 0; | ||
| const internalRootRef = useRef(null); | ||
| const anchorRef = useRef(null); | ||
| const listRef = useRef(null); | ||
| const isControlled = valueProp !== void 0; | ||
| const [internalValue, setInternalValue] = useState(defaultValue); | ||
| const value = isControlled ? valueProp : internalValue; | ||
| const [open, setOpen] = useState(false); | ||
| const [query, setQuery] = useState(""); | ||
| const [activeIndex, setActiveIndex] = useState(0); | ||
| const isGrouped = Boolean(groups && groups.length > 0); | ||
| const allOptions = useMemo(() => flattenOptions(options, groups), [options, groups]); | ||
| const filteredGroups = useMemo(() => { | ||
| if (!isGrouped || !groups) return []; | ||
| return filterGroups(groups, query, filter); | ||
| }, [groups, isGrouped, query, filter]); | ||
| const filtered = useMemo(() => { | ||
| if (isGrouped) return filteredGroups.flatMap((group) => group.options); | ||
| if (!query) return options; | ||
| return options.filter((option) => filter(option, query)); | ||
| }, [filteredGroups, filter, isGrouped, options, query]); | ||
| const highlightedIndex = filtered.length === 0 ? 0 : Math.min(activeIndex, filtered.length - 1); | ||
| const activeOptionId = open && filtered.length > 0 ? `${id}-option-${highlightedIndex}` : void 0; | ||
| const selected = allOptions.find((option) => option.value === value); | ||
| const setRootRef = useCallback( | ||
| (node) => { | ||
| internalRootRef.current = node; | ||
| if (typeof ref === "function") ref(node); | ||
| else if (ref) ref.current = node; | ||
| }, | ||
| [ref] | ||
| ); | ||
| useEffect(() => { | ||
| if (!open) return; | ||
| function onClickOutside(e) { | ||
| const target = e.target; | ||
| if (internalRootRef.current?.contains(target)) return; | ||
| if (listRef.current?.contains(target)) return; | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| document.addEventListener("mousedown", onClickOutside); | ||
| return () => { | ||
| document.removeEventListener("mousedown", onClickOutside); | ||
| }; | ||
| }, [open]); | ||
| useEffect(() => { | ||
| if (!activeOptionId) return; | ||
| document.getElementById(activeOptionId)?.scrollIntoView({ block: "nearest" }); | ||
| }, [activeOptionId]); | ||
| const dropdownSearchCallbackRef = useCallback((node) => { | ||
| if (node && searchPlacement === "dropdown") { | ||
| queueMicrotask(() => node.focus()); | ||
| } | ||
| }, [searchPlacement]); | ||
| const commit = (option) => { | ||
| if (option.disabled) return; | ||
| if (!isControlled) setInternalValue(option.value); | ||
| onChange?.(option.value); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| }; | ||
| const onKeyDown = (e) => { | ||
| if (e.key === "ArrowDown") { | ||
| e.preventDefault(); | ||
| setOpen(true); | ||
| setActiveIndex((index) => Math.min(index + 1, filtered.length - 1)); | ||
| } else if (e.key === "ArrowUp") { | ||
| e.preventDefault(); | ||
| setActiveIndex((index) => Math.max(index - 1, 0)); | ||
| } else if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| const option = filtered[highlightedIndex]; | ||
| if (option) commit(option); | ||
| } else if (e.key === "Escape") { | ||
| if (open) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| } | ||
| }; | ||
| let flatIndex = 0; | ||
| const optionList = /* @__PURE__ */ jsxs("ul", { className: "m-0 list-none p-0", children: [ | ||
| filtered.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-tollerud-text-muted", children: "No results" }), | ||
| isGrouped ? filteredGroups.map((group) => /* @__PURE__ */ jsxs("li", { role: "presentation", children: [ | ||
| /* @__PURE__ */ jsx("div", { className: "px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-[0.06em] text-tollerud-text-muted", children: group.label }), | ||
| /* @__PURE__ */ jsx("ul", { role: "group", "aria-label": group.label, children: group.options.map((option) => { | ||
| const index = flatIndex++; | ||
| const isSelected = option.value === value; | ||
| return /* @__PURE__ */ jsxs( | ||
| "li", | ||
| { | ||
| id: `${id}-option-${index}`, | ||
| role: "option", | ||
| "aria-selected": isSelected, | ||
| onMouseDown: (e) => { | ||
| e.preventDefault(); | ||
| commit(option); | ||
| }, | ||
| onMouseEnter: () => setActiveIndex(index), | ||
| className: cn( | ||
| "flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer", | ||
| index === highlightedIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary", | ||
| option.disabled && "opacity-40 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| option.label, | ||
| isSelected && /* @__PURE__ */ jsx(Check, { size: 14, className: "text-tollerud-yellow" }) | ||
| ] | ||
| }, | ||
| option.value | ||
| ); | ||
| }) }) | ||
| ] }, group.label)) : filtered.map((option, index) => { | ||
| const isSelected = option.value === value; | ||
| return /* @__PURE__ */ jsxs( | ||
| "li", | ||
| { | ||
| id: `${id}-option-${index}`, | ||
| role: "option", | ||
| "aria-selected": isSelected, | ||
| onMouseDown: (e) => { | ||
| e.preventDefault(); | ||
| commit(option); | ||
| }, | ||
| onMouseEnter: () => setActiveIndex(index), | ||
| className: cn( | ||
| "flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer", | ||
| index === highlightedIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary", | ||
| option.disabled && "opacity-40 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| option.label, | ||
| isSelected && /* @__PURE__ */ jsx(Check, { size: 14, className: "text-tollerud-yellow" }) | ||
| ] | ||
| }, | ||
| option.value | ||
| ); | ||
| }) | ||
| ] }); | ||
| return /* @__PURE__ */ jsxs("div", { ref: setRootRef, className: cn("relative flex flex-col gap-1", className), children: [ | ||
| label && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: [ | ||
| label, | ||
| required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-tollerud-error", children: "*" }) | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { ref: anchorRef, className: "relative", children: searchPlacement === "dropdown" ? /* @__PURE__ */ jsxs( | ||
| "button", | ||
| { | ||
| id, | ||
| type: "button", | ||
| role: "combobox", | ||
| "aria-expanded": open, | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-haspopup": "listbox", | ||
| "aria-required": required || void 0, | ||
| "aria-invalid": error ? true : void 0, | ||
| "aria-describedby": errorId, | ||
| disabled, | ||
| onClick: () => { | ||
| if (open) { | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } else { | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| } | ||
| }, | ||
| onKeyDown: (e) => { | ||
| if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") { | ||
| e.preventDefault(); | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| } else if (e.key === "Escape") { | ||
| if (open) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| setOpen(false); | ||
| setQuery(""); | ||
| } | ||
| } | ||
| }, | ||
| className: cn( | ||
| "w-full font-sans text-base px-3 py-2.5 rounded text-left flex items-center justify-between gap-2", | ||
| "bg-tollerud-surface-raised border", | ||
| "transition-[border-color] duration-fast", | ||
| "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]", | ||
| error ? "border-tollerud-error" : "border-tollerud-border", | ||
| disabled && "opacity-50 pointer-events-none" | ||
| ), | ||
| children: [ | ||
| /* @__PURE__ */ jsx("span", { className: selected ? "text-tollerud-text-primary" : "text-tollerud-text-muted", children: selected?.label ?? placeholder }), | ||
| /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 15, | ||
| className: cn( | ||
| "shrink-0 text-tollerud-text-muted transition-transform duration-fast", | ||
| open && "rotate-180" | ||
| ) | ||
| } | ||
| ) | ||
| ] | ||
| } | ||
| ) : /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| id, | ||
| role: "combobox", | ||
| "aria-expanded": open, | ||
| "aria-autocomplete": "list", | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-activedescendant": activeOptionId, | ||
| "aria-required": required || void 0, | ||
| "aria-invalid": error ? true : void 0, | ||
| "aria-describedby": errorId, | ||
| disabled, | ||
| value: open ? query : selected?.label ?? "", | ||
| placeholder: selected ? selected.label : placeholder, | ||
| onFocus: () => { | ||
| setOpen(true); | ||
| setActiveIndex(0); | ||
| }, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setActiveIndex(0); | ||
| if (!open) setOpen(true); | ||
| }, | ||
| onKeyDown, | ||
| className: cn( | ||
| "w-full font-sans text-base px-3 py-2.5 pr-9 rounded", | ||
| "bg-tollerud-surface-raised border", | ||
| "text-tollerud-text-primary placeholder:text-tollerud-text-muted", | ||
| "transition-[border-color] duration-fast", | ||
| "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]", | ||
| error ? "border-tollerud-error" : "border-tollerud-border", | ||
| disabled && "opacity-50 pointer-events-none" | ||
| ) | ||
| } | ||
| ), | ||
| /* @__PURE__ */ jsx( | ||
| ChevronDown, | ||
| { | ||
| size: 15, | ||
| className: cn( | ||
| "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-fast", | ||
| open && "rotate-180" | ||
| ) | ||
| } | ||
| ) | ||
| ] }) }), | ||
| /* @__PURE__ */ jsx( | ||
| FloatingDropdownPortal, | ||
| { | ||
| open, | ||
| anchorRef, | ||
| popoverRef: listRef, | ||
| id: `${id}-listbox`, | ||
| role: "listbox", | ||
| placementOptions: { maxHeight: 320 }, | ||
| className: cn( | ||
| "rounded-lg border border-tollerud-border bg-tollerud-surface-overlay", | ||
| searchPlacement === "dropdown" ? "overflow-hidden" : "overflow-auto py-1" | ||
| ), | ||
| children: searchPlacement === "dropdown" ? /* @__PURE__ */ jsxs(Fragment, { children: [ | ||
| /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-tollerud-border px-3 py-2", children: [ | ||
| /* @__PURE__ */ jsx(Search, { size: 13, className: "shrink-0 text-tollerud-text-muted" }), | ||
| /* @__PURE__ */ jsx( | ||
| "input", | ||
| { | ||
| ref: dropdownSearchCallbackRef, | ||
| type: "text", | ||
| role: "combobox", | ||
| "aria-expanded": true, | ||
| "aria-autocomplete": "list", | ||
| "aria-controls": `${id}-listbox`, | ||
| "aria-activedescendant": activeOptionId, | ||
| placeholder, | ||
| value: query, | ||
| onChange: (e) => { | ||
| setQuery(e.target.value); | ||
| setActiveIndex(0); | ||
| }, | ||
| onKeyDown, | ||
| className: "w-full bg-transparent text-sm pointer-coarse:text-base text-tollerud-text-primary placeholder:text-tollerud-text-muted focus:outline-none" | ||
| } | ||
| ) | ||
| ] }), | ||
| /* @__PURE__ */ jsx("div", { className: "overflow-auto py-1", style: { maxHeight: 256 }, children: optionList }) | ||
| ] }) : optionList | ||
| } | ||
| ), | ||
| error && /* @__PURE__ */ jsx("p", { id: errorId, className: "text-xs text-tollerud-error mt-0.5", children: error }) | ||
| ] }); | ||
| }); | ||
| Combobox.displayName = "Combobox"; | ||
| export { Combobox }; | ||
| //# sourceMappingURL=chunk-X3TTOIGW.js.map | ||
| //# sourceMappingURL=chunk-X3TTOIGW.js.map |
| {"version":3,"sources":["../components/Combobox.tsx"],"names":["Combobox"],"mappings":"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","file":"chunk-X3TTOIGW.js","sourcesContent":["'use client'\n\nimport { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { Check, ChevronDown, Search } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { FloatingDropdownPortal } from '@/lib/floating-dropdown'\n\nexport interface ComboboxOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface ComboboxGroup {\n /** Section title shown above options in the dropdown */\n label: string\n options: ComboboxOption[]\n}\n\nexport interface ComboboxProps {\n /** Flat option list — use when sections are not needed */\n options?: ComboboxOption[]\n /** Grouped options with section titles — takes precedence over `options` when provided */\n groups?: ComboboxGroup[]\n value?: string\n defaultValue?: string\n onChange?: (value: string) => void\n placeholder?: string\n label?: string\n error?: string\n /** Filter predicate, defaults to a case-insensitive substring match on the label */\n filter?: (option: ComboboxOption, query: string) => boolean\n /**\n * Where the search input lives.\n * - `'trigger'` (default) — the trigger input doubles as the search field, matching the original behaviour.\n * - `'dropdown'` — the trigger is a button showing the selected value; the search input appears at the top of the dropdown panel.\n */\n searchPlacement?: 'trigger' | 'dropdown'\n className?: string\n disabled?: boolean\n required?: boolean\n}\n\nconst defaultFilter = (option: ComboboxOption, query: string) =>\n option.label.toLowerCase().includes(query.toLowerCase())\n\nfunction flattenOptions(options: ComboboxOption[], groups?: ComboboxGroup[]) {\n if (groups && groups.length > 0) {\n return groups.flatMap((group) => group.options)\n }\n return options\n}\n\nfunction filterGroups(\n groups: ComboboxGroup[],\n query: string,\n filter: (option: ComboboxOption, query: string) => boolean\n): ComboboxGroup[] {\n if (!query) return groups\n return groups\n .map((group) => ({\n ...group,\n options: group.options.filter((option) => filter(option, query)),\n }))\n .filter((group) => group.options.length > 0)\n}\n\nconst Combobox = forwardRef<HTMLDivElement, ComboboxProps>(function Combobox(\n {\n options = [],\n groups,\n value: valueProp,\n defaultValue,\n onChange,\n placeholder = 'Search…',\n label,\n error,\n filter = defaultFilter,\n searchPlacement = 'trigger',\n className,\n disabled,\n required,\n },\n ref\n) {\n const id = useId()\n const autoErrorId = useId()\n const errorId = error ? autoErrorId : undefined\n const internalRootRef = useRef<HTMLDivElement>(null)\n const anchorRef = useRef<HTMLDivElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n const isControlled = valueProp !== undefined\n const [internalValue, setInternalValue] = useState(defaultValue)\n const value = isControlled ? valueProp : internalValue\n\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [activeIndex, setActiveIndex] = useState(0)\n\n const isGrouped = Boolean(groups && groups.length > 0)\n const allOptions = useMemo(() => flattenOptions(options, groups), [options, groups])\n\n const filteredGroups = useMemo(() => {\n if (!isGrouped || !groups) return []\n return filterGroups(groups, query, filter)\n }, [groups, isGrouped, query, filter])\n\n const filtered = useMemo(() => {\n if (isGrouped) return filteredGroups.flatMap((group) => group.options)\n if (!query) return options\n return options.filter((option) => filter(option, query))\n }, [filteredGroups, filter, isGrouped, options, query])\n\n const highlightedIndex =\n filtered.length === 0 ? 0 : Math.min(activeIndex, filtered.length - 1)\n const activeOptionId =\n open && filtered.length > 0 ? `${id}-option-${highlightedIndex}` : undefined\n\n const selected = allOptions.find((option) => option.value === value)\n\n const setRootRef = useCallback(\n (node: HTMLDivElement | null) => {\n internalRootRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n },\n [ref]\n )\n\n useEffect(() => {\n if (!open) return\n function onClickOutside(e: MouseEvent) {\n const target = e.target as Node\n if (internalRootRef.current?.contains(target)) return\n if (listRef.current?.contains(target)) return\n setOpen(false)\n setQuery('')\n }\n document.addEventListener('mousedown', onClickOutside)\n return () => {\n document.removeEventListener('mousedown', onClickOutside)\n }\n }, [open])\n\n // Keep the highlighted option visible while arrowing through a list taller\n // than the dropdown. The options live in a portal, so resolve them by id.\n useEffect(() => {\n if (!activeOptionId) return\n document.getElementById(activeOptionId)?.scrollIntoView({ block: 'nearest' })\n }, [activeOptionId])\n\n // Focus the search input as soon as it mounts (the portal renders null until\n // coords are computed, so a dep-effect on `open` always fires one render too\n // early). queueMicrotask defers until after React's layout effects, ensuring\n // FloatingDropdownPortal's useDialogEscapeHatch (useLayoutEffect) has already\n // attached its focusin stopPropagation before the focus event fires.\n const dropdownSearchCallbackRef = useCallback((node: HTMLInputElement | null) => {\n if (node && searchPlacement === 'dropdown') {\n queueMicrotask(() => node.focus())\n }\n }, [searchPlacement])\n\n const commit = (option: ComboboxOption) => {\n if (option.disabled) return\n if (!isControlled) setInternalValue(option.value)\n onChange?.(option.value)\n setOpen(false)\n setQuery('')\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n setActiveIndex((index) => Math.min(index + 1, filtered.length - 1))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setActiveIndex((index) => Math.max(index - 1, 0))\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const option = filtered[highlightedIndex]\n if (option) commit(option)\n } else if (e.key === 'Escape') {\n // Close only the innermost layer: consume the event while the dropdown\n // is open so a surrounding Dialog stays open; let it through otherwise.\n if (open) {\n e.preventDefault()\n e.stopPropagation()\n setOpen(false)\n setQuery('')\n }\n }\n }\n\n let flatIndex = 0\n\n const optionList = (\n <ul className=\"m-0 list-none p-0\">\n {filtered.length === 0 && (\n <li className=\"px-3 py-2 text-sm text-tollerud-text-muted\">No results</li>\n )}\n\n {isGrouped\n ? filteredGroups.map((group) => (\n <li key={group.label} role=\"presentation\">\n <div className=\"px-3 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-[0.06em] text-tollerud-text-muted\">\n {group.label}\n </div>\n <ul role=\"group\" aria-label={group.label}>\n {group.options.map((option) => {\n const index = flatIndex++\n const isSelected = option.value === value\n return (\n <li\n key={option.value}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => {\n e.preventDefault()\n commit(option)\n }}\n onMouseEnter={() => setActiveIndex(index)}\n className={cn(\n 'flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer',\n index === highlightedIndex\n ? 'bg-tollerud-surface-hover text-tollerud-text-primary'\n : 'text-tollerud-text-secondary',\n option.disabled && 'opacity-40 pointer-events-none'\n )}\n >\n {option.label}\n {isSelected && <Check size={14} className=\"text-tollerud-yellow\" />}\n </li>\n )\n })}\n </ul>\n </li>\n ))\n : filtered.map((option, index) => {\n const isSelected = option.value === value\n return (\n <li\n key={option.value}\n id={`${id}-option-${index}`}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => {\n e.preventDefault()\n commit(option)\n }}\n onMouseEnter={() => setActiveIndex(index)}\n className={cn(\n 'flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer',\n index === highlightedIndex\n ? 'bg-tollerud-surface-hover text-tollerud-text-primary'\n : 'text-tollerud-text-secondary',\n option.disabled && 'opacity-40 pointer-events-none'\n )}\n >\n {option.label}\n {isSelected && <Check size={14} className=\"text-tollerud-yellow\" />}\n </li>\n )\n })}\n </ul>\n )\n\n return (\n <div ref={setRootRef} className={cn('relative flex flex-col gap-1', className)}>\n {label && (\n <label htmlFor={id} className=\"text-xs font-medium text-tollerud-text-muted\">\n {label}\n {required && <span aria-hidden=\"true\" className=\"ml-0.5 text-tollerud-error\">*</span>}\n </label>\n )}\n <div ref={anchorRef} className=\"relative\">\n {searchPlacement === 'dropdown' ? (\n <button\n id={id}\n type=\"button\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-haspopup=\"listbox\"\n aria-required={required || undefined}\n aria-invalid={error ? true : undefined}\n aria-describedby={errorId}\n disabled={disabled}\n onClick={() => {\n if (open) {\n setOpen(false)\n setQuery('')\n } else {\n setOpen(true)\n setActiveIndex(0)\n }\n }}\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setOpen(true)\n setActiveIndex(0)\n } else if (e.key === 'Escape') {\n if (open) {\n e.preventDefault()\n e.stopPropagation()\n setOpen(false)\n setQuery('')\n }\n }\n }}\n className={cn(\n 'w-full font-sans text-base px-3 py-2.5 rounded text-left flex items-center justify-between gap-2',\n 'bg-tollerud-surface-raised border',\n 'transition-[border-color] duration-fast',\n 'focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]',\n error ? 'border-tollerud-error' : 'border-tollerud-border',\n disabled && 'opacity-50 pointer-events-none'\n )}\n >\n <span className={selected ? 'text-tollerud-text-primary' : 'text-tollerud-text-muted'}>\n {selected?.label ?? placeholder}\n </span>\n <ChevronDown\n size={15}\n className={cn(\n 'shrink-0 text-tollerud-text-muted transition-transform duration-fast',\n open && 'rotate-180'\n )}\n />\n </button>\n ) : (\n <>\n <input\n id={id}\n role=\"combobox\"\n aria-expanded={open}\n aria-autocomplete=\"list\"\n aria-controls={`${id}-listbox`}\n aria-activedescendant={activeOptionId}\n aria-required={required || undefined}\n aria-invalid={error ? true : undefined}\n aria-describedby={errorId}\n disabled={disabled}\n value={open ? query : selected?.label ?? ''}\n placeholder={selected ? selected.label : placeholder}\n onFocus={() => {\n setOpen(true)\n setActiveIndex(0)\n }}\n onChange={(e) => {\n setQuery(e.target.value)\n setActiveIndex(0)\n if (!open) setOpen(true)\n }}\n onKeyDown={onKeyDown}\n className={cn(\n 'w-full font-sans text-base px-3 py-2.5 pr-9 rounded',\n 'bg-tollerud-surface-raised border',\n 'text-tollerud-text-primary placeholder:text-tollerud-text-muted',\n 'transition-[border-color] duration-fast',\n 'focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_var(--tollerud-yellow-warm,#E8D500)]',\n error ? 'border-tollerud-error' : 'border-tollerud-border',\n disabled && 'opacity-50 pointer-events-none'\n )}\n />\n <ChevronDown\n size={15}\n className={cn(\n 'pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-fast',\n open && 'rotate-180'\n )}\n />\n </>\n )}\n </div>\n\n <FloatingDropdownPortal\n open={open}\n anchorRef={anchorRef}\n popoverRef={listRef}\n id={`${id}-listbox`}\n role=\"listbox\"\n placementOptions={{ maxHeight: 320 }}\n className={cn(\n 'rounded-lg border border-tollerud-border bg-tollerud-surface-overlay',\n searchPlacement === 'dropdown' ? 'overflow-hidden' : 'overflow-auto py-1'\n )}\n >\n {searchPlacement === 'dropdown' ? (\n <>\n <div className=\"flex items-center gap-2 border-b border-tollerud-border px-3 py-2\">\n <Search size={13} className=\"shrink-0 text-tollerud-text-muted\" />\n <input\n ref={dropdownSearchCallbackRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={true}\n aria-autocomplete=\"list\"\n aria-controls={`${id}-listbox`}\n aria-activedescendant={activeOptionId}\n placeholder={placeholder}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value)\n setActiveIndex(0)\n }}\n onKeyDown={onKeyDown}\n // pointer-coarse:text-base forces ≥16px on touch devices so iOS\n // Safari does not auto-zoom (and trigger a scroll) when the field\n // is focused on open. Desktop keeps the 14px (text-sm) sizing.\n className=\"w-full bg-transparent text-sm pointer-coarse:text-base text-tollerud-text-primary placeholder:text-tollerud-text-muted focus:outline-none\"\n />\n </div>\n <div className=\"overflow-auto py-1\" style={{ maxHeight: 256 }}>\n {optionList}\n </div>\n </>\n ) : (\n optionList\n )}\n </FloatingDropdownPortal>\n\n {error && <p id={errorId} className=\"text-xs text-tollerud-error mt-0.5\">{error}</p>}\n </div>\n )\n})\nCombobox.displayName = 'Combobox'\n\nexport { Combobox }\n"]} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Unidentified License
LicenseSomething that seems like a license was found, but its contents could not be matched with a known license.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Unidentified License
LicenseSomething that seems like a license was found, but its contents could not be matched with a known license.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
3286325
0.37%15920
0.38%