@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,136 @@
1
+ <script lang="ts" generics="Item">
2
+ import type { Component, Snippet } from 'svelte';
3
+ import { useTableI18n } from '../i18n';
4
+ import { customCellVariants, type CustomCellVariantProps } from '../variants';
5
+
6
+ const tt = useTableI18n();
7
+
8
+ export type CustomCellProps<Item> = {
9
+ item: Item;
10
+ content?: (item: Item) => string;
11
+ component?: Component<Record<string, unknown>>;
12
+ componentProps?: Record<string, unknown>;
13
+ children?: Snippet<[item: Item]>;
14
+ class?: string;
15
+ style?: string;
16
+ align?: CustomCellVariantProps['align'];
17
+ wrap?: CustomCellVariantProps['wrap'];
18
+ truncate?: CustomCellVariantProps['truncate'];
19
+ interactive?: CustomCellVariantProps['interactive'];
20
+ size?: CustomCellVariantProps['size'];
21
+ onClick?: (item: Item) => void;
22
+ onDoubleClick?: (item: Item) => void;
23
+ title?: string | ((item: Item) => string);
24
+ testId?: string;
25
+ };
26
+
27
+ let {
28
+ item,
29
+ content = undefined,
30
+ component = undefined,
31
+ componentProps = {},
32
+ class: className = '',
33
+ style = '',
34
+ align = 'left',
35
+ wrap = false,
36
+ truncate = true,
37
+ interactive = false,
38
+ size = 'md',
39
+ onClick = undefined,
40
+ onDoubleClick = undefined,
41
+ title = undefined,
42
+ testId = undefined,
43
+ children = undefined
44
+ }: CustomCellProps<Item> = $props();
45
+
46
+ // Compute dynamic properties
47
+ const isClickable = $derived(Boolean(onClick || onDoubleClick || interactive));
48
+
49
+ const computedTitle = $derived.by(() => {
50
+ if (typeof title === 'function') {
51
+ return title(item);
52
+ }
53
+ return title;
54
+ });
55
+
56
+ const computedTestId = $derived.by(() => {
57
+ if (testId) return testId;
58
+ if (item && typeof item === 'object' && 'id' in item) {
59
+ return `custom-cell-${item.id}`;
60
+ }
61
+ return 'custom-cell';
62
+ });
63
+
64
+ // Tailwind-Variants styling
65
+ const styles = $derived(
66
+ customCellVariants({
67
+ align,
68
+ wrap,
69
+ truncate,
70
+ interactive: isClickable,
71
+ size
72
+ })
73
+ );
74
+
75
+ // Event handlers
76
+ function handleClick(event: MouseEvent) {
77
+ if (onClick) {
78
+ event.stopPropagation();
79
+ onClick(item);
80
+ }
81
+ }
82
+
83
+ function handleDoubleClick(event: MouseEvent) {
84
+ if (onDoubleClick) {
85
+ event.stopPropagation();
86
+ onDoubleClick(item);
87
+ }
88
+ }
89
+
90
+ function handleKeyDown(event: KeyboardEvent) {
91
+ if (isClickable && (event.key === 'Enter' || event.key === ' ')) {
92
+ event.preventDefault();
93
+ if (onClick) {
94
+ onClick(item);
95
+ }
96
+ }
97
+ }
98
+ </script>
99
+
100
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
101
+ <div
102
+ class="{styles.container()} {className}"
103
+ {style}
104
+ title={computedTitle}
105
+ data-testid={computedTestId}
106
+ onclick={handleClick}
107
+ ondblclick={handleDoubleClick}
108
+ onkeydown={handleKeyDown}
109
+ role={isClickable ? 'button' : undefined}
110
+ tabindex={isClickable ? 0 : undefined}
111
+ aria-label={isClickable ? tt('aria.interactiveCell') : undefined}
112
+ >
113
+ <div class={styles.content()}>
114
+ {#if component}
115
+ <!-- Svelte Component Rendering -->
116
+ {@const Component = component}
117
+ <Component {item} {...componentProps} />
118
+ {:else if content}
119
+ <!-- HTML Content Function — consumers opt into this API explicitly
120
+ and are responsible for sanitising the output; the `content`
121
+ callback is their authoring surface. -->
122
+ <div class={styles.text()}>
123
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
124
+ {@html content(item)}
125
+ </div>
126
+ {:else if children}
127
+ <!-- Snippet-based Content -->
128
+ {@render children(item)}
129
+ {:else}
130
+ <!-- Default fallback content -->
131
+ <span class={styles.fallback()}>
132
+ {JSON.stringify(item)}
133
+ </span>
134
+ {/if}
135
+ </div>
136
+ </div>
@@ -0,0 +1,44 @@
1
+ import type { Component, Snippet } from 'svelte';
2
+ import { type CustomCellVariantProps } from '../variants';
3
+ export type CustomCellProps<Item> = {
4
+ item: Item;
5
+ content?: (item: Item) => string;
6
+ component?: Component<Record<string, unknown>>;
7
+ componentProps?: Record<string, unknown>;
8
+ children?: Snippet<[item: Item]>;
9
+ class?: string;
10
+ style?: string;
11
+ align?: CustomCellVariantProps['align'];
12
+ wrap?: CustomCellVariantProps['wrap'];
13
+ truncate?: CustomCellVariantProps['truncate'];
14
+ interactive?: CustomCellVariantProps['interactive'];
15
+ size?: CustomCellVariantProps['size'];
16
+ onClick?: (item: Item) => void;
17
+ onDoubleClick?: (item: Item) => void;
18
+ title?: string | ((item: Item) => string);
19
+ testId?: string;
20
+ };
21
+ declare function $$render<Item>(): {
22
+ props: CustomCellProps<Item>;
23
+ exports: {};
24
+ bindings: "";
25
+ slots: {};
26
+ events: {};
27
+ };
28
+ declare class __sveltets_Render<Item> {
29
+ props(): ReturnType<typeof $$render<Item>>['props'];
30
+ events(): ReturnType<typeof $$render<Item>>['events'];
31
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
32
+ bindings(): "";
33
+ exports(): {};
34
+ }
35
+ interface $$IsomorphicComponent {
36
+ new <Item>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Item>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Item>['props']>, ReturnType<__sveltets_Render<Item>['events']>, ReturnType<__sveltets_Render<Item>['slots']>> & {
37
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
38
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
39
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
40
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
41
+ }
42
+ declare const CustomCell: $$IsomorphicComponent;
43
+ type CustomCell<Item> = InstanceType<typeof CustomCell<Item>>;
44
+ export default CustomCell;
@@ -0,0 +1,194 @@
1
+ <script lang="ts" generics="Item">
2
+ import { dateCellVariants, type DateCellVariantProps } from '../variants';
3
+
4
+ export type DateCellProps<Item> = {
5
+ item: Item;
6
+ dateKey?: keyof Item;
7
+ format?: 'short' | 'medium' | 'long' | 'relative' | 'datetime';
8
+ customFormat?: (date: Date) => string;
9
+ locale?: string;
10
+ showTime?: boolean;
11
+ timezone?: string;
12
+ fallback?: string;
13
+ onClick?: (item: Item, date: Date | null) => void;
14
+ className?: string;
15
+ testId?: string;
16
+ size?: DateCellVariantProps['size'];
17
+ };
18
+
19
+ let {
20
+ item,
21
+ dateKey = undefined,
22
+ format = 'medium',
23
+ customFormat = undefined,
24
+ locale = undefined as string | undefined,
25
+ showTime = false,
26
+ timezone = undefined,
27
+ fallback = '—',
28
+ onClick = undefined,
29
+ className = '',
30
+ testId = undefined,
31
+ size = 'md'
32
+ }: DateCellProps<Item> = $props();
33
+
34
+ // Extract date value from item
35
+ const extractDate = (item: Item, key?: keyof Item): Date | null => {
36
+ if (!key) return null;
37
+
38
+ const value = item[key];
39
+ if (!value) return null;
40
+
41
+ if (value instanceof Date) return value;
42
+ if (typeof value === 'string' || typeof value === 'number') {
43
+ const parsed = new Date(value);
44
+ return isNaN(parsed.getTime()) ? null : parsed;
45
+ }
46
+
47
+ return null;
48
+ };
49
+
50
+ // Get the date value
51
+ const dateValue = $derived.by(() => extractDate(item, dateKey));
52
+
53
+ // Check if date is clickable
54
+ const isClickable = $derived(Boolean(onClick && dateValue));
55
+
56
+ // Format the date based on options
57
+ const formatDate = (date: Date | null): string => {
58
+ if (!date) return fallback;
59
+
60
+ try {
61
+ // Custom formatter takes precedence
62
+ if (customFormat) {
63
+ return customFormat(date);
64
+ }
65
+
66
+ // Built-in format options
67
+ const options: Intl.DateTimeFormatOptions = {};
68
+
69
+ switch (format) {
70
+ case 'short':
71
+ options.dateStyle = 'short';
72
+ break;
73
+ case 'medium':
74
+ options.dateStyle = 'medium';
75
+ break;
76
+ case 'long':
77
+ options.dateStyle = 'long';
78
+ break;
79
+ case 'relative':
80
+ return formatRelativeDate(date);
81
+ default:
82
+ options.dateStyle = 'medium';
83
+ }
84
+
85
+ if (showTime) {
86
+ options.timeStyle = 'short';
87
+ }
88
+
89
+ if (timezone) {
90
+ options.timeZone = timezone;
91
+ }
92
+
93
+ return new Intl.DateTimeFormat(locale, options).format(date);
94
+ } catch (error) {
95
+ console.warn('DateCell: Error formatting date', error);
96
+ return fallback;
97
+ }
98
+ };
99
+
100
+ const formatRelativeDate = (date: Date): string => {
101
+ const now = new Date();
102
+ const diffMs = now.getTime() - date.getTime();
103
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
104
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
105
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
106
+
107
+ if (Math.abs(diffDays) > 7) {
108
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'short' }).format(date);
109
+ }
110
+
111
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
112
+
113
+ if (diffMs < 0) {
114
+ const futureDays = Math.abs(diffDays);
115
+ const futureHours = Math.abs(diffHours);
116
+ const futureMinutes = Math.abs(diffMinutes);
117
+
118
+ if (futureDays > 0) return rtf.format(futureDays, 'day');
119
+ if (futureHours > 0) return rtf.format(futureHours, 'hour');
120
+ if (futureMinutes > 0) return rtf.format(futureMinutes, 'minute');
121
+ return rtf.format(0, 'second');
122
+ }
123
+
124
+ if (diffDays >= 1) return rtf.format(-diffDays, 'day');
125
+ if (diffHours >= 1) return rtf.format(-diffHours, 'hour');
126
+ if (diffMinutes >= 1) return rtf.format(-diffMinutes, 'minute');
127
+ return rtf.format(0, 'second');
128
+ };
129
+
130
+ // Get formatted date string
131
+ const formattedDate = $derived.by(() => formatDate(dateValue));
132
+
133
+ // Generate tooltip with full date info
134
+ const tooltipText = $derived.by(() => {
135
+ const date = dateValue;
136
+ if (!date) return undefined;
137
+
138
+ const fullFormat = new Intl.DateTimeFormat(locale, {
139
+ dateStyle: 'full',
140
+ timeStyle: 'medium',
141
+ timeZone: timezone
142
+ }).format(date);
143
+
144
+ return fullFormat;
145
+ });
146
+
147
+ const tooltipValue = $derived(tooltipText);
148
+
149
+ // Generate test ID
150
+ const computedTestId = $derived.by(() => {
151
+ if (testId) return testId;
152
+ if (item && typeof item === 'object' && 'id' in item) {
153
+ return `date-cell-${item.id}-${String(dateKey)}`;
154
+ }
155
+ return undefined;
156
+ });
157
+
158
+ // TV Styles
159
+ const styles = $derived(dateCellVariants({ interactive: isClickable, size, format }));
160
+
161
+ // Event handlers
162
+ function handleClick(event: MouseEvent) {
163
+ const date = dateValue;
164
+ if (onClick && date) {
165
+ event.stopPropagation();
166
+ onClick(item, date);
167
+ }
168
+ }
169
+
170
+ function handleKeyDown(event: KeyboardEvent) {
171
+ if (isClickable && (event.key === 'Enter' || event.key === ' ')) {
172
+ event.preventDefault();
173
+ const date = dateValue;
174
+ if (onClick && date) {
175
+ onClick(item, date);
176
+ }
177
+ }
178
+ }
179
+ </script>
180
+
181
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
182
+ <div
183
+ class="{styles.container()} {className}"
184
+ title={tooltipValue}
185
+ data-testid={computedTestId}
186
+ onclick={handleClick}
187
+ onkeydown={handleKeyDown}
188
+ role={isClickable ? 'button' : undefined}
189
+ tabindex={isClickable ? 0 : undefined}
190
+ >
191
+ <time datetime={dateValue?.toISOString()} class={styles.date()}>
192
+ {formattedDate}
193
+ </time>
194
+ </div>
@@ -0,0 +1,39 @@
1
+ import { type DateCellVariantProps } from '../variants';
2
+ export type DateCellProps<Item> = {
3
+ item: Item;
4
+ dateKey?: keyof Item;
5
+ format?: 'short' | 'medium' | 'long' | 'relative' | 'datetime';
6
+ customFormat?: (date: Date) => string;
7
+ locale?: string;
8
+ showTime?: boolean;
9
+ timezone?: string;
10
+ fallback?: string;
11
+ onClick?: (item: Item, date: Date | null) => void;
12
+ className?: string;
13
+ testId?: string;
14
+ size?: DateCellVariantProps['size'];
15
+ };
16
+ declare function $$render<Item>(): {
17
+ props: DateCellProps<Item>;
18
+ exports: {};
19
+ bindings: "";
20
+ slots: {};
21
+ events: {};
22
+ };
23
+ declare class __sveltets_Render<Item> {
24
+ props(): ReturnType<typeof $$render<Item>>['props'];
25
+ events(): ReturnType<typeof $$render<Item>>['events'];
26
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
27
+ bindings(): "";
28
+ exports(): {};
29
+ }
30
+ interface $$IsomorphicComponent {
31
+ new <Item>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Item>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Item>['props']>, ReturnType<__sveltets_Render<Item>['events']>, ReturnType<__sveltets_Render<Item>['slots']>> & {
32
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
33
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
34
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
35
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
36
+ }
37
+ declare const DateCell: $$IsomorphicComponent;
38
+ type DateCell<Item> = InstanceType<typeof DateCell<Item>>;
39
+ export default DateCell;
@@ -0,0 +1,240 @@
1
+ <script lang="ts" generics="Item">
2
+ import { useTableI18n } from '../i18n';
3
+ import { linkCellVariants, type LinkCellVariantProps } from '../variants';
4
+
5
+ const tt = useTableI18n();
6
+
7
+ export type LinkCellProps<Item> = {
8
+ item: Item;
9
+ href?: string | ((item: Item) => string);
10
+ urlKey?: keyof Item;
11
+ text?: string | ((item: Item) => string);
12
+ textKey?: keyof Item;
13
+ target?: '_blank' | '_self' | '_parent' | '_top';
14
+ external?: boolean;
15
+ download?: boolean | string;
16
+ rel?: string;
17
+ onClick?: (item: Item, event: MouseEvent) => void;
18
+ fallback?: string;
19
+ maxLength?: number;
20
+ showIcon?: boolean;
21
+ className?: string;
22
+ testId?: string;
23
+ } & LinkCellVariantProps;
24
+
25
+ // Props with sensible defaults
26
+ let {
27
+ item,
28
+ href = undefined,
29
+ urlKey = undefined,
30
+ text = undefined,
31
+ textKey = undefined,
32
+ target = '_self',
33
+ external = false,
34
+ download = false,
35
+ rel = undefined,
36
+ onClick = undefined,
37
+ fallback = '—',
38
+ maxLength = undefined,
39
+ showIcon = true,
40
+ className = '',
41
+ testId = undefined,
42
+ size = 'md',
43
+ variant = 'default',
44
+ disabled = false
45
+ }: LinkCellProps<Item> = $props();
46
+
47
+ // Extract URL from item or prop
48
+ const extractUrl = (item: Item): string | null => {
49
+ if (typeof href === 'function') {
50
+ return href(item);
51
+ }
52
+ if (typeof href === 'string') {
53
+ return href;
54
+ }
55
+ if (urlKey) {
56
+ const value = item[urlKey];
57
+ return typeof value === 'string' ? value : null;
58
+ }
59
+ return null;
60
+ };
61
+
62
+ // Extract display text from item or prop
63
+ const extractText = (item: Item): string => {
64
+ if (typeof text === 'function') {
65
+ return text(item);
66
+ }
67
+ if (typeof text === 'string') {
68
+ return text;
69
+ }
70
+ if (textKey) {
71
+ const value = item[textKey];
72
+ if (typeof value === 'string') return value;
73
+ }
74
+
75
+ // Fallback to URL if no text specified
76
+ const url = extractUrl(item);
77
+ if (url) {
78
+ try {
79
+ const urlObj = new URL(url);
80
+ return urlObj.hostname + urlObj.pathname;
81
+ } catch {
82
+ return url;
83
+ }
84
+ }
85
+
86
+ return fallback;
87
+ };
88
+
89
+ // Get computed values
90
+ const computedHref = $derived(() => extractUrl(item));
91
+ const computedText = $derived(() => extractText(item));
92
+
93
+ // Determine if link is valid
94
+ const isValidLink = $derived(() => {
95
+ const url = computedHref();
96
+ return url && url.trim().length > 0;
97
+ });
98
+
99
+ // Determine if link should open externally
100
+ const isExternal = $derived(() => {
101
+ if (external) return true;
102
+ if (target === '_blank') return true;
103
+
104
+ const url = computedHref();
105
+ if (!url) return false;
106
+
107
+ try {
108
+ const urlObj = new URL(url);
109
+ return urlObj.origin !== window.location.origin;
110
+ } catch {
111
+ return false;
112
+ }
113
+ });
114
+
115
+ // Compute rel attribute
116
+ const computedRel = $derived(() => {
117
+ if (rel) return rel;
118
+ if (isExternal()) return 'noopener noreferrer';
119
+ return undefined;
120
+ });
121
+
122
+ // Truncate text if maxLength is specified
123
+ const displayText = $derived.by(() => {
124
+ const text = computedText();
125
+ if (!maxLength || text.length <= maxLength) {
126
+ return text;
127
+ }
128
+ return text.substring(0, maxLength - 3) + '...';
129
+ });
130
+
131
+ // Generate tooltip
132
+ const tooltipText = $derived.by(() => {
133
+ const url = computedHref();
134
+ const text = computedText();
135
+
136
+ if (!url) return undefined;
137
+
138
+ if (maxLength && text.length > maxLength) {
139
+ return `${text}\n${url}`;
140
+ }
141
+
142
+ if (text !== url) {
143
+ return url;
144
+ }
145
+
146
+ return undefined;
147
+ });
148
+
149
+ const tooltipValue = $derived(tooltipText);
150
+
151
+ // Generate test ID
152
+ const computedTestId = $derived(() => {
153
+ if (testId) return testId;
154
+ if (item && typeof item === 'object' && 'id' in item) {
155
+ return `link-cell-${item.id}`;
156
+ }
157
+ return undefined;
158
+ });
159
+
160
+ // Determine variant based on state
161
+ const linkVariant = $derived.by(() => {
162
+ if (!isValidLink()) return 'default'; // Will be overridden by disabled state in template
163
+ if (isExternal()) return 'external';
164
+ return variant;
165
+ });
166
+
167
+ const styles = $derived(
168
+ linkCellVariants({ size, variant: linkVariant, disabled: !isValidLink() || disabled })
169
+ );
170
+
171
+ // Event handlers
172
+ function handleClick(event: MouseEvent) {
173
+ if (onClick) {
174
+ onClick(item, event);
175
+ }
176
+ }
177
+
178
+ function handleKeyDown(event: KeyboardEvent) {
179
+ if (event.key === 'Enter' || event.key === ' ') {
180
+ event.preventDefault();
181
+ const linkElement = event.target as HTMLAnchorElement;
182
+ linkElement.click();
183
+ }
184
+ }
185
+ </script>
186
+
187
+ {#if isValidLink()}
188
+ <div class={styles.container()}>
189
+ <!-- `href` is consumer-provided at runtime; `resolve()` only applies to
190
+ internal SvelteKit routes and would false-positive on any external
191
+ (mailto:/tel:/http) link the consumer configures. -->
192
+ <!-- eslint-disable svelte/no-navigation-without-resolve -->
193
+ <a
194
+ href={computedHref()}
195
+ {target}
196
+ rel={computedRel()}
197
+ download={download === true ? true : download || undefined}
198
+ class="{styles.link()} {className}"
199
+ title={tooltipValue}
200
+ data-testid={computedTestId}
201
+ onclick={handleClick}
202
+ onkeydown={handleKeyDown}
203
+ >
204
+ <span class={styles.text()}>
205
+ {displayText}
206
+ </span>
207
+
208
+ {#if showIcon && isExternal()}
209
+ <svg
210
+ class={styles.icon()}
211
+ width="14"
212
+ height="14"
213
+ viewBox="0 0 24 24"
214
+ fill="none"
215
+ stroke="currentColor"
216
+ stroke-width="2"
217
+ stroke-linecap="round"
218
+ stroke-linejoin="round"
219
+ aria-hidden="true"
220
+ >
221
+ <path d="M7 17L17 7" />
222
+ <path d="M7 7h10v10" />
223
+ </svg>
224
+ {/if}
225
+ </a>
226
+ <!-- eslint-enable svelte/no-navigation-without-resolve -->
227
+ </div>
228
+ {:else}
229
+ <div class={styles.container()}>
230
+ <span
231
+ class="{styles.link()} {className}"
232
+ title={tt('table.link.invalid')}
233
+ data-testid={computedTestId}
234
+ >
235
+ <span class={styles.text()}>
236
+ {displayText}
237
+ </span>
238
+ </span>
239
+ </div>
240
+ {/if}
@@ -0,0 +1,42 @@
1
+ import { type LinkCellVariantProps } from '../variants';
2
+ export type LinkCellProps<Item> = {
3
+ item: Item;
4
+ href?: string | ((item: Item) => string);
5
+ urlKey?: keyof Item;
6
+ text?: string | ((item: Item) => string);
7
+ textKey?: keyof Item;
8
+ target?: '_blank' | '_self' | '_parent' | '_top';
9
+ external?: boolean;
10
+ download?: boolean | string;
11
+ rel?: string;
12
+ onClick?: (item: Item, event: MouseEvent) => void;
13
+ fallback?: string;
14
+ maxLength?: number;
15
+ showIcon?: boolean;
16
+ className?: string;
17
+ testId?: string;
18
+ } & LinkCellVariantProps;
19
+ declare function $$render<Item>(): {
20
+ props: LinkCellProps<Item>;
21
+ exports: {};
22
+ bindings: "";
23
+ slots: {};
24
+ events: {};
25
+ };
26
+ declare class __sveltets_Render<Item> {
27
+ props(): ReturnType<typeof $$render<Item>>['props'];
28
+ events(): ReturnType<typeof $$render<Item>>['events'];
29
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
30
+ bindings(): "";
31
+ exports(): {};
32
+ }
33
+ interface $$IsomorphicComponent {
34
+ new <Item>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Item>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Item>['props']>, ReturnType<__sveltets_Render<Item>['events']>, ReturnType<__sveltets_Render<Item>['slots']>> & {
35
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
36
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
37
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
38
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
39
+ }
40
+ declare const LinkCell: $$IsomorphicComponent;
41
+ type LinkCell<Item> = InstanceType<typeof LinkCell<Item>>;
42
+ export default LinkCell;