@dowel-ui/react 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/blocks/admin-dashboard/meta.js +31 -0
- package/dist/blocks/admin-dashboard/meta.js.map +1 -0
- package/dist/blocks/agent-console/meta.js +27 -0
- package/dist/blocks/agent-console/meta.js.map +1 -0
- package/dist/blocks/ai-dashboard/meta.js +25 -0
- package/dist/blocks/ai-dashboard/meta.js.map +1 -0
- package/dist/blocks/ai-workspace/meta.js +33 -0
- package/dist/blocks/ai-workspace/meta.js.map +1 -0
- package/dist/blocks/analytics/meta.js +24 -0
- package/dist/blocks/analytics/meta.js.map +1 -0
- package/dist/blocks/billing/meta.js +25 -0
- package/dist/blocks/billing/meta.js.map +1 -0
- package/dist/blocks/command-center/meta.js +28 -0
- package/dist/blocks/command-center/meta.js.map +1 -0
- package/dist/blocks/crm/meta.js +32 -0
- package/dist/blocks/crm/meta.js.map +1 -0
- package/dist/blocks/onboarding/meta.js +23 -0
- package/dist/blocks/onboarding/meta.js.map +1 -0
- package/dist/components/accordion/accordion.js +1 -1
- package/dist/components/accordion/accordion.js.map +1 -1
- package/dist/components/activity-feed/activity-feed.js +1 -1
- package/dist/components/activity-feed/activity-feed.js.map +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
- package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
- package/dist/components/ai-extraction-review/ai-extraction-review.js +1 -1
- package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -1
- package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
- package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
- package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
- package/dist/components/ai-response/ai-response.js +2 -2
- package/dist/components/ai-response/ai-response.js.map +1 -1
- package/dist/components/ai-tool/ai-tool.js +2 -2
- package/dist/components/ai-tool/ai-tool.js.map +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.js +141 -0
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/breadcrumb/index.js +2 -0
- package/dist/components/breadcrumb/meta.js +17 -0
- package/dist/components/breadcrumb/meta.js.map +1 -0
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/calendar/calendar.js +2 -2
- package/dist/components/calendar/calendar.js.map +1 -1
- package/dist/components/code-block/code-block.js +1 -1
- package/dist/components/code-block/code-block.js.map +1 -1
- package/dist/components/collapsible/collapsible.d.ts +30 -0
- package/dist/components/collapsible/collapsible.d.ts.map +1 -0
- package/dist/components/collapsible/collapsible.js +38 -0
- package/dist/components/collapsible/collapsible.js.map +1 -0
- package/dist/components/collapsible/index.d.ts +2 -0
- package/dist/components/collapsible/index.js +2 -0
- package/dist/components/collapsible/meta.js +17 -0
- package/dist/components/collapsible/meta.js.map +1 -0
- package/dist/components/combobox/combobox.js +2 -2
- package/dist/components/combobox/combobox.js.map +1 -1
- package/dist/components/command/command.js +1 -1
- package/dist/components/command/command.js.map +1 -1
- package/dist/components/data-table/data-table.d.ts.map +1 -1
- package/dist/components/data-table/data-table.js +4 -1
- package/dist/components/data-table/data-table.js.map +1 -1
- package/dist/components/dialog/dialog.js +2 -2
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/diff-viewer/diff-viewer.js +4 -4
- package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
- package/dist/components/direction/direction.d.ts +35 -0
- package/dist/components/direction/direction.d.ts.map +1 -0
- package/dist/components/direction/direction.js +14 -0
- package/dist/components/direction/direction.js.map +1 -0
- package/dist/components/direction/index.d.ts +2 -0
- package/dist/components/direction/index.js +2 -0
- package/dist/components/direction/meta.js +17 -0
- package/dist/components/direction/meta.js.map +1 -0
- package/dist/components/drawer/drawer.js +1 -1
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
- package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/input/input.js +1 -1
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/log-viewer/log-viewer.js +2 -2
- package/dist/components/log-viewer/log-viewer.js.map +1 -1
- package/dist/components/meter/meter.js +1 -1
- package/dist/components/meter/meter.js.map +1 -1
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +3 -2
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/permission-matrix/permission-matrix.js +3 -3
- package/dist/components/permission-matrix/permission-matrix.js.map +1 -1
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/record-diff/record-diff.js +4 -4
- package/dist/components/record-diff/record-diff.js.map +1 -1
- package/dist/components/select/select.js +2 -2
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/sheet/sheet.d.ts.map +1 -1
- package/dist/components/sheet/sheet.js +2 -2
- package/dist/components/sheet/sheet.js.map +1 -1
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.js +2 -0
- package/dist/components/sidebar/meta.js +17 -0
- package/dist/components/sidebar/meta.js.map +1 -0
- package/dist/components/sidebar/sidebar.d.ts +69 -0
- package/dist/components/sidebar/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.js +256 -0
- package/dist/components/sidebar/sidebar.js.map +1 -0
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/table/table.js +2 -2
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tags-input/tags-input.js +1 -1
- package/dist/components/tags-input/tags-input.js.map +1 -1
- package/dist/components/textarea/index.d.ts +2 -0
- package/dist/components/textarea/index.js +2 -0
- package/dist/components/textarea/meta.js +17 -0
- package/dist/components/textarea/meta.js.map +1 -0
- package/dist/components/textarea/textarea.d.ts +43 -0
- package/dist/components/textarea/textarea.d.ts.map +1 -0
- package/dist/components/textarea/textarea.js +119 -0
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/time-range-picker/time-range-picker.js +3 -3
- package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
- package/dist/components/toast/toast.d.ts +1 -1
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +2 -2
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.js +7 -2
- package/dist/lib/styles.d.ts +16 -1
- package/dist/lib/styles.d.ts.map +1 -1
- package/dist/lib/styles.js +16 -1
- package/dist/lib/styles.js.map +1 -1
- package/dist/registry/blocks.d.ts.map +1 -1
- package/dist/registry/blocks.js +27 -9
- package/dist/registry/blocks.js.map +1 -1
- package/dist/registry/components.d.ts.map +1 -1
- package/dist/registry/components.js +60 -50
- package/dist/registry/components.js.map +1 -1
- package/dist/registry/schema.d.ts +8 -0
- package/dist/registry/schema.d.ts.map +1 -1
- package/dist/registry/schema.js.map +1 -1
- package/package.json +5 -4
- package/src/components/accordion/accordion.tsx +1 -1
- package/src/components/activity-feed/activity-feed.tsx +1 -1
- package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
- package/src/components/ai-extraction-review/ai-extraction-review.tsx +1 -1
- package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
- package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
- package/src/components/ai-response/ai-response.tsx +2 -2
- package/src/components/ai-tool/ai-tool.tsx +2 -2
- package/src/components/breadcrumb/breadcrumb.tsx +157 -0
- package/src/components/breadcrumb/index.ts +10 -0
- package/src/components/breadcrumb/meta.ts +19 -0
- package/src/components/calendar/calendar.tsx +2 -2
- package/src/components/code-block/code-block.tsx +1 -1
- package/src/components/collapsible/collapsible.tsx +63 -0
- package/src/components/collapsible/index.ts +6 -0
- package/src/components/collapsible/meta.ts +16 -0
- package/src/components/combobox/combobox.tsx +2 -2
- package/src/components/command/command.tsx +1 -1
- package/src/components/data-table/data-table.tsx +14 -3
- package/src/components/dialog/dialog.tsx +2 -2
- package/src/components/diff-viewer/diff-viewer.tsx +4 -4
- package/src/components/direction/direction.tsx +38 -0
- package/src/components/direction/index.ts +1 -0
- package/src/components/direction/meta.ts +19 -0
- package/src/components/drawer/drawer.tsx +1 -1
- package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
- package/src/components/input/input.tsx +1 -1
- package/src/components/log-viewer/log-viewer.tsx +2 -2
- package/src/components/meter/meter.tsx +1 -1
- package/src/components/pagination/pagination.tsx +13 -2
- package/src/components/permission-matrix/permission-matrix.tsx +4 -4
- package/src/components/record-diff/record-diff.tsx +4 -4
- package/src/components/select/select.tsx +2 -2
- package/src/components/sheet/sheet.tsx +8 -4
- package/src/components/sidebar/index.ts +19 -0
- package/src/components/sidebar/meta.ts +25 -0
- package/src/components/sidebar/sidebar.tsx +374 -0
- package/src/components/table/table.tsx +3 -3
- package/src/components/tags-input/tags-input.tsx +1 -1
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/meta.ts +21 -0
- package/src/components/textarea/textarea.tsx +210 -0
- package/src/components/time-range-picker/time-range-picker.tsx +3 -3
- package/src/components/toast/toast.tsx +14 -6
- package/src/index.ts +5 -0
- package/src/lib/styles.ts +16 -0
- package/src/registry/blocks.ts +18 -0
- package/src/registry/components.ts +10 -0
- package/src/registry/schema.ts +8 -0
- package/src/blocks/admin-users/admin-users.tsx +0 -251
- package/src/blocks/admin-users/index.ts +0 -1
- package/src/blocks/admin-users/meta.ts +0 -27
- package/src/blocks/ai-chat/ai-chat.tsx +0 -280
- package/src/blocks/ai-chat/index.ts +0 -7
- package/src/blocks/ai-chat/meta.ts +0 -32
- package/src/blocks/dashboard/dashboard.tsx +0 -192
- package/src/blocks/dashboard/index.ts +0 -6
- package/src/blocks/dashboard/meta.ts +0 -18
- package/src/blocks/forgot-password/forgot-password.tsx +0 -158
- package/src/blocks/forgot-password/index.ts +0 -1
- package/src/blocks/forgot-password/meta.ts +0 -18
- package/src/blocks/login/index.ts +0 -1
- package/src/blocks/login/login.tsx +0 -178
- package/src/blocks/login/meta.ts +0 -18
- package/src/blocks/pricing/index.ts +0 -1
- package/src/blocks/pricing/meta.ts +0 -18
- package/src/blocks/pricing/pricing.tsx +0 -176
- package/src/blocks/settings/index.ts +0 -6
- package/src/blocks/settings/meta.ts +0 -29
- package/src/blocks/settings/settings.tsx +0 -317
- package/src/blocks/signup/index.ts +0 -1
- package/src/blocks/signup/meta.ts +0 -18
- package/src/blocks/signup/signup.tsx +0 -240
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-table.js","names":["Table"],"sources":["../../../src/components/data-table/data-table.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n flexRender,\n type RowData,\n type Table as TanstackTable,\n type TableFeatures,\n} from \"@tanstack/react-table\";\nimport type { ComponentPropsWithRef, ReactNode } from \"react\";\n\nimport { Button } from \"@/components/button\";\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/dropdown-menu\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/select\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/table\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Presentation for a TanStack Table instance.\n *\n * Deliberately not a component that takes `data` and `columns` and does\n * everything. Sorting, filtering, selection and pagination are the table\n * library's job and it does them well; wrapping them in props would re-expose\n * an API that already exists, badly, and block anything the wrapper did not\n * anticipate. The consumer owns the instance:\n *\n * const table = useTable({ features, columns, data });\n * <DataTable table={table} />\n *\n * ## How these are typed\n *\n * The table library's features are opt-in, and its types follow: a method like\n * `getCanSort` exists only when the sorting feature is enabled.\n *\n * The controls below therefore declare **the shape they need** rather than\n * being generic over feature flags. A column from a table without sorting does\n * not satisfy `SortableColumn`, so passing one is a compile error at the call\n * site — the guarantee we wanted, and it survives the library reshuffling its\n * internal feature types.\n *\n * `DataTable` itself has to render any table, so it uses the core API and\n * detects the optional extras with real type guards.\n */\n\n/* -------------------------------------------------------------------------- */\n/* Capability detection */\n/* -------------------------------------------------------------------------- */\n\ninterface WithSortState {\n getIsSorted: () => \"asc\" | \"desc\" | false;\n getCanSort: () => boolean;\n}\n\ninterface WithSize {\n getSize: () => number;\n}\n\ninterface WithSelection {\n getIsSelected: () => boolean;\n}\n\ninterface WithVisibility {\n getIsVisible: () => boolean;\n}\n\nfunction hasSortState(column: object): column is WithSortState {\n return \"getIsSorted\" in column && \"getCanSort\" in column;\n}\n\nfunction hasSize(header: object): header is WithSize {\n return \"getSize\" in header;\n}\n\nfunction hasSelection(row: object): row is WithSelection {\n return \"getIsSelected\" in row;\n}\n\nfunction hasVisibility(column: object): column is WithVisibility {\n return \"getIsVisible\" in column;\n}\n\n/**\n * Whether a column's cells should render.\n *\n * A table without the visibility feature has no notion of hidden columns, so\n * everything renders. With the feature on, this has to be honoured for cells as\n * well as headers — dropping a header while still rendering its cells shifts\n * every row out of alignment with the columns above it.\n */\nfunction isColumnVisible(column: object): boolean {\n return !hasVisibility(column) || column.getIsVisible();\n}\n\nfunction ariaSort(column: object): \"ascending\" | \"descending\" | \"none\" | undefined {\n if (!hasSortState(column)) return undefined;\n\n const sorted = column.getIsSorted();\n if (sorted === \"asc\") return \"ascending\";\n if (sorted === \"desc\") return \"descending\";\n return column.getCanSort() ? \"none\" : undefined;\n}\n\nfunction columnWidth(header: object): { width: number } | undefined {\n if (!hasSize(header)) return undefined;\n\n const size = header.getSize();\n return size ? { width: size } : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Table */\n/* -------------------------------------------------------------------------- */\n\nexport interface DataTableProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n> extends Omit<ComponentPropsWithRef<\"table\">, \"children\"> {\n table: TanstackTable<TFeatures, TData>;\n /** Shown in place of rows when there are none. */\n empty?: ReactNode;\n}\n\nexport function DataTable<TFeatures extends TableFeatures, TData extends RowData>({\n table,\n empty,\n className,\n ...props\n}: DataTableProps<TFeatures, TData>) {\n const rows = table.getRowModel().rows;\n const columnCount = table.getAllLeafColumns().length;\n\n return (\n <div className=\"rounded-lg border border-border\">\n <Table className={cn(className)} {...props}>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n colSpan={header.colSpan}\n style={columnWidth(header)}\n // Announces the current sort. Without it the arrow icon is the\n // only signal, which is no signal at all for a screen reader.\n aria-sort={ariaSort(header.column)}\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {rows.length === 0 ? (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell colSpan={columnCount} className=\"h-32 p-0 text-center\">\n {empty ?? <span className=\"text-sm text-muted-foreground\">No results.</span>}\n </TableCell>\n </TableRow>\n ) : (\n rows.map((row) => (\n <TableRow\n key={row.id}\n data-state={hasSelection(row) && row.getIsSelected() ? \"selected\" : undefined}\n >\n {row\n .getAllCells()\n .filter((cell) => isColumnVisible(cell.column))\n .map((cell) => (\n <TableCell key={cell.id}>\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </TableCell>\n ))}\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Sortable column header */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The slice of a column a sortable header needs.\n *\n * A column from a table without the sorting feature does not have these\n * methods, so passing one is a compile error rather than a runtime crash.\n */\nexport interface SortableColumn {\n getCanSort: () => boolean;\n getIsSorted: () => \"asc\" | \"desc\" | false;\n toggleSorting: (desc?: boolean) => void;\n}\n\nexport interface DataTableColumnHeaderProps extends ComponentPropsWithRef<\"div\"> {\n column: SortableColumn;\n title: string;\n}\n\n/**\n * A sortable column header.\n *\n * Falls back to plain text when the column cannot be sorted, rather than\n * rendering a button that does nothing — a control that looks interactive and\n * is not is worse than no control.\n */\nexport function DataTableColumnHeader({\n column,\n title,\n className,\n ...props\n}: DataTableColumnHeaderProps) {\n if (!column.getCanSort()) {\n return (\n <div className={cn(\"text-xs font-medium\", className)} {...props}>\n {title}\n </div>\n );\n }\n\n const sorted = column.getIsSorted();\n\n return (\n <div className={cn(\"flex items-center\", className)} {...props}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ml-2 h-7 gap-1 px-2 font-medium text-muted-foreground data-[state=open]:bg-accent\"\n >\n {title}\n <SortIcon direction={sorted === false ? undefined : sorted} />\n <span className=\"sr-only\">\n {sorted === \"asc\"\n ? `${title}, sorted ascending`\n : sorted === \"desc\"\n ? `${title}, sorted descending`\n : `${title}, not sorted`}\n </span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem\n onSelect={() => {\n column.toggleSorting(false);\n }}\n >\n Sort ascending\n </DropdownMenuItem>\n <DropdownMenuItem\n onSelect={() => {\n column.toggleSorting(true);\n }}\n >\n Sort descending\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n\nfunction SortIcon({ direction }: { direction?: \"asc\" | \"desc\" }) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5 opacity-70\">\n <path\n d={\n direction === \"asc\"\n ? \"m7 14 5-5 5 5\"\n : direction === \"desc\"\n ? \"m7 10 5 5 5-5\"\n : \"m8 10 4-4 4 4M8 14l4 4 4-4\"\n }\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Column visibility */\n/* -------------------------------------------------------------------------- */\n\n/** The slice of a table the column toggle needs. */\nexport interface HideableTable {\n getAllColumns: () => {\n id: string;\n getCanHide: () => boolean;\n getIsVisible: () => boolean;\n toggleVisibility: (visible?: boolean) => void;\n }[];\n}\n\nexport interface DataTableViewOptionsProps {\n table: HideableTable;\n label?: string;\n}\n\n/** Toggles column visibility. */\nexport function DataTableViewOptions({ table, label = \"Columns\" }: DataTableViewOptionsProps) {\n const columns = table.getAllColumns().filter((column) => column.getCanHide());\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\">\n {label}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-44\">\n <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {columns.map((column) => (\n <DropdownMenuCheckboxItem\n key={column.id}\n checked={column.getIsVisible()}\n onCheckedChange={(checked) => {\n column.toggleVisibility(checked === true);\n }}\n >\n {column.id}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Pagination */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The slice of a table the page controls need.\n *\n * `state` is a property, not a `getState()` call — the table library moved to a\n * reactive state object, and reading it the old way fails at runtime rather\n * than at compile time.\n */\nexport interface PaginatedTable {\n state: { pagination: { pageIndex: number; pageSize: number } };\n getPageCount: () => number;\n setPageSize: (size: number) => void;\n previousPage: () => void;\n nextPage: () => void;\n getCanPreviousPage: () => boolean;\n getCanNextPage: () => boolean;\n}\n\nexport interface DataTablePaginationProps {\n table: PaginatedTable;\n pageSizes?: number[];\n /**\n * Replaces the page position on the left.\n *\n * A slot rather than a `showSelectionCount` flag: a selection count needs the\n * selection and filtering features, and demanding those here would break\n * every table that only paginates.\n */\n status?: ReactNode;\n}\n\n/**\n * Page controls for a table.\n *\n * The page position is a polite live region: paging replaces the rows in place,\n * and without an announcement a screen reader user gets no confirmation that\n * anything happened.\n */\nexport function DataTablePagination({\n table,\n pageSizes = [10, 20, 50, 100],\n status,\n}: DataTablePaginationProps) {\n const { pageIndex, pageSize } = table.state.pagination;\n const pageCount = table.getPageCount();\n\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-4 px-1 py-3\">\n <div className=\"text-sm text-muted-foreground\" aria-live=\"polite\">\n {status ?? (\n <>\n Page {pageIndex + 1} of {Math.max(1, pageCount)}\n </>\n )}\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\" id=\"rows-per-page\">\n Rows per page\n </span>\n <Select\n value={String(pageSize)}\n onValueChange={(value) => {\n table.setPageSize(Number(value));\n }}\n >\n <SelectTrigger triggerSize=\"sm\" className=\"w-18\" aria-labelledby=\"rows-per-page\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pageSizes.map((size) => (\n <SelectItem key={size} value={String(size)}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label=\"Go to previous page\"\n disabled={!table.getCanPreviousPage()}\n onClick={() => {\n table.previousPage();\n }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m15 18-6-6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label=\"Go to next page\"\n disabled={!table.getCanNextPage()}\n onClick={() => {\n table.nextPage();\n }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m9 18 6-6-6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-table.js","names":["Table"],"sources":["../../../src/components/data-table/data-table.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n flexRender,\n type RowData,\n type Table as TanstackTable,\n type TableFeatures,\n} from \"@tanstack/react-table\";\nimport type { ComponentPropsWithRef, ReactNode } from \"react\";\n\nimport { Button } from \"@/components/button\";\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/dropdown-menu\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/select\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/table\";\nimport { mirrorForDirection } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Presentation for a TanStack Table instance.\n *\n * Deliberately not a component that takes `data` and `columns` and does\n * everything. Sorting, filtering, selection and pagination are the table\n * library's job and it does them well; wrapping them in props would re-expose\n * an API that already exists, badly, and block anything the wrapper did not\n * anticipate. The consumer owns the instance:\n *\n * const table = useTable({ features, columns, data });\n * <DataTable table={table} />\n *\n * ## How these are typed\n *\n * The table library's features are opt-in, and its types follow: a method like\n * `getCanSort` exists only when the sorting feature is enabled.\n *\n * The controls below therefore declare **the shape they need** rather than\n * being generic over feature flags. A column from a table without sorting does\n * not satisfy `SortableColumn`, so passing one is a compile error at the call\n * site — the guarantee we wanted, and it survives the library reshuffling its\n * internal feature types.\n *\n * `DataTable` itself has to render any table, so it uses the core API and\n * detects the optional extras with real type guards.\n */\n\n/* -------------------------------------------------------------------------- */\n/* Capability detection */\n/* -------------------------------------------------------------------------- */\n\ninterface WithSortState {\n getIsSorted: () => \"asc\" | \"desc\" | false;\n getCanSort: () => boolean;\n}\n\ninterface WithSize {\n getSize: () => number;\n}\n\ninterface WithSelection {\n getIsSelected: () => boolean;\n}\n\ninterface WithVisibility {\n getIsVisible: () => boolean;\n}\n\nfunction hasSortState(column: object): column is WithSortState {\n return \"getIsSorted\" in column && \"getCanSort\" in column;\n}\n\nfunction hasSize(header: object): header is WithSize {\n return \"getSize\" in header;\n}\n\nfunction hasSelection(row: object): row is WithSelection {\n return \"getIsSelected\" in row;\n}\n\nfunction hasVisibility(column: object): column is WithVisibility {\n return \"getIsVisible\" in column;\n}\n\n/**\n * Whether a column's cells should render.\n *\n * A table without the visibility feature has no notion of hidden columns, so\n * everything renders. With the feature on, this has to be honoured for cells as\n * well as headers — dropping a header while still rendering its cells shifts\n * every row out of alignment with the columns above it.\n */\nfunction isColumnVisible(column: object): boolean {\n return !hasVisibility(column) || column.getIsVisible();\n}\n\nfunction ariaSort(column: object): \"ascending\" | \"descending\" | \"none\" | undefined {\n if (!hasSortState(column)) return undefined;\n\n const sorted = column.getIsSorted();\n if (sorted === \"asc\") return \"ascending\";\n if (sorted === \"desc\") return \"descending\";\n return column.getCanSort() ? \"none\" : undefined;\n}\n\nfunction columnWidth(header: object): { width: number } | undefined {\n if (!hasSize(header)) return undefined;\n\n const size = header.getSize();\n return size ? { width: size } : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Table */\n/* -------------------------------------------------------------------------- */\n\nexport interface DataTableProps<\n TFeatures extends TableFeatures,\n TData extends RowData,\n> extends Omit<ComponentPropsWithRef<\"table\">, \"children\"> {\n table: TanstackTable<TFeatures, TData>;\n /** Shown in place of rows when there are none. */\n empty?: ReactNode;\n}\n\nexport function DataTable<TFeatures extends TableFeatures, TData extends RowData>({\n table,\n empty,\n className,\n ...props\n}: DataTableProps<TFeatures, TData>) {\n const rows = table.getRowModel().rows;\n const columnCount = table.getAllLeafColumns().length;\n\n return (\n <div className=\"rounded-lg border border-border\">\n <Table className={cn(className)} {...props}>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n colSpan={header.colSpan}\n style={columnWidth(header)}\n // Announces the current sort. Without it the arrow icon is the\n // only signal, which is no signal at all for a screen reader.\n aria-sort={ariaSort(header.column)}\n >\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {rows.length === 0 ? (\n <TableRow className=\"hover:bg-transparent\">\n <TableCell colSpan={columnCount} className=\"h-32 p-0 text-center\">\n {empty ?? <span className=\"text-sm text-muted-foreground\">No results.</span>}\n </TableCell>\n </TableRow>\n ) : (\n rows.map((row) => (\n <TableRow\n key={row.id}\n data-state={hasSelection(row) && row.getIsSelected() ? \"selected\" : undefined}\n >\n {row\n .getAllCells()\n .filter((cell) => isColumnVisible(cell.column))\n .map((cell) => (\n <TableCell key={cell.id}>\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </TableCell>\n ))}\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Sortable column header */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The slice of a column a sortable header needs.\n *\n * A column from a table without the sorting feature does not have these\n * methods, so passing one is a compile error rather than a runtime crash.\n */\nexport interface SortableColumn {\n getCanSort: () => boolean;\n getIsSorted: () => \"asc\" | \"desc\" | false;\n toggleSorting: (desc?: boolean) => void;\n}\n\nexport interface DataTableColumnHeaderProps extends ComponentPropsWithRef<\"div\"> {\n column: SortableColumn;\n title: string;\n}\n\n/**\n * A sortable column header.\n *\n * Falls back to plain text when the column cannot be sorted, rather than\n * rendering a button that does nothing — a control that looks interactive and\n * is not is worse than no control.\n */\nexport function DataTableColumnHeader({\n column,\n title,\n className,\n ...props\n}: DataTableColumnHeaderProps) {\n if (!column.getCanSort()) {\n return (\n <div className={cn(\"text-xs font-medium\", className)} {...props}>\n {title}\n </div>\n );\n }\n\n const sorted = column.getIsSorted();\n\n return (\n <div className={cn(\"flex items-center\", className)} {...props}>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ms-2 h-7 gap-1 px-2 font-medium text-muted-foreground data-[state=open]:bg-accent\"\n >\n {title}\n <SortIcon direction={sorted === false ? undefined : sorted} />\n <span className=\"sr-only\">\n {sorted === \"asc\"\n ? `${title}, sorted ascending`\n : sorted === \"desc\"\n ? `${title}, sorted descending`\n : `${title}, not sorted`}\n </span>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\">\n <DropdownMenuItem\n onSelect={() => {\n column.toggleSorting(false);\n }}\n >\n Sort ascending\n </DropdownMenuItem>\n <DropdownMenuItem\n onSelect={() => {\n column.toggleSorting(true);\n }}\n >\n Sort descending\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n\nfunction SortIcon({ direction }: { direction?: \"asc\" | \"desc\" }) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5 opacity-70\">\n <path\n d={\n direction === \"asc\"\n ? \"m7 14 5-5 5 5\"\n : direction === \"desc\"\n ? \"m7 10 5 5 5-5\"\n : \"m8 10 4-4 4 4M8 14l4 4 4-4\"\n }\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Column visibility */\n/* -------------------------------------------------------------------------- */\n\n/** The slice of a table the column toggle needs. */\nexport interface HideableTable {\n getAllColumns: () => {\n id: string;\n getCanHide: () => boolean;\n getIsVisible: () => boolean;\n toggleVisibility: (visible?: boolean) => void;\n }[];\n}\n\nexport interface DataTableViewOptionsProps {\n table: HideableTable;\n label?: string;\n}\n\n/** Toggles column visibility. */\nexport function DataTableViewOptions({ table, label = \"Columns\" }: DataTableViewOptionsProps) {\n const columns = table.getAllColumns().filter((column) => column.getCanHide());\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\">\n {label}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-44\">\n <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {columns.map((column) => (\n <DropdownMenuCheckboxItem\n key={column.id}\n checked={column.getIsVisible()}\n onCheckedChange={(checked) => {\n column.toggleVisibility(checked === true);\n }}\n >\n {column.id}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Pagination */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The slice of a table the page controls need.\n *\n * `state` is a property, not a `getState()` call — the table library moved to a\n * reactive state object, and reading it the old way fails at runtime rather\n * than at compile time.\n */\nexport interface PaginatedTable {\n state: { pagination: { pageIndex: number; pageSize: number } };\n getPageCount: () => number;\n setPageSize: (size: number) => void;\n previousPage: () => void;\n nextPage: () => void;\n getCanPreviousPage: () => boolean;\n getCanNextPage: () => boolean;\n}\n\nexport interface DataTablePaginationProps {\n table: PaginatedTable;\n pageSizes?: number[];\n /**\n * Replaces the page position on the left.\n *\n * A slot rather than a `showSelectionCount` flag: a selection count needs the\n * selection and filtering features, and demanding those here would break\n * every table that only paginates.\n */\n status?: ReactNode;\n}\n\n/**\n * Page controls for a table.\n *\n * The page position is a polite live region: paging replaces the rows in place,\n * and without an announcement a screen reader user gets no confirmation that\n * anything happened.\n */\nexport function DataTablePagination({\n table,\n pageSizes = [10, 20, 50, 100],\n status,\n}: DataTablePaginationProps) {\n const { pageIndex, pageSize } = table.state.pagination;\n const pageCount = table.getPageCount();\n\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-4 px-1 py-3\">\n <div className=\"text-sm text-muted-foreground\" aria-live=\"polite\">\n {status ?? (\n <>\n Page {pageIndex + 1} of {Math.max(1, pageCount)}\n </>\n )}\n </div>\n\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\" id=\"rows-per-page\">\n Rows per page\n </span>\n <Select\n value={String(pageSize)}\n onValueChange={(value) => {\n table.setPageSize(Number(value));\n }}\n >\n <SelectTrigger triggerSize=\"sm\" className=\"w-18\" aria-labelledby=\"rows-per-page\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pageSizes.map((size) => (\n <SelectItem key={size} value={String(size)}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label=\"Go to previous page\"\n disabled={!table.getCanPreviousPage()}\n onClick={() => {\n table.previousPage();\n }}\n >\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={mirrorForDirection}\n >\n <path\n d=\"m15 18-6-6 6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label=\"Go to next page\"\n disabled={!table.getCanNextPage()}\n onClick={() => {\n table.nextPage();\n }}\n >\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={mirrorForDirection}\n >\n <path\n d=\"m9 18 6-6-6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAsFA,SAAS,aAAa,QAAyC;CAC7D,OAAO,iBAAiB,UAAU,gBAAgB;AACpD;AAEA,SAAS,QAAQ,QAAoC;CACnD,OAAO,aAAa;AACtB;AAEA,SAAS,aAAa,KAAmC;CACvD,OAAO,mBAAmB;AAC5B;AAEA,SAAS,cAAc,QAA0C;CAC/D,OAAO,kBAAkB;AAC3B;;;;;;;;;AAUA,SAAS,gBAAgB,QAAyB;CAChD,OAAO,CAAC,cAAc,MAAM,KAAK,OAAO,aAAa;AACvD;AAEA,SAAS,SAAS,QAAiE;CACjF,IAAI,CAAC,aAAa,MAAM,GAAG,OAAO,KAAA;CAElC,MAAM,SAAS,OAAO,YAAY;CAClC,IAAI,WAAW,OAAO,OAAO;CAC7B,IAAI,WAAW,QAAQ,OAAO;CAC9B,OAAO,OAAO,WAAW,IAAI,SAAS,KAAA;AACxC;AAEA,SAAS,YAAY,QAA+C;CAClE,IAAI,CAAC,QAAQ,MAAM,GAAG,OAAO,KAAA;CAE7B,MAAM,OAAO,OAAO,QAAQ;CAC5B,OAAO,OAAO,EAAE,OAAO,KAAK,IAAI,KAAA;AAClC;AAeA,SAAgB,UAAkE,EAChF,OACA,OACA,WACA,GAAG,SACgC;CACnC,MAAM,OAAO,MAAM,YAAY,CAAC,CAAC;CACjC,MAAM,cAAc,MAAM,kBAAkB,CAAC,CAAC;CAE9C,OACE,oBAAC,OAAD;EAAK,WAAU;EACb,UAAA,qBAACA,SAAD;GAAO,WAAW,GAAG,SAAS;GAAG,GAAI;GAArC,UAAA,CACE,oBAAC,aAAD,EAAA,UACG,MAAM,gBAAgB,CAAC,CAAC,KAAK,gBAC5B,oBAAC,UAAD,EAAA,UACG,YAAY,QAAQ,KAAK,WACxB,oBAAC,WAAD;IAEE,SAAS,OAAO;IAChB,OAAO,YAAY,MAAM;IAGzB,aAAW,SAAS,OAAO,MAAM;IAEhC,UAAA,OAAO,gBACJ,OACA,WAAW,OAAO,OAAO,UAAU,QAAQ,OAAO,WAAW,CAAC;GACzD,GAVJ,OAAO,EAUH,CACZ,EACO,GAfK,YAAY,EAejB,CACX,EACU,CAAA,GACb,oBAAC,WAAD,EAAA,UACG,KAAK,WAAW,IACf,oBAAC,UAAD;IAAU,WAAU;IAClB,UAAA,oBAAC,WAAD;KAAW,SAAS;KAAa,WAAU;KACxC,UAAA,SAAS,oBAAC,QAAD;MAAM,WAAU;MAAgC,UAAA;KAAiB,CAAA;IAClE,CAAA;GACH,CAAA,IAEV,KAAK,KAAK,QACR,oBAAC,UAAD;IAEE,cAAY,aAAa,GAAG,KAAK,IAAI,cAAc,IAAI,aAAa,KAAA;IAEnE,UAAA,IACE,YAAY,CAAC,CACb,QAAQ,SAAS,gBAAgB,KAAK,MAAM,CAAC,CAAC,CAC9C,KAAK,SACJ,oBAAC,WAAD,EAAA,UACG,WAAW,KAAK,OAAO,UAAU,MAAM,KAAK,WAAW,CAAC,EAChD,GAFK,KAAK,EAEV,CACZ;GACK,GAXH,IAAI,EAWD,CACX,EAEM,CAAA,CACN;;CACJ,CAAA;AAET;;;;;;;;AA8BA,SAAgB,sBAAsB,EACpC,QACA,OACA,WACA,GAAG,SAC0B;CAC7B,IAAI,CAAC,OAAO,WAAW,GACrB,OACE,oBAAC,OAAD;EAAK,WAAW,GAAG,uBAAuB,SAAS;EAAG,GAAI;EACvD,UAAA;CACE,CAAA;CAIT,MAAM,SAAS,OAAO,YAAY;CAElC,OACE,oBAAC,OAAD;EAAK,WAAW,GAAG,qBAAqB,SAAS;EAAG,GAAI;EACtD,UAAA,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GAAqB,SAAA;GACnB,UAAA,qBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IAHZ,UAAA;KAKG;KACD,oBAAC,UAAD,EAAU,WAAW,WAAW,QAAQ,KAAA,IAAY,OAAS,CAAA;KAC7D,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,WAAW,QACR,GAAG,MAAM,sBACT,WAAW,SACT,GAAG,MAAM,uBACT,GAAG,MAAM;KACX,CAAA;IACA;;EACW,CAAA,GACrB,qBAAC,qBAAD;GAAqB,OAAM;GAA3B,UAAA,CACE,oBAAC,kBAAD;IACE,gBAAgB;KACd,OAAO,cAAc,KAAK;IAC5B;IACD,UAAA;GAEiB,CAAA,GAClB,oBAAC,kBAAD;IACE,gBAAgB;KACd,OAAO,cAAc,IAAI;IAC3B;IACD,UAAA;GAEiB,CAAA,CACC;EACT,CAAA,CAAA,EAAA,CAAA;CACX,CAAA;AAET;AAEA,SAAS,SAAS,EAAE,aAA6C;CAC/D,OACE,oBAAC,OAAD;EAAK,SAAQ;EAAY,MAAK;EAAO,eAAY;EAAO,WAAU;EAChE,UAAA,oBAAC,QAAD;GACE,GACE,cAAc,QACV,kBACA,cAAc,SACZ,kBACA;GAER,QAAO;GACP,aAAY;GACZ,eAAc;GACd,gBAAe;EAChB,CAAA;CACE,CAAA;AAET;;AAsBA,SAAgB,qBAAqB,EAAE,OAAO,QAAQ,aAAwC;CAC5F,MAAM,UAAU,MAAM,cAAc,CAAC,CAAC,QAAQ,WAAW,OAAO,WAAW,CAAC;CAE5E,OACE,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;EAAqB,SAAA;EACnB,UAAA,oBAAC,QAAD;GAAQ,SAAQ;GAAU,MAAK;GAC5B,UAAA;EACK,CAAA;CACW,CAAA,GACrB,qBAAC,qBAAD;EAAqB,OAAM;EAAM,WAAU;EAA3C,UAAA;GACE,oBAAC,mBAAD,EAAA,UAAmB,iBAAiC,CAAA;GACpD,oBAAC,uBAAD,CAAwB,CAAA;GACvB,QAAQ,KAAK,WACZ,oBAAC,0BAAD;IAEE,SAAS,OAAO,aAAa;IAC7B,kBAAkB,YAAY;KAC5B,OAAO,iBAAiB,YAAY,IAAI;IAC1C;IAEC,UAAA,OAAO;GACgB,GAPnB,OAAO,EAOY,CAC3B;EACkB;CACT,CAAA,CAAA,EAAA,CAAA;AAElB;;;;;;;;AA2CA,SAAgB,oBAAoB,EAClC,OACA,YAAY;CAAC;CAAI;CAAI;CAAI;AAAG,GAC5B,UAC2B;CAC3B,MAAM,EAAE,WAAW,aAAa,MAAM,MAAM;CAC5C,MAAM,YAAY,MAAM,aAAa;CAErC,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,OAAD;GAAK,WAAU;GAAgC,aAAU;GACtD,UAAA,UACC,qBAAA,UAAA,EAAA,UAAA;IAAE;IACM,YAAY;IAAE;IAAK,KAAK,IAAI,GAAG,SAAS;GAC9C,EAAA,CAAA;EAED,CAAA,GAEL,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAAgC,IAAG;KAAgB,UAAA;IAE7D,CAAA,GACN,qBAAC,QAAD;KACE,OAAO,OAAO,QAAQ;KACtB,gBAAgB,UAAU;MACxB,MAAM,YAAY,OAAO,KAAK,CAAC;KACjC;KAJF,UAAA,CAME,oBAAC,eAAD;MAAe,aAAY;MAAK,WAAU;MAAO,mBAAgB;MAC/D,UAAA,oBAAC,aAAD,CAAc,CAAA;KACD,CAAA,GACf,oBAAC,eAAD,EAAA,UACG,UAAU,KAAK,SACd,oBAAC,YAAD;MAAuB,OAAO,OAAO,IAAI;MACtC,UAAA;KACS,GAFK,IAEL,CACb,EACY,CAAA,CACT;IACL,CAAA,CAAA;GAEL,CAAA,GAAA,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KACE,SAAQ;KACR,MAAK;KACL,cAAW;KACX,UAAU,CAAC,MAAM,mBAAmB;KACpC,eAAe;MACb,MAAM,aAAa;KACrB;KAEA,UAAA,oBAAC,OAAD;MACE,SAAQ;MACR,MAAK;MACL,eAAY;MACZ,WAAW;MAEX,UAAA,oBAAC,QAAD;OACE,GAAE;OACF,QAAO;OACP,aAAY;OACZ,eAAc;OACd,gBAAe;MAChB,CAAA;KACE,CAAA;IACC,CAAA,GACR,oBAAC,QAAD;KACE,SAAQ;KACR,MAAK;KACL,cAAW;KACX,UAAU,CAAC,MAAM,eAAe;KAChC,eAAe;MACb,MAAM,SAAS;KACjB;KAEA,UAAA,oBAAC,OAAD;MACE,SAAQ;MACR,MAAK;MACL,eAAY;MACZ,WAAW;MAEX,UAAA,oBAAC,QAAD;OACE,GAAE;OACF,QAAO;OACP,aAAY;OACZ,eAAc;OACd,gBAAe;MAChB,CAAA;KACE,CAAA;IACC,CAAA,CACL;GACF,CAAA,CAAA;EACF,CAAA,CAAA;;AAET"}
|
|
@@ -30,7 +30,7 @@ function DialogContent({ className, children, showCloseButton = true, ...props }
|
|
|
30
30
|
children: [children, showCloseButton ? /* @__PURE__ */ jsx(Dialog.Close, {
|
|
31
31
|
"data-slot": "dialog-close",
|
|
32
32
|
"aria-label": "Close",
|
|
33
|
-
className: cn("absolute
|
|
33
|
+
className: cn("absolute end-4 top-4 grid size-7 place-items-center rounded-md text-muted-foreground", "transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground", "[&_svg]:size-4", focusRing),
|
|
34
34
|
children: /* @__PURE__ */ jsx("svg", {
|
|
35
35
|
viewBox: "0 0 24 24",
|
|
36
36
|
fill: "none",
|
|
@@ -48,7 +48,7 @@ function DialogContent({ className, children, showCloseButton = true, ...props }
|
|
|
48
48
|
function DialogHeader({ className, ...props }) {
|
|
49
49
|
return /* @__PURE__ */ jsx("div", {
|
|
50
50
|
"data-slot": "dialog-header",
|
|
51
|
-
className: cn("flex flex-col gap-1.5
|
|
51
|
+
className: cn("flex flex-col gap-1.5 pe-8 text-start", className),
|
|
52
52
|
...props
|
|
53
53
|
});
|
|
54
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","names":["Dialog","DialogPrimitive"],"sources":["../../../src/components/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A modal window that interrupts the user to gather a response.\n *\n * Focus is trapped while open, restored to the trigger on close, and the rest\n * of the page is hidden from assistive technology — all handled by the\n * underlying primitive rather than reimplemented here.\n */\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\nexport const DialogPortal = DialogPrimitive.Portal;\nexport const DialogClose = DialogPrimitive.Close;\n\nexport type DialogOverlayProps = ComponentPropsWithRef<typeof DialogPrimitive.Overlay>;\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay backdrop-blur-[2px]\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface DialogContentProps extends ComponentPropsWithRef<\n typeof DialogPrimitive.Content\n> {\n /** Renders the built-in close button. Turn off to supply your own. */\n showCloseButton?: boolean;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2\",\n \"grid gap-4 rounded-xl border border-border bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-dialog-out data-[state=open]:animate-dialog-in\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute
|
|
1
|
+
{"version":3,"file":"dialog.js","names":["Dialog","DialogPrimitive"],"sources":["../../../src/components/dialog/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A modal window that interrupts the user to gather a response.\n *\n * Focus is trapped while open, restored to the trigger on close, and the rest\n * of the page is hidden from assistive technology — all handled by the\n * underlying primitive rather than reimplemented here.\n */\nexport const Dialog = DialogPrimitive.Root;\nexport const DialogTrigger = DialogPrimitive.Trigger;\nexport const DialogPortal = DialogPrimitive.Portal;\nexport const DialogClose = DialogPrimitive.Close;\n\nexport type DialogOverlayProps = ComponentPropsWithRef<typeof DialogPrimitive.Overlay>;\n\nexport function DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay backdrop-blur-[2px]\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface DialogContentProps extends ComponentPropsWithRef<\n typeof DialogPrimitive.Content\n> {\n /** Renders the built-in close button. Turn off to supply your own. */\n showCloseButton?: boolean;\n}\n\nexport function DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: DialogContentProps) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2\",\n \"grid gap-4 rounded-xl border border-border bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-dialog-out data-[state=open]:animate-dialog-in\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute end-4 top-4 grid size-7 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n \"[&_svg]:size-4\",\n focusRing,\n )}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m6 6 12 12M18 6 6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </DialogPrimitive.Close>\n ) : null}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nexport function DialogHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-1.5 pe-8 text-start\", className)}\n {...props}\n />\n );\n}\n\nexport function DialogFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogTitleProps = ComponentPropsWithRef<typeof DialogPrimitive.Title>;\n\nexport function DialogTitle({ className, ...props }: DialogTitleProps) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogDescriptionProps = ComponentPropsWithRef<typeof DialogPrimitive.Description>;\n\nexport function DialogDescription({ className, ...props }: DialogDescriptionProps) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAaA,WAASC,OAAgB;AACtC,MAAa,gBAAgBA,OAAgB;AAC7C,MAAa,eAAeA,OAAgB;AAC5C,MAAa,cAAcA,OAAgB;AAI3C,SAAgB,cAAc,EAAE,WAAW,GAAG,SAA6B;CACzE,OACE,oBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,qEACA,gFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AASA,SAAgB,cAAc,EAC5B,WACA,UACA,kBAAkB,MAClB,GAAG,SACkB;CACrB,OACE,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAChB,qBAACA,OAAgB,SAAjB;EACE,aAAU;EACV,WAAW,GACT,4GACA,yFACA,8EACA,SACF;EACA,GAAI;EARN,UAAA,CAUG,UACA,kBACC,oBAACA,OAAgB,OAAjB;GACE,aAAU;GACV,cAAW;GACX,WAAW,GACT,wFACA,2FACA,kBACA,SACF;GAEA,UAAA,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAC/C,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;IACf,CAAA;GACE,CAAA;EACgB,CAAA,IACrB,IACmB;CACb,CAAA,CAAA,EAAA,CAAA;AAElB;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,yCAAyC,SAAS;EAChE,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,0DAA0D,SAAS;EACjF,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAA2B;CACrE,OACE,oBAACA,OAAgB,OAAjB;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAIA,SAAgB,kBAAkB,EAAE,WAAW,GAAG,SAAiC;CACjF,OACE,oBAACA,OAAgB,aAAjB;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -135,12 +135,12 @@ function UnifiedRow({ row }) {
|
|
|
135
135
|
/* @__PURE__ */ jsx(LineNumber, { value: row.before }),
|
|
136
136
|
/* @__PURE__ */ jsx(LineNumber, { value: row.after }),
|
|
137
137
|
/* @__PURE__ */ jsx("td", {
|
|
138
|
-
className: "w-4
|
|
138
|
+
className: "w-4 pe-1 text-center text-muted-foreground select-none",
|
|
139
139
|
"aria-hidden": "true",
|
|
140
140
|
children: row.kind === "added" ? "+" : row.kind === "removed" ? "−" : ""
|
|
141
141
|
}),
|
|
142
142
|
/* @__PURE__ */ jsxs("td", {
|
|
143
|
-
className: "w-full py-0.5
|
|
143
|
+
className: "w-full py-0.5 pe-3 break-all whitespace-pre-wrap",
|
|
144
144
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
145
145
|
className: "sr-only",
|
|
146
146
|
children: [KIND_LABEL[row.kind], ": "]
|
|
@@ -158,7 +158,7 @@ function SplitCell({ row, side }) {
|
|
|
158
158
|
className: "w-1/2 bg-muted/20"
|
|
159
159
|
})] });
|
|
160
160
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(LineNumber, { value: side === "before" ? row.before : row.after }), /* @__PURE__ */ jsxs("td", {
|
|
161
|
-
className: cn("w-1/2 py-0.5
|
|
161
|
+
className: cn("w-1/2 py-0.5 pe-3 break-all whitespace-pre-wrap", ROW_STYLES[row.kind]),
|
|
162
162
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
163
163
|
className: "sr-only",
|
|
164
164
|
children: [KIND_LABEL[row.kind], ": "]
|
|
@@ -175,7 +175,7 @@ function RowContent({ row }) {
|
|
|
175
175
|
function LineNumber({ value }) {
|
|
176
176
|
return /* @__PURE__ */ jsx("td", {
|
|
177
177
|
"aria-hidden": "true",
|
|
178
|
-
className: "w-10 border-
|
|
178
|
+
className: "w-10 border-e border-border bg-muted/40 px-2 py-0.5 text-end text-muted-foreground tabular-nums select-none",
|
|
179
179
|
children: value ?? ""
|
|
180
180
|
});
|
|
181
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-viewer.js","names":[],"sources":["../../../src/components/diff-viewer/diff-viewer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\nimport { countChanges, toSplitRows, type DiffHunk, type DiffRow } from \"./diff-model\";\n\n/**\n * A diff, and a decision about it.\n *\n * The rendering half exists because every packaged viewer brings its own\n * styling system — emotion, or HTML strings plus a stylesheet — which design\n * tokens cannot reach and which is awkward under RSC. The deciding half exists\n * because Dowel is for AI products, and an agent proposing a change to a file is\n * the case that needs it: what every coding agent ships, and no component\n * library does.\n *\n * Decisions are controlled. The component never mutates the diff or applies\n * anything; it reports which hunks were accepted and rejected and leaves the\n * consequences to the application, the same way ai-action-ledger does.\n *\n * Not a grid. A code listing is a table of text, and role=\"grid\" would promise\n * cell-by-cell arrow navigation that neither exists here nor makes sense for\n * reading code. Line kind is carried in text, because a plus sign and a green\n * background are not information.\n */\n\nexport type HunkDecision = \"accepted\" | \"rejected\";\n\nconst KIND_LABEL: Record<DiffRow[\"kind\"], string> = {\n added: \"Added\",\n removed: \"Removed\",\n context: \"Unchanged\",\n};\n\nexport interface DiffViewerProps extends Omit<ComponentPropsWithRef<\"div\">, \"children\"> {\n hunks: DiffHunk[];\n /** Names the diff — usually the path of the file being changed. */\n label: string;\n view?: \"unified\" | \"split\";\n /** Per-hunk decisions, keyed by hunk id. Controlled. */\n decisions?: Record<string, HunkDecision>;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n children?: ReactNode;\n}\n\nexport function DiffViewer({\n className,\n hunks,\n label,\n view = \"unified\",\n decisions,\n onDecision,\n children,\n ...props\n}: DiffViewerProps) {\n const counts = useMemo(() => countChanges(hunks), [hunks]);\n\n return (\n <div\n data-slot=\"diff-viewer\"\n data-view={view}\n className={cn(\"flex flex-col gap-2\", className)}\n {...props}\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"font-mono text-sm font-medium\">{label}</span>\n {/* Said in words as well as coloured, and before the diff rather than\n after it: a reader deserves to know the size of the change before\n they start reading it. */}\n <span data-slot=\"diff-viewer-summary\" className=\"text-xs text-muted-foreground\">\n <span className=\"text-success\">+{counts.added}</span>{\" \"}\n <span className=\"text-destructive\">−{counts.removed}</span>\n <span className=\"sr-only\">\n {` — ${String(counts.added)} lines added, ${String(counts.removed)} removed`}\n </span>\n </span>\n </div>\n\n {children}\n\n {hunks.length === 0 ? (\n <p className=\"rounded-lg border border-border bg-muted/30 p-3 text-sm text-muted-foreground\">\n No changes.\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border\">\n {hunks.map((hunk) => (\n <DiffHunkView\n key={hunk.id}\n hunk={hunk}\n label={label}\n view={view}\n decision={decisions?.[hunk.id]}\n onDecision={onDecision}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction DiffHunkView({\n hunk,\n label,\n view,\n decision,\n onDecision,\n}: {\n hunk: DiffHunk;\n label: string;\n view: \"unified\" | \"split\";\n decision?: HunkDecision;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n}) {\n const splitRows = useMemo(\n () => (view === \"split\" ? toSplitRows(hunk.rows) : []),\n [view, hunk.rows],\n );\n\n return (\n <section\n data-slot=\"diff-hunk\"\n data-decision={decision}\n aria-label={`${label}, hunk ${hunk.id}`}\n className={cn(\n \"border-b border-border last:border-b-0\",\n decision === \"rejected\" && \"opacity-55\",\n )}\n >\n {hunk.skippedBefore > 0 ? (\n <p\n data-slot=\"diff-hunk-skipped\"\n className=\"border-b border-border bg-muted/40 px-3 py-1 font-mono text-2xs text-muted-foreground\"\n >\n {hunk.skippedBefore} unchanged {hunk.skippedBefore === 1 ? \"line\" : \"lines\"} hidden\n </p>\n ) : null}\n\n {onDecision ? (\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5\">\n <span className=\"flex-1 text-xs text-muted-foreground\">\n {decision === \"accepted\"\n ? \"Accepted\"\n : decision === \"rejected\"\n ? \"Rejected\"\n : \"Not decided\"}\n </span>\n <HunkButton\n pressed={decision === \"accepted\"}\n onClick={() => {\n onDecision(hunk.id, \"accepted\");\n }}\n >\n Accept\n </HunkButton>\n <HunkButton\n pressed={decision === \"rejected\"}\n onClick={() => {\n onDecision(hunk.id, \"rejected\");\n }}\n >\n Reject\n </HunkButton>\n </div>\n ) : null}\n\n <div className=\"overflow-x-auto\">\n <table data-slot=\"diff-table\" className=\"w-full border-collapse font-mono text-xs\">\n <tbody>\n {view === \"split\"\n ? splitRows.map((pair, index) => (\n <tr key={index} data-slot=\"diff-row\">\n <SplitCell row={pair.left} side=\"before\" />\n <SplitCell row={pair.right} side=\"after\" />\n </tr>\n ))\n : hunk.rows.map((row, index) => <UnifiedRow key={index} row={row} />)}\n </tbody>\n </table>\n </div>\n </section>\n );\n}\n\nconst ROW_STYLES: Record<DiffRow[\"kind\"], string> = {\n added: \"bg-success/10\",\n removed: \"bg-destructive/10\",\n context: \"\",\n};\n\nfunction UnifiedRow({ row }: { row: DiffRow }) {\n return (\n <tr data-slot=\"diff-row\" data-kind={row.kind} className={ROW_STYLES[row.kind]}>\n <LineNumber value={row.before} />\n <LineNumber value={row.after} />\n <td className=\"w-4 pr-1 text-center text-muted-foreground select-none\" aria-hidden=\"true\">\n {row.kind === \"added\" ? \"+\" : row.kind === \"removed\" ? \"−\" : \"\"}\n </td>\n <td className=\"w-full py-0.5 pr-3 break-all whitespace-pre-wrap\">\n {/* The kind, for anyone who cannot see the sign or the tint. Reading a\n diff aloud without it is reading the same file twice. */}\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </tr>\n );\n}\n\nfunction SplitCell({ row, side }: { row: DiffRow | null; side: \"before\" | \"after\" }) {\n if (!row) {\n // An empty half of a pair, not a blank line of code. Hidden from assistive\n // technology so a reader is not read padding.\n return (\n <>\n <td aria-hidden=\"true\" className=\"w-10 bg-muted/40\" />\n <td aria-hidden=\"true\" className=\"w-1/2 bg-muted/20\" />\n </>\n );\n }\n\n return (\n <>\n <LineNumber value={side === \"before\" ? row.before : row.after} />\n <td\n className={cn(\"w-1/2 py-0.5 pr-3 break-all whitespace-pre-wrap\", ROW_STYLES[row.kind])}\n >\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </>\n );\n}\n\nfunction RowContent({ row }: { row: DiffRow }) {\n if (!row.segments) return <>{row.content}</>;\n\n return (\n <>\n {row.segments.map((segment, index) =>\n segment.changed ? (\n // A mark element, so the changed words survive as structure rather\n // than existing only as a slightly stronger background.\n <mark\n key={index}\n className={cn(\n \"rounded-[2px] text-inherit\",\n row.kind === \"added\" ? \"bg-success/30\" : \"bg-destructive/30\",\n )}\n >\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </>\n );\n}\n\nfunction LineNumber({ value }: { value?: number }) {\n return (\n <td\n // Decorative: the line number is orientation for a sighted reader, and\n // announcing two numbers before every line makes the diff unlistenable.\n aria-hidden=\"true\"\n className=\"w-10 border-r border-border bg-muted/40 px-2 py-0.5 text-right text-muted-foreground tabular-nums select-none\"\n >\n {value ?? \"\"}\n </td>\n );\n}\n\nfunction HunkButton({\n className,\n pressed,\n ...props\n}: ComponentPropsWithRef<\"button\"> & { pressed: boolean }) {\n return (\n <button\n type=\"button\"\n aria-pressed={pressed}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium transition-colors\",\n pressed\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Switches between unified and side-by-side. */\nexport function DiffViewerToolbar({\n className,\n view,\n onViewChange,\n ...props\n}: Omit<ComponentPropsWithRef<\"div\">, \"onChange\"> & {\n view: \"unified\" | \"split\";\n onViewChange: (view: \"unified\" | \"split\") => void;\n}) {\n return (\n <div\n data-slot=\"diff-viewer-toolbar\"\n role=\"group\"\n aria-label=\"Diff layout\"\n className={cn(\"flex items-center gap-1\", className)}\n {...props}\n >\n {([\"unified\", \"split\"] as const).map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={view === option}\n onClick={() => {\n onViewChange(option);\n }}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium capitalize transition-colors\",\n view === option\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-input bg-background text-muted-foreground hover:text-foreground\",\n focusRing,\n )}\n >\n {option}\n </button>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff-viewer.js","names":[],"sources":["../../../src/components/diff-viewer/diff-viewer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\nimport { countChanges, toSplitRows, type DiffHunk, type DiffRow } from \"./diff-model\";\n\n/**\n * A diff, and a decision about it.\n *\n * The rendering half exists because every packaged viewer brings its own\n * styling system — emotion, or HTML strings plus a stylesheet — which design\n * tokens cannot reach and which is awkward under RSC. The deciding half exists\n * because Dowel is for AI products, and an agent proposing a change to a file is\n * the case that needs it: what every coding agent ships, and no component\n * library does.\n *\n * Decisions are controlled. The component never mutates the diff or applies\n * anything; it reports which hunks were accepted and rejected and leaves the\n * consequences to the application, the same way ai-action-ledger does.\n *\n * Not a grid. A code listing is a table of text, and role=\"grid\" would promise\n * cell-by-cell arrow navigation that neither exists here nor makes sense for\n * reading code. Line kind is carried in text, because a plus sign and a green\n * background are not information.\n */\n\nexport type HunkDecision = \"accepted\" | \"rejected\";\n\nconst KIND_LABEL: Record<DiffRow[\"kind\"], string> = {\n added: \"Added\",\n removed: \"Removed\",\n context: \"Unchanged\",\n};\n\nexport interface DiffViewerProps extends Omit<ComponentPropsWithRef<\"div\">, \"children\"> {\n hunks: DiffHunk[];\n /** Names the diff — usually the path of the file being changed. */\n label: string;\n view?: \"unified\" | \"split\";\n /** Per-hunk decisions, keyed by hunk id. Controlled. */\n decisions?: Record<string, HunkDecision>;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n children?: ReactNode;\n}\n\nexport function DiffViewer({\n className,\n hunks,\n label,\n view = \"unified\",\n decisions,\n onDecision,\n children,\n ...props\n}: DiffViewerProps) {\n const counts = useMemo(() => countChanges(hunks), [hunks]);\n\n return (\n <div\n data-slot=\"diff-viewer\"\n data-view={view}\n className={cn(\"flex flex-col gap-2\", className)}\n {...props}\n >\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"font-mono text-sm font-medium\">{label}</span>\n {/* Said in words as well as coloured, and before the diff rather than\n after it: a reader deserves to know the size of the change before\n they start reading it. */}\n <span data-slot=\"diff-viewer-summary\" className=\"text-xs text-muted-foreground\">\n <span className=\"text-success\">+{counts.added}</span>{\" \"}\n <span className=\"text-destructive\">−{counts.removed}</span>\n <span className=\"sr-only\">\n {` — ${String(counts.added)} lines added, ${String(counts.removed)} removed`}\n </span>\n </span>\n </div>\n\n {children}\n\n {hunks.length === 0 ? (\n <p className=\"rounded-lg border border-border bg-muted/30 p-3 text-sm text-muted-foreground\">\n No changes.\n </p>\n ) : (\n <div className=\"overflow-hidden rounded-lg border border-border\">\n {hunks.map((hunk) => (\n <DiffHunkView\n key={hunk.id}\n hunk={hunk}\n label={label}\n view={view}\n decision={decisions?.[hunk.id]}\n onDecision={onDecision}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction DiffHunkView({\n hunk,\n label,\n view,\n decision,\n onDecision,\n}: {\n hunk: DiffHunk;\n label: string;\n view: \"unified\" | \"split\";\n decision?: HunkDecision;\n onDecision?: (hunkId: string, decision: HunkDecision) => void;\n}) {\n const splitRows = useMemo(\n () => (view === \"split\" ? toSplitRows(hunk.rows) : []),\n [view, hunk.rows],\n );\n\n return (\n <section\n data-slot=\"diff-hunk\"\n data-decision={decision}\n aria-label={`${label}, hunk ${hunk.id}`}\n className={cn(\n \"border-b border-border last:border-b-0\",\n decision === \"rejected\" && \"opacity-55\",\n )}\n >\n {hunk.skippedBefore > 0 ? (\n <p\n data-slot=\"diff-hunk-skipped\"\n className=\"border-b border-border bg-muted/40 px-3 py-1 font-mono text-2xs text-muted-foreground\"\n >\n {hunk.skippedBefore} unchanged {hunk.skippedBefore === 1 ? \"line\" : \"lines\"} hidden\n </p>\n ) : null}\n\n {onDecision ? (\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5\">\n <span className=\"flex-1 text-xs text-muted-foreground\">\n {decision === \"accepted\"\n ? \"Accepted\"\n : decision === \"rejected\"\n ? \"Rejected\"\n : \"Not decided\"}\n </span>\n <HunkButton\n pressed={decision === \"accepted\"}\n onClick={() => {\n onDecision(hunk.id, \"accepted\");\n }}\n >\n Accept\n </HunkButton>\n <HunkButton\n pressed={decision === \"rejected\"}\n onClick={() => {\n onDecision(hunk.id, \"rejected\");\n }}\n >\n Reject\n </HunkButton>\n </div>\n ) : null}\n\n <div className=\"overflow-x-auto\">\n <table data-slot=\"diff-table\" className=\"w-full border-collapse font-mono text-xs\">\n <tbody>\n {view === \"split\"\n ? splitRows.map((pair, index) => (\n <tr key={index} data-slot=\"diff-row\">\n <SplitCell row={pair.left} side=\"before\" />\n <SplitCell row={pair.right} side=\"after\" />\n </tr>\n ))\n : hunk.rows.map((row, index) => <UnifiedRow key={index} row={row} />)}\n </tbody>\n </table>\n </div>\n </section>\n );\n}\n\nconst ROW_STYLES: Record<DiffRow[\"kind\"], string> = {\n added: \"bg-success/10\",\n removed: \"bg-destructive/10\",\n context: \"\",\n};\n\nfunction UnifiedRow({ row }: { row: DiffRow }) {\n return (\n <tr data-slot=\"diff-row\" data-kind={row.kind} className={ROW_STYLES[row.kind]}>\n <LineNumber value={row.before} />\n <LineNumber value={row.after} />\n <td className=\"w-4 pe-1 text-center text-muted-foreground select-none\" aria-hidden=\"true\">\n {row.kind === \"added\" ? \"+\" : row.kind === \"removed\" ? \"−\" : \"\"}\n </td>\n <td className=\"w-full py-0.5 pe-3 break-all whitespace-pre-wrap\">\n {/* The kind, for anyone who cannot see the sign or the tint. Reading a\n diff aloud without it is reading the same file twice. */}\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </tr>\n );\n}\n\nfunction SplitCell({ row, side }: { row: DiffRow | null; side: \"before\" | \"after\" }) {\n if (!row) {\n // An empty half of a pair, not a blank line of code. Hidden from assistive\n // technology so a reader is not read padding.\n return (\n <>\n <td aria-hidden=\"true\" className=\"w-10 bg-muted/40\" />\n <td aria-hidden=\"true\" className=\"w-1/2 bg-muted/20\" />\n </>\n );\n }\n\n return (\n <>\n <LineNumber value={side === \"before\" ? row.before : row.after} />\n <td\n className={cn(\"w-1/2 py-0.5 pe-3 break-all whitespace-pre-wrap\", ROW_STYLES[row.kind])}\n >\n <span className=\"sr-only\">{KIND_LABEL[row.kind]}: </span>\n <RowContent row={row} />\n </td>\n </>\n );\n}\n\nfunction RowContent({ row }: { row: DiffRow }) {\n if (!row.segments) return <>{row.content}</>;\n\n return (\n <>\n {row.segments.map((segment, index) =>\n segment.changed ? (\n // A mark element, so the changed words survive as structure rather\n // than existing only as a slightly stronger background.\n <mark\n key={index}\n className={cn(\n \"rounded-[2px] text-inherit\",\n row.kind === \"added\" ? \"bg-success/30\" : \"bg-destructive/30\",\n )}\n >\n {segment.text}\n </mark>\n ) : (\n <span key={index}>{segment.text}</span>\n ),\n )}\n </>\n );\n}\n\nfunction LineNumber({ value }: { value?: number }) {\n return (\n <td\n // Decorative: the line number is orientation for a sighted reader, and\n // announcing two numbers before every line makes the diff unlistenable.\n aria-hidden=\"true\"\n className=\"w-10 border-e border-border bg-muted/40 px-2 py-0.5 text-end text-muted-foreground tabular-nums select-none\"\n >\n {value ?? \"\"}\n </td>\n );\n}\n\nfunction HunkButton({\n className,\n pressed,\n ...props\n}: ComponentPropsWithRef<\"button\"> & { pressed: boolean }) {\n return (\n <button\n type=\"button\"\n aria-pressed={pressed}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium transition-colors\",\n pressed\n ? \"border-primary bg-primary text-primary-foreground\"\n : \"border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Switches between unified and side-by-side. */\nexport function DiffViewerToolbar({\n className,\n view,\n onViewChange,\n ...props\n}: Omit<ComponentPropsWithRef<\"div\">, \"onChange\"> & {\n view: \"unified\" | \"split\";\n onViewChange: (view: \"unified\" | \"split\") => void;\n}) {\n return (\n <div\n data-slot=\"diff-viewer-toolbar\"\n role=\"group\"\n aria-label=\"Diff layout\"\n className={cn(\"flex items-center gap-1\", className)}\n {...props}\n >\n {([\"unified\", \"split\"] as const).map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={view === option}\n onClick={() => {\n onViewChange(option);\n }}\n className={cn(\n \"rounded-md border px-2 py-0.5 text-xs font-medium capitalize transition-colors\",\n view === option\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-input bg-background text-muted-foreground hover:text-foreground\",\n focusRing,\n )}\n >\n {option}\n </button>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/direction/direction.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Tells the component set which way the writing runs.
|
|
5
|
+
*
|
|
6
|
+
* Every direction-dependent style in this library is written logically —
|
|
7
|
+
* `ps-`, `me-`, `text-end` — so the CSS follows `dir` on its own. The
|
|
8
|
+
* primitives underneath do not: several of them read direction from React
|
|
9
|
+
* context rather than from the document, and with no provider they assume
|
|
10
|
+
* left-to-right and set `dir="ltr"` on their own elements. The result is a page
|
|
11
|
+
* that mirrors correctly except for its menus, selects and sliders, which is
|
|
12
|
+
* worse than one that does not mirror at all, because it looks deliberate.
|
|
13
|
+
*
|
|
14
|
+
* So an RTL application needs both: `dir` on the document for the CSS, and this
|
|
15
|
+
* for the primitives.
|
|
16
|
+
*
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <html dir="rtl" lang="ar">
|
|
19
|
+
* <body>
|
|
20
|
+
* <DirectionProvider dir="rtl">{children}</DirectionProvider>
|
|
21
|
+
* </body>
|
|
22
|
+
* </html>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* A left-to-right application needs neither. Nothing here is required to render
|
|
26
|
+
* English correctly, which is why it is easy to ship without it and never know.
|
|
27
|
+
*/
|
|
28
|
+
interface DirectionProviderProps {
|
|
29
|
+
dir: "ltr" | "rtl";
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
declare function DirectionProvider({ dir, children }: DirectionProviderProps): import("react").JSX.Element;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { DirectionProvider, DirectionProviderProps };
|
|
35
|
+
//# sourceMappingURL=direction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direction.d.ts","names":[],"sources":["../../../src/components/direction/direction.tsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BiB;EACf;EACA,WAAW;;iBAGG,oBAAoB,KAAK,YAAY,yCAAsB,IAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Direction } from "radix-ui";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/direction/direction.tsx
|
|
5
|
+
function DirectionProvider({ dir, children }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(Direction.Provider, {
|
|
7
|
+
dir,
|
|
8
|
+
children
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DirectionProvider };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=direction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direction.js","names":["DirectionPrimitive"],"sources":["../../../src/components/direction/direction.tsx"],"sourcesContent":["\"use client\";\n\nimport { Direction as DirectionPrimitive } from \"radix-ui\";\nimport type { ReactNode } from \"react\";\n\n/**\n * Tells the component set which way the writing runs.\n *\n * Every direction-dependent style in this library is written logically —\n * `ps-`, `me-`, `text-end` — so the CSS follows `dir` on its own. The\n * primitives underneath do not: several of them read direction from React\n * context rather than from the document, and with no provider they assume\n * left-to-right and set `dir=\"ltr\"` on their own elements. The result is a page\n * that mirrors correctly except for its menus, selects and sliders, which is\n * worse than one that does not mirror at all, because it looks deliberate.\n *\n * So an RTL application needs both: `dir` on the document for the CSS, and this\n * for the primitives.\n *\n * ```tsx\n * <html dir=\"rtl\" lang=\"ar\">\n * <body>\n * <DirectionProvider dir=\"rtl\">{children}</DirectionProvider>\n * </body>\n * </html>\n * ```\n *\n * A left-to-right application needs neither. Nothing here is required to render\n * English correctly, which is why it is easy to ship without it and never know.\n */\nexport interface DirectionProviderProps {\n dir: \"ltr\" | \"rtl\";\n children?: ReactNode;\n}\n\nexport function DirectionProvider({ dir, children }: DirectionProviderProps) {\n return <DirectionPrimitive.Provider dir={dir}>{children}</DirectionPrimitive.Provider>;\n}\n"],"mappings":";;;;AAmCA,SAAgB,kBAAkB,EAAE,KAAK,YAAoC;CAC3E,OAAO,oBAACA,UAAmB,UAApB;EAAkC;EAAM;CAAsC,CAAA;AACvF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineMeta } from "../../registry/schema.js";
|
|
2
|
+
//#region src/components/direction/meta.ts
|
|
3
|
+
const meta = defineMeta({
|
|
4
|
+
name: "direction",
|
|
5
|
+
title: "Direction Provider",
|
|
6
|
+
description: "Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.",
|
|
7
|
+
category: "foundation",
|
|
8
|
+
status: "stable",
|
|
9
|
+
dependencies: ["radix-ui"],
|
|
10
|
+
registryDependencies: [],
|
|
11
|
+
files: ["direction.tsx"],
|
|
12
|
+
a11y: "Needed only for right-to-left languages, and needed in addition to `dir` on the document rather than instead of it. The styling here is written with logical properties and follows `dir` by itself; the primitives underneath read direction from React context and assume left-to-right without a provider, which mirrors a page correctly except for its menus, selects and sliders — worse than not mirroring at all, because it looks deliberate."
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { meta };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.js","names":[],"sources":["../../../src/components/direction/meta.ts"],"sourcesContent":["import { defineMeta } from \"@/registry/schema\";\n\nexport const meta = defineMeta({\n name: \"direction\",\n title: \"Direction Provider\",\n description:\n \"Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.\",\n category: \"foundation\",\n status: \"stable\",\n dependencies: [\"radix-ui\"],\n registryDependencies: [],\n files: [\"direction.tsx\"],\n a11y:\n \"Needed only for right-to-left languages, and needed in addition to `dir` on the document \" +\n \"rather than instead of it. The styling here is written with logical properties and follows \" +\n \"`dir` by itself; the primitives underneath read direction from React context and assume \" +\n \"left-to-right without a provider, which mirrors a page correctly except for its menus, \" +\n \"selects and sliders — worse than not mirroring at all, because it looks deliberate.\",\n});\n"],"mappings":";;AAEA,MAAa,OAAO,WAAW;CAC7B,MAAM;CACN,OAAO;CACP,aACE;CACF,UAAU;CACV,QAAQ;CACR,cAAc,CAAC,UAAU;CACzB,sBAAsB,CAAC;CACvB,OAAO,CAAC,eAAe;CACvB,MACE;AAKJ,CAAC"}
|
|
@@ -107,7 +107,7 @@ function DrawerContent({ className, children, showHandle = true, ...props }) {
|
|
|
107
107
|
function DrawerHeader({ className, ...props }) {
|
|
108
108
|
return /* @__PURE__ */ jsx("div", {
|
|
109
109
|
"data-slot": "drawer-header",
|
|
110
|
-
className: cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-
|
|
110
|
+
className: cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-start", className),
|
|
111
111
|
...props
|
|
112
112
|
});
|
|
113
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","names":["DrawerPrimitive"],"sources":["../../../src/components/drawer/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DrawerPrimitive } from \"radix-ui\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type PointerEvent as ReactPointerEvent,\n} from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A bottom sheet that can be dismissed by dragging it down.\n *\n * Drag is a pointer-only affordance. Escape, the overlay and the close button\n * are the accessible equivalents and are always present, so keyboard and screen\n * reader users are never dependent on the gesture.\n *\n * Deliberately bottom-anchored only. A drawer that enters from the side with no\n * gesture is a Sheet, and having two components that differ by nothing but a\n * name is worse than having one. Snap points are not supported in this version.\n */\n\ninterface DrawerContextValue {\n close: () => void;\n}\n\nconst DrawerContext = createContext<DrawerContextValue | null>(null);\n\nfunction useDrawerContext(component: string): DrawerContextValue {\n const context = useContext(DrawerContext);\n if (!context) {\n throw new Error(`${component} must be rendered inside <Drawer>.`);\n }\n return context;\n}\n\nexport interface DrawerProps extends Omit<\n ComponentPropsWithRef<typeof DrawerPrimitive.Root>,\n \"onOpenChange\"\n> {\n /**\n * Redeclared as a property rather than inherited as a method signature. The\n * inherited shorthand reads as a method, which makes destructuring it look\n * like an unbound `this` hazard to static analysis; a plain function property\n * says what this actually is.\n */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function Drawer({ open, defaultOpen, onOpenChange, children, ...props }: DrawerProps) {\n // State is owned here (while still supporting the controlled form) because\n // dismissing by drag has to close the drawer, and the primitive exposes no\n // imperative close.\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen ?? false);\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : uncontrolledOpen;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const contextValue = useMemo<DrawerContextValue>(\n () => ({ close: () => setOpen(false) }),\n [setOpen],\n );\n\n return (\n <DrawerPrimitive.Root open={isOpen} onOpenChange={setOpen} {...props}>\n <DrawerContext.Provider value={contextValue}>{children}</DrawerContext.Provider>\n </DrawerPrimitive.Root>\n );\n}\n\nexport const DrawerTrigger = DrawerPrimitive.Trigger;\nexport const DrawerPortal = DrawerPrimitive.Portal;\nexport const DrawerClose = DrawerPrimitive.Close;\n\nexport function DrawerOverlay({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Fraction of the drawer's height past which release dismisses it. */\nconst DISMISS_DISTANCE_RATIO = 0.25;\n/** Downward speed (px/ms) that dismisses a short but deliberate flick. */\nconst DISMISS_VELOCITY = 0.5;\n/**\n * Travel below which nothing dismisses, however fast.\n *\n * A tap on the handle registers as a few pixels of movement over a couple of\n * milliseconds, which is an enormous velocity. Without a floor, touching the\n * drawer would close it.\n */\nconst MIN_FLICK_DISTANCE = 24;\n\nexport interface DrawerContentProps extends ComponentPropsWithRef<\n typeof DrawerPrimitive.Content\n> {\n /** Shows the grab handle. Turn off only if the drawer cannot be dragged. */\n showHandle?: boolean;\n}\n\nexport function DrawerContent({\n className,\n children,\n showHandle = true,\n ...props\n}: DrawerContentProps) {\n const { close } = useDrawerContext(\"DrawerContent\");\n const contentRef = useRef<HTMLDivElement | null>(null);\n const gesture = useRef<{ startY: number; startTime: number } | null>(null);\n const [offset, setOffset] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const handlePointerDown = (event: ReactPointerEvent<HTMLElement>) => {\n if (event.button !== 0) return;\n gesture.current = { startY: event.clientY, startTime: event.timeStamp };\n setIsDragging(true);\n event.currentTarget.setPointerCapture(event.pointerId);\n };\n\n const handlePointerMove = (event: ReactPointerEvent<HTMLElement>) => {\n if (!gesture.current) return;\n // Downward only: dragging up must not detach the drawer from the edge.\n setOffset(Math.max(0, event.clientY - gesture.current.startY));\n };\n\n const handlePointerUp = (event: ReactPointerEvent<HTMLElement>) => {\n if (!gesture.current) return;\n\n const distance = Math.max(0, event.clientY - gesture.current.startY);\n const elapsed = Math.max(1, event.timeStamp - gesture.current.startTime);\n const velocity = distance / elapsed;\n const height = contentRef.current?.getBoundingClientRect().height ?? 0;\n\n gesture.current = null;\n setIsDragging(false);\n setOffset(0);\n\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n\n const draggedPastThreshold = height > 0 && distance > height * DISMISS_DISTANCE_RATIO;\n const flicked = distance >= MIN_FLICK_DISTANCE && velocity > DISMISS_VELOCITY;\n\n if (draggedPastThreshold || flicked) {\n close();\n }\n };\n\n const dragHandlers = {\n onPointerDown: handlePointerDown,\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerUp,\n };\n\n return (\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n ref={contentRef}\n data-slot=\"drawer-content\"\n data-dragging={isDragging || undefined}\n style={offset > 0 ? { transform: `translate3d(0, ${String(offset)}px, 0)` } : undefined}\n className={cn(\n \"fixed inset-x-0 bottom-0 z-[var(--z-drawer)] flex max-h-[92svh] flex-col\",\n \"rounded-t-2xl border-t border-border bg-card text-card-foreground shadow-xl\",\n \"[--slide-y:100%]\",\n \"data-[state=closed]:animate-slide-out data-[state=open]:animate-slide-in\",\n // While a finger is down the transform is driven directly; afterwards\n // it springs back. Suppressing the entry animation mid-drag stops the\n // two from fighting over the same property.\n \"data-[dragging]:animate-none data-[dragging]:transition-none\",\n \"transition-transform duration-[var(--duration-normal)] ease-[var(--ease-out-quint)]\",\n className,\n )}\n {...props}\n >\n {showHandle ? (\n <div\n data-slot=\"drawer-handle\"\n aria-hidden=\"true\"\n className=\"flex shrink-0 cursor-grab touch-none justify-center pt-3 pb-1 active:cursor-grabbing\"\n {...dragHandlers}\n >\n <div className=\"h-1.5 w-10 rounded-full bg-border-strong\" />\n </div>\n ) : null}\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nexport function DrawerHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\"flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-left\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerBody({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-body\"\n className={cn(\"overflow-y-auto overscroll-contain px-6 py-2\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"flex shrink-0 flex-col gap-2 px-6 pt-2 pb-6\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerTitle({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerDescription({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n/** Close control for the drawer footer, styled as a full-width secondary action. */\nexport function DrawerCancel({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Close>) {\n return (\n <DrawerPrimitive.Close\n data-slot=\"drawer-cancel\"\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-5 text-sm font-medium\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n className,\n )}\n {...props}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drawer.js","names":["DrawerPrimitive"],"sources":["../../../src/components/drawer/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport { Dialog as DrawerPrimitive } from \"radix-ui\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type PointerEvent as ReactPointerEvent,\n} from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A bottom sheet that can be dismissed by dragging it down.\n *\n * Drag is a pointer-only affordance. Escape, the overlay and the close button\n * are the accessible equivalents and are always present, so keyboard and screen\n * reader users are never dependent on the gesture.\n *\n * Deliberately bottom-anchored only. A drawer that enters from the side with no\n * gesture is a Sheet, and having two components that differ by nothing but a\n * name is worse than having one. Snap points are not supported in this version.\n */\n\ninterface DrawerContextValue {\n close: () => void;\n}\n\nconst DrawerContext = createContext<DrawerContextValue | null>(null);\n\nfunction useDrawerContext(component: string): DrawerContextValue {\n const context = useContext(DrawerContext);\n if (!context) {\n throw new Error(`${component} must be rendered inside <Drawer>.`);\n }\n return context;\n}\n\nexport interface DrawerProps extends Omit<\n ComponentPropsWithRef<typeof DrawerPrimitive.Root>,\n \"onOpenChange\"\n> {\n /**\n * Redeclared as a property rather than inherited as a method signature. The\n * inherited shorthand reads as a method, which makes destructuring it look\n * like an unbound `this` hazard to static analysis; a plain function property\n * says what this actually is.\n */\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function Drawer({ open, defaultOpen, onOpenChange, children, ...props }: DrawerProps) {\n // State is owned here (while still supporting the controlled form) because\n // dismissing by drag has to close the drawer, and the primitive exposes no\n // imperative close.\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen ?? false);\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : uncontrolledOpen;\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setUncontrolledOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const contextValue = useMemo<DrawerContextValue>(\n () => ({ close: () => setOpen(false) }),\n [setOpen],\n );\n\n return (\n <DrawerPrimitive.Root open={isOpen} onOpenChange={setOpen} {...props}>\n <DrawerContext.Provider value={contextValue}>{children}</DrawerContext.Provider>\n </DrawerPrimitive.Root>\n );\n}\n\nexport const DrawerTrigger = DrawerPrimitive.Trigger;\nexport const DrawerPortal = DrawerPrimitive.Portal;\nexport const DrawerClose = DrawerPrimitive.Close;\n\nexport function DrawerOverlay({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Fraction of the drawer's height past which release dismisses it. */\nconst DISMISS_DISTANCE_RATIO = 0.25;\n/** Downward speed (px/ms) that dismisses a short but deliberate flick. */\nconst DISMISS_VELOCITY = 0.5;\n/**\n * Travel below which nothing dismisses, however fast.\n *\n * A tap on the handle registers as a few pixels of movement over a couple of\n * milliseconds, which is an enormous velocity. Without a floor, touching the\n * drawer would close it.\n */\nconst MIN_FLICK_DISTANCE = 24;\n\nexport interface DrawerContentProps extends ComponentPropsWithRef<\n typeof DrawerPrimitive.Content\n> {\n /** Shows the grab handle. Turn off only if the drawer cannot be dragged. */\n showHandle?: boolean;\n}\n\nexport function DrawerContent({\n className,\n children,\n showHandle = true,\n ...props\n}: DrawerContentProps) {\n const { close } = useDrawerContext(\"DrawerContent\");\n const contentRef = useRef<HTMLDivElement | null>(null);\n const gesture = useRef<{ startY: number; startTime: number } | null>(null);\n const [offset, setOffset] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n\n const handlePointerDown = (event: ReactPointerEvent<HTMLElement>) => {\n if (event.button !== 0) return;\n gesture.current = { startY: event.clientY, startTime: event.timeStamp };\n setIsDragging(true);\n event.currentTarget.setPointerCapture(event.pointerId);\n };\n\n const handlePointerMove = (event: ReactPointerEvent<HTMLElement>) => {\n if (!gesture.current) return;\n // Downward only: dragging up must not detach the drawer from the edge.\n setOffset(Math.max(0, event.clientY - gesture.current.startY));\n };\n\n const handlePointerUp = (event: ReactPointerEvent<HTMLElement>) => {\n if (!gesture.current) return;\n\n const distance = Math.max(0, event.clientY - gesture.current.startY);\n const elapsed = Math.max(1, event.timeStamp - gesture.current.startTime);\n const velocity = distance / elapsed;\n const height = contentRef.current?.getBoundingClientRect().height ?? 0;\n\n gesture.current = null;\n setIsDragging(false);\n setOffset(0);\n\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId);\n }\n\n const draggedPastThreshold = height > 0 && distance > height * DISMISS_DISTANCE_RATIO;\n const flicked = distance >= MIN_FLICK_DISTANCE && velocity > DISMISS_VELOCITY;\n\n if (draggedPastThreshold || flicked) {\n close();\n }\n };\n\n const dragHandlers = {\n onPointerDown: handlePointerDown,\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerUp,\n };\n\n return (\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n ref={contentRef}\n data-slot=\"drawer-content\"\n data-dragging={isDragging || undefined}\n style={offset > 0 ? { transform: `translate3d(0, ${String(offset)}px, 0)` } : undefined}\n className={cn(\n \"fixed inset-x-0 bottom-0 z-[var(--z-drawer)] flex max-h-[92svh] flex-col\",\n \"rounded-t-2xl border-t border-border bg-card text-card-foreground shadow-xl\",\n \"[--slide-y:100%]\",\n \"data-[state=closed]:animate-slide-out data-[state=open]:animate-slide-in\",\n // While a finger is down the transform is driven directly; afterwards\n // it springs back. Suppressing the entry animation mid-drag stops the\n // two from fighting over the same property.\n \"data-[dragging]:animate-none data-[dragging]:transition-none\",\n \"transition-transform duration-[var(--duration-normal)] ease-[var(--ease-out-quint)]\",\n className,\n )}\n {...props}\n >\n {showHandle ? (\n <div\n data-slot=\"drawer-handle\"\n aria-hidden=\"true\"\n className=\"flex shrink-0 cursor-grab touch-none justify-center pt-3 pb-1 active:cursor-grabbing\"\n {...dragHandlers}\n >\n <div className=\"h-1.5 w-10 rounded-full bg-border-strong\" />\n </div>\n ) : null}\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nexport function DrawerHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\"flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-start\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerBody({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-body\"\n className={cn(\"overflow-y-auto overscroll-contain px-6 py-2\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"flex shrink-0 flex-col gap-2 px-6 pt-2 pb-6\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerTitle({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport function DrawerDescription({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n/** Close control for the drawer footer, styled as a full-width secondary action. */\nexport function DrawerCancel({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DrawerPrimitive.Close>) {\n return (\n <DrawerPrimitive.Close\n data-slot=\"drawer-cancel\"\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-5 text-sm font-medium\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-accent-foreground\",\n focusRing,\n className,\n )}\n {...props}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.d.ts","names":[],"sources":["../../../src/components/dropdown-menu/dropdown-menu.tsx"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"dropdown-menu.d.ts","names":[],"sources":["../../../src/components/dropdown-menu/dropdown-menu.tsx"],"mappings":";;;;;;;;;;cAea,gCAAY,GAAA,aAAA;cACZ,qCAAmB,0BAAA,aAAA,2CAAA,cAAA;cACnB,mCAAiB,0BAAA,aAAA,yCAAA,cAAA;cACjB,oCAAkB,GAAA,aAAA;cAClB,iCAAe,GAAA,aAAA;cACf,wCAAsB,0BAAA,aAAA,8CAAA,cAAA;KAqBvB,2BAA2B,6BAC9B,aAAsB;iBAGf,sBACd,WACA,eACG,SACF,2CAAwB,IAAA;UAaV,8BAA8B,6BACtC,aAAsB;;EAG7B;;EAEA;;iBAGc,mBACd,WACA,SACA,UACG,SACF,wCAAqB,IAAA;iBAyCR,2BACd,WACA,aACG,SACF,6BAA6B,aAAsB,gCAAa,IAAA;iBAanD,wBACd,WACA,aACG,SACF,6BAA6B,aAAsB,6BAAU,IAAA;UAa/C,+BAA+B,6BACvC,aAAsB;EAE7B;;iBAGc,oBAAoB,WAAW,UAAU,SAAS,yCAAsB,IAAA;iBAcxE,wBACd,cACG,SACF,6BAA6B,aAAsB,6BAAU,IAAA;;;iBAYhD,uBAAuB,cAAc,SAAS,gDAA6B,IAAA;UAW1E,oCAAoC,6BAC5C,aAAsB;EAE7B;;iBAGc,yBACd,WACA,OACA,aACG,SACF,8CAA2B,IAAA;iBA0Bd,yBACd,cACG,SACF,6BAA6B,aAAsB,8BAAW,IAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { mirrorForDirection } from "../../lib/styles.js";
|
|
2
3
|
import { cn } from "../../lib/utils.js";
|
|
3
4
|
import { DropdownMenu } from "radix-ui";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -33,7 +34,7 @@ function DropdownMenuItem({ className, variant = "default", inset, ...props }) {
|
|
|
33
34
|
return /* @__PURE__ */ jsx(DropdownMenu.Item, {
|
|
34
35
|
"data-slot": "dropdown-menu-item",
|
|
35
36
|
"data-variant": variant,
|
|
36
|
-
className: cn(menuItem, inset && "
|
|
37
|
+
className: cn(menuItem, inset && "ps-8", variant === "destructive" && "text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive", className),
|
|
37
38
|
...props
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -41,7 +42,7 @@ function DropdownMenuItem({ className, variant = "default", inset, ...props }) {
|
|
|
41
42
|
* aria-checked, so announcing it twice would be noise. */
|
|
42
43
|
function ItemIndicator({ radio = false }) {
|
|
43
44
|
return /* @__PURE__ */ jsx("span", {
|
|
44
|
-
className: "pointer-events-none absolute
|
|
45
|
+
className: "pointer-events-none absolute start-2 grid size-4 place-items-center",
|
|
45
46
|
children: /* @__PURE__ */ jsx(DropdownMenu.ItemIndicator, { children: radio ? /* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-current" }) : /* @__PURE__ */ jsx("svg", {
|
|
46
47
|
viewBox: "0 0 24 24",
|
|
47
48
|
fill: "none",
|
|
@@ -60,7 +61,7 @@ function ItemIndicator({ radio = false }) {
|
|
|
60
61
|
function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
61
62
|
return /* @__PURE__ */ jsxs(DropdownMenu.CheckboxItem, {
|
|
62
63
|
"data-slot": "dropdown-menu-checkbox-item",
|
|
63
|
-
className: cn(menuItem, "
|
|
64
|
+
className: cn(menuItem, "ps-8", className),
|
|
64
65
|
...props,
|
|
65
66
|
children: [/* @__PURE__ */ jsx(ItemIndicator, {}), children]
|
|
66
67
|
});
|
|
@@ -68,7 +69,7 @@ function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
|
68
69
|
function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
69
70
|
return /* @__PURE__ */ jsxs(DropdownMenu.RadioItem, {
|
|
70
71
|
"data-slot": "dropdown-menu-radio-item",
|
|
71
|
-
className: cn(menuItem, "
|
|
72
|
+
className: cn(menuItem, "ps-8", className),
|
|
72
73
|
...props,
|
|
73
74
|
children: [/* @__PURE__ */ jsx(ItemIndicator, { radio: true }), children]
|
|
74
75
|
});
|
|
@@ -76,7 +77,7 @@ function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
|
76
77
|
function DropdownMenuLabel({ className, inset, ...props }) {
|
|
77
78
|
return /* @__PURE__ */ jsx(DropdownMenu.Label, {
|
|
78
79
|
"data-slot": "dropdown-menu-label",
|
|
79
|
-
className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", inset && "
|
|
80
|
+
className: cn("px-2 py-1.5 text-xs font-medium text-muted-foreground", inset && "ps-8", className),
|
|
80
81
|
...props
|
|
81
82
|
});
|
|
82
83
|
}
|
|
@@ -93,20 +94,20 @@ function DropdownMenuShortcut({ className, ...props }) {
|
|
|
93
94
|
return /* @__PURE__ */ jsx("span", {
|
|
94
95
|
"data-slot": "dropdown-menu-shortcut",
|
|
95
96
|
"aria-hidden": "true",
|
|
96
|
-
className: cn("
|
|
97
|
+
className: cn("ms-auto text-2xs tracking-wide text-muted-foreground", className),
|
|
97
98
|
...props
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
|
|
101
102
|
return /* @__PURE__ */ jsxs(DropdownMenu.SubTrigger, {
|
|
102
103
|
"data-slot": "dropdown-menu-sub-trigger",
|
|
103
|
-
className: cn(menuItem, "data-[state=open]:bg-accent", inset && "
|
|
104
|
+
className: cn(menuItem, "data-[state=open]:bg-accent", inset && "ps-8", className),
|
|
104
105
|
...props,
|
|
105
106
|
children: [children, /* @__PURE__ */ jsx("svg", {
|
|
106
107
|
viewBox: "0 0 24 24",
|
|
107
108
|
fill: "none",
|
|
108
109
|
"aria-hidden": "true",
|
|
109
|
-
className: "
|
|
110
|
+
className: cn("ms-auto size-3.5", mirrorForDirection),
|
|
110
111
|
children: /* @__PURE__ */ jsx("path", {
|
|
111
112
|
d: "m9 18 6-6-6-6",
|
|
112
113
|
stroke: "currentColor",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.js","names":["DropdownMenu","DropdownMenuPrimitive"],"sources":["../../../src/components/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A menu of actions revealed from a trigger.\n *\n * Actions, not navigation and not form state: a menu item does something. For a\n * list of links use a nav, and for a value the user picks from use Select\n * (Phase 3) — the roles differ and assistive technology treats them differently.\n */\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\nexport const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\n/** Shared surface styling for the root menu and every submenu. */\nconst menuSurface = cn(\n \"z-[var(--z-popover)] min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-lg border border-border\",\n \"bg-popover p-1 text-popover-foreground shadow-lg outline-none\",\n \"max-h-[var(--radix-dropdown-menu-content-available-height)]\",\n \"origin-[var(--radix-dropdown-menu-content-transform-origin)]\",\n \"data-[state=closed]:animate-float-out data-[state=open]:animate-float-in\",\n);\n\n/** Shared item styling. Highlight follows data-highlighted, which the primitive\n * drives from both pointer and keyboard, so the two never disagree. */\nconst menuItem = cn(\n \"relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-none select-none\",\n \"transition-colors duration-[var(--duration-instant)]\",\n \"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-55\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n);\n\nexport type DropdownMenuContentProps = ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Content\n>;\n\nexport function DropdownMenuContent({\n className,\n sideOffset = 6,\n ...props\n}: DropdownMenuContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(menuSurface, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport interface DropdownMenuItemProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Item\n> {\n /** Styles the item as destructive. Pair with a label that says what is destroyed. */\n variant?: \"default\" | \"destructive\";\n /** Indents the item to align with items that have a leading indicator. */\n inset?: boolean;\n}\n\nexport function DropdownMenuItem({\n className,\n variant = \"default\",\n inset,\n ...props\n}: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-variant={variant}\n className={cn(\n menuItem,\n inset && \"pl-8\",\n variant === \"destructive\" &&\n \"text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Tick shown by checkbox and radio items. Decorative: the state is already in\n * aria-checked, so announcing it twice would be noise. */\nfunction ItemIndicator({ radio = false }: { radio?: boolean }) {\n return (\n <span className=\"pointer-events-none absolute left-2 grid size-4 place-items-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n {radio ? (\n <span className=\"size-2 rounded-full bg-current\" />\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5\">\n <path\n d=\"m5 13 4 4L19 7\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n );\n}\n\nexport function DropdownMenuCheckboxItem({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(menuItem, \"pl-8\", className)}\n {...props}\n >\n <ItemIndicator />\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nexport function DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(menuItem, \"pl-8\", className)}\n {...props}\n >\n <ItemIndicator radio />\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nexport interface DropdownMenuLabelProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Label\n> {\n inset?: boolean;\n}\n\nexport function DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n className={cn(\n \"px-2 py-1.5 text-xs font-medium text-muted-foreground\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function DropdownMenuSeparator({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\n/** Keyboard hint. aria-hidden because the shortcut is decoration here — bind it\n * for real at the application level. */\nexport function DropdownMenuShortcut({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n aria-hidden=\"true\"\n className={cn(\"ml-auto text-2xs tracking-wide text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport interface DropdownMenuSubTriggerProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.SubTrigger\n> {\n inset?: boolean;\n}\n\nexport function DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: DropdownMenuSubTriggerProps) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n className={cn(menuItem, \"data-[state=open]:bg-accent\", inset && \"pl-8\", className)}\n {...props}\n >\n {children}\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"ml-auto size-3.5\">\n <path\n d=\"m9 18 6-6-6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nexport function DropdownMenuSubContent({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(menuSurface, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-menu.js","names":["DropdownMenu","DropdownMenuPrimitive"],"sources":["../../../src/components/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { mirrorForDirection } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A menu of actions revealed from a trigger.\n *\n * Actions, not navigation and not form state: a menu item does something. For a\n * list of links use a nav, and for a value the user picks from use Select\n * (Phase 3) — the roles differ and assistive technology treats them differently.\n */\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\nexport const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\n/** Shared surface styling for the root menu and every submenu. */\nconst menuSurface = cn(\n \"z-[var(--z-popover)] min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-lg border border-border\",\n \"bg-popover p-1 text-popover-foreground shadow-lg outline-none\",\n \"max-h-[var(--radix-dropdown-menu-content-available-height)]\",\n \"origin-[var(--radix-dropdown-menu-content-transform-origin)]\",\n \"data-[state=closed]:animate-float-out data-[state=open]:animate-float-in\",\n);\n\n/** Shared item styling. Highlight follows data-highlighted, which the primitive\n * drives from both pointer and keyboard, so the two never disagree. */\nconst menuItem = cn(\n \"relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-none select-none\",\n \"transition-colors duration-[var(--duration-instant)]\",\n \"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-55\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n);\n\nexport type DropdownMenuContentProps = ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Content\n>;\n\nexport function DropdownMenuContent({\n className,\n sideOffset = 6,\n ...props\n}: DropdownMenuContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(menuSurface, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport interface DropdownMenuItemProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Item\n> {\n /** Styles the item as destructive. Pair with a label that says what is destroyed. */\n variant?: \"default\" | \"destructive\";\n /** Indents the item to align with items that have a leading indicator. */\n inset?: boolean;\n}\n\nexport function DropdownMenuItem({\n className,\n variant = \"default\",\n inset,\n ...props\n}: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-variant={variant}\n className={cn(\n menuItem,\n inset && \"ps-8\",\n variant === \"destructive\" &&\n \"text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** Tick shown by checkbox and radio items. Decorative: the state is already in\n * aria-checked, so announcing it twice would be noise. */\nfunction ItemIndicator({ radio = false }: { radio?: boolean }) {\n return (\n <span className=\"pointer-events-none absolute start-2 grid size-4 place-items-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n {radio ? (\n <span className=\"size-2 rounded-full bg-current\" />\n ) : (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" className=\"size-3.5\">\n <path\n d=\"m5 13 4 4L19 7\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n );\n}\n\nexport function DropdownMenuCheckboxItem({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(menuItem, \"ps-8\", className)}\n {...props}\n >\n <ItemIndicator />\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nexport function DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(menuItem, \"ps-8\", className)}\n {...props}\n >\n <ItemIndicator radio />\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nexport interface DropdownMenuLabelProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.Label\n> {\n inset?: boolean;\n}\n\nexport function DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n className={cn(\n \"px-2 py-1.5 text-xs font-medium text-muted-foreground\",\n inset && \"ps-8\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function DropdownMenuSeparator({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\n/** Keyboard hint. aria-hidden because the shortcut is decoration here — bind it\n * for real at the application level. */\nexport function DropdownMenuShortcut({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n aria-hidden=\"true\"\n className={cn(\"ms-auto text-2xs tracking-wide text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport interface DropdownMenuSubTriggerProps extends ComponentPropsWithRef<\n typeof DropdownMenuPrimitive.SubTrigger\n> {\n inset?: boolean;\n}\n\nexport function DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: DropdownMenuSubTriggerProps) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n className={cn(menuItem, \"data-[state=open]:bg-accent\", inset && \"ps-8\", className)}\n {...props}\n >\n {children}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(\"ms-auto size-3.5\", mirrorForDirection)}\n >\n <path\n d=\"m9 18 6-6-6-6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nexport function DropdownMenuSubContent({\n className,\n ...props\n}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(menuSurface, className)}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { cn } from "../../lib/utils.js";
|
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
//#region src/components/input/input.tsx
|
|
6
|
-
const inputVariants = cva(cn("flex w-full min-w-0 rounded-md border border-input bg-background text-foreground shadow-xs", "transition-[border-color,box-shadow] duration-[var(--duration-fast)] ease-[var(--ease-out-quint)]", "placeholder:text-muted-foreground", "file:
|
|
6
|
+
const inputVariants = cva(cn("flex w-full min-w-0 rounded-md border border-input bg-background text-foreground shadow-xs", "transition-[border-color,box-shadow] duration-[var(--duration-fast)] ease-[var(--ease-out-quint)]", "placeholder:text-muted-foreground", "file:me-3 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground", "focus-visible:border-ring", "disabled:cursor-not-allowed disabled:opacity-55", focusRingInset, invalidStyles), {
|
|
7
7
|
variants: { inputSize: {
|
|
8
8
|
sm: "h-8 px-2.5 text-sm",
|
|
9
9
|
md: "h-9 px-3 text-sm",
|