@placeholderco/placeholder-ui 1.0.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +270 -137
  99. package/dist/form/Autocomplete.svelte.d.ts +58 -10
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +124 -43
  103. package/dist/form/Checkbox.svelte.d.ts +26 -2
  104. package/dist/form/Chips.svelte +47 -17
  105. package/dist/form/Chips.svelte.d.ts +24 -3
  106. package/dist/form/ComboBox.svelte +30 -20
  107. package/dist/form/ComboBox.svelte.d.ts +1 -1
  108. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  109. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  110. package/dist/form/ComboBoxMulti.svelte +38 -24
  111. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  112. package/dist/form/CronBuilder.svelte +84 -67
  113. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  114. package/dist/form/DatePicker.svelte +177 -191
  115. package/dist/form/DatePicker.svelte.d.ts +17 -2
  116. package/dist/form/DateRangePicker.svelte +775 -0
  117. package/dist/form/DateRangePicker.svelte.d.ts +23 -0
  118. package/dist/form/DateTimePicker.svelte +168 -209
  119. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  120. package/dist/form/FormGroup.svelte +33 -10
  121. package/dist/form/FormGroup.svelte.d.ts +12 -2
  122. package/dist/form/Number.svelte +71 -29
  123. package/dist/form/Number.svelte.d.ts +39 -6
  124. package/dist/form/Radio.svelte +174 -0
  125. package/dist/form/Radio.svelte.d.ts +21 -0
  126. package/dist/form/RadioGroup.svelte +52 -138
  127. package/dist/form/RadioGroup.svelte.d.ts +19 -2
  128. package/dist/form/SegmentedControl.svelte +103 -57
  129. package/dist/form/SegmentedControl.svelte.d.ts +12 -2
  130. package/dist/form/Select.svelte +160 -229
  131. package/dist/form/Select.svelte.d.ts +35 -6
  132. package/dist/form/SelectMulti.svelte +166 -277
  133. package/dist/form/SelectMulti.svelte.d.ts +37 -6
  134. package/dist/form/Slider.svelte +185 -268
  135. package/dist/form/Slider.svelte.d.ts +33 -16
  136. package/dist/form/StringArrayBuilder.svelte +249 -0
  137. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  138. package/dist/form/Switch.svelte +164 -106
  139. package/dist/form/Switch.svelte.d.ts +22 -3
  140. package/dist/form/TextArea.svelte +80 -45
  141. package/dist/form/TextArea.svelte.d.ts +35 -10
  142. package/dist/form/Textbox.svelte +80 -47
  143. package/dist/form/Textbox.svelte.d.ts +41 -8
  144. package/dist/form/TimePicker.svelte +42 -47
  145. package/dist/form/TimePicker.svelte.d.ts +12 -2
  146. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  147. package/dist/form/comboBoxCore.svelte.js +243 -0
  148. package/dist/icon/Icon.svelte +8 -3
  149. package/dist/icon/Icon.svelte.d.ts +8 -3
  150. package/dist/icon/cube-send.svg +1 -0
  151. package/dist/icon/file-type-bmp.svg +1 -0
  152. package/dist/icon/file-type-csv.svg +1 -0
  153. package/dist/icon/file-type-doc.svg +1 -0
  154. package/dist/icon/file-type-docx.svg +1 -0
  155. package/dist/icon/file-type-jpg.svg +1 -0
  156. package/dist/icon/file-type-pdf.svg +1 -0
  157. package/dist/icon/file-type-png.svg +1 -0
  158. package/dist/icon/file-type-ppt.svg +1 -0
  159. package/dist/icon/file-type-svg.svg +1 -0
  160. package/dist/icon/file-type-txt.svg +1 -0
  161. package/dist/icon/file-type-xls.svg +1 -0
  162. package/dist/icon/file-type-zip.svg +1 -0
  163. package/dist/icon/index.d.ts +166 -145
  164. package/dist/icon/index.js +166 -146
  165. package/dist/icon/layout-sidebar-left-collapse.svg +1 -0
  166. package/dist/icon/layout-sidebar-left-expand.svg +1 -0
  167. package/dist/icon/logo-placeholder.svg +6 -0
  168. package/dist/icon/paperclip.svg +1 -0
  169. package/dist/icon/source-code.svg +1 -0
  170. package/dist/icon/sun-moon.svg +1 -0
  171. package/dist/icon/table.svg +6 -0
  172. package/dist/icon/thumb-down.svg +1 -0
  173. package/dist/icon/thumb-up.svg +1 -0
  174. package/dist/index.d.ts +93 -22
  175. package/dist/index.js +28 -27
  176. package/dist/layout/AppShell.svelte +12 -162
  177. package/dist/layout/AppShell.svelte.d.ts +6 -27
  178. package/dist/layout/CustomNavbar.svelte +9 -7
  179. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  180. package/dist/layout/Navbar.svelte +158 -110
  181. package/dist/layout/Navbar.svelte.d.ts +26 -12
  182. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  183. package/dist/layout/NavbarItemView.svelte +182 -0
  184. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  185. package/dist/layout/Sidenav.svelte +280 -341
  186. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  187. package/dist/models/ComboBoxItem.d.ts +2 -0
  188. package/dist/models/Employee.d.ts +53 -0
  189. package/dist/models/Employee.js +23 -0
  190. package/dist/models/Hyperlink.d.ts +3 -1
  191. package/dist/models/LinkCollectionItem.d.ts +3 -1
  192. package/dist/models/NavbarItem.d.ts +6 -2
  193. package/dist/theme.svelte.d.ts +0 -1
  194. package/dist/theme.svelte.js +46 -58
  195. package/dist/ui/Accordion.svelte +81 -242
  196. package/dist/ui/Accordion.svelte.d.ts +37 -14
  197. package/dist/ui/AccordionItem.svelte +303 -0
  198. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  199. package/dist/ui/ActionIcon.svelte +25 -21
  200. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  201. package/dist/ui/Badge.svelte +170 -161
  202. package/dist/ui/Badge.svelte.d.ts +16 -4
  203. package/dist/ui/Button.svelte +178 -205
  204. package/dist/ui/Button.svelte.d.ts +19 -2
  205. package/dist/ui/ButtonVariant.d.ts +15 -1
  206. package/dist/ui/ContextMenu.svelte +146 -0
  207. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  208. package/dist/ui/ContextMenuList.svelte +188 -0
  209. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  210. package/dist/ui/Dialog.svelte +183 -104
  211. package/dist/ui/Dialog.svelte.d.ts +21 -2
  212. package/dist/ui/Drawer.svelte +237 -184
  213. package/dist/ui/Drawer.svelte.d.ts +20 -2
  214. package/dist/ui/Dropdown.svelte +24 -14
  215. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  216. package/dist/ui/Dropzone.svelte +159 -57
  217. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  218. package/dist/ui/Link.svelte +12 -4
  219. package/dist/ui/Link.svelte.d.ts +8 -2
  220. package/dist/ui/Loader.svelte +23 -18
  221. package/dist/ui/Loader.svelte.d.ts +5 -2
  222. package/dist/ui/Logo.svelte +37 -0
  223. package/dist/ui/Logo.svelte.d.ts +16 -0
  224. package/dist/ui/MultiSortable.svelte +287 -0
  225. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  226. package/dist/ui/Pagination.svelte +12 -6
  227. package/dist/ui/Pagination.svelte.d.ts +6 -2
  228. package/dist/ui/Popover.svelte +43 -65
  229. package/dist/ui/Popover.svelte.d.ts +14 -5
  230. package/dist/ui/ProgressBar.svelte +169 -0
  231. package/dist/ui/ProgressBar.svelte.d.ts +30 -0
  232. package/dist/ui/ProgressBarVariant.d.ts +7 -0
  233. package/dist/ui/ProgressBarVariant.js +1 -0
  234. package/dist/ui/SegmentedControl.svelte +0 -0
  235. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  236. package/dist/ui/Sortable.svelte +163 -0
  237. package/dist/ui/Sortable.svelte.d.ts +47 -0
  238. package/dist/ui/Table.svelte +497 -56
  239. package/dist/ui/Table.svelte.d.ts +64 -2
  240. package/dist/ui/Tabs.svelte +22 -18
  241. package/dist/ui/Tabs.svelte.d.ts +6 -2
  242. package/dist/ui/ThemeSwitcher.svelte +26 -7
  243. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  244. package/dist/ui/Tooltip.svelte +312 -180
  245. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  246. package/dist/uiTheme.svelte.d.ts +86 -0
  247. package/dist/uiTheme.svelte.js +214 -0
  248. package/dist/util/DateFunctions.js +1 -1
  249. package/dist/util/DragDrop.d.ts +50 -0
  250. package/dist/util/DragDrop.js +154 -0
  251. package/dist/util/NavigateTo.js +2 -2
  252. package/dist/util/dayjs.d.ts +4 -0
  253. package/dist/util/dayjs.js +15 -0
  254. package/dist/util/interceptLinkClick.d.ts +13 -0
  255. package/dist/util/interceptLinkClick.js +33 -0
  256. package/package.json +66 -26
  257. package/dist/display/Alert.svelte +0 -179
  258. package/dist/display/Alert.svelte.d.ts +0 -13
  259. package/dist/form/FileInput.svelte +0 -235
  260. package/dist/form/FileInput.svelte.d.ts +0 -23
  261. package/dist/form/PasswordInput.svelte +0 -252
  262. package/dist/form/PasswordInput.svelte.d.ts +0 -33
  263. package/dist/form/Rating.svelte +0 -235
  264. package/dist/form/Rating.svelte.d.ts +0 -21
  265. package/dist/styles/components.css +0 -199
  266. package/dist/styles/dark.css +0 -146
  267. package/dist/styles/index.css +0 -116
  268. package/dist/styles/reset.css +0 -110
  269. package/dist/styles/semantic.css +0 -86
  270. package/dist/styles/tokens.css +0 -203
  271. package/dist/styles/utilities.css +0 -523
  272. package/dist/ui/Breadcrumbs.svelte +0 -131
  273. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  274. package/dist/ui/LoadingOverlay.svelte +0 -53
  275. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  276. package/dist/ui/Progress.svelte +0 -191
  277. package/dist/ui/Progress.svelte.d.ts +0 -15
  278. package/dist/ui/RingProgress.svelte +0 -141
  279. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  280. package/dist/ui/Skeleton.svelte +0 -85
  281. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  282. package/dist/ui/Stepper.svelte +0 -355
  283. package/dist/ui/Stepper.svelte.d.ts +0 -24
  284. package/dist/ui/Timeline.svelte +0 -225
  285. package/dist/ui/Timeline.svelte.d.ts +0 -23
  286. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  287. /package/dist/{actions → util}/ClickOutside.js +0 -0
  288. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  289. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -1,8 +1,17 @@
1
1
  <script lang="ts">
2
2
  import { type Snippet } from 'svelte';
3
3
  import Textbox from '../form/Textbox.svelte';
4
+ import Checkbox from '../form/Checkbox.svelte';
4
5
  import Icon from '../icon/Icon.svelte';
5
- import { iconArrowsSort, iconChevronDown, iconChevronUp, iconSearch } from '../icon/index.js';
6
+ import Pagination from './Pagination.svelte';
7
+ import {
8
+ iconArrowsSort,
9
+ iconChevronDown,
10
+ iconChevronRight,
11
+ iconChevronUp,
12
+ iconSearch
13
+ } from '../icon/index.js';
14
+ import { navigateTo } from '../util/NavigateTo.js';
6
15
 
7
16
  export interface Column<T = any> {
8
17
  key: string;
@@ -10,23 +19,81 @@
10
19
  sortable?: boolean;
11
20
  width?: string;
12
21
  align?: 'left' | 'center' | 'right';
22
+ /** Snippet rendered as the cell content. Takes precedence over `render`. */
23
+ cell?: Snippet<[{ value: any; row: T; index: number }]>;
24
+ /** @deprecated Returns raw HTML injected via {@html} — unsafe for untrusted
25
+ * data and cannot render components. Use `cell` instead. */
13
26
  render?: (value: any, row: T) => any;
14
27
  }
15
28
 
16
- interface Props<T = any> {
29
+ export interface TableProps<T = any> {
30
+ /** Column definitions for the table */
17
31
  columns: Column<T>[];
32
+ /** Data rows to display */
18
33
  rows: T[];
34
+ /** Enable search/filter functionality */
19
35
  searchable?: boolean;
36
+ /** Placeholder text for search input */
20
37
  searchPlaceholder?: string;
38
+ /** Additional CSS classes */
21
39
  class?: string;
40
+ /** Enable alternating row colors */
22
41
  striped?: boolean;
42
+ /** Disable rounded corners on table container */
23
43
  notRounded?: boolean;
44
+ /** Enable hover effect on rows */
24
45
  hover?: boolean;
46
+ /** Message shown when no data is available */
25
47
  emptyMessage?: string;
48
+ /** Show loading state */
26
49
  loading?: boolean;
50
+ /** Content to render below the table */
27
51
  children?: Snippet;
52
+ /** Action buttons snippet, receives row data and index */
28
53
  buttons?: Snippet<[T, number]>;
54
+ /** Callback when a row is clicked */
29
55
  onrowclick?: (row: T, index: number) => void;
56
+ /** Returns a URL for a row. When provided, rows act as links: a plain click
57
+ * navigates client-side, while middle-click or ctrl/cmd/shift-click opens the
58
+ * URL in a new tab. Return a falsy value to make a row non-navigable. */
59
+ rowHref?: (row: T, index: number) => string | undefined | null;
60
+ /** Enable checkbox selection */
61
+ selectable?: boolean;
62
+ /** Field name used to uniquely identify rows (required when selectable is true) */
63
+ rowKey?: string;
64
+ /** Currently selected row objects (bindable) */
65
+ selectedRows?: T[];
66
+ /** Callback when selection changes */
67
+ onselect?: (selectedRows: T[]) => void;
68
+ /** Detail content rendered in a full-width row beneath an expanded row.
69
+ * Providing this enables row expansion: expandable rows toggle on row click
70
+ * or via their chevron button. Requires rowKey. */
71
+ expandedContent?: Snippet<[T, number]>;
72
+ /** Limits which rows can expand (e.g. only group rows). Default: all rows. */
73
+ canExpand?: (row: T, index: number) => boolean;
74
+ /** Keys (via rowKey) of currently expanded rows (bindable) */
75
+ expandedKeys?: any[];
76
+ /** Callback when a row is expanded or collapsed — trigger on-demand fetches here */
77
+ onexpand?: (row: T, index: number, expanded: boolean) => void;
78
+ /** Rows per page. Setting this enables built-in client-side pagination,
79
+ * applied after search filtering and sorting. */
80
+ pageSize?: number;
81
+ /** Current page number, 1-based (bindable) */
82
+ page?: number;
83
+ /** Callback when the current page changes */
84
+ onpagechange?: (page: number) => void;
85
+ /** Key of the column currently sorted by, or null for unsorted (bindable).
86
+ * Set it to render the sort indicator for a server-supplied sort order. */
87
+ sortKey?: string | null;
88
+ /** Current sort direction (bindable) */
89
+ sortDirection?: 'asc' | 'desc';
90
+ /** Callback when the user clicks a sortable header, after `sortKey` and
91
+ * `sortDirection` have been updated. Use it to request sorted data from a
92
+ * server. Paginated tables also reset to page 1 before this fires. */
93
+ onsort?: (key: string, direction: 'asc' | 'desc') => void;
94
+ /** Skip the built-in client-side sort and render `rows` in the order given.
95
+ * Use with `onsort` when the server returns pre-sorted rows. */
96
+ manualSort?: boolean;
30
97
  }
31
98
 
32
99
  let {
@@ -42,22 +109,190 @@
42
109
  loading = false,
43
110
  children,
44
111
  buttons,
45
- onrowclick
46
- }: Props = $props();
112
+ onrowclick,
113
+ rowHref = undefined,
114
+ selectable = false,
115
+ rowKey = undefined,
116
+ selectedRows = $bindable([]),
117
+ onselect = undefined,
118
+ expandedContent = undefined,
119
+ canExpand = undefined,
120
+ expandedKeys = $bindable([]),
121
+ onexpand = undefined,
122
+ pageSize = undefined,
123
+ page = $bindable(1),
124
+ onpagechange = undefined,
125
+ sortKey = $bindable(null),
126
+ sortDirection = $bindable('asc'),
127
+ onsort = undefined,
128
+ manualSort = false
129
+ }: TableProps = $props();
47
130
 
48
131
  let searchQuery = $state('');
49
- let sortColumn = $state<string | null>(null);
50
- let sortDirection = $state<'asc' | 'desc'>('asc');
132
+
133
+ let _selectionWarned = false;
134
+ $effect(() => {
135
+ if (selectable && !rowKey && !_selectionWarned) {
136
+ _selectionWarned = true;
137
+ console.warn('Table: rowKey is required when selectable is true');
138
+ }
139
+ });
140
+
141
+ let _expandWarned = false;
142
+ $effect(() => {
143
+ if (expandedContent && !rowKey && !_expandWarned) {
144
+ _expandWarned = true;
145
+ console.warn('Table: rowKey is required when expandedContent is provided');
146
+ }
147
+ });
148
+
149
+ const expandable = $derived(!!expandedContent && !!rowKey);
150
+
151
+ // Prune expandedKeys when rows changes to remove entries no longer present
152
+ $effect(() => {
153
+ if (!expandable || expandedKeys.length === 0) return;
154
+ const validKeys = new Set(rows.map((r) => getValue(r, rowKey!)));
155
+ const pruned = expandedKeys.filter((k) => validKeys.has(k));
156
+ if (pruned.length !== expandedKeys.length) {
157
+ expandedKeys = pruned;
158
+ }
159
+ });
160
+
161
+ // Set of expanded row keys for O(1) lookup
162
+ const expandedKeySet = $derived(new Set(expandedKeys));
163
+
164
+ function isRowExpanded(row: any): boolean {
165
+ if (!expandable) return false;
166
+ return expandedKeySet.has(getValue(row, rowKey!));
167
+ }
168
+
169
+ function rowCanExpand(row: any, index: number): boolean {
170
+ if (!expandable) return false;
171
+ return canExpand ? canExpand(row, index) : true;
172
+ }
173
+
174
+ function toggleExpand(row: any, index: number) {
175
+ if (!rowCanExpand(row, index)) return;
176
+
177
+ const key = getValue(row, rowKey!);
178
+ const expanded = !expandedKeySet.has(key);
179
+ if (expanded) {
180
+ expandedKeys = [...expandedKeys, key];
181
+ } else {
182
+ expandedKeys = expandedKeys.filter((k) => k !== key);
183
+ }
184
+ onexpand?.(row, index, expanded);
185
+ }
186
+
187
+ // Prune selectedRows when rows changes to remove entries no longer present
188
+ $effect(() => {
189
+ if (!selectable || !rowKey || selectedRows.length === 0) return;
190
+ const validKeys = new Set(rows.map((r) => getValue(r, rowKey)));
191
+ const pruned = selectedRows.filter((r) => validKeys.has(getValue(r, rowKey)));
192
+ if (pruned.length !== selectedRows.length) {
193
+ selectedRows = pruned;
194
+ onselect?.(selectedRows);
195
+ }
196
+ });
197
+
198
+ // Set of selected row keys for O(1) lookup
199
+ const selectedKeys = $derived.by(() => {
200
+ if (!selectable || !rowKey) return new Set<any>();
201
+ return new Set(selectedRows.map((r) => getValue(r, rowKey)));
202
+ });
203
+
204
+ // Check if a row is selected
205
+ function isRowSelected(row: any): boolean {
206
+ if (!selectable || !rowKey) return false;
207
+ return selectedKeys.has(getValue(row, rowKey));
208
+ }
209
+
210
+ // Toggle individual row selection
211
+ function toggleRow(row: any) {
212
+ if (!selectable || !rowKey) return;
213
+
214
+ const key = getValue(row, rowKey);
215
+ if (selectedKeys.has(key)) {
216
+ // Deselect: remove from selectedRows
217
+ selectedRows = selectedRows.filter((r) => getValue(r, rowKey) !== key);
218
+ } else {
219
+ // Select: add to selectedRows
220
+ selectedRows = [...selectedRows, row];
221
+ }
222
+ onselect?.(selectedRows);
223
+ }
224
+
225
+ // Handle a row click. Middle-click and ctrl/cmd/shift-click are treated as
226
+ // open-in-new-tab intent when a rowHref is provided.
227
+ function handleRowClick(row: any, index: number, event: MouseEvent) {
228
+ const href = rowHref?.(row, index);
229
+ const newTabIntent = event.button === 1 || event.ctrlKey || event.metaKey || event.shiftKey;
230
+
231
+ // Expansion/selection toggling only applies to a plain primary click without
232
+ // nav intent. Expansion wins on rows that can expand; selection stays
233
+ // available via the checkbox and still handles non-expandable rows.
234
+ if (expandable && !href && event.button !== 1 && rowCanExpand(row, index)) {
235
+ toggleExpand(row, index);
236
+ } else if (selectable && !href && event.button !== 1) {
237
+ toggleRow(row);
238
+ }
239
+
240
+ onrowclick?.(row, index);
241
+
242
+ if (href) {
243
+ event.preventDefault();
244
+ if (newTabIntent) window.open(href, '_blank', 'noopener');
245
+ else navigateTo(href);
246
+ }
247
+ }
248
+
249
+ // Toggle all visible rows (the current page when paginated, else all filtered rows)
250
+ function toggleAll() {
251
+ if (!selectable || !rowKey) return;
252
+
253
+ // Check if all visible rows are selected
254
+ const allSelected =
255
+ pagedRows.length > 0 && pagedRows.every((row) => selectedKeys.has(getValue(row, rowKey!)));
256
+
257
+ if (allSelected) {
258
+ // Deselect all visible rows
259
+ const visibleKeys = new Set(pagedRows.map((r) => getValue(r, rowKey!)));
260
+ selectedRows = selectedRows.filter((r) => !visibleKeys.has(getValue(r, rowKey!)));
261
+ } else {
262
+ // Select all visible rows (merge with existing selections elsewhere)
263
+ const existingKeys = new Set(selectedRows.map((r) => getValue(r, rowKey!)));
264
+ const newSelections = pagedRows.filter((r) => !existingKeys.has(getValue(r, rowKey!)));
265
+ selectedRows = [...selectedRows, ...newSelections];
266
+ }
267
+ onselect?.(selectedRows);
268
+ }
269
+
270
+ // Derived states for select-all checkbox
271
+ const isAllSelected = $derived.by(() => {
272
+ if (!selectable || !rowKey || pagedRows.length === 0) return false;
273
+ return pagedRows.every((row) => selectedKeys.has(getValue(row, rowKey)));
274
+ });
275
+
276
+ const isSomeSelected = $derived.by(() => {
277
+ if (!selectable || !rowKey || pagedRows.length === 0) return false;
278
+ const selectedCount = pagedRows.filter((row) => selectedKeys.has(getValue(row, rowKey))).length;
279
+ return selectedCount > 0 && selectedCount < pagedRows.length;
280
+ });
51
281
 
52
282
  function handleSort(column: Column) {
53
283
  if (!column.sortable) return;
54
284
 
55
- if (sortColumn === column.key) {
285
+ if (sortKey === column.key) {
56
286
  sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
57
287
  } else {
58
- sortColumn = column.key;
288
+ sortKey = column.key;
59
289
  sortDirection = 'asc';
60
290
  }
291
+
292
+ // A new sort order invalidates the current page position
293
+ if (paginated && page !== 1) page = 1;
294
+
295
+ onsort?.(column.key, sortDirection);
61
296
  }
62
297
 
63
298
  function getValue(row: any, key: string): any {
@@ -74,8 +309,8 @@
74
309
 
75
310
  // Apply search filter
76
311
  if (searchable && searchQuery) {
77
- filtered = filtered.filter(row => {
78
- return columns.some(column => {
312
+ filtered = filtered.filter((row) => {
313
+ return columns.some((column) => {
79
314
  const value = getValue(row, column.key);
80
315
  if (value === null || value === undefined) return false;
81
316
  return String(value).toLowerCase().includes(searchQuery.toLowerCase());
@@ -83,11 +318,11 @@
83
318
  });
84
319
  }
85
320
 
86
- // Apply sorting
87
- if (sortColumn) {
321
+ // Apply sorting — skipped when the caller sorts the rows itself (e.g. server-side)
322
+ if (sortKey && !manualSort) {
88
323
  filtered.sort((a, b) => {
89
- const aValue = getValue(a, sortColumn!);
90
- const bValue = getValue(b, sortColumn!);
324
+ const aValue = getValue(a, sortKey!);
325
+ const bValue = getValue(b, sortKey!);
91
326
 
92
327
  if (aValue === null || aValue === undefined) return 1;
93
328
  if (bValue === null || bValue === undefined) return -1;
@@ -106,15 +341,51 @@
106
341
  return filtered;
107
342
  });
108
343
 
344
+ // Pagination — applied after search filtering and sorting
345
+ const paginated = $derived(!!pageSize && pageSize > 0);
346
+ const totalPages = $derived(
347
+ paginated ? Math.max(1, Math.ceil(filteredRows.length / pageSize!)) : 1
348
+ );
349
+
350
+ // Clamp page when filtering or a rows update shrinks the page count
351
+ $effect(() => {
352
+ if (page > totalPages) page = totalPages;
353
+ else if (page < 1) page = 1;
354
+ });
355
+
356
+ // A new search invalidates the current page position — jump back to the first page
357
+ let _lastQuery = '';
358
+ $effect(() => {
359
+ if (searchQuery !== _lastQuery) {
360
+ _lastQuery = searchQuery;
361
+ if (page !== 1) page = 1;
362
+ }
363
+ });
364
+
365
+ const pagedRows = $derived.by(() => {
366
+ if (!paginated) return filteredRows;
367
+ const start = (page - 1) * pageSize!;
368
+ return filteredRows.slice(start, start + pageSize!);
369
+ });
370
+
371
+ const pageStart = $derived(paginated ? (page - 1) * pageSize! + 1 : 1);
372
+ const pageEnd = $derived(
373
+ paginated ? Math.min(page * pageSize!, filteredRows.length) : filteredRows.length
374
+ );
375
+
109
376
  const getSortIcon = (column: Column) => {
110
377
  if (!column.sortable) return '';
111
- if (sortColumn !== column.key) return iconArrowsSort;
378
+ if (sortKey !== column.key) return iconArrowsSort;
112
379
  return sortDirection === 'asc' ? iconChevronUp : iconChevronDown;
113
380
  };
114
381
 
115
382
  const tableClasses = $derived(
116
383
  `pui-table ${tableClass} ${striped ? 'striped' : ''} ${hover ? 'hover' : ''}`
117
384
  );
385
+
386
+ const totalColumns = $derived(
387
+ columns.length + (expandable ? 1 : 0) + (selectable && rowKey ? 1 : 0) + (buttons ? 1 : 0)
388
+ );
118
389
  </script>
119
390
 
120
391
  <div class="table-container">
@@ -133,6 +404,20 @@
133
404
  <table class={tableClasses}>
134
405
  <thead>
135
406
  <tr>
407
+ {#if expandable}
408
+ <th class="table-header expand-cell" style="width: 40px"></th>
409
+ {/if}
410
+ {#if selectable && rowKey}
411
+ <th class="table-header checkbox-cell" style="width: 40px">
412
+ <Checkbox
413
+ controlled
414
+ checked={isAllSelected}
415
+ indeterminate={isSomeSelected}
416
+ onchange={() => toggleAll()}
417
+ ariaLabel="Select all rows"
418
+ />
419
+ </th>
420
+ {/if}
136
421
  {#each columns as column}
137
422
  <th
138
423
  class="table-header {column.sortable ? 'sortable' : ''} {column.align || 'left'}"
@@ -143,10 +428,7 @@
143
428
  <span>{column.label}</span>
144
429
  {#if column.sortable}
145
430
  <div class="sort-icon">
146
- <Icon
147
- svg={getSortIcon(column)}
148
- size="14px"
149
- />
431
+ <Icon svg={getSortIcon(column)} size="14px" />
150
432
  </div>
151
433
  {/if}
152
434
  </div>
@@ -160,26 +442,70 @@
160
442
  <tbody>
161
443
  {#if loading}
162
444
  <tr>
163
- <td colspan={columns.length + (buttons ? 1 : 0)} class="loading-cell">
445
+ <td colspan={totalColumns} class="loading-cell">
164
446
  <div class="loading-content">Loading...</div>
165
447
  </td>
166
448
  </tr>
167
449
  {:else if filteredRows.length === 0}
168
450
  <tr>
169
- <td colspan={columns.length + (buttons ? 1 : 0)} class="empty-cell">
451
+ <td colspan={totalColumns} class="empty-cell">
170
452
  <div class="empty-content">{emptyMessage}</div>
171
453
  </td>
172
454
  </tr>
173
455
  {:else}
174
- {#each filteredRows as row, index}
456
+ {#each pagedRows as row, index}
457
+ {@const expanded = isRowExpanded(row)}
175
458
  <tr
176
- class={onrowclick ? 'clickable-row' : ''}
177
- onclick={() => onrowclick?.(row, index)}
459
+ class={(onrowclick ||
460
+ selectable ||
461
+ rowHref ||
462
+ (expandable && rowCanExpand(row, index))
463
+ ? 'clickable-row'
464
+ : '') +
465
+ (selectable && isRowSelected(row) ? ' selected' : '') +
466
+ (expanded ? ' expanded' : '')}
467
+ class:row-even={index % 2 === 1}
468
+ onclick={(e) => handleRowClick(row, index, e)}
469
+ onmousedown={(e) => {
470
+ // Suppress the default middle-button action (autoscroll) so auxclick fires.
471
+ if (e.button === 1 && rowHref?.(row, index)) e.preventDefault();
472
+ }}
473
+ onauxclick={(e) => {
474
+ if (e.button === 1) handleRowClick(row, index, e);
475
+ }}
178
476
  >
477
+ {#if expandable}
478
+ <td class="table-cell expand-cell" onclick={(e) => e.stopPropagation()}>
479
+ {#if rowCanExpand(row, index)}
480
+ <button
481
+ type="button"
482
+ class="expand-toggle"
483
+ class:expanded
484
+ aria-expanded={expanded}
485
+ aria-label={expanded ? 'Collapse row' : 'Expand row'}
486
+ onclick={() => toggleExpand(row, index)}
487
+ >
488
+ <Icon svg={iconChevronRight} size="16px" />
489
+ </button>
490
+ {/if}
491
+ </td>
492
+ {/if}
493
+ {#if selectable && rowKey}
494
+ <td class="table-cell checkbox-cell" onclick={(e) => e.stopPropagation()}>
495
+ <Checkbox
496
+ controlled
497
+ checked={isRowSelected(row)}
498
+ onchange={() => toggleRow(row)}
499
+ ariaLabel="Select row"
500
+ />
501
+ </td>
502
+ {/if}
179
503
  {#each columns as column}
180
504
  {@const value = getValue(row, column.key)}
181
505
  <td class="table-cell {column.align || 'left'}">
182
- {#if column.render}
506
+ {#if column.cell}
507
+ {@render column.cell({ value, row, index })}
508
+ {:else if column.render}
183
509
  {@html column.render(value, row)}
184
510
  {:else if typeof value === 'function'}
185
511
  {@render value()}
@@ -194,12 +520,28 @@
194
520
  </td>
195
521
  {/if}
196
522
  </tr>
523
+ {#if expanded && expandedContent}
524
+ <tr class="detail-row" class:row-even={index % 2 === 1}>
525
+ <td class="detail-cell" colspan={totalColumns}>
526
+ {@render expandedContent(row, index)}
527
+ </td>
528
+ </tr>
529
+ {/if}
197
530
  {/each}
198
531
  {/if}
199
532
  </tbody>
200
533
  </table>
201
534
  </div>
202
535
 
536
+ {#if paginated && !loading && totalPages > 1}
537
+ <div class="table-pagination">
538
+ <span class="pagination-info">
539
+ Showing {pageStart}–{pageEnd} of {filteredRows.length}
540
+ </span>
541
+ <Pagination bind:selectedPage={page} {totalPages} onPageChange={(p) => onpagechange?.(p)} />
542
+ </div>
543
+ {/if}
544
+
203
545
  {@render children?.()}
204
546
  </div>
205
547
 
@@ -209,45 +551,45 @@
209
551
  }
210
552
 
211
553
  .table-search {
212
- margin-bottom: var(--pui-spacing-4);
554
+ margin-bottom: 1rem;
213
555
  max-width: 400px;
214
556
  }
215
557
 
216
558
  .table-wrapper {
217
559
  width: 100%;
218
560
  overflow-x: auto;
219
- border: 1px solid var(--pui-border-default);
220
- background-color: var(--pui-input-bg);
561
+ border: 1px solid var(--border-color);
562
+ background-color: var(--input-bg-color);
221
563
  }
222
564
 
223
565
  .table-wrapper.rounded {
224
- border-radius: var(--pui-radius-lg);
566
+ border-radius: 0.5rem;
225
567
  }
226
568
 
227
569
  .pui-table {
228
570
  width: 100%;
229
571
  border-collapse: collapse;
230
- font-size: var(--pui-font-size-base);
572
+ font-size: 0.875rem;
231
573
  }
232
574
 
233
575
  .table-header {
234
- padding: var(--pui-spacing-2);
576
+ padding: 0.5rem;
235
577
  text-align: left;
236
- font-weight: var(--pui-font-weight-semibold);
237
- color: var(--pui-text-primary);
238
- background-color: var(--pui-bg-surface-raised);
239
- border-bottom: 2px solid var(--pui-border-default);
578
+ font-weight: 600;
579
+ color: var(--text-color);
580
+ background-color: var(--bg-color);
581
+ border-bottom: 2px solid var(--border-color);
240
582
  user-select: none;
241
583
  white-space: nowrap;
242
584
  }
243
585
 
244
586
  .table-header.sortable {
245
587
  cursor: pointer;
246
- transition: background-color var(--pui-transition-fast) var(--pui-ease-out);
588
+ transition: background-color 0.2s;
247
589
  }
248
590
 
249
591
  .table-header.sortable:hover {
250
- background-color: var(--pui-bg-hover);
592
+ background-color: var(--hover-bg);
251
593
  }
252
594
 
253
595
  .table-header.center {
@@ -261,7 +603,7 @@
261
603
  .header-content {
262
604
  display: flex;
263
605
  align-items: center;
264
- gap: var(--pui-spacing-2);
606
+ gap: 0.5rem;
265
607
  justify-content: space-between;
266
608
  }
267
609
 
@@ -269,7 +611,7 @@
269
611
  display: inline-flex;
270
612
  align-items: center;
271
613
  opacity: 0.5;
272
- transition: opacity var(--pui-transition-fast) var(--pui-ease-out);
614
+ transition: opacity 0.2s;
273
615
  }
274
616
 
275
617
  .table-header.sortable:hover .sort-icon {
@@ -277,10 +619,10 @@
277
619
  }
278
620
 
279
621
  .table-cell {
280
- padding: var(--pui-spacing-2);
622
+ padding: 0.5rem;
281
623
  text-align: left;
282
- color: var(--pui-text-primary);
283
- border-bottom: 1px solid var(--pui-border-default);
624
+ color: var(--text-color);
625
+ border-bottom: 1px solid var(--border-color);
284
626
  }
285
627
 
286
628
  .table-cell.center {
@@ -291,13 +633,88 @@
291
633
  text-align: right;
292
634
  }
293
635
 
294
- .pui-table.striped tbody tr:nth-child(even) {
295
- background-color: var(--pui-bg-hover);
636
+ .checkbox-cell {
637
+ width: 40px;
638
+ text-align: center;
639
+ padding: 0.25rem;
640
+ }
641
+
642
+ .checkbox-cell :global(.checkbox-container) {
643
+ display: inline-flex;
644
+ justify-content: center;
645
+ }
646
+
647
+ .checkbox-cell :global(input) {
648
+ margin: 2px;
649
+ }
650
+
651
+ .expand-cell {
652
+ width: 40px;
653
+ text-align: center;
654
+ padding: 0.25rem;
655
+ }
656
+
657
+ .expand-toggle {
658
+ display: inline-flex;
659
+ align-items: center;
660
+ justify-content: center;
661
+ width: 24px;
662
+ height: 24px;
663
+ padding: 0;
664
+ border: none;
665
+ background: transparent;
666
+ color: var(--text-color);
667
+ cursor: pointer;
668
+ border-radius: 0.25rem;
669
+ transition:
670
+ transform 0.15s ease,
671
+ background-color 0.2s;
672
+ }
673
+
674
+ .expand-toggle:hover {
675
+ background-color: var(--hover-bg);
296
676
  }
297
677
 
298
- .pui-table.hover tbody tr:hover {
299
- background-color: var(--pui-bg-hover);
300
- transition: background-color var(--pui-transition-fast) var(--pui-ease-out);
678
+ .expand-toggle.expanded {
679
+ transform: rotate(90deg);
680
+ }
681
+
682
+ /* An expanded row and its detail row read as one unit: the parent's border
683
+ * moves to the bottom of the detail row. */
684
+ tbody tr.expanded td {
685
+ border-bottom: none;
686
+ }
687
+
688
+ .detail-cell {
689
+ padding: 0.5rem;
690
+ color: var(--text-color);
691
+ border-bottom: 1px solid var(--border-color);
692
+ }
693
+
694
+ .pui-table.striped tbody tr.row-even {
695
+ background-color: var(--stripe-bg);
696
+ }
697
+
698
+ .pui-table.hover tbody tr:not(.detail-row):hover {
699
+ background-color: var(--hover-bg);
700
+ transition: background-color 0.2s;
701
+ }
702
+
703
+ .pui-table tbody tr.selected,
704
+ .pui-table.striped tbody tr.selected.row-even {
705
+ background-color: rgba(var(--ui-tertiary-rgbc), 0.15);
706
+ }
707
+ .pui-table tbody tr.selected:hover,
708
+ .pui-table.hover tbody tr.selected:hover {
709
+ background-color: rgba(var(--ui-tertiary-rgbc), 0.22);
710
+ }
711
+ :global(.dark) .pui-table tbody tr.selected,
712
+ :global(.dark) .pui-table.striped tbody tr.selected.row-even {
713
+ background-color: rgba(var(--ui-tertiary-rgbc), 0.12);
714
+ }
715
+ :global(.dark) .pui-table tbody tr.selected:hover,
716
+ :global(.dark) .pui-table.hover tbody tr.selected:hover {
717
+ background-color: rgba(var(--ui-tertiary-rgbc), 0.16);
301
718
  }
302
719
 
303
720
  .clickable-row {
@@ -305,8 +722,8 @@
305
722
  }
306
723
 
307
724
  .clickable-row:hover {
308
- background-color: var(--pui-bg-hover);
309
- transition: background-color var(--pui-transition-fast) var(--pui-ease-out);
725
+ background-color: var(--hover-bg);
726
+ transition: background-color 0.2s;
310
727
  }
311
728
 
312
729
  tbody tr:last-child td {
@@ -315,9 +732,9 @@
315
732
 
316
733
  .empty-cell,
317
734
  .loading-cell {
318
- padding: var(--pui-spacing-8);
735
+ padding: 2rem;
319
736
  text-align: center;
320
- color: var(--pui-text-muted);
737
+ color: var(--text-muted);
321
738
  border-bottom: none;
322
739
  }
323
740
 
@@ -326,21 +743,45 @@
326
743
  font-style: italic;
327
744
  }
328
745
 
329
- /* Icon styles for sort indicators */
746
+ .table-pagination {
747
+ display: flex;
748
+ align-items: center;
749
+ justify-content: space-between;
750
+ gap: 1rem;
751
+ flex-wrap: wrap;
752
+ margin-top: 1rem;
753
+ }
754
+
755
+ .pagination-info {
756
+ color: var(--text-muted);
757
+ font-size: 0.875rem;
758
+ }
759
+
760
+ :global(:root) {
761
+ --stripe-bg: rgba(0, 0, 0, 0.02);
762
+ --hover-bg: rgba(0, 0, 0, 0.04);
763
+ --text-muted: #6b7280;
764
+ }
765
+
766
+ :global(.dark) {
767
+ --stripe-bg: rgba(255, 255, 255, 0.02);
768
+ --hover-bg: rgba(255, 255, 255, 0.04);
769
+ --text-muted: #9ca3af;
770
+ }
771
+
330
772
  :global(.pui-table .sort-icon svg) {
331
773
  width: 14px;
332
774
  height: 14px;
333
775
  }
334
776
 
335
- /* Responsive adjustments */
336
777
  @media (max-width: 640px) {
337
778
  .table-header,
338
779
  .table-cell {
339
- padding: var(--pui-spacing-2) var(--pui-spacing-3);
780
+ padding: 0.5rem 0.75rem;
340
781
  }
341
782
 
342
783
  .pui-table {
343
- font-size: var(--pui-font-size-xs);
784
+ font-size: 0.75rem;
344
785
  }
345
786
  }
346
- </style>
787
+ </style>