@urbicon-ui/table 6.1.4

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 (154) hide show
  1. package/README.md +153 -0
  2. package/dist/cells/ActionButtons.svelte +224 -0
  3. package/dist/cells/ActionButtons.svelte.d.ts +74 -0
  4. package/dist/cells/CopyButton.svelte +89 -0
  5. package/dist/cells/CopyButton.svelte.d.ts +33 -0
  6. package/dist/cells/CustomCell.svelte +136 -0
  7. package/dist/cells/CustomCell.svelte.d.ts +44 -0
  8. package/dist/cells/DateCell.svelte +194 -0
  9. package/dist/cells/DateCell.svelte.d.ts +39 -0
  10. package/dist/cells/LinkCell.svelte +240 -0
  11. package/dist/cells/LinkCell.svelte.d.ts +42 -0
  12. package/dist/cells/NumberCell.svelte +225 -0
  13. package/dist/cells/NumberCell.svelte.d.ts +47 -0
  14. package/dist/cells/StatusBadge.svelte +121 -0
  15. package/dist/cells/StatusBadge.svelte.d.ts +44 -0
  16. package/dist/cells/UserAvatar.svelte +71 -0
  17. package/dist/cells/UserAvatar.svelte.d.ts +37 -0
  18. package/dist/cells/index.d.ts +8 -0
  19. package/dist/cells/index.js +9 -0
  20. package/dist/core/EmptyState.svelte +161 -0
  21. package/dist/core/EmptyState.svelte.d.ts +16 -0
  22. package/dist/core/ErrorState.svelte +158 -0
  23. package/dist/core/ErrorState.svelte.d.ts +15 -0
  24. package/dist/core/GroupedRow.svelte +239 -0
  25. package/dist/core/GroupedRow.svelte.d.ts +18 -0
  26. package/dist/core/LoadingState.svelte +75 -0
  27. package/dist/core/LoadingState.svelte.d.ts +14 -0
  28. package/dist/core/MobileCard.svelte +151 -0
  29. package/dist/core/MobileCard.svelte.d.ts +15 -0
  30. package/dist/core/TableCell.svelte +105 -0
  31. package/dist/core/TableCell.svelte.d.ts +14 -0
  32. package/dist/core/TableDesktop.svelte +480 -0
  33. package/dist/core/TableDesktop.svelte.d.ts +26 -0
  34. package/dist/core/TableHead.svelte +314 -0
  35. package/dist/core/TableHead.svelte.d.ts +7 -0
  36. package/dist/core/TableMobile.svelte +112 -0
  37. package/dist/core/TableMobile.svelte.d.ts +13 -0
  38. package/dist/core/TableProvider.svelte +271 -0
  39. package/dist/core/TableProvider.svelte.d.ts +40 -0
  40. package/dist/core/TableRow.svelte +171 -0
  41. package/dist/core/TableRow.svelte.d.ts +16 -0
  42. package/dist/core/index.d.ts +17 -0
  43. package/dist/core/index.js +14 -0
  44. package/dist/core/sticky-context.svelte.d.ts +48 -0
  45. package/dist/core/sticky-context.svelte.js +88 -0
  46. package/dist/core/table/Table.svelte +304 -0
  47. package/dist/core/table/Table.svelte.d.ts +26 -0
  48. package/dist/core/table/index.d.ts +448 -0
  49. package/dist/core/table/index.js +1 -0
  50. package/dist/core/table-style-context.d.ts +66 -0
  51. package/dist/core/table-style-context.js +26 -0
  52. package/dist/factories/ColumnValidation.d.ts +49 -0
  53. package/dist/factories/ColumnValidation.js +188 -0
  54. package/dist/factories/TableColumns.d.ts +97 -0
  55. package/dist/factories/TableColumns.js +262 -0
  56. package/dist/factories/TypedColumnBuilder.d.ts +41 -0
  57. package/dist/factories/TypedColumnBuilder.js +72 -0
  58. package/dist/factories/index.d.ts +12 -0
  59. package/dist/factories/index.js +13 -0
  60. package/dist/features/HeaderMenu.svelte +236 -0
  61. package/dist/features/HeaderMenu.svelte.d.ts +8 -0
  62. package/dist/features/LiveUpdateBanner.svelte +66 -0
  63. package/dist/features/LiveUpdateBanner.svelte.d.ts +6 -0
  64. package/dist/features/SearchHighlight.svelte +21 -0
  65. package/dist/features/SearchHighlight.svelte.d.ts +8 -0
  66. package/dist/features/SmartFilterBar/ChipsField.svelte +104 -0
  67. package/dist/features/SmartFilterBar/ChipsField.svelte.d.ts +5 -0
  68. package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte +84 -0
  69. package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte.d.ts +3 -0
  70. package/dist/features/SmartFilterBar/FilterMenu.svelte +367 -0
  71. package/dist/features/SmartFilterBar/FilterMenu.svelte.d.ts +3 -0
  72. package/dist/features/SmartFilterBar/GroupingMenu.svelte +82 -0
  73. package/dist/features/SmartFilterBar/GroupingMenu.svelte.d.ts +3 -0
  74. package/dist/features/SmartFilterBar/SmartFilterBar.svelte +109 -0
  75. package/dist/features/SmartFilterBar/SmartFilterBar.svelte.d.ts +11 -0
  76. package/dist/features/SmartFilterBar/SummaryMenu.svelte +118 -0
  77. package/dist/features/SmartFilterBar/SummaryMenu.svelte.d.ts +3 -0
  78. package/dist/features/SummaryRow.svelte +97 -0
  79. package/dist/features/SummaryRow.svelte.d.ts +8 -0
  80. package/dist/features/index.d.ts +4 -0
  81. package/dist/features/index.js +4 -0
  82. package/dist/i18n/index.d.ts +366 -0
  83. package/dist/i18n/index.js +21 -0
  84. package/dist/index.d.ts +28 -0
  85. package/dist/index.js +41 -0
  86. package/dist/stores/TableStore.svelte.d.ts +192 -0
  87. package/dist/stores/TableStore.svelte.js +362 -0
  88. package/dist/stores/concerns/index.d.ts +15 -0
  89. package/dist/stores/concerns/index.js +14 -0
  90. package/dist/stores/concerns/types.d.ts +31 -0
  91. package/dist/stores/concerns/types.js +1 -0
  92. package/dist/stores/concerns/useColumnOrder.svelte.d.ts +16 -0
  93. package/dist/stores/concerns/useColumnOrder.svelte.js +81 -0
  94. package/dist/stores/concerns/useColumnVisibility.svelte.d.ts +16 -0
  95. package/dist/stores/concerns/useColumnVisibility.svelte.js +58 -0
  96. package/dist/stores/concerns/useExpansion.svelte.d.ts +9 -0
  97. package/dist/stores/concerns/useExpansion.svelte.js +32 -0
  98. package/dist/stores/concerns/useFiltering.svelte.d.ts +20 -0
  99. package/dist/stores/concerns/useFiltering.svelte.js +109 -0
  100. package/dist/stores/concerns/useFocusManagement.svelte.d.ts +15 -0
  101. package/dist/stores/concerns/useFocusManagement.svelte.js +52 -0
  102. package/dist/stores/concerns/useGrouping.svelte.d.ts +15 -0
  103. package/dist/stores/concerns/useGrouping.svelte.js +86 -0
  104. package/dist/stores/concerns/useLiveUpdates.svelte.d.ts +45 -0
  105. package/dist/stores/concerns/useLiveUpdates.svelte.js +175 -0
  106. package/dist/stores/concerns/usePagination.svelte.d.ts +18 -0
  107. package/dist/stores/concerns/usePagination.svelte.js +54 -0
  108. package/dist/stores/concerns/usePersistence.svelte.d.ts +36 -0
  109. package/dist/stores/concerns/usePersistence.svelte.js +167 -0
  110. package/dist/stores/concerns/useRemoteData.svelte.d.ts +21 -0
  111. package/dist/stores/concerns/useRemoteData.svelte.js +64 -0
  112. package/dist/stores/concerns/useSearch.svelte.d.ts +8 -0
  113. package/dist/stores/concerns/useSearch.svelte.js +16 -0
  114. package/dist/stores/concerns/useSelection.svelte.d.ts +21 -0
  115. package/dist/stores/concerns/useSelection.svelte.js +110 -0
  116. package/dist/stores/concerns/useSorting.svelte.d.ts +11 -0
  117. package/dist/stores/concerns/useSorting.svelte.js +70 -0
  118. package/dist/stores/concerns/useSummary.svelte.d.ts +18 -0
  119. package/dist/stores/concerns/useSummary.svelte.js +96 -0
  120. package/dist/stores/index.d.ts +1 -0
  121. package/dist/stores/index.js +1 -0
  122. package/dist/style/index.css +137 -0
  123. package/dist/style/index.d.ts +2 -0
  124. package/dist/style/index.js +2 -0
  125. package/dist/style/table-theme.css +131 -0
  126. package/dist/style/themes/comfortable.css +20 -0
  127. package/dist/style/themes/compact.css +20 -0
  128. package/dist/translations/de.d.ts +177 -0
  129. package/dist/translations/de.js +176 -0
  130. package/dist/translations/en.d.ts +177 -0
  131. package/dist/translations/en.js +176 -0
  132. package/dist/types/index.d.ts +1 -0
  133. package/dist/types/index.js +1 -0
  134. package/dist/types/tableTypes.d.ts +262 -0
  135. package/dist/types/tableTypes.js +1 -0
  136. package/dist/utils/index.d.ts +165 -0
  137. package/dist/utils/index.js +330 -0
  138. package/dist/utils/sticky-measure.d.ts +54 -0
  139. package/dist/utils/sticky-measure.js +107 -0
  140. package/dist/utils/virtualizer.d.ts +43 -0
  141. package/dist/utils/virtualizer.js +43 -0
  142. package/dist/variants/index.d.ts +11 -0
  143. package/dist/variants/index.js +15 -0
  144. package/dist/variants/table-cells.variants.d.ts +827 -0
  145. package/dist/variants/table-cells.variants.js +627 -0
  146. package/dist/variants/table-features.variants.d.ts +547 -0
  147. package/dist/variants/table-features.variants.js +412 -0
  148. package/dist/variants/table-states.variants.d.ts +594 -0
  149. package/dist/variants/table-states.variants.js +394 -0
  150. package/dist/variants/table.system.d.ts +301 -0
  151. package/dist/variants/table.system.js +314 -0
  152. package/dist/variants/table.variants.d.ts +428 -0
  153. package/dist/variants/table.variants.js +360 -0
  154. package/package.json +93 -0
@@ -0,0 +1,188 @@
1
+ import { resolveColumnId } from '../utils';
2
+ /**
3
+ * Validation utilities for column configurations.
4
+ *
5
+ * In the 2.x column shape, identification is the column's `id` (with a
6
+ * string-accessor fallback). Validation enforces that every column resolves
7
+ * to a non-empty id, that ids are unique within a set, and that no column
8
+ * fails the basic structural checks.
9
+ */
10
+ // biome-ignore lint/complexity/noStaticOnlyClass: intentional namespace grouping of column-validation utilities.
11
+ export class ColumnValidation {
12
+ /**
13
+ * Validates a single column configuration
14
+ */
15
+ static validateColumn(column) {
16
+ const errors = [];
17
+ // Identifier validation — `id` is the canonical identifier; for
18
+ // string-accessor columns it may be omitted (falls back to the accessor).
19
+ const id = resolveColumnId(column);
20
+ if (!id || typeof id !== 'string') {
21
+ errors.push('Column requires a non-empty `id` (or a string `accessor` to default the id from)');
22
+ }
23
+ // Accessor validation — synthetic columns omit it, but when present it
24
+ // must be either a string or a function.
25
+ if (column.accessor !== undefined) {
26
+ const accessorType = typeof column.accessor;
27
+ if (accessorType !== 'string' && accessorType !== 'function') {
28
+ errors.push('Column `accessor` must be a string property name or a function');
29
+ }
30
+ }
31
+ // Title validation — empty string is the documented idiom for icon-only
32
+ // columns (e.g. `{ id: 'actions', title: '', component: ActionButtons }`),
33
+ // so only the type is enforced, not non-emptiness.
34
+ if (typeof column.title !== 'string') {
35
+ errors.push("Column `title` must be a string (use '' for icon-only columns)");
36
+ }
37
+ // Width validation
38
+ if (column.width && typeof column.width !== 'string') {
39
+ errors.push('Column width must be a string');
40
+ }
41
+ // MinWidth validation
42
+ if (column.minWidth && typeof column.minWidth !== 'string') {
43
+ errors.push('Column minWidth must be a string');
44
+ }
45
+ // Priority validation
46
+ if (column.priority && ![1, 2, 3].includes(column.priority)) {
47
+ errors.push('Column priority must be 1, 2, or 3');
48
+ }
49
+ // Align validation
50
+ if (column.align && !['left', 'center', 'right'].includes(column.align)) {
51
+ errors.push('Column align must be left, center, or right');
52
+ }
53
+ // DataType validation (only meaningful on data columns)
54
+ const validDataTypes = ['text', 'number', 'date', 'boolean', 'email', 'url'];
55
+ if ('dataType' in column && column.dataType && !validDataTypes.includes(column.dataType)) {
56
+ errors.push(`Column dataType must be one of: ${validDataTypes.join(', ')}`);
57
+ }
58
+ return {
59
+ isValid: errors.length === 0,
60
+ errors
61
+ };
62
+ }
63
+ /**
64
+ * Validates an array of columns
65
+ */
66
+ static validateColumns(columns) {
67
+ const errors = [];
68
+ const ids = new Set();
69
+ // Check for empty array
70
+ if (!Array.isArray(columns) || columns.length === 0) {
71
+ errors.push('Columns array is required and must not be empty');
72
+ return { isValid: false, errors };
73
+ }
74
+ // Validate each column and check for duplicate ids
75
+ columns.forEach((column, index) => {
76
+ const validation = ColumnValidation.validateColumn(column);
77
+ if (!validation.isValid) {
78
+ errors.push(`Column ${index}: ${validation.errors.join(', ')}`);
79
+ }
80
+ const id = resolveColumnId(column);
81
+ if (id && ids.has(id)) {
82
+ errors.push(`Duplicate column id found: ${id}`);
83
+ }
84
+ else if (id) {
85
+ ids.add(id);
86
+ }
87
+ });
88
+ // Check for at least one visible column (priority 1 or 2)
89
+ const hasVisibleColumns = columns.some((col) => !col.priority || col.priority <= 2);
90
+ if (!hasVisibleColumns) {
91
+ errors.push('At least one column should have priority 1 or 2 for mobile visibility');
92
+ }
93
+ return {
94
+ isValid: errors.length === 0,
95
+ errors
96
+ };
97
+ }
98
+ /**
99
+ * Validates column configuration for specific use cases
100
+ */
101
+ static validateForUseCase(columns, useCase) {
102
+ const warnings = [];
103
+ switch (useCase) {
104
+ case 'mobile': {
105
+ // Check for too many priority 1 columns
106
+ const priority1Count = columns.filter((col) => col.priority === 1).length;
107
+ if (priority1Count > 2) {
108
+ warnings.push(`Mobile: ${priority1Count} priority 1 columns may cause horizontal scrolling`);
109
+ }
110
+ // Check for very wide columns
111
+ const wideColumns = columns.filter((col) => col.width?.includes('px') && parseInt(col.width, 10) > 200);
112
+ if (wideColumns.length > 0) {
113
+ warnings.push('Mobile: Some columns have fixed widths > 200px which may cause layout issues');
114
+ }
115
+ break;
116
+ }
117
+ case 'desktop': {
118
+ // Check for flex columns without proper balance
119
+ const flexColumns = columns.filter((col) => col.flex === true);
120
+ const fixedColumns = columns.filter((col) => col.width && !col.flex);
121
+ if (flexColumns.length === 0 && fixedColumns.length === columns.length) {
122
+ warnings.push('Desktop: Consider adding at least one flexible column for better space utilization');
123
+ }
124
+ break;
125
+ }
126
+ case 'print': {
127
+ // Check for interactive elements
128
+ const interactiveColumns = columns.filter((col) => resolveColumnId(col) === 'actions' || col.component);
129
+ if (interactiveColumns.length > 0) {
130
+ warnings.push('Print: Interactive columns may not display well in print media');
131
+ }
132
+ break;
133
+ }
134
+ }
135
+ return {
136
+ isValid: true,
137
+ warnings
138
+ };
139
+ }
140
+ }
141
+ /**
142
+ * Helper functions for common validation patterns
143
+ */
144
+ export const ValidationHelpers = {
145
+ /**
146
+ * Checks if columns are suitable for mobile display
147
+ */
148
+ isMobileFriendly: (columns) => {
149
+ const priority1Count = columns.filter((col) => col.priority === 1).length;
150
+ return priority1Count <= 2;
151
+ },
152
+ /**
153
+ * Gets recommended mobile columns (priority 1 and 2)
154
+ */
155
+ getMobileColumns: (columns) => {
156
+ return columns.filter((col) => !col.priority || col.priority <= 2);
157
+ },
158
+ /**
159
+ * Suggests improvements for column configuration
160
+ */
161
+ suggestImprovements: (columns) => {
162
+ const suggestions = [];
163
+ // Check for missing priorities
164
+ const noPriorityCount = columns.filter((col) => !col.priority).length;
165
+ if (noPriorityCount > 0) {
166
+ suggestions.push(`Consider adding priority to ${noPriorityCount} columns for better responsive behavior`);
167
+ }
168
+ // Check for actions column placement
169
+ const actionsColumn = columns.find((col) => resolveColumnId(col) === 'actions');
170
+ const actionsIndex = actionsColumn ? columns.indexOf(actionsColumn) : -1;
171
+ if (actionsColumn && actionsIndex !== columns.length - 1) {
172
+ suggestions.push('Actions column is typically placed as the last column');
173
+ }
174
+ // Check for sortable/searchable balance — synthetic columns are
175
+ // structurally excluded from both via the Column shape, so we only
176
+ // count data columns here.
177
+ const dataColumns = columns.filter((col) => col.accessor !== undefined);
178
+ const sortableCount = dataColumns.filter((col) => col.sortable !== false).length;
179
+ const searchableCount = dataColumns.filter((col) => col.searchable !== false).length;
180
+ if (sortableCount === 0) {
181
+ suggestions.push('Consider making at least one column sortable for better UX');
182
+ }
183
+ if (searchableCount === 0) {
184
+ suggestions.push('Consider making at least one column searchable for better UX');
185
+ }
186
+ return suggestions;
187
+ }
188
+ };
@@ -0,0 +1,97 @@
1
+ import type { Column, DataAccessor } from '../types/tableTypes';
2
+ import { type ActionButtonsProps } from '../cells/ActionButtons.svelte';
3
+ import { type CopyButtonProps } from '../cells/CopyButton.svelte';
4
+ import { type CustomCellProps } from '../cells/CustomCell.svelte';
5
+ import { type DateCellProps } from '../cells/DateCell.svelte';
6
+ import { type LinkCellProps } from '../cells/LinkCell.svelte';
7
+ import { type NumberCellProps } from '../cells/NumberCell.svelte';
8
+ import { type StatusBadgeProps } from '../cells/StatusBadge.svelte';
9
+ import { type UserAvatarProps } from '../cells/UserAvatar.svelte';
10
+ /** Common column configuration properties shared across all factory methods. */
11
+ type BaseColumnProps = {
12
+ sortable?: boolean;
13
+ searchable?: boolean;
14
+ groupable?: boolean;
15
+ summable?: boolean;
16
+ dataType?: 'text' | 'number' | 'date' | 'boolean' | 'email' | 'url';
17
+ priority?: 1 | 2 | 3;
18
+ align?: 'left' | 'center' | 'right';
19
+ width?: string;
20
+ minWidth?: string;
21
+ flex?: boolean;
22
+ };
23
+ type UserAvatarFactoryOptions<Item> = BaseColumnProps & Partial<Omit<UserAvatarProps<Item>, 'item'>>;
24
+ type ActionButtonsFactoryOptions<Item> = BaseColumnProps & Omit<ActionButtonsProps<Item>, 'item'>;
25
+ type StatusBadgeFactoryOptions<Item> = BaseColumnProps & Partial<Omit<StatusBadgeProps<Item>, 'item'>>;
26
+ type CopyButtonFactoryOptions<Item> = BaseColumnProps & Partial<Omit<CopyButtonProps<Item>, 'item'>>;
27
+ type CustomCellFactoryOptions<Item> = BaseColumnProps & Partial<Omit<CustomCellProps<Item>, 'item'>>;
28
+ type DateCellFactoryOptions<Item> = BaseColumnProps & Partial<Omit<DateCellProps<Item>, 'item'>>;
29
+ type LinkCellFactoryOptions<Item> = BaseColumnProps & Partial<Omit<LinkCellProps<Item>, 'item'>>;
30
+ type NumberCellFactoryOptions<Item> = BaseColumnProps & Partial<Omit<NumberCellProps<Item>, 'item'>>;
31
+ /**
32
+ * Column factory functions for common cell types.
33
+ *
34
+ * Data-column factories (everything except {@link TableColumns.actions}) bind
35
+ * the cell to a primitive-valued property via a string accessor. The
36
+ * property name doubles as the column id — search, sort, group and summary
37
+ * all read through the accessor, so values stay consistent across views.
38
+ *
39
+ * The synthetic {@link TableColumns.actions} factory produces a column
40
+ * without an accessor — by definition it is not searchable, sortable, or
41
+ * groupable, and the type system reflects that.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const columns = [
46
+ * TableColumns.userAvatar<User>('name', 'Employee'),
47
+ * TableColumns.status<User>('status'),
48
+ * TableColumns.number<User>('salary', 'Salary', { summable: true }),
49
+ * TableColumns.date<User>('hireDate', 'Hired'),
50
+ * TableColumns.actions<User>('', { onView: (item) => goto(`/users/${item.id}`) }),
51
+ * ];
52
+ * ```
53
+ */
54
+ export declare const TableColumns: {
55
+ /**
56
+ * Creates a UserAvatar column displaying an avatar alongside user name/email.
57
+ */
58
+ userAvatar: <Item>(accessor: DataAccessor<Item>, title?: string, options?: UserAvatarFactoryOptions<Item>) => Column<Item>;
59
+ /**
60
+ * Creates an ActionButtons column (view/edit/delete). Synthetic — no data
61
+ * accessor, structurally excluded from search/sort/group.
62
+ */
63
+ actions: <Item>(title?: string, options?: ActionButtonsFactoryOptions<Item>) => Column<Item>;
64
+ /**
65
+ * Creates a StatusBadge column displaying a colored badge. Centered, groupable.
66
+ */
67
+ status: <Item>(accessor: DataAccessor<Item>, title?: string, options?: StatusBadgeFactoryOptions<Item>) => Column<Item>;
68
+ /**
69
+ * Creates a CopyButton column with a click-to-copy button. Centered, non-sortable.
70
+ */
71
+ copy: <Item>(accessor: DataAccessor<Item>, title?: string, options?: CopyButtonFactoryOptions<Item>) => Column<Item>;
72
+ /**
73
+ * Creates a CustomCell column for generic text content with optional styling.
74
+ */
75
+ custom: <Item>(accessor: DataAccessor<Item>, title: string, options?: CustomCellFactoryOptions<Item>) => Column<Item>;
76
+ /**
77
+ * Creates a DateCell column with locale-aware date formatting.
78
+ */
79
+ date: <Item>(accessor: DataAccessor<Item>, title?: string, options?: DateCellFactoryOptions<Item>) => Column<Item>;
80
+ /**
81
+ * Creates a LinkCell column rendering an anchor tag.
82
+ */
83
+ link: <Item>(accessor: DataAccessor<Item>, title?: string, options?: LinkCellFactoryOptions<Item>) => Column<Item>;
84
+ /**
85
+ * Creates a NumberCell column with locale-aware number formatting.
86
+ */
87
+ number: <Item>(accessor: DataAccessor<Item>, title?: string, options?: NumberCellFactoryOptions<Item>) => Column<Item>;
88
+ /**
89
+ * Creates a plain text column with optional formatter. Auto-detects
90
+ * numeric-named accessors (e.g. `salary`, `amount`) and adjusts
91
+ * alignment/summability.
92
+ */
93
+ text: <Item>(accessor: DataAccessor<Item>, title: string, options?: BaseColumnProps & {
94
+ formatter?: (value: unknown, item: Item) => string;
95
+ }) => Column<Item>;
96
+ };
97
+ export type { ActionButtonsFactoryOptions, BaseColumnProps, CopyButtonFactoryOptions, CustomCellFactoryOptions, DateCellFactoryOptions, LinkCellFactoryOptions, NumberCellFactoryOptions, StatusBadgeFactoryOptions, UserAvatarFactoryOptions };
@@ -0,0 +1,262 @@
1
+ import ActionButtons, {} from '../cells/ActionButtons.svelte';
2
+ import CopyButton, {} from '../cells/CopyButton.svelte';
3
+ import CustomCell, {} from '../cells/CustomCell.svelte';
4
+ import DateCell, {} from '../cells/DateCell.svelte';
5
+ import LinkCell, {} from '../cells/LinkCell.svelte';
6
+ import NumberCell, {} from '../cells/NumberCell.svelte';
7
+ import StatusBadge, {} from '../cells/StatusBadge.svelte';
8
+ import UserAvatar, {} from '../cells/UserAvatar.svelte';
9
+ // ===================================================================
10
+ // Factory implementation
11
+ // ===================================================================
12
+ /**
13
+ * Column factory functions for common cell types.
14
+ *
15
+ * Data-column factories (everything except {@link TableColumns.actions}) bind
16
+ * the cell to a primitive-valued property via a string accessor. The
17
+ * property name doubles as the column id — search, sort, group and summary
18
+ * all read through the accessor, so values stay consistent across views.
19
+ *
20
+ * The synthetic {@link TableColumns.actions} factory produces a column
21
+ * without an accessor — by definition it is not searchable, sortable, or
22
+ * groupable, and the type system reflects that.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const columns = [
27
+ * TableColumns.userAvatar<User>('name', 'Employee'),
28
+ * TableColumns.status<User>('status'),
29
+ * TableColumns.number<User>('salary', 'Salary', { summable: true }),
30
+ * TableColumns.date<User>('hireDate', 'Hired'),
31
+ * TableColumns.actions<User>('', { onView: (item) => goto(`/users/${item.id}`) }),
32
+ * ];
33
+ * ```
34
+ */
35
+ export const TableColumns = {
36
+ /**
37
+ * Creates a UserAvatar column displaying an avatar alongside user name/email.
38
+ */
39
+ userAvatar: (accessor, title = 'User', options = {}) => {
40
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
41
+ return {
42
+ accessor,
43
+ title,
44
+ component: UserAvatar,
45
+ componentProps: (item) => ({
46
+ item,
47
+ ...componentProps
48
+ }),
49
+ sortable: sortable ?? true,
50
+ searchable: searchable ?? true,
51
+ groupable: groupable ?? true,
52
+ summable: summable ?? false,
53
+ dataType: dataType ?? 'text',
54
+ priority,
55
+ align,
56
+ width,
57
+ minWidth,
58
+ flex
59
+ };
60
+ },
61
+ /**
62
+ * Creates an ActionButtons column (view/edit/delete). Synthetic — no data
63
+ * accessor, structurally excluded from search/sort/group.
64
+ */
65
+ actions: (title = 'Actions', options = {}) => {
66
+ const { priority, align, width, minWidth, flex, ...componentProps } = options;
67
+ return {
68
+ id: 'actions',
69
+ title,
70
+ component: ActionButtons,
71
+ componentProps: (item) => ({
72
+ item,
73
+ ...componentProps
74
+ }),
75
+ priority,
76
+ align: align ?? 'right',
77
+ width: width ?? '120px',
78
+ minWidth,
79
+ flex
80
+ };
81
+ },
82
+ /**
83
+ * Creates a StatusBadge column displaying a colored badge. Centered, groupable.
84
+ */
85
+ status: (accessor, title = 'Status', options = {}) => {
86
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
87
+ return {
88
+ accessor,
89
+ title,
90
+ component: StatusBadge,
91
+ componentProps: (item) => ({
92
+ item,
93
+ statusKey: accessor,
94
+ ...componentProps
95
+ }),
96
+ sortable: sortable ?? true,
97
+ searchable: searchable ?? true,
98
+ groupable: groupable ?? true,
99
+ summable: summable ?? false,
100
+ dataType: dataType ?? 'text',
101
+ priority,
102
+ align: align ?? 'center',
103
+ width: width ?? '100px',
104
+ minWidth,
105
+ flex
106
+ };
107
+ },
108
+ /**
109
+ * Creates a CopyButton column with a click-to-copy button. Centered, non-sortable.
110
+ */
111
+ copy: (accessor, title = 'Copy', options = {}) => {
112
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
113
+ return {
114
+ accessor,
115
+ title,
116
+ component: CopyButton,
117
+ componentProps: (item) => ({
118
+ item,
119
+ valueKey: accessor,
120
+ ...componentProps
121
+ }),
122
+ sortable: sortable ?? false,
123
+ searchable: searchable ?? false,
124
+ groupable: groupable ?? false,
125
+ summable: summable ?? false,
126
+ dataType: dataType ?? 'text',
127
+ priority,
128
+ align: align ?? 'center',
129
+ width: width ?? '100px',
130
+ minWidth,
131
+ flex
132
+ };
133
+ },
134
+ /**
135
+ * Creates a CustomCell column for generic text content with optional styling.
136
+ */
137
+ custom: (accessor, title, options = {}) => {
138
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
139
+ return {
140
+ accessor,
141
+ title,
142
+ component: CustomCell,
143
+ componentProps: (item) => ({
144
+ item,
145
+ ...componentProps
146
+ }),
147
+ sortable: sortable ?? true,
148
+ searchable: searchable ?? true,
149
+ groupable: groupable ?? false,
150
+ summable: summable ?? false,
151
+ dataType: dataType ?? 'text',
152
+ priority,
153
+ align,
154
+ width,
155
+ minWidth,
156
+ flex
157
+ };
158
+ },
159
+ /**
160
+ * Creates a DateCell column with locale-aware date formatting.
161
+ */
162
+ date: (accessor, title = 'Date', options = {}) => {
163
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
164
+ return {
165
+ accessor,
166
+ title,
167
+ component: DateCell,
168
+ componentProps: (item) => ({
169
+ item,
170
+ dateKey: accessor,
171
+ ...componentProps
172
+ }),
173
+ sortable: sortable ?? true,
174
+ searchable: searchable ?? true,
175
+ groupable: groupable ?? true,
176
+ summable: summable ?? false,
177
+ dataType: dataType ?? 'date',
178
+ priority,
179
+ align,
180
+ width,
181
+ minWidth,
182
+ flex
183
+ };
184
+ },
185
+ /**
186
+ * Creates a LinkCell column rendering an anchor tag.
187
+ */
188
+ link: (accessor, title = 'Link', options = {}) => {
189
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
190
+ return {
191
+ accessor,
192
+ title,
193
+ component: LinkCell,
194
+ componentProps: (item) => ({
195
+ item,
196
+ urlKey: accessor,
197
+ ...componentProps
198
+ }),
199
+ sortable: sortable ?? true,
200
+ searchable: searchable ?? true,
201
+ groupable: groupable ?? false,
202
+ summable: summable ?? false,
203
+ dataType: dataType ?? 'url',
204
+ priority,
205
+ align,
206
+ width,
207
+ minWidth,
208
+ flex
209
+ };
210
+ },
211
+ /**
212
+ * Creates a NumberCell column with locale-aware number formatting.
213
+ */
214
+ number: (accessor, title = 'Number', options = {}) => {
215
+ const { sortable, searchable, groupable, summable, dataType, priority, align, width, minWidth, flex, ...componentProps } = options;
216
+ return {
217
+ accessor,
218
+ title,
219
+ component: NumberCell,
220
+ componentProps: (item) => ({
221
+ item,
222
+ valueKey: accessor,
223
+ ...componentProps
224
+ }),
225
+ sortable: sortable ?? true,
226
+ searchable: searchable ?? true,
227
+ groupable: groupable ?? true,
228
+ summable: summable ?? true,
229
+ dataType: dataType ?? 'number',
230
+ priority,
231
+ align: align ?? 'right',
232
+ width,
233
+ minWidth,
234
+ flex
235
+ };
236
+ },
237
+ /**
238
+ * Creates a plain text column with optional formatter. Auto-detects
239
+ * numeric-named accessors (e.g. `salary`, `amount`) and adjusts
240
+ * alignment/summability.
241
+ */
242
+ text: (accessor, title, options = {}) => {
243
+ const { formatter, ...columnProps } = options;
244
+ const accessorStr = String(accessor);
245
+ const isNumericKey = /^(age|salary|price|amount|count|number|projectsCompleted|rating|score)$/i.test(accessorStr);
246
+ return {
247
+ accessor,
248
+ title,
249
+ formatter,
250
+ sortable: columnProps.sortable ?? true,
251
+ searchable: columnProps.searchable ?? true,
252
+ summable: columnProps.summable ?? (columnProps.dataType === 'number' || isNumericKey),
253
+ dataType: columnProps.dataType ?? (isNumericKey ? 'number' : 'text'),
254
+ groupable: columnProps.groupable ?? true,
255
+ priority: columnProps.priority,
256
+ align: columnProps.align ?? (isNumericKey ? 'right' : 'left'),
257
+ width: columnProps.width,
258
+ minWidth: columnProps.minWidth,
259
+ flex: columnProps.flex
260
+ };
261
+ }
262
+ };
@@ -0,0 +1,41 @@
1
+ import type { ActionButtonsFactoryOptions, BaseColumnProps, CopyButtonFactoryOptions, CustomCellFactoryOptions, DateCellFactoryOptions, LinkCellFactoryOptions, NumberCellFactoryOptions, StatusBadgeFactoryOptions, UserAvatarFactoryOptions } from '..';
2
+ import type { Column, DataAccessor } from '../types/tableTypes';
3
+ /**
4
+ * Typed column builder providing a fluent API for defining table columns.
5
+ * The generic parameter `Item` constrains accessor arguments to primitive
6
+ * properties of the data type, mirroring the runtime constraint that
7
+ * search/sort/group only meaningfully operate on stringifiable values.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * interface User { id: number; name: string; status: string; salary: number; }
12
+ *
13
+ * const columns = TypedColumnBuilder.for<User>()
14
+ * .userAvatar('name', 'Employee')
15
+ * .status('status')
16
+ * .number('salary', 'Salary')
17
+ * .actions()
18
+ * .build();
19
+ * ```
20
+ */
21
+ export declare class TypedColumnBuilder<Item> {
22
+ private columns;
23
+ userAvatar(accessor: DataAccessor<Item>, title?: string, options?: UserAvatarFactoryOptions<Item>): this;
24
+ actions(title?: string, options?: ActionButtonsFactoryOptions<Item>): this;
25
+ status(accessor: DataAccessor<Item>, title?: string, options?: StatusBadgeFactoryOptions<Item>): this;
26
+ copy(accessor: DataAccessor<Item>, title?: string, options?: CopyButtonFactoryOptions<Item>): this;
27
+ customCell(accessor: DataAccessor<Item>, title: string, options?: CustomCellFactoryOptions<Item>): this;
28
+ date(accessor: DataAccessor<Item>, title?: string, options?: DateCellFactoryOptions<Item>): this;
29
+ link(accessor: DataAccessor<Item>, title?: string, options?: LinkCellFactoryOptions<Item>): this;
30
+ number(accessor: DataAccessor<Item>, title?: string, options?: NumberCellFactoryOptions<Item>): this;
31
+ text(accessor: DataAccessor<Item>, title: string, options?: BaseColumnProps & {
32
+ formatter?: (value: unknown, item: Item) => string;
33
+ }): this;
34
+ custom(column: Column<Item>): this;
35
+ /**
36
+ * Returns the accumulated `Column<Item>[]` directly. Pair with the generic
37
+ * `Table<Item>` component to keep type-safety end-to-end without `as` casts.
38
+ */
39
+ build(): Column<Item>[];
40
+ static for<T>(): TypedColumnBuilder<T>;
41
+ }
@@ -0,0 +1,72 @@
1
+ import { TableColumns } from '..';
2
+ /**
3
+ * Typed column builder providing a fluent API for defining table columns.
4
+ * The generic parameter `Item` constrains accessor arguments to primitive
5
+ * properties of the data type, mirroring the runtime constraint that
6
+ * search/sort/group only meaningfully operate on stringifiable values.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * interface User { id: number; name: string; status: string; salary: number; }
11
+ *
12
+ * const columns = TypedColumnBuilder.for<User>()
13
+ * .userAvatar('name', 'Employee')
14
+ * .status('status')
15
+ * .number('salary', 'Salary')
16
+ * .actions()
17
+ * .build();
18
+ * ```
19
+ */
20
+ export class TypedColumnBuilder {
21
+ columns = [];
22
+ userAvatar(accessor, title = 'User', options = {}) {
23
+ this.columns.push(TableColumns.userAvatar(accessor, title, options));
24
+ return this;
25
+ }
26
+ actions(title = 'Actions', options = {}) {
27
+ this.columns.push(TableColumns.actions(title, options));
28
+ return this;
29
+ }
30
+ status(accessor, title = 'Status', options = {}) {
31
+ this.columns.push(TableColumns.status(accessor, title, options));
32
+ return this;
33
+ }
34
+ copy(accessor, title = 'Copy', options = {}) {
35
+ this.columns.push(TableColumns.copy(accessor, title, options));
36
+ return this;
37
+ }
38
+ customCell(accessor, title, options = {}) {
39
+ this.columns.push(TableColumns.custom(accessor, title, options));
40
+ return this;
41
+ }
42
+ date(accessor, title = 'Date', options = {}) {
43
+ this.columns.push(TableColumns.date(accessor, title, options));
44
+ return this;
45
+ }
46
+ link(accessor, title = 'Link', options = {}) {
47
+ this.columns.push(TableColumns.link(accessor, title, options));
48
+ return this;
49
+ }
50
+ number(accessor, title = 'Number', options = {}) {
51
+ this.columns.push(TableColumns.number(accessor, title, options));
52
+ return this;
53
+ }
54
+ text(accessor, title, options = {}) {
55
+ this.columns.push(TableColumns.text(accessor, title, options));
56
+ return this;
57
+ }
58
+ custom(column) {
59
+ this.columns.push(column);
60
+ return this;
61
+ }
62
+ /**
63
+ * Returns the accumulated `Column<Item>[]` directly. Pair with the generic
64
+ * `Table<Item>` component to keep type-safety end-to-end without `as` casts.
65
+ */
66
+ build() {
67
+ return [...this.columns];
68
+ }
69
+ static for() {
70
+ return new TypedColumnBuilder();
71
+ }
72
+ }
@@ -0,0 +1,12 @@
1
+ export { default as ActionButtons } from '../cells/ActionButtons.svelte';
2
+ export { default as CopyButton } from '../cells/CopyButton.svelte';
3
+ export { default as CustomCell } from '../cells/CustomCell.svelte';
4
+ export { default as DateCell } from '../cells/DateCell.svelte';
5
+ export { default as LinkCell } from '../cells/LinkCell.svelte';
6
+ export { default as NumberCell } from '../cells/NumberCell.svelte';
7
+ export { default as StatusBadge } from '../cells/StatusBadge.svelte';
8
+ export { default as UserAvatar } from '../cells/UserAvatar.svelte';
9
+ export { ColumnValidation, ValidationHelpers } from './ColumnValidation';
10
+ export type { ActionButtonsFactoryOptions, BaseColumnProps, CopyButtonFactoryOptions, CustomCellFactoryOptions, DateCellFactoryOptions, LinkCellFactoryOptions, NumberCellFactoryOptions, StatusBadgeFactoryOptions, UserAvatarFactoryOptions } from './TableColumns';
11
+ export { TableColumns } from './TableColumns';
12
+ export { TypedColumnBuilder } from './TypedColumnBuilder';