@pihanga2/shadcn 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/AGENT.building-cards.md +87 -0
  2. package/AGENT.using-cards.md +47 -0
  3. package/README.md +5 -1
  4. package/cards/avatar/avatar.component.d.ts +11 -0
  5. package/cards/avatar/avatar.component.js +26 -0
  6. package/cards/avatar/avatar.component.js.map +1 -0
  7. package/cards/avatar/avatar.types.d.ts +46 -0
  8. package/cards/avatar/avatar.types.js +7 -0
  9. package/cards/avatar/avatar.types.js.map +1 -0
  10. package/cards/avatar/index.d.ts +1 -0
  11. package/cards/avatar/index.js +12 -0
  12. package/cards/avatar/index.js.map +1 -0
  13. package/cards/badge/badge.component.js +4 -4
  14. package/cards/codeMirror/codeMirror.component.d.ts +4 -0
  15. package/cards/codeMirror/codeMirror.types.d.ts +83 -0
  16. package/cards/codeMirror/index.d.ts +1 -0
  17. package/cards/dataTable/dataTable.component.js +43 -42
  18. package/cards/dataTable/dataTable.component.js.map +1 -1
  19. package/cards/dataTable/dataTable.types.d.ts +26 -2
  20. package/cards/dataTable/dataTable.types.js +1 -0
  21. package/cards/dataTable/dataTable.types.js.map +1 -1
  22. package/cards/drawer/drawer.component.d.ts +4 -0
  23. package/cards/drawer/drawer.component.js +83 -0
  24. package/cards/drawer/drawer.component.js.map +1 -0
  25. package/cards/drawer/drawer.types.d.ts +95 -0
  26. package/cards/drawer/drawer.types.js +11 -0
  27. package/cards/drawer/drawer.types.js.map +1 -0
  28. package/cards/drawer/index.d.ts +1 -0
  29. package/cards/drawer/index.js +13 -0
  30. package/cards/drawer/index.js.map +1 -0
  31. package/cards/dropDownMenu/dropdown-menu.ui.js +41 -41
  32. package/cards/emptyCard/index.d.ts +7 -0
  33. package/cards/fileDrop/fileDrop.component.js +1 -1
  34. package/cards/fileDrop/fileDrop.css +1 -0
  35. package/cards/graphin/eventDispatcher.component.d.ts +2 -1
  36. package/cards/graphin/graphin.types.d.ts +14 -0
  37. package/cards/jsonViewer/jsonViewer.component.js +1 -1
  38. package/cards/jsonViewer/jsonViewer.css +1 -0
  39. package/cards/list/list.component.js +21 -21
  40. package/cards/loadingOverlay/loadingOverlay.component.js +1 -1
  41. package/cards/loadingOverlay/loadingOverlay.css +1 -0
  42. package/cards/pageWithNavbar/pageWithNavbar.component.js +1 -1
  43. package/cards/pageWithNavbar/pageWithNavbar.css +1 -0
  44. package/cards/pageWithNavbarMeta/index.d.ts +1 -0
  45. package/cards/pageWithNavbarMeta/index.js +118 -0
  46. package/cards/pageWithNavbarMeta/index.js.map +1 -0
  47. package/cards/pageWithNavbarMeta/pageWithNavbarMeta.types.d.ts +78 -0
  48. package/cards/pageWithNavbarMeta/pageWithNavbarMeta.types.js +7 -0
  49. package/cards/pageWithNavbarMeta/pageWithNavbarMeta.types.js.map +1 -0
  50. package/cards/pasteTarget/pasteTarget.component.js +1 -1
  51. package/cards/pasteTarget/pasteTarget.css +1 -0
  52. package/cards/scrollbarWithAnnotations/index.d.ts +1 -0
  53. package/cards/scrollbarWithAnnotations/index.js +13 -0
  54. package/cards/scrollbarWithAnnotations/index.js.map +1 -0
  55. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.component.d.ts +23 -0
  56. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.component.js +78 -0
  57. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.component.js.map +1 -0
  58. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.css +1 -0
  59. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.types.d.ts +123 -0
  60. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.types.js +7 -0
  61. package/cards/scrollbarWithAnnotations/scrollbarWithAnnotations.types.js.map +1 -0
  62. package/cards/slider/slider.component.js +67 -48
  63. package/cards/slider/slider.component.js.map +1 -1
  64. package/cards/slider/slider.types.d.ts +31 -0
  65. package/cards/slider/slider.types.js.map +1 -1
  66. package/cards/stepper/stepper.component.js +1 -1
  67. package/cards/stepper/stepper.css +1 -0
  68. package/components/ui/avatar.js +5 -5
  69. package/components/ui/badge.js +7 -7
  70. package/components/ui/button.js +6 -6
  71. package/components/ui/drawer.js +79 -0
  72. package/components/ui/drawer.js.map +1 -0
  73. package/components/ui/field.js +16 -16
  74. package/components/ui/label.js +3 -3
  75. package/components/ui/separator.js +1 -1
  76. package/components/ui/toggle-group.js +5 -5
  77. package/components/ui/toggle.js +2 -2
  78. package/lib/utils.d.ts +15 -0
  79. package/lib/utils.js.map +1 -1
  80. package/package.json +28 -3
  81. package/pihanga-shadcn.css +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"dataTable.component.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.component.tsx"],"sourcesContent":["import React, {useMemo, useState} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {\n Check,\n ChevronDown,\n ChevronRight,\n ChevronUp,\n ChevronsUpDown,\n X,\n} from \"lucide-react\";\nimport {cn} from \"@/lib/utils\";\nimport {Badge} from \"@/components/ui/badge\";\nimport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/ui/table\";\nimport type {\n DataTableColumn,\n DataTableEvents,\n DataTableProps,\n DataTableRow as DataTableRowType,\n} from \"./dataTable.types\";\n\n// ---------------------------------------------------------------------------\n// Cell renderer\n// ---------------------------------------------------------------------------\n\nfunction renderCell(\n column: DataTableColumn,\n value: unknown,\n parentCard: string,\n): React.ReactNode {\n if (value == null) return null;\n\n switch (column.type) {\n case \"number\": {\n const num = typeof value === \"number\" ? value : Number(value);\n return isNaN(num)\n ? String(value)\n : column.format\n ? column.format(num)\n : String(num);\n }\n\n case \"date\": {\n if (column.format) {\n return column.format(value as Date | string);\n }\n const d = value instanceof Date ? value : new Date(String(value));\n return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString();\n }\n\n case \"badge\": {\n const strVal = String(value);\n const variant = column.variants?.[strVal] ?? \"secondary\";\n return <Badge variant={variant}>{strVal}</Badge>;\n }\n\n case \"boolean\": {\n return value ? (\n <Check className=\"size-4 text-green-600\" aria-label=\"true\" />\n ) : (\n <X className=\"size-4 text-muted-foreground\" aria-label=\"false\" />\n );\n }\n\n case \"card\": {\n // Cell value is expected to be a PiCardRef (string)\n return <Card cardName={String(value)} parentCard={parentCard} />;\n }\n\n default:\n // \"text\" or omitted type\n return String(value);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sort icon\n// ---------------------------------------------------------------------------\n\nfunction SortIcon({\n columnKey,\n activeKey,\n ascending,\n}: {\n columnKey: string;\n activeKey: string | undefined;\n ascending: boolean;\n}): React.ReactNode {\n if (columnKey !== activeKey) {\n return <ChevronsUpDown className=\"ml-1 inline size-3.5 opacity-40\" />;\n }\n return ascending ? (\n <ChevronUp className=\"ml-1 inline size-3.5\" />\n ) : (\n <ChevronDown className=\"ml-1 inline size-3.5\" />\n );\n}\n\n// ---------------------------------------------------------------------------\n// Pagination controls\n// ---------------------------------------------------------------------------\n\nfunction PaginationBar({\n page,\n totalPages,\n onPrev,\n onNext,\n}: {\n page: number;\n totalPages: number;\n onPrev: () => void;\n onNext: () => void;\n}): React.ReactNode {\n return (\n <div className=\"flex items-center justify-end gap-2 mt-2 px-2\">\n <span className=\"text-sm text-muted-foreground\">\n Page {page + 1} of {totalPages}\n </span>\n <button\n type=\"button\"\n className={cn(\n \"rounded border px-2 py-1 text-sm transition-colors\",\n \"hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n disabled={page === 0}\n onClick={onPrev}\n >\n Previous\n </button>\n <button\n type=\"button\"\n className={cn(\n \"rounded border px-2 py-1 text-sm transition-colors\",\n \"hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n disabled={page >= totalPages - 1}\n onClick={onNext}\n >\n Next\n </button>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Helper: derive a human-readable title from a camelCase key\n// ---------------------------------------------------------------------------\n\nfunction keyToTitle(key: string): string {\n return key\n .replace(/([A-Z])/g, \" $1\")\n .replace(/^./, (s) => s.toUpperCase())\n .trim();\n}\n\n// ---------------------------------------------------------------------------\n// Main data-table component\n// ---------------------------------------------------------------------------\n\nexport const DataTableComponent = (\n props: PiCardProps<DataTableProps, DataTableEvents>,\n): React.ReactNode => {\n const {\n columns,\n rows,\n caption,\n sortKey: sortKeyProp,\n sortAscending: sortAscendingProp,\n pageSize,\n currentPage: currentPageProp,\n stickyHeader = false,\n striped = false,\n hoverable = true,\n compact = false,\n emptyCard,\n emptyText = \"No data\",\n className,\n cardName,\n onRowClicked,\n onSortChanged,\n onShowDetail,\n onHideDetail,\n onPageChanged,\n } = props;\n\n // ── Expanded-rows state ──────────────────────────────────────────────────\n const [expandedRows, setExpandedRows] = useState<Set<string | number>>(\n new Set(),\n );\n\n // ── Sort state (uncontrolled; props override when present) ───────────────\n const [localSortKey, setLocalSortKey] = useState<string | undefined>(\n sortKeyProp,\n );\n const [localSortAsc, setLocalSortAsc] = useState<boolean>(\n sortAscendingProp ?? true,\n );\n const activeSortKey = sortKeyProp !== undefined ? sortKeyProp : localSortKey;\n const activeSortAsc =\n sortAscendingProp !== undefined ? sortAscendingProp : localSortAsc;\n\n // ── Pagination state (uncontrolled; props override when present) ─────────\n const [localPage, setLocalPage] = useState<number>(currentPageProp ?? 0);\n const activePage =\n currentPageProp !== undefined ? currentPageProp : localPage;\n\n // ── Derived flags ────────────────────────────────────────────────────────\n const hasDetailRows = rows.some((row) => row.detailCard != null);\n // Total column count used for detail-row colSpan\n const colCount = columns.length + (hasDetailRows ? 1 : 0);\n\n // ── Sorted rows ──────────────────────────────────────────────────────────\n const sortedRows = useMemo(() => {\n if (!activeSortKey) return rows;\n return [...rows].sort((a, b) => {\n const av = (a.data as Record<string, unknown>)[activeSortKey];\n const bv = (b.data as Record<string, unknown>)[activeSortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = av < bv ? -1 : av > bv ? 1 : 0;\n return activeSortAsc ? cmp : -cmp;\n });\n }, [rows, activeSortKey, activeSortAsc]);\n\n // ── Paginated slice ──────────────────────────────────────────────────────\n const paginatedRows = useMemo(() => {\n if (!pageSize) return sortedRows;\n const start = activePage * pageSize;\n return sortedRows.slice(start, start + pageSize);\n }, [sortedRows, pageSize, activePage]);\n\n const totalPages = pageSize ? Math.ceil(sortedRows.length / pageSize) : 1;\n\n // ── Handlers ─────────────────────────────────────────────────────────────\n const handleSortClick = (key: string) => {\n const newAsc = activeSortKey === key ? !activeSortAsc : true;\n setLocalSortKey(key);\n setLocalSortAsc(newAsc);\n onSortChanged({key, ascending: newAsc});\n };\n\n const handleToggleDetail = (row: DataTableRowType, e: React.MouseEvent) => {\n e.stopPropagation();\n setExpandedRows((prev) => {\n const next = new Set(prev);\n if (next.has(row.id)) {\n next.delete(row.id);\n onHideDetail({rowId: row.id, row});\n } else {\n next.add(row.id);\n onShowDetail({rowId: row.id, row});\n }\n return next;\n });\n };\n\n const handleRowClick = (row: DataTableRowType) => {\n onRowClicked({rowId: row.id, row});\n };\n\n const handlePageChange = (page: number) => {\n setLocalPage(page);\n if (pageSize) {\n onPageChanged({page, pageSize});\n }\n };\n\n // ── Cell padding ─────────────────────────────────────────────────────────\n const cellPad = compact ? \"py-1 px-2\" : \"py-2 px-3\";\n\n // ── Render ────────────────────────────────────────────────────────────────\n return (\n <div className={cn(\"w-full\", className)} data-pihanga={cardName}>\n <Table>\n {caption && <TableCaption>{caption}</TableCaption>}\n\n {/* ── Header ─────────────────────────────────────────────────────── */}\n <TableHeader\n className={\n stickyHeader ? \"sticky top-0 z-10 bg-background\" : undefined\n }\n >\n <TableRow>\n {/* Expand-toggle placeholder header cell */}\n {hasDetailRows && (\n <TableHead className=\"w-10 px-2\" aria-label=\"Row details\" />\n )}\n\n {columns.map((col) => (\n <TableHead\n key={col.key}\n style={col.width ? {width: col.width} : undefined}\n className={cn(\n cellPad,\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n col.sortable && \"cursor-pointer select-none\",\n col.headerClassName,\n )}\n onClick={\n col.sortable ? () => handleSortClick(col.key) : undefined\n }\n >\n <span className=\"inline-flex items-center\">\n {col.title ?? keyToTitle(col.key)}\n {col.sortable && (\n <SortIcon\n columnKey={col.key}\n activeKey={activeSortKey}\n ascending={activeSortAsc}\n />\n )}\n </span>\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n\n {/* ── Body ───────────────────────────────────────────────────────── */}\n <TableBody>\n {paginatedRows.length === 0 ? (\n /* Empty state */\n <TableRow>\n <TableCell\n colSpan={colCount}\n className=\"py-8 text-center text-muted-foreground\"\n >\n {emptyCard ? (\n <Card cardName={emptyCard} parentCard={cardName} />\n ) : (\n emptyText\n )}\n </TableCell>\n </TableRow>\n ) : (\n paginatedRows.map((row, rowIdx) => {\n const isExpanded = expandedRows.has(row.id);\n const isStriped = striped && rowIdx % 2 === 1;\n\n return (\n <React.Fragment key={row.id}>\n {/* ── Data row ─────────────────────────────────────────── */}\n <TableRow\n className={cn(\n isStriped && \"bg-muted/30\",\n hoverable && \"cursor-pointer\",\n )}\n data-expanded={isExpanded || undefined}\n onClick={() => handleRowClick(row)}\n >\n {/* Expand / collapse toggle */}\n {hasDetailRows && (\n <TableCell className=\"w-10 px-2\">\n {row.detailCard && (\n <button\n type=\"button\"\n aria-label={\n isExpanded ? \"Collapse detail\" : \"Expand detail\"\n }\n aria-expanded={isExpanded}\n className={cn(\n \"flex items-center justify-center rounded p-0.5 transition-colors\",\n \"hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\",\n )}\n onClick={(e) => handleToggleDetail(row, e)}\n >\n {isExpanded ? (\n <ChevronDown className=\"size-4\" />\n ) : (\n <ChevronRight className=\"size-4\" />\n )}\n </button>\n )}\n </TableCell>\n )}\n\n {/* Data cells */}\n {columns.map((col) => {\n const value = (row.data as Record<string, unknown>)[\n col.key\n ];\n return (\n <TableCell\n key={col.key}\n className={cn(\n cellPad,\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n col.cellClassName,\n )}\n >\n {renderCell(col, value, cardName)}\n </TableCell>\n );\n })}\n </TableRow>\n\n {/* ── Detail row (full-width, shown when expanded) ──────── */}\n {isExpanded && row.detailCard && (\n <TableRow\n // Suppress the default hover/click styles on the detail row\n className=\"hover:bg-transparent\"\n onClick={(e) => e.stopPropagation()}\n >\n <TableCell colSpan={colCount} className=\"border-b p-0\">\n <div className=\"bg-muted/20 px-4 py-3\">\n {/*\n * cardKey creates a unique virtual card instance per row\n * (e.g. \"myDetailTemplate@detail-42\").\n * The `row` prop is forwarded as ctxtProps so the detail\n * card's state-mapper functions can access it via\n * `ctx.ctxtProps.row` — no need to pre-register one card\n * per row.\n */}\n <Card\n cardName={row.detailCard}\n cardKey={`detail-${row.id}`}\n row={row}\n parentCard={cardName}\n key={`${cardName}-${row.id}-detail`}\n />\n </div>\n </TableCell>\n </TableRow>\n )}\n </React.Fragment>\n );\n })\n )}\n </TableBody>\n </Table>\n\n {/* ── Pagination controls ──────────────────────────────────────────── */}\n {pageSize && totalPages > 1 && (\n <PaginationBar\n page={activePage}\n totalPages={totalPages}\n onPrev={() => handlePageChange(activePage - 1)}\n onNext={() => handlePageChange(activePage + 1)}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"dataTable.component.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.component.tsx"],"sourcesContent":["import React, {useMemo, useState} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {\n Check,\n ChevronDown,\n ChevronRight,\n ChevronUp,\n ChevronsUpDown,\n X,\n} from \"lucide-react\";\nimport {cn} from \"@/lib/utils\";\nimport {Badge} from \"@/components/ui/badge\";\nimport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/ui/table\";\nimport type {\n DataTableColumn,\n DataTableEvents,\n DataTableProps,\n DataTableRow as DataTableRowType,\n} from \"./dataTable.types\";\n\n// ---------------------------------------------------------------------------\n// Cell renderer\n// ---------------------------------------------------------------------------\n\nfunction renderCell(\n column: DataTableColumn,\n value: unknown,\n parentCard: string,\n): React.ReactNode {\n if (value == null) return null;\n\n switch (column.type) {\n case \"number\": {\n const num = typeof value === \"number\" ? value : Number(value);\n return isNaN(num)\n ? String(value)\n : column.format\n ? column.format(num)\n : String(num);\n }\n\n case \"date\": {\n if (column.format) {\n return column.format(value as Date | string);\n }\n const d = value instanceof Date ? value : new Date(String(value));\n return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString();\n }\n\n case \"badge\": {\n const strVal = String(value);\n const variant = column.variants?.[strVal] ?? \"secondary\";\n return <Badge variant={variant}>{strVal}</Badge>;\n }\n\n case \"boolean\": {\n return value ? (\n <Check className=\"size-4 text-green-600\" aria-label=\"true\" />\n ) : (\n <X className=\"size-4 text-muted-foreground\" aria-label=\"false\" />\n );\n }\n\n case \"card\": {\n // Cell value is expected to be a PiCardRef (string)\n return <Card cardName={String(value)} parentCard={parentCard} />;\n }\n\n default:\n // \"text\" or omitted type\n return String(value);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sort icon\n// ---------------------------------------------------------------------------\n\nfunction SortIcon({\n columnKey,\n activeKey,\n ascending,\n}: {\n columnKey: string;\n activeKey: string | undefined;\n ascending: boolean;\n}): React.ReactNode {\n if (columnKey !== activeKey) {\n return <ChevronsUpDown className=\"ml-1 inline size-3.5 opacity-40\" />;\n }\n return ascending ? (\n <ChevronUp className=\"ml-1 inline size-3.5\" />\n ) : (\n <ChevronDown className=\"ml-1 inline size-3.5\" />\n );\n}\n\n// ---------------------------------------------------------------------------\n// Pagination controls\n// ---------------------------------------------------------------------------\n\nfunction PaginationBar({\n page,\n totalPages,\n onPrev,\n onNext,\n}: {\n page: number;\n totalPages: number;\n onPrev: () => void;\n onNext: () => void;\n}): React.ReactNode {\n return (\n <div className=\"flex items-center justify-end gap-2 mt-2 px-2\">\n <span className=\"text-sm text-muted-foreground\">\n Page {page + 1} of {totalPages}\n </span>\n <button\n type=\"button\"\n className={cn(\n \"rounded border px-2 py-1 text-sm transition-colors\",\n \"hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n disabled={page === 0}\n onClick={onPrev}\n >\n Previous\n </button>\n <button\n type=\"button\"\n className={cn(\n \"rounded border px-2 py-1 text-sm transition-colors\",\n \"hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n disabled={page >= totalPages - 1}\n onClick={onNext}\n >\n Next\n </button>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Helper: derive a human-readable title from a camelCase key\n// ---------------------------------------------------------------------------\n\nfunction keyToTitle(key: string): string {\n return key\n .replace(/([A-Z])/g, \" $1\")\n .replace(/^./, (s) => s.toUpperCase())\n .trim();\n}\n\n// ---------------------------------------------------------------------------\n// Main data-table component\n// ---------------------------------------------------------------------------\n\nexport const DataTableComponent = (\n props: PiCardProps<DataTableProps, DataTableEvents>,\n): React.ReactNode => {\n const {\n columns,\n rows,\n caption,\n sortKey: sortKeyProp,\n sortAscending: sortAscendingProp,\n pageSize,\n currentPage: currentPageProp,\n stickyHeader = false,\n striped = false,\n hoverable = true,\n compact = false,\n emptyCard,\n emptyText = \"No data\",\n className,\n cardName,\n onRowClicked,\n onSortChanged,\n onShowDetail,\n onHideDetail,\n onPageChanged,\n } = props;\n\n // ── Expanded-rows state ──────────────────────────────────────────────────\n const [expandedRows, setExpandedRows] = useState<Set<string | number>>(\n new Set(),\n );\n\n // ── Sort state (uncontrolled; props override when present) ───────────────\n const [localSortKey, setLocalSortKey] = useState<string | undefined>(\n sortKeyProp,\n );\n const [localSortAsc, setLocalSortAsc] = useState<boolean>(\n sortAscendingProp ?? true,\n );\n const activeSortKey = sortKeyProp !== undefined ? sortKeyProp : localSortKey;\n const activeSortAsc =\n sortAscendingProp !== undefined ? sortAscendingProp : localSortAsc;\n\n // ── Pagination state (uncontrolled; props override when present) ─────────\n const [localPage, setLocalPage] = useState<number>(currentPageProp ?? 0);\n const activePage =\n currentPageProp !== undefined ? currentPageProp : localPage;\n\n // ── Derived flags ────────────────────────────────────────────────────────\n const hasDetailRows = rows.some((row) => row.detailCard != null);\n // Total column count used for detail-row colSpan\n const colCount = columns.length + (hasDetailRows ? 1 : 0);\n\n // ── Sorted rows ──────────────────────────────────────────────────────────\n const sortedRows = useMemo(() => {\n if (!activeSortKey) return rows;\n return [...rows].sort((a, b) => {\n const av = (a.data as Record<string, unknown>)[activeSortKey];\n const bv = (b.data as Record<string, unknown>)[activeSortKey];\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = av < bv ? -1 : av > bv ? 1 : 0;\n return activeSortAsc ? cmp : -cmp;\n });\n }, [rows, activeSortKey, activeSortAsc]);\n\n // ── Paginated slice ──────────────────────────────────────────────────────\n const paginatedRows = useMemo(() => {\n if (!pageSize) return sortedRows;\n const start = activePage * pageSize;\n return sortedRows.slice(start, start + pageSize);\n }, [sortedRows, pageSize, activePage]);\n\n const totalPages = pageSize ? Math.ceil(sortedRows.length / pageSize) : 1;\n\n // ── Handlers ─────────────────────────────────────────────────────────────\n const handleSortClick = (key: string) => {\n const newAsc = activeSortKey === key ? !activeSortAsc : true;\n setLocalSortKey(key);\n setLocalSortAsc(newAsc);\n onSortChanged({key, ascending: newAsc});\n };\n\n const handleToggleDetail = (row: DataTableRowType, e: React.MouseEvent) => {\n e.stopPropagation();\n setExpandedRows((prev) => {\n const next = new Set(prev);\n if (next.has(row.id)) {\n next.delete(row.id);\n onHideDetail({rowId: row.id, row});\n } else {\n next.add(row.id);\n onShowDetail({rowId: row.id, row});\n }\n return next;\n });\n };\n\n const handleRowClick = (row: DataTableRowType) => {\n onRowClicked({rowId: row.id, row});\n };\n\n const handlePageChange = (page: number) => {\n setLocalPage(page);\n if (pageSize) {\n onPageChanged({page, pageSize});\n }\n };\n\n // ── Cell padding ─────────────────────────────────────────────────────────\n const cellPad = compact ? \"py-1 px-2\" : \"py-2 px-3\";\n\n // ── Render ────────────────────────────────────────────────────────────────\n return (\n <div className={cn(\"w-full\", className)} data-pihanga={cardName}>\n <Table>\n {caption && <TableCaption>{caption}</TableCaption>}\n\n {/* ── Header ─────────────────────────────────────────────────────── */}\n <TableHeader\n className={\n stickyHeader ? \"sticky top-0 z-10 bg-background\" : undefined\n }\n >\n <TableRow>\n {/* Expand-toggle placeholder header cell */}\n {hasDetailRows && (\n <TableHead className=\"w-10 px-2\" aria-label=\"Row details\" />\n )}\n\n {columns.map((col) => (\n <TableHead\n key={col.key}\n style={col.width ? {width: col.width} : undefined}\n className={cn(\n cellPad,\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n col.sortable && \"cursor-pointer select-none\",\n col.headerClassName,\n )}\n onClick={\n col.sortable ? () => handleSortClick(col.key) : undefined\n }\n >\n <span className=\"inline-flex items-center\">\n {col.title ?? keyToTitle(col.key)}\n {col.sortable && (\n <SortIcon\n columnKey={col.key}\n activeKey={activeSortKey}\n ascending={activeSortAsc}\n />\n )}\n </span>\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n\n {/* ── Body ───────────────────────────────────────────────────────── */}\n <TableBody>\n {paginatedRows.length === 0 ? (\n /* Empty state */\n <TableRow>\n <TableCell\n colSpan={colCount}\n className=\"py-8 text-center text-muted-foreground\"\n >\n {emptyCard ? (\n <Card cardName={emptyCard} parentCard={cardName} />\n ) : (\n emptyText\n )}\n </TableCell>\n </TableRow>\n ) : (\n paginatedRows.map((row, rowIdx) => {\n const isExpanded = expandedRows.has(row.id);\n const isStriped = striped && rowIdx % 2 === 1;\n\n return (\n <React.Fragment key={row.id}>\n {/* ── Data row ─────────────────────────────────────────── */}\n <TableRow\n className={cn(\n isStriped && \"bg-muted/30\",\n hoverable && \"cursor-pointer\",\n )}\n data-expanded={isExpanded || undefined}\n onClick={() => handleRowClick(row)}\n >\n {/* Expand / collapse toggle */}\n {hasDetailRows && (\n <TableCell className=\"w-10 px-2\">\n {row.detailCard && (\n <button\n type=\"button\"\n aria-label={\n isExpanded ? \"Collapse detail\" : \"Expand detail\"\n }\n aria-expanded={isExpanded}\n className={cn(\n \"flex items-center justify-center rounded p-0.5 transition-colors\",\n \"hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\",\n )}\n onClick={(e) => handleToggleDetail(row, e)}\n >\n {isExpanded ? (\n <ChevronDown className=\"size-4\" />\n ) : (\n <ChevronRight className=\"size-4\" />\n )}\n </button>\n )}\n </TableCell>\n )}\n\n {/* Data cells */}\n {columns.map((col) => {\n const value = (row.data as Record<string, unknown>)[\n col.key\n ];\n const resolvedClassName =\n typeof col.cellClassName === \"function\"\n ? col.cellClassName(value, row)\n : col.cellClassName;\n const resolvedStyle =\n typeof col.cellStyle === \"function\"\n ? col.cellStyle(value, row)\n : col.cellStyle;\n return (\n <TableCell\n key={col.key}\n className={cn(\n cellPad,\n col.align === \"center\" && \"text-center\",\n col.align === \"right\" && \"text-right\",\n resolvedClassName,\n )}\n style={resolvedStyle}\n >\n {renderCell(col, value, cardName)}\n </TableCell>\n );\n })}\n </TableRow>\n\n {/* ── Detail row (full-width, shown when expanded) ──────── */}\n {isExpanded && row.detailCard && (\n <TableRow\n // Suppress the default hover/click styles on the detail row\n className=\"hover:bg-transparent\"\n onClick={(e) => e.stopPropagation()}\n >\n <TableCell colSpan={colCount} className=\"border-b p-0\">\n <div className=\"bg-muted/20 px-4 py-3\">\n {/*\n * cardKey creates a unique virtual card instance per row\n * (e.g. \"myDetailTemplate@detail-42\").\n * The `row` prop is forwarded as ctxtProps so the detail\n * card's state-mapper functions can access it via\n * `ctx.ctxtProps.row` — no need to pre-register one card\n * per row.\n */}\n <Card\n cardName={row.detailCard}\n cardKey={`detail-${row.id}`}\n row={row}\n parentCard={cardName}\n key={`${cardName}-${row.id}-detail`}\n />\n </div>\n </TableCell>\n </TableRow>\n )}\n </React.Fragment>\n );\n })\n )}\n </TableBody>\n </Table>\n\n {/* ── Pagination controls ──────────────────────────────────────────── */}\n {pageSize && totalPages > 1 && (\n <PaginationBar\n page={activePage}\n totalPages={totalPages}\n onPrev={() => handlePageChange(activePage - 1)}\n onNext={() => handlePageChange(activePage + 1)}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { default as React } from 'react';
1
2
  import { PiCardRef } from '@pihanga2/core';
2
3
  export declare const DATA_TABLE_CARD = "shad/data-table";
3
4
  export declare const DataTable: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<DataTableProps<Record<string, unknown>>, S, DataTableEvents>) => import('@pihanga2/core').PiCardDef;
@@ -40,8 +41,31 @@ export type DataTableColumnBase = {
40
41
  sortable?: boolean;
41
42
  /** Extra CSS class applied to the `<th>` header cell */
42
43
  headerClassName?: string;
43
- /** Extra CSS class applied to every `<td>` data cell in this column */
44
- cellClassName?: string;
44
+ /**
45
+ * Extra CSS class applied to every `<td>` data cell in this column.
46
+ * May be a static string **or** a function called per-cell:
47
+ * `(value, row) => "bg-red-500 text-white"`.
48
+ */
49
+ cellClassName?: string | ((value: unknown, row: DataTableRow) => string | undefined);
50
+ /**
51
+ * Inline style applied to every `<td>` data cell in this column.
52
+ * May be a static `React.CSSProperties` object **or** a function called
53
+ * per-cell: `(value, row) => ({ backgroundColor: "blue", color: "white" })`.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * cellStyle: (value) => {
58
+ * const map: Record<string, React.CSSProperties> = {
59
+ * Superior: { backgroundColor: "#4472C4", color: "#fff" },
60
+ * Satisfactory: { backgroundColor: "#70AD47", color: "#fff" },
61
+ * "Needs Improvement": { backgroundColor: "#FFC000", color: "#fff" },
62
+ * Unsatisfactory: { backgroundColor: "#FF0000", color: "#fff" },
63
+ * };
64
+ * return map[String(value)];
65
+ * }
66
+ * ```
67
+ */
68
+ cellStyle?: React.CSSProperties | ((value: unknown, row: DataTableRow) => React.CSSProperties | undefined);
45
69
  };
46
70
  /** Plain text column — cell value rendered as a string */
47
71
  export type TextColumn = DataTableColumnBase & {
@@ -1,3 +1,4 @@
1
+ import "react";
1
2
  import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
3
  //#region src/cards/dataTable/dataTable.types.ts
3
4
  var r = "shad/data-table", i = e(r), a = n(r, [
@@ -1 +1 @@
1
- {"version":3,"file":"dataTable.types.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.types.ts"],"sourcesContent":["import {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const DATA_TABLE_CARD = \"shad/data-table\";\n\nexport const DataTable = createCardDeclaration<DataTableProps, DataTableEvents>(\n DATA_TABLE_CARD,\n);\n\nexport const DATA_TABLE_ACTION = registerActions(DATA_TABLE_CARD, [\n \"row_clicked\",\n \"sort_changed\",\n \"show_detail\",\n \"hide_detail\",\n \"page_changed\",\n]);\n\nexport const onDataTableRowClicked = createOnAction<DataTableRowClickedEvent>(\n DATA_TABLE_ACTION.ROW_CLICKED,\n);\nexport const onDataTableSortChanged = createOnAction<DataTableSortChangedEvent>(\n DATA_TABLE_ACTION.SORT_CHANGED,\n);\nexport const onDataTableShowDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.SHOW_DETAIL,\n);\nexport const onDataTableHideDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.HIDE_DETAIL,\n);\nexport const onDataTablePageChanged = createOnAction<DataTablePageChangedEvent>(\n DATA_TABLE_ACTION.PAGE_CHANGED,\n);\n\n// ---------------------------------------------------------------------------\n// Column type definitions\n// ---------------------------------------------------------------------------\n\n/** Shared base for all column definitions */\nexport type DataTableColumnBase = {\n /** Key into the row's `data` object used to retrieve the cell value */\n key: string;\n /**\n * Column header label.\n * Defaults to the `key` with camelCase expanded to \"Title Case\".\n */\n title?: string;\n /** Column width as a CSS value (e.g. \"120px\", \"10%\", \"1fr\") */\n width?: string | number;\n /** Horizontal alignment of cell content. Defaults to \"left\". */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether the column header is clickable for sorting */\n sortable?: boolean;\n /** Extra CSS class applied to the `<th>` header cell */\n headerClassName?: string;\n /** Extra CSS class applied to every `<td>` data cell in this column */\n cellClassName?: string;\n};\n\n/** Plain text column — cell value rendered as a string */\nexport type TextColumn = DataTableColumnBase & {\n type?: \"text\";\n};\n\n/** Numeric column with optional custom formatter */\nexport type NumberColumn = DataTableColumnBase & {\n type: \"number\";\n /** Custom number formatter; defaults to `String(value)` */\n format?: (n: number) => string;\n};\n\n/** Badge/chip column — cell value is rendered inside a `<Badge>` */\nexport type BadgeColumn = DataTableColumnBase & {\n type: \"badge\";\n /**\n * Map from cell value (as a string) to a shadcn Badge variant.\n * Falls back to \"secondary\" for unrecognised values.\n */\n variants?: Record<\n string,\n \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n >;\n};\n\n/** Date column with optional custom formatter */\nexport type DateColumn = DataTableColumnBase & {\n type: \"date\";\n /** Custom date formatter; defaults to `toLocaleDateString()` */\n format?: (d: Date | string) => string;\n};\n\n/** Boolean column — renders a check-mark or cross icon */\nexport type BooleanColumn = DataTableColumnBase & {\n type: \"boolean\";\n};\n\n/**\n * Card column — cell value must be a `PiCardRef` (string).\n * The referenced card is rendered inline via Pihanga's `<Card />`.\n * This enables full reuse of any registered Pihanga card as cell content.\n */\nexport type CardColumn = DataTableColumnBase & {\n type: \"card\";\n};\n\nexport type DataTableColumn =\n | TextColumn\n | NumberColumn\n | BadgeColumn\n | DateColumn\n | BooleanColumn\n | CardColumn;\n\n// ---------------------------------------------------------------------------\n// Row\n// ---------------------------------------------------------------------------\n\nexport type DataTableRow<T = Record<string, unknown>> = {\n /** Unique row identifier used as the React key and in events */\n id: string | number;\n /**\n * Row payload — values are keyed by the column's `key` field.\n * For \"card\" columns the value should be a `PiCardRef` string.\n */\n data: T;\n /**\n * When set, the row shows an expand/collapse toggle button.\n * The referenced card is rendered full-width below the row on expand.\n * Use `onShowDetail` / `onHideDetail` events to track expanded state in\n * your redux store and update `detailCard` per row as needed.\n */\n detailCard?: PiCardRef;\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type DataTableProps<T = Record<string, unknown>> = {\n /** Column definitions — order determines display order */\n columns: DataTableColumn[];\n /** Row data */\n rows: DataTableRow<T>[];\n /** Optional visible `<caption>` rendered below the table */\n caption?: string;\n\n // --- Sorting ---\n /**\n * Key of the currently sorted column.\n * When provided the sort state is controlled; otherwise managed internally.\n */\n sortKey?: string;\n /**\n * Sort direction.\n * When provided together with `sortKey` the sort state is controlled.\n */\n sortAscending?: boolean;\n\n // --- Pagination ---\n /** Maximum number of rows per page. Omit to disable pagination. */\n pageSize?: number;\n /** Current page index (0-based). Treated as controlled when provided. */\n currentPage?: number;\n /**\n * Total row count, used for server-side pagination display.\n * Leave unset for client-side pagination derived from `rows.length`.\n */\n totalRows?: number;\n\n // --- Display ---\n /** Keep the header visible while the body scrolls */\n stickyHeader?: boolean;\n /** Alternate row background colour (zebra striping) */\n striped?: boolean;\n /** Highlight rows on hover (default: true) */\n hoverable?: boolean;\n /** Use compact row padding */\n compact?: boolean;\n\n // --- Empty state ---\n /** Card rendered when `rows` is empty */\n emptyCard?: PiCardRef;\n /** Fallback text when `rows` is empty and `emptyCard` is not set */\n emptyText?: string;\n\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type DataTableRowClickedEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTableSortChangedEvent = {\n /** The column key that was clicked */\n key: string;\n /** `true` = ascending, `false` = descending */\n ascending: boolean;\n};\n\nexport type DataTableDetailEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTablePageChangedEvent = {\n /** New page index (0-based) */\n page: number;\n pageSize: number;\n};\n\nexport type DataTableEvents = {\n onRowClicked: DataTableRowClickedEvent;\n onSortChanged: DataTableSortChangedEvent;\n onShowDetail: DataTableDetailEvent;\n onHideDetail: DataTableDetailEvent;\n onPageChanged: DataTablePageChangedEvent;\n};\n\n// ---------------------------------------------------------------------------\n// DataTableRowDetail — generic single-template detail panel\n// ---------------------------------------------------------------------------\n\n/** Configuration for a single field shown in the detail panel. */\nexport type DataTableRowDetailField = {\n /** Key to look up in `row.data` */\n key: string;\n /** Optional human-readable label prefix (e.g. \"Director\") */\n label?: string;\n /** Render style: \"title\" → h4 bold, \"muted\" → muted foreground, \"text\" (default) → plain */\n type?: \"title\" | \"text\" | \"muted\";\n /** Additional Tailwind class names forwarded to the element */\n className?: string;\n};\n\nexport type DataTableRowDetailProps = {\n /**\n * The row to display. Set this via a state mapper so a single template\n * card works for all rows:\n * ```ts\n * DataTableRowDetail({\n * row: (_, ctx) => ctx.ctxtProps?.row,\n * fields: [{ key: \"title\", type: \"title\" }, ...],\n * })\n * ```\n */\n row?: DataTableRow<Record<string, unknown>>;\n /**\n * Fields to render in order.\n * When omitted, every key in `row.data` is rendered as a plain key:value pair.\n */\n fields?: DataTableRowDetailField[];\n};\n\nexport const DATA_TABLE_ROW_DETAIL_CARD = \"shad/data-table-row-detail\";\nexport const DataTableRowDetail =\n createCardDeclaration<DataTableRowDetailProps>(DATA_TABLE_ROW_DETAIL_CARD);\n"],"mappings":";;AAOA,IAAa,IAAkB,mBAElB,IAAY,EACvB,CACF,GAEa,IAAoB,EAAgB,GAAiB;CAChE;CACA;CACA;CACA;CACA;AACF,CAAC,GAEY,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GAkOa,IAA6B,8BAC7B,IACX,EAA+C,CAA0B"}
1
+ {"version":3,"file":"dataTable.types.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.types.ts"],"sourcesContent":["import React from \"react\";\nimport {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const DATA_TABLE_CARD = \"shad/data-table\";\n\nexport const DataTable = createCardDeclaration<DataTableProps, DataTableEvents>(\n DATA_TABLE_CARD,\n);\n\nexport const DATA_TABLE_ACTION = registerActions(DATA_TABLE_CARD, [\n \"row_clicked\",\n \"sort_changed\",\n \"show_detail\",\n \"hide_detail\",\n \"page_changed\",\n]);\n\nexport const onDataTableRowClicked = createOnAction<DataTableRowClickedEvent>(\n DATA_TABLE_ACTION.ROW_CLICKED,\n);\nexport const onDataTableSortChanged = createOnAction<DataTableSortChangedEvent>(\n DATA_TABLE_ACTION.SORT_CHANGED,\n);\nexport const onDataTableShowDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.SHOW_DETAIL,\n);\nexport const onDataTableHideDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.HIDE_DETAIL,\n);\nexport const onDataTablePageChanged = createOnAction<DataTablePageChangedEvent>(\n DATA_TABLE_ACTION.PAGE_CHANGED,\n);\n\n// ---------------------------------------------------------------------------\n// Column type definitions\n// ---------------------------------------------------------------------------\n\n/** Shared base for all column definitions */\nexport type DataTableColumnBase = {\n /** Key into the row's `data` object used to retrieve the cell value */\n key: string;\n /**\n * Column header label.\n * Defaults to the `key` with camelCase expanded to \"Title Case\".\n */\n title?: string;\n /** Column width as a CSS value (e.g. \"120px\", \"10%\", \"1fr\") */\n width?: string | number;\n /** Horizontal alignment of cell content. Defaults to \"left\". */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether the column header is clickable for sorting */\n sortable?: boolean;\n /** Extra CSS class applied to the `<th>` header cell */\n headerClassName?: string;\n /**\n * Extra CSS class applied to every `<td>` data cell in this column.\n * May be a static string **or** a function called per-cell:\n * `(value, row) => \"bg-red-500 text-white\"`.\n */\n cellClassName?:\n | string\n | ((value: unknown, row: DataTableRow) => string | undefined);\n /**\n * Inline style applied to every `<td>` data cell in this column.\n * May be a static `React.CSSProperties` object **or** a function called\n * per-cell: `(value, row) => ({ backgroundColor: \"blue\", color: \"white\" })`.\n *\n * @example\n * ```ts\n * cellStyle: (value) => {\n * const map: Record<string, React.CSSProperties> = {\n * Superior: { backgroundColor: \"#4472C4\", color: \"#fff\" },\n * Satisfactory: { backgroundColor: \"#70AD47\", color: \"#fff\" },\n * \"Needs Improvement\": { backgroundColor: \"#FFC000\", color: \"#fff\" },\n * Unsatisfactory: { backgroundColor: \"#FF0000\", color: \"#fff\" },\n * };\n * return map[String(value)];\n * }\n * ```\n */\n cellStyle?:\n | React.CSSProperties\n | ((value: unknown, row: DataTableRow) => React.CSSProperties | undefined);\n};\n\n/** Plain text column — cell value rendered as a string */\nexport type TextColumn = DataTableColumnBase & {\n type?: \"text\";\n};\n\n/** Numeric column with optional custom formatter */\nexport type NumberColumn = DataTableColumnBase & {\n type: \"number\";\n /** Custom number formatter; defaults to `String(value)` */\n format?: (n: number) => string;\n};\n\n/** Badge/chip column — cell value is rendered inside a `<Badge>` */\nexport type BadgeColumn = DataTableColumnBase & {\n type: \"badge\";\n /**\n * Map from cell value (as a string) to a shadcn Badge variant.\n * Falls back to \"secondary\" for unrecognised values.\n */\n variants?: Record<\n string,\n \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n >;\n};\n\n/** Date column with optional custom formatter */\nexport type DateColumn = DataTableColumnBase & {\n type: \"date\";\n /** Custom date formatter; defaults to `toLocaleDateString()` */\n format?: (d: Date | string) => string;\n};\n\n/** Boolean column — renders a check-mark or cross icon */\nexport type BooleanColumn = DataTableColumnBase & {\n type: \"boolean\";\n};\n\n/**\n * Card column — cell value must be a `PiCardRef` (string).\n * The referenced card is rendered inline via Pihanga's `<Card />`.\n * This enables full reuse of any registered Pihanga card as cell content.\n */\nexport type CardColumn = DataTableColumnBase & {\n type: \"card\";\n};\n\nexport type DataTableColumn =\n | TextColumn\n | NumberColumn\n | BadgeColumn\n | DateColumn\n | BooleanColumn\n | CardColumn;\n\n// ---------------------------------------------------------------------------\n// Row\n// ---------------------------------------------------------------------------\n\nexport type DataTableRow<T = Record<string, unknown>> = {\n /** Unique row identifier used as the React key and in events */\n id: string | number;\n /**\n * Row payload — values are keyed by the column's `key` field.\n * For \"card\" columns the value should be a `PiCardRef` string.\n */\n data: T;\n /**\n * When set, the row shows an expand/collapse toggle button.\n * The referenced card is rendered full-width below the row on expand.\n * Use `onShowDetail` / `onHideDetail` events to track expanded state in\n * your redux store and update `detailCard` per row as needed.\n */\n detailCard?: PiCardRef;\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type DataTableProps<T = Record<string, unknown>> = {\n /** Column definitions — order determines display order */\n columns: DataTableColumn[];\n /** Row data */\n rows: DataTableRow<T>[];\n /** Optional visible `<caption>` rendered below the table */\n caption?: string;\n\n // --- Sorting ---\n /**\n * Key of the currently sorted column.\n * When provided the sort state is controlled; otherwise managed internally.\n */\n sortKey?: string;\n /**\n * Sort direction.\n * When provided together with `sortKey` the sort state is controlled.\n */\n sortAscending?: boolean;\n\n // --- Pagination ---\n /** Maximum number of rows per page. Omit to disable pagination. */\n pageSize?: number;\n /** Current page index (0-based). Treated as controlled when provided. */\n currentPage?: number;\n /**\n * Total row count, used for server-side pagination display.\n * Leave unset for client-side pagination derived from `rows.length`.\n */\n totalRows?: number;\n\n // --- Display ---\n /** Keep the header visible while the body scrolls */\n stickyHeader?: boolean;\n /** Alternate row background colour (zebra striping) */\n striped?: boolean;\n /** Highlight rows on hover (default: true) */\n hoverable?: boolean;\n /** Use compact row padding */\n compact?: boolean;\n\n // --- Empty state ---\n /** Card rendered when `rows` is empty */\n emptyCard?: PiCardRef;\n /** Fallback text when `rows` is empty and `emptyCard` is not set */\n emptyText?: string;\n\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type DataTableRowClickedEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTableSortChangedEvent = {\n /** The column key that was clicked */\n key: string;\n /** `true` = ascending, `false` = descending */\n ascending: boolean;\n};\n\nexport type DataTableDetailEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTablePageChangedEvent = {\n /** New page index (0-based) */\n page: number;\n pageSize: number;\n};\n\nexport type DataTableEvents = {\n onRowClicked: DataTableRowClickedEvent;\n onSortChanged: DataTableSortChangedEvent;\n onShowDetail: DataTableDetailEvent;\n onHideDetail: DataTableDetailEvent;\n onPageChanged: DataTablePageChangedEvent;\n};\n\n// ---------------------------------------------------------------------------\n// DataTableRowDetail — generic single-template detail panel\n// ---------------------------------------------------------------------------\n\n/** Configuration for a single field shown in the detail panel. */\nexport type DataTableRowDetailField = {\n /** Key to look up in `row.data` */\n key: string;\n /** Optional human-readable label prefix (e.g. \"Director\") */\n label?: string;\n /** Render style: \"title\" → h4 bold, \"muted\" → muted foreground, \"text\" (default) → plain */\n type?: \"title\" | \"text\" | \"muted\";\n /** Additional Tailwind class names forwarded to the element */\n className?: string;\n};\n\nexport type DataTableRowDetailProps = {\n /**\n * The row to display. Set this via a state mapper so a single template\n * card works for all rows:\n * ```ts\n * DataTableRowDetail({\n * row: (_, ctx) => ctx.ctxtProps?.row,\n * fields: [{ key: \"title\", type: \"title\" }, ...],\n * })\n * ```\n */\n row?: DataTableRow<Record<string, unknown>>;\n /**\n * Fields to render in order.\n * When omitted, every key in `row.data` is rendered as a plain key:value pair.\n */\n fields?: DataTableRowDetailField[];\n};\n\nexport const DATA_TABLE_ROW_DETAIL_CARD = \"shad/data-table-row-detail\";\nexport const DataTableRowDetail =\n createCardDeclaration<DataTableRowDetailProps>(DATA_TABLE_ROW_DETAIL_CARD);\n"],"mappings":";;;AAQA,IAAa,IAAkB,mBAElB,IAAY,EACvB,CACF,GAEa,IAAoB,EAAgB,GAAiB;CAChE;CACA;CACA;CACA;CACA;AACF,CAAC,GAEY,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GA6Pa,IAA6B,8BAC7B,IACX,EAA+C,CAA0B"}
@@ -0,0 +1,4 @@
1
+ import { default as React } from 'react';
2
+ import { PiCardProps } from '@pihanga2/core';
3
+ import { PiDrawerEvents, PiDrawerProps } from './drawer.types';
4
+ export declare const DrawerComponent: (props: PiCardProps<PiDrawerProps, PiDrawerEvents>) => React.ReactNode;
@@ -0,0 +1,83 @@
1
+ import { Button as e } from "../../components/ui/button.js";
2
+ import { Drawer as t, DrawerClose as n, DrawerContent as r, DrawerDescription as i, DrawerFooter as a, DrawerHeader as o, DrawerTitle as s, DrawerTrigger as c } from "../../components/ui/drawer.js";
3
+ import l from "react";
4
+ import { Card as u } from "@pihanga2/core";
5
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
6
+ //#region src/cards/drawer/drawer.component.tsx
7
+ var p = (p) => {
8
+ let { id: m, trigger: h, content: g, title: _, description: v, open: y, direction: b = "bottom", dismissible: x = !0, footer: S, footerCloseButtonText: C, className: w, cardName: T, onOpened: E, onClosed: D, onOpenChanged: O } = p, k = C === void 0 ? "Close" : C, [A, j] = l.useState(!1), M = y ?? A, N = l.useRef(y);
9
+ l.useEffect(() => {
10
+ N.current === !0 && y === !1 && M === !1 && D({
11
+ id: m,
12
+ reason: "programmatic"
13
+ }), N.current = y;
14
+ }, [
15
+ y,
16
+ M,
17
+ m,
18
+ D
19
+ ]);
20
+ let P = (e) => {
21
+ y === void 0 && j(e), O({
22
+ open: e,
23
+ id: m
24
+ }), e ? E({ id: m }) : y === void 0 && D({
25
+ id: m,
26
+ reason: "user"
27
+ });
28
+ };
29
+ function F() {
30
+ return h ? /* @__PURE__ */ d("span", {
31
+ className: "inline-flex",
32
+ "data-pihanga-trigger-wrapper": !0,
33
+ children: /* @__PURE__ */ d(u, {
34
+ cardName: h,
35
+ parentCard: T
36
+ })
37
+ }) : null;
38
+ }
39
+ function I() {
40
+ return !_ && !v ? null : /* @__PURE__ */ f(o, { children: [_ && /* @__PURE__ */ d(s, { children: _ }), v && /* @__PURE__ */ d(i, { children: v })] });
41
+ }
42
+ function L() {
43
+ return S ? /* @__PURE__ */ d(a, { children: /* @__PURE__ */ d(u, {
44
+ cardName: S,
45
+ parentCard: T
46
+ }) }) : k === null ? null : /* @__PURE__ */ d(a, { children: /* @__PURE__ */ d(n, {
47
+ asChild: !0,
48
+ children: /* @__PURE__ */ d(e, {
49
+ variant: "outline",
50
+ onClick: () => P(!1),
51
+ children: k
52
+ })
53
+ }) });
54
+ }
55
+ return /* @__PURE__ */ f(t, {
56
+ open: M,
57
+ onOpenChange: P,
58
+ direction: b,
59
+ dismissible: x,
60
+ children: [h && /* @__PURE__ */ d(c, {
61
+ asChild: !0,
62
+ children: F()
63
+ }), /* @__PURE__ */ f(r, {
64
+ "data-pihanga": T,
65
+ className: w,
66
+ children: [
67
+ I(),
68
+ /* @__PURE__ */ d("div", {
69
+ className: "flex-1 overflow-y-auto p-4",
70
+ children: /* @__PURE__ */ d(u, {
71
+ cardName: g,
72
+ parentCard: T
73
+ })
74
+ }),
75
+ L()
76
+ ]
77
+ })]
78
+ });
79
+ };
80
+ //#endregion
81
+ export { p as DrawerComponent };
82
+
83
+ //# sourceMappingURL=drawer.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer.component.js","names":[],"sources":["../../../src/cards/drawer/drawer.component.tsx"],"sourcesContent":["import React from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\n\nimport {\n Drawer as DrawerRoot,\n DrawerTrigger,\n DrawerContent,\n DrawerHeader,\n DrawerTitle,\n DrawerDescription,\n DrawerFooter,\n DrawerClose,\n} from \"@/registry/ui/drawer\";\nimport {Button} from \"@/registry/ui/button\";\n\nimport type {PiDrawerEvents, PiDrawerProps} from \"./drawer.types\";\n\nexport const DrawerComponent = (\n props: PiCardProps<PiDrawerProps, PiDrawerEvents>,\n): React.ReactNode => {\n const {\n id,\n trigger,\n content,\n title,\n description,\n open: controlledOpen,\n direction = \"bottom\",\n dismissible = true,\n footer,\n footerCloseButtonText,\n className,\n cardName,\n onOpened,\n onClosed,\n onOpenChanged,\n } = props;\n\n const effectiveFooterCloseButtonText =\n footerCloseButtonText === undefined ? \"Close\" : footerCloseButtonText;\n\n const [internalOpen, setInternalOpen] = React.useState(false);\n const open = controlledOpen ?? internalOpen;\n const prevControlledOpenRef = React.useRef(controlledOpen);\n\n React.useEffect(() => {\n if (\n prevControlledOpenRef.current === true &&\n controlledOpen === false &&\n open === false\n ) {\n onClosed({id, reason: \"programmatic\"});\n }\n prevControlledOpenRef.current = controlledOpen;\n }, [controlledOpen, open, id, onClosed]);\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (controlledOpen === undefined) {\n setInternalOpen(nextOpen);\n }\n onOpenChanged({open: nextOpen, id});\n if (nextOpen) {\n onOpened({id});\n } else if (controlledOpen === undefined) {\n onClosed({id, reason: \"user\"});\n }\n };\n\n function renderTrigger() {\n if (!trigger) return null;\n return (\n <span className=\"inline-flex\" data-pihanga-trigger-wrapper>\n <Card cardName={trigger} parentCard={cardName} />\n </span>\n );\n }\n\n function renderHeader() {\n if (!title && !description) return null;\n return (\n <DrawerHeader>\n {title && <DrawerTitle>{title}</DrawerTitle>}\n {description && <DrawerDescription>{description}</DrawerDescription>}\n </DrawerHeader>\n );\n }\n\n function renderFooter() {\n if (footer) {\n return (\n <DrawerFooter>\n <Card cardName={footer} parentCard={cardName} />\n </DrawerFooter>\n );\n }\n if (effectiveFooterCloseButtonText !== null) {\n return (\n <DrawerFooter>\n <DrawerClose asChild>\n <Button variant=\"outline\" onClick={() => handleOpenChange(false)}>\n {effectiveFooterCloseButtonText}\n </Button>\n </DrawerClose>\n </DrawerFooter>\n );\n }\n return null;\n }\n\n return (\n <DrawerRoot\n open={open}\n onOpenChange={handleOpenChange}\n direction={direction}\n dismissible={dismissible}\n >\n {trigger && <DrawerTrigger asChild>{renderTrigger()}</DrawerTrigger>}\n <DrawerContent data-pihanga={cardName} className={className}>\n {renderHeader()}\n <div className=\"flex-1 overflow-y-auto p-4\">\n <Card cardName={content} parentCard={cardName} />\n </div>\n {renderFooter()}\n </DrawerContent>\n </DrawerRoot>\n );\n};\n"],"mappings":";;;;;;AAiBA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,OACA,YACA,YACA,UACA,gBACA,MAAM,GACN,eAAY,UACZ,iBAAc,IACd,WACA,0BACA,cACA,aACA,aACA,aACA,qBACE,GAEE,IACJ,MAA0B,KAAA,IAAY,UAAU,GAE5C,CAAC,GAAc,KAAmB,EAAM,SAAS,EAAK,GACtD,IAAO,KAAkB,GACzB,IAAwB,EAAM,OAAO,CAAc;CAEzD,EAAM,gBAAgB;EAQpB,AANE,EAAsB,YAAY,MAClC,MAAmB,MACnB,MAAS,MAET,EAAS;GAAC;GAAI,QAAQ;EAAc,CAAC,GAEvC,EAAsB,UAAU;CAClC,GAAG;EAAC;EAAgB;EAAM;EAAI;CAAQ,CAAC;CAEvC,IAAM,KAAoB,MAAsB;EAK9C,AAJI,MAAmB,KAAA,KACrB,EAAgB,CAAQ,GAE1B,EAAc;GAAC,MAAM;GAAU;EAAE,CAAC,GAC9B,IACF,EAAS,EAAC,MAAE,CAAC,IACJ,MAAmB,KAAA,KAC5B,EAAS;GAAC;GAAI,QAAQ;EAAM,CAAC;CAEjC;CAEA,SAAS,IAAgB;EAEvB,OADK,IAEH,kBAAC,QAAD;GAAM,WAAU;GAAc,gCAAA;aAC5B,kBAAC,GAAD;IAAM,UAAU;IAAS,YAAY;GAAW,CAAA;EAC5C,CAAA,IAJa;CAMvB;CAEA,SAAS,IAAe;EAEtB,OADI,CAAC,KAAS,CAAC,IAAoB,OAEjC,kBAAC,GAAD,EAAA,UAAA,CACG,KAAS,kBAAC,GAAD,EAAA,UAAc,EAAmB,CAAA,GAC1C,KAAe,kBAAC,GAAD,EAAA,UAAoB,EAA+B,CAAA,CACvD,EAAA,CAAA;CAElB;CAEA,SAAS,IAAe;EAmBtB,OAlBI,IAEA,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAM,UAAU;GAAQ,YAAY;EAAW,CAAA,EACnC,CAAA,IAGd,MAAmC,OAWhC,OATH,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAa,SAAA;aACX,kBAAC,GAAD;IAAQ,SAAQ;IAAU,eAAe,EAAiB,EAAK;cAC5D;GACK,CAAA;EACG,CAAA,EACD,CAAA;CAIpB;CAEA,OACE,kBAAC,GAAD;EACQ;EACN,cAAc;EACH;EACE;YAJf,CAMG,KAAW,kBAAC,GAAD;GAAe,SAAA;aAAS,EAAc;EAAiB,CAAA,GACnE,kBAAC,GAAD;GAAe,gBAAc;GAAqB;aAAlD;IACG,EAAa;IACd,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAM,UAAU;MAAS,YAAY;KAAW,CAAA;IAC7C,CAAA;IACJ,EAAa;GACD;IACL;;AAEhB"}
@@ -0,0 +1,95 @@
1
+ import { PiCardRef } from '@pihanga2/core';
2
+ export declare const PI_DRAWER_CARD = "pi/drawer";
3
+ export declare const Drawer: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<PiDrawerProps, S, PiDrawerEvents>) => import('@pihanga2/core').PiCardDef;
4
+ export declare const PI_DRAWER_ACTION: {
5
+ CLOSED: string;
6
+ OPENED: string;
7
+ OPEN_CHANGED: string;
8
+ };
9
+ export declare const onDrawerOpened: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
10
+ cardID: string;
11
+ } & DrawerOpenedEvent>) => void;
12
+ export declare const onDrawerClosed: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
13
+ cardID: string;
14
+ } & DrawerClosedEvent>) => void;
15
+ export declare const onDrawerOpenChanged: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core').PiRegister, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
16
+ cardID: string;
17
+ } & DrawerOpenChangedEvent>) => void;
18
+ export type PiDrawerProps = {
19
+ /**
20
+ * Optional id passed through to events for identification.
21
+ */
22
+ id?: string;
23
+ /**
24
+ * Optional drawer trigger card (e.g., a button).
25
+ *
26
+ * When provided, this card will be used as the drawer trigger.
27
+ * When omitted, the drawer must be controlled via the `open` prop.
28
+ */
29
+ trigger?: PiCardRef;
30
+ /**
31
+ * Card to render as the drawer body content.
32
+ */
33
+ content: PiCardRef;
34
+ /**
35
+ * Optional drawer title (rendered in header).
36
+ */
37
+ title?: string;
38
+ /**
39
+ * Optional drawer description (rendered below title).
40
+ */
41
+ description?: string;
42
+ /**
43
+ * Controlled open state.
44
+ *
45
+ * When provided, drawer visibility is fully managed externally.
46
+ * When undefined, drawer manages its own state.
47
+ */
48
+ open?: boolean;
49
+ /**
50
+ * Direction the drawer slides in from.
51
+ * @default "bottom"
52
+ */
53
+ direction?: "top" | "bottom" | "left" | "right";
54
+ /**
55
+ * Whether clicking outside closes the drawer (default: true).
56
+ */
57
+ dismissible?: boolean;
58
+ /**
59
+ * Optional footer card to render at the bottom of the drawer.
60
+ * When provided, this takes precedence over the default close button.
61
+ */
62
+ footer?: PiCardRef;
63
+ /**
64
+ * Text for the default close button in the footer.
65
+ * Only used when `footer` is not provided.
66
+ * Set to `null` to hide the default close button entirely.
67
+ * @default "Close"
68
+ */
69
+ footerCloseButtonText?: string | null;
70
+ /**
71
+ * Additional CSS classes for the drawer content.
72
+ */
73
+ className?: string;
74
+ };
75
+ export type DrawerOpenedEvent = {
76
+ id?: string;
77
+ };
78
+ export type DrawerClosedEvent = {
79
+ id?: string;
80
+ /**
81
+ * Reason for closing:
82
+ * - 'user': User dismissed (clicked outside, dragged, or pressed ESC)
83
+ * - 'programmatic': Closed via props.open change
84
+ */
85
+ reason?: "user" | "programmatic";
86
+ };
87
+ export type DrawerOpenChangedEvent = {
88
+ open: boolean;
89
+ id?: string;
90
+ };
91
+ export type PiDrawerEvents = {
92
+ onOpened: DrawerOpenedEvent;
93
+ onClosed: DrawerClosedEvent;
94
+ onOpenChanged: DrawerOpenChangedEvent;
95
+ };
@@ -0,0 +1,11 @@
1
+ import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
2
+ //#region src/cards/drawer/drawer.types.ts
3
+ var r = "pi/drawer", i = e(r), a = n(r, [
4
+ "opened",
5
+ "closed",
6
+ "open_changed"
7
+ ]), o = t(a.OPENED), s = t(a.CLOSED), c = t(a.OPEN_CHANGED);
8
+ //#endregion
9
+ export { i as Drawer, a as PI_DRAWER_ACTION, r as PI_DRAWER_CARD, s as onDrawerClosed, c as onDrawerOpenChanged, o as onDrawerOpened };
10
+
11
+ //# sourceMappingURL=drawer.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer.types.js","names":[],"sources":["../../../src/cards/drawer/drawer.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_DRAWER_CARD = \"pi/drawer\";\n\nexport const Drawer = createCardDeclaration<PiDrawerProps, PiDrawerEvents>(\n PI_DRAWER_CARD,\n);\n\nexport const PI_DRAWER_ACTION = registerActions(PI_DRAWER_CARD, [\n \"opened\",\n \"closed\",\n \"open_changed\",\n]);\n\nexport const onDrawerOpened = createOnAction<DrawerOpenedEvent>(\n PI_DRAWER_ACTION.OPENED,\n);\n\nexport const onDrawerClosed = createOnAction<DrawerClosedEvent>(\n PI_DRAWER_ACTION.CLOSED,\n);\n\nexport const onDrawerOpenChanged = createOnAction<DrawerOpenChangedEvent>(\n PI_DRAWER_ACTION.OPEN_CHANGED,\n);\n\nexport type PiDrawerProps = {\n /**\n * Optional id passed through to events for identification.\n */\n id?: string;\n\n /**\n * Optional drawer trigger card (e.g., a button).\n *\n * When provided, this card will be used as the drawer trigger.\n * When omitted, the drawer must be controlled via the `open` prop.\n */\n trigger?: PiCardRef;\n\n /**\n * Card to render as the drawer body content.\n */\n content: PiCardRef;\n\n /**\n * Optional drawer title (rendered in header).\n */\n title?: string;\n\n /**\n * Optional drawer description (rendered below title).\n */\n description?: string;\n\n /**\n * Controlled open state.\n *\n * When provided, drawer visibility is fully managed externally.\n * When undefined, drawer manages its own state.\n */\n open?: boolean;\n\n /**\n * Direction the drawer slides in from.\n * @default \"bottom\"\n */\n direction?: \"top\" | \"bottom\" | \"left\" | \"right\";\n\n /**\n * Whether clicking outside closes the drawer (default: true).\n */\n dismissible?: boolean;\n\n /**\n * Optional footer card to render at the bottom of the drawer.\n * When provided, this takes precedence over the default close button.\n */\n footer?: PiCardRef;\n\n /**\n * Text for the default close button in the footer.\n * Only used when `footer` is not provided.\n * Set to `null` to hide the default close button entirely.\n * @default \"Close\"\n */\n footerCloseButtonText?: string | null;\n\n /**\n * Additional CSS classes for the drawer content.\n */\n className?: string;\n};\n\nexport type DrawerOpenedEvent = {\n id?: string;\n};\n\nexport type DrawerClosedEvent = {\n id?: string;\n /**\n * Reason for closing:\n * - 'user': User dismissed (clicked outside, dragged, or pressed ESC)\n * - 'programmatic': Closed via props.open change\n */\n reason?: \"user\" | \"programmatic\";\n};\n\nexport type DrawerOpenChangedEvent = {\n open: boolean;\n id?: string;\n};\n\nexport type PiDrawerEvents = {\n onOpened: DrawerOpenedEvent;\n onClosed: DrawerClosedEvent;\n onOpenChanged: DrawerOpenChangedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAiB,aAEjB,IAAS,EACpB,CACF,GAEa,IAAmB,EAAgB,GAAgB;CAC9D;CACA;CACA;AACF,CAAC,GAEY,IAAiB,EAC5B,EAAiB,MACnB,GAEa,IAAiB,EAC5B,EAAiB,MACnB,GAEa,IAAsB,EACjC,EAAiB,YACnB"}
@@ -0,0 +1 @@
1
+ export * from './drawer.types';
@@ -0,0 +1,13 @@
1
+ import { DrawerComponent as e } from "./drawer.component.js";
2
+ import { Drawer as t, PI_DRAWER_ACTION as n, PI_DRAWER_CARD as r, onDrawerClosed as i, onDrawerOpenChanged as a, onDrawerOpened as o } from "./drawer.types.js";
3
+ import { actionTypesToEvents as s, registerCardComponent as c } from "@pihanga2/core";
4
+ //#region src/cards/drawer/index.ts
5
+ c({
6
+ name: r,
7
+ component: e,
8
+ events: s(n)
9
+ });
10
+ //#endregion
11
+ export { t as Drawer, n as PI_DRAWER_ACTION, r as PI_DRAWER_CARD, i as onDrawerClosed, a as onDrawerOpenChanged, o as onDrawerOpened };
12
+
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/cards/drawer/index.ts"],"sourcesContent":["import {actionTypesToEvents, registerCardComponent} from \"@pihanga2/core\";\n\nimport {DrawerComponent} from \"./drawer.component\";\nimport {PI_DRAWER_ACTION, PI_DRAWER_CARD} from \"./drawer.types\";\n\nexport * from \"./drawer.types\";\n\nregisterCardComponent({\n name: PI_DRAWER_CARD,\n component: DrawerComponent,\n events: actionTypesToEvents(PI_DRAWER_ACTION),\n});\n"],"mappings":";;;;AAOA,EAAsB;CACpB,MAAM;CACN,WAAW;CACX,QAAQ,EAAoB,CAAgB;AAC9C,CAAC"}
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import { cn as e } from "../../lib/utils.js";
3
3
  import "react";
4
- import { cva as t } from "class-variance-authority";
5
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
5
+ import { cva as r } from "class-variance-authority";
6
6
  import { CheckIcon as i, ChevronRightIcon as a } from "lucide-react";
7
7
  import * as o from "@radix-ui/react-dropdown-menu";
8
8
  //#region src/cards/dropDownMenu/dropdown-menu.ui.tsx
9
- var s = t(e("relative flex cursor-pointer items-center gap-2 rounded-md align-middle text-sm no-focus-ring transition-bg-ease select-none data-disabled:pointer-events-none data-disabled:opacity-50", "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-subtle-foreground", "text-accent-foreground hover:bg-accent focus:bg-accent focus:text-accent-foreground"), {
9
+ var s = r(e("relative flex cursor-pointer items-center gap-2 rounded-md align-middle text-sm no-focus-ring transition-bg-ease select-none data-disabled:pointer-events-none data-disabled:opacity-50", "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-subtle-foreground", "text-accent-foreground hover:bg-accent focus:bg-accent focus:text-accent-foreground"), {
10
10
  defaultVariants: {
11
11
  size: "default",
12
12
  variant: "default"
@@ -21,87 +21,87 @@ var s = t(e("relative flex cursor-pointer items-center gap-2 rounded-md align-mi
21
21
  none: ""
22
22
  }
23
23
  }
24
- }), c = t(e("mt-1.5 mb-2 cursor-default px-[14px] text-xs font-medium text-muted-foreground select-none"), { variants: { inset: { true: "pl-8" } } });
24
+ }), c = r(e("mt-1.5 mb-2 cursor-default px-[14px] text-xs font-medium text-muted-foreground select-none"), { variants: { inset: { true: "pl-8" } } });
25
25
  function l(e) {
26
- return /* @__PURE__ */ n(o.Root, { ...e });
26
+ return /* @__PURE__ */ t(o.Root, { ...e });
27
27
  }
28
28
  function u(e) {
29
- return /* @__PURE__ */ n(o.Trigger, { ...e });
29
+ return /* @__PURE__ */ t(o.Trigger, { ...e });
30
30
  }
31
31
  function d(e) {
32
- return /* @__PURE__ */ n(o.Group, {
32
+ return /* @__PURE__ */ t(o.Group, {
33
33
  className: "py-1.5",
34
34
  ...e
35
35
  });
36
36
  }
37
37
  function f(e) {
38
- return /* @__PURE__ */ n(o.Sub, { ...e });
38
+ return /* @__PURE__ */ t(o.Sub, { ...e });
39
39
  }
40
40
  function p(e) {
41
- return /* @__PURE__ */ n(o.RadioGroup, { ...e });
41
+ return /* @__PURE__ */ t(o.RadioGroup, { ...e });
42
42
  }
43
- function m({ children: t, className: i, inset: s, ...c }) {
44
- return /* @__PURE__ */ r(o.SubTrigger, {
43
+ function m({ children: r, className: i, inset: s, ...c }) {
44
+ return /* @__PURE__ */ n(o.SubTrigger, {
45
45
  className: e("mx-1 flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent data-[state=open]:bg-accent", "no-focus-ring", "data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", s && "pl-8", i),
46
46
  ...c,
47
- children: [t, /* @__PURE__ */ n(a, { className: "ml-auto" })]
47
+ children: [r, /* @__PURE__ */ t(a, { className: "ml-auto" })]
48
48
  });
49
49
  }
50
- function h({ className: t, ...r }) {
51
- return /* @__PURE__ */ n(o.SubContent, {
52
- className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-floating data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", t),
50
+ function h({ className: n, ...r }) {
51
+ return /* @__PURE__ */ t(o.SubContent, {
52
+ className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-floating data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", n),
53
53
  ...r
54
54
  });
55
55
  }
56
- function g({ className: t, portal: r, ...i }) {
57
- let a = /* @__PURE__ */ n(o.Content, {
58
- className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-0 text-sm text-popover-foreground shadow-floating no-focus-ring", "data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left data-[side=top]:origin-bottom data-[state=closed]:hidden data-[state=open]:animate-zoom", t),
56
+ function g({ className: n, portal: r, ...i }) {
57
+ let a = /* @__PURE__ */ t(o.Content, {
58
+ className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-0 text-sm text-popover-foreground shadow-floating no-focus-ring", "data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left data-[side=top]:origin-bottom data-[state=closed]:hidden data-[state=open]:animate-zoom", n),
59
59
  sideOffset: 4,
60
60
  ...i
61
61
  });
62
- return r ? /* @__PURE__ */ n(o.Portal, { children: a }) : a;
62
+ return r ? /* @__PURE__ */ t(o.Portal, { children: a }) : a;
63
63
  }
64
- function _({ className: t, ...r }) {
65
- return /* @__PURE__ */ n(o.Item, {
66
- className: e(s(), t),
64
+ function _({ className: n, ...r }) {
65
+ return /* @__PURE__ */ t(o.Item, {
66
+ className: e(s(), n),
67
67
  ...r
68
68
  });
69
69
  }
70
- function v({ children: t, className: a, ...s }) {
71
- return /* @__PURE__ */ r(o.CheckboxItem, {
70
+ function v({ children: r, className: a, ...s }) {
71
+ return /* @__PURE__ */ n(o.CheckboxItem, {
72
72
  className: e("relative flex items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 no-focus-ring transition-bg-ease select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4", "mx-1 h-[28px] cursor-pointer data-[state=highlighted]:bg-accent data-[state=highlighted]:text-accent-foreground", a),
73
73
  ...s,
74
- children: [/* @__PURE__ */ n("span", {
74
+ children: [/* @__PURE__ */ t("span", {
75
75
  className: "absolute right-2 flex size-3.5 items-center justify-center",
76
- children: /* @__PURE__ */ n(o.ItemIndicator, { children: /* @__PURE__ */ n(i, {}) })
77
- }), t]
76
+ children: /* @__PURE__ */ t(o.ItemIndicator, { children: /* @__PURE__ */ t(i, {}) })
77
+ }), r]
78
78
  });
79
79
  }
80
- function y({ children: t, className: a, hideIcon: s, ...c }) {
81
- return /* @__PURE__ */ r(o.RadioItem, {
80
+ function y({ children: r, className: a, hideIcon: s, ...c }) {
81
+ return /* @__PURE__ */ n(o.RadioItem, {
82
82
  className: e("relative flex items-center rounded-sm pr-2 pl-8 no-focus-ring transition-bg-ease select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "mx-1 h-[28px] cursor-pointer gap-2 data-[state=highlighted]:bg-accent data-[state=highlighted]:text-accent-foreground [&_svg]:size-4", a),
83
83
  ...c,
84
- children: [!s && /* @__PURE__ */ n("span", {
84
+ children: [!s && /* @__PURE__ */ t("span", {
85
85
  className: "absolute left-2 flex size-3.5 items-center justify-center",
86
- children: /* @__PURE__ */ n(o.ItemIndicator, { children: /* @__PURE__ */ n(i, {}) })
87
- }), t]
86
+ children: /* @__PURE__ */ t(o.ItemIndicator, { children: /* @__PURE__ */ t(i, {}) })
87
+ }), r]
88
88
  });
89
89
  }
90
- function b({ className: t, inset: r, ...i }) {
91
- return /* @__PURE__ */ n(o.Label, {
92
- className: e(c({ inset: r }), t),
90
+ function b({ className: n, inset: r, ...i }) {
91
+ return /* @__PURE__ */ t(o.Label, {
92
+ className: e(c({ inset: r }), n),
93
93
  ...i
94
94
  });
95
95
  }
96
- function x({ className: t, ...r }) {
97
- return /* @__PURE__ */ n(o.Separator, {
98
- className: e("-mx-1 my-1 h-px bg-muted", t),
96
+ function x({ className: n, ...r }) {
97
+ return /* @__PURE__ */ t(o.Separator, {
98
+ className: e("-mx-1 my-1 h-px bg-muted", n),
99
99
  ...r
100
100
  });
101
101
  }
102
- function S({ className: t, ...r }) {
103
- return /* @__PURE__ */ n("span", {
104
- className: e("ml-auto text-xs tracking-widest opacity-60", t),
102
+ function S({ className: n, ...r }) {
103
+ return /* @__PURE__ */ t("span", {
104
+ className: e("ml-auto text-xs tracking-widest opacity-60", n),
105
105
  ...r
106
106
  });
107
107
  }
@@ -0,0 +1,7 @@
1
+ import { PiCardProps } from '@pihanga2/core';
2
+ import * as React from "react";
3
+ export declare const EmptyCard: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<EmptyProps, S, {}>) => import('@pihanga2/core').PiCardDef;
4
+ export type EmptyProps = {
5
+ style?: React.CSSProperties;
6
+ };
7
+ export declare const EmptyComponent: (props: PiCardProps<EmptyProps>) => React.ReactNode;
@@ -1,5 +1,5 @@
1
1
  import { DEF_FILE_DROP_FILE_TYPES as e } from "./fileDrop.types.js";
2
- /* empty css */
2
+ import './fileDrop.css';/* empty css */
3
3
  import "react";
4
4
  import { jsx as t, jsxs as n } from "react/jsx-runtime";
5
5
  import { FileUploader as r } from "react-drag-drop-files";
@@ -0,0 +1 @@
1
+ .pi-file-drop-root label{border:2px dashed #999;flex-grow:1;justify-content:space-between;max-width:100%;display:flex}.pi-file-drop-root .dropzone-msg{cursor:pointer;text-align:center;flex-grow:4;margin:3em 0}.pi-file-drop-root .pi-progress{max-width:100%;padding:1.25em}.pi-file-drop-root .pi-progress-label{font-size:1rem;font-weight:600;line-height:1.5rem}.pi-file-drop -root.pi-progress-container{background-color:#dadfe5;border-radius:4px;height:1rem;display:flex;position:relative;overflow:hidden}.pi-file-drop-root .pi-progress-bar{color:#fff;background-color:#0054a6;flex-direction:column;justify-content:center;display:flex;overflow:hidden}.pi-file-drop-root .pi-progress .label{font-size:1em;display:block}