@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,225 @@
1
+ <script lang="ts" generics="Item">
2
+ import { useTableI18n } from '../i18n';
3
+ import { numberCellVariants, type NumberCellVariantProps } from '../variants';
4
+
5
+ const tt = useTableI18n();
6
+
7
+ export type NumberCellProps<Item> = {
8
+ item: Item;
9
+ valueKey?: keyof Item;
10
+ value?: number | ((item: Item) => number);
11
+ format?: 'integer' | 'decimal' | 'currency' | 'percentage' | 'custom';
12
+ currency?: string;
13
+ locale?: string;
14
+ decimals?: number;
15
+ customFormatter?: (value: number) => string;
16
+ prefix?: string;
17
+ suffix?: string;
18
+ fallback?: string;
19
+ colorMode?: 'none' | 'positive-negative' | 'threshold';
20
+ thresholds?: {
21
+ danger?: number;
22
+ warning?: number;
23
+ success?: number;
24
+ };
25
+ onClick?: (item: Item, value: number | null) => void;
26
+ className?: string;
27
+ testId?: string;
28
+ } & NumberCellVariantProps;
29
+
30
+ // Props with sensible defaults
31
+ let {
32
+ item,
33
+ valueKey = undefined,
34
+ value = undefined,
35
+ format = 'decimal',
36
+ currency = 'EUR',
37
+ locale = 'de-DE',
38
+ decimals = 2,
39
+ customFormatter = undefined,
40
+ prefix = '',
41
+ suffix = '',
42
+ fallback = '—',
43
+ colorMode = 'none',
44
+ thresholds = {},
45
+ onClick = undefined,
46
+ className = '',
47
+ testId = undefined,
48
+ size = 'md',
49
+ align = 'right',
50
+ variant = 'default'
51
+ }: NumberCellProps<Item> = $props();
52
+
53
+ // Extract numeric value from item or prop
54
+ const extractValue = (item: Item): number | null => {
55
+ if (typeof value === 'function') {
56
+ return value(item);
57
+ }
58
+ if (typeof value === 'number') {
59
+ return value;
60
+ }
61
+ if (valueKey) {
62
+ const extractedValue = item[valueKey];
63
+ if (typeof extractedValue === 'number') return extractedValue;
64
+ if (typeof extractedValue === 'string') {
65
+ const parsed = parseFloat(extractedValue);
66
+ return isNaN(parsed) ? null : parsed;
67
+ }
68
+ }
69
+ return null;
70
+ };
71
+
72
+ // Get the numeric value
73
+ const numericValue = $derived.by(() => extractValue(item));
74
+
75
+ // Check if value is clickable
76
+ const isClickable = $derived(Boolean(onClick && numericValue !== null));
77
+
78
+ // Format the number based on options
79
+ const formatNumber = (num: number | null): string => {
80
+ if (num === null) return fallback;
81
+
82
+ try {
83
+ // Custom formatter takes precedence
84
+ if (customFormatter) {
85
+ return prefix + customFormatter(num) + suffix;
86
+ }
87
+
88
+ let formatted: string;
89
+
90
+ switch (format) {
91
+ case 'integer':
92
+ formatted = new Intl.NumberFormat(locale, {
93
+ maximumFractionDigits: 0
94
+ }).format(num);
95
+ break;
96
+
97
+ case 'decimal':
98
+ formatted = new Intl.NumberFormat(locale, {
99
+ minimumFractionDigits: decimals,
100
+ maximumFractionDigits: decimals
101
+ }).format(num);
102
+ break;
103
+
104
+ case 'currency':
105
+ formatted = new Intl.NumberFormat(locale, {
106
+ style: 'currency',
107
+ currency: currency,
108
+ minimumFractionDigits: decimals,
109
+ maximumFractionDigits: decimals
110
+ }).format(num);
111
+ break;
112
+
113
+ case 'percentage':
114
+ formatted = new Intl.NumberFormat(locale, {
115
+ style: 'percent',
116
+ minimumFractionDigits: decimals,
117
+ maximumFractionDigits: decimals
118
+ }).format(num / 100);
119
+ break;
120
+
121
+ default:
122
+ formatted = new Intl.NumberFormat(locale).format(num);
123
+ }
124
+
125
+ return prefix + formatted + suffix;
126
+ } catch (error) {
127
+ console.warn('NumberCell: Error formatting number', error);
128
+ return fallback;
129
+ }
130
+ };
131
+
132
+ // Get formatted number string
133
+ const formattedNumber = $derived.by(() => formatNumber(numericValue));
134
+
135
+ // Determine color variant based on value
136
+ type NumberVariant = 'default' | 'neutral' | 'positive' | 'negative' | 'currency';
137
+ const getColorVariant = (num: number | null): NumberVariant => {
138
+ if (num === null || colorMode === 'none') return variant;
139
+
140
+ if (colorMode === 'positive-negative') {
141
+ if (num > 0) return 'positive';
142
+ if (num < 0) return 'negative';
143
+ return 'neutral';
144
+ }
145
+
146
+ if (colorMode === 'threshold' && thresholds) {
147
+ if (thresholds.danger !== undefined && num <= thresholds.danger) {
148
+ return 'negative';
149
+ }
150
+ if (thresholds.warning !== undefined && num <= thresholds.warning) {
151
+ return 'neutral';
152
+ }
153
+ if (thresholds.success !== undefined && num >= thresholds.success) {
154
+ return 'positive';
155
+ }
156
+ }
157
+
158
+ return variant;
159
+ };
160
+
161
+ const colorVariant = $derived.by(() => getColorVariant(numericValue));
162
+
163
+ // Generate tooltip with raw value
164
+ const tooltipText = $derived.by(() => {
165
+ const num = numericValue;
166
+ if (num === null) return undefined;
167
+
168
+ // Show raw value if different from formatted
169
+ const raw = num.toString();
170
+ const formatted = formattedNumber;
171
+
172
+ if (raw !== formatted && !formatted.includes(raw)) {
173
+ return tt('number.valueLabel', { value: raw });
174
+ }
175
+
176
+ return undefined;
177
+ });
178
+
179
+ const tooltipValue = $derived(tooltipText);
180
+
181
+ // Generate test ID
182
+ const computedTestId = $derived.by(() => {
183
+ if (testId) return testId;
184
+ if (item && typeof item === 'object' && 'id' in item) {
185
+ return `number-cell-${item.id}-${String(valueKey)}`;
186
+ }
187
+ return undefined;
188
+ });
189
+
190
+ const styles = $derived(numberCellVariants({ size, align, variant: colorVariant }));
191
+
192
+ // Event handlers
193
+ function handleClick(event: MouseEvent) {
194
+ const num = numericValue;
195
+ if (onClick && num !== null) {
196
+ event.stopPropagation();
197
+ onClick(item, num);
198
+ }
199
+ }
200
+
201
+ function handleKeyDown(event: KeyboardEvent) {
202
+ if (isClickable && (event.key === 'Enter' || event.key === ' ')) {
203
+ event.preventDefault();
204
+ const num = numericValue;
205
+ if (onClick && num !== null) {
206
+ onClick(item, num);
207
+ }
208
+ }
209
+ }
210
+ </script>
211
+
212
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
213
+ <div
214
+ class="{styles.container()} {className}"
215
+ title={tooltipValue}
216
+ data-testid={computedTestId}
217
+ onclick={handleClick}
218
+ onkeydown={handleKeyDown}
219
+ role={isClickable ? 'button' : undefined}
220
+ tabindex={isClickable ? 0 : undefined}
221
+ >
222
+ <span class={styles.number()}>
223
+ {formattedNumber}
224
+ </span>
225
+ </div>
@@ -0,0 +1,47 @@
1
+ import { type NumberCellVariantProps } from '../variants';
2
+ export type NumberCellProps<Item> = {
3
+ item: Item;
4
+ valueKey?: keyof Item;
5
+ value?: number | ((item: Item) => number);
6
+ format?: 'integer' | 'decimal' | 'currency' | 'percentage' | 'custom';
7
+ currency?: string;
8
+ locale?: string;
9
+ decimals?: number;
10
+ customFormatter?: (value: number) => string;
11
+ prefix?: string;
12
+ suffix?: string;
13
+ fallback?: string;
14
+ colorMode?: 'none' | 'positive-negative' | 'threshold';
15
+ thresholds?: {
16
+ danger?: number;
17
+ warning?: number;
18
+ success?: number;
19
+ };
20
+ onClick?: (item: Item, value: number | null) => void;
21
+ className?: string;
22
+ testId?: string;
23
+ } & NumberCellVariantProps;
24
+ declare function $$render<Item>(): {
25
+ props: NumberCellProps<Item>;
26
+ exports: {};
27
+ bindings: "";
28
+ slots: {};
29
+ events: {};
30
+ };
31
+ declare class __sveltets_Render<Item> {
32
+ props(): ReturnType<typeof $$render<Item>>['props'];
33
+ events(): ReturnType<typeof $$render<Item>>['events'];
34
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
35
+ bindings(): "";
36
+ exports(): {};
37
+ }
38
+ interface $$IsomorphicComponent {
39
+ 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']>> & {
40
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
41
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
42
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
43
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
44
+ }
45
+ declare const NumberCell: $$IsomorphicComponent;
46
+ type NumberCell<Item> = InstanceType<typeof NumberCell<Item>>;
47
+ export default NumberCell;
@@ -0,0 +1,121 @@
1
+ <script lang="ts" generics="Item">
2
+ import { Badge } from '@urbicon-ui/blocks';
3
+ import type { BadgeProps } from '@urbicon-ui/blocks';
4
+ import { useTableI18n } from '../i18n';
5
+ import { customCellVariants, type CustomCellVariantProps } from '../variants';
6
+
7
+ const tt = useTableI18n();
8
+
9
+ export type StatusConfig = {
10
+ intent: BadgeProps['intent'];
11
+ text: string;
12
+ icon: boolean;
13
+ };
14
+
15
+ export type StatusBadgeProps<Item> = {
16
+ item: Item;
17
+ statusKey?: keyof Item;
18
+ statusMap?: Record<string, StatusConfig>;
19
+ size?: BadgeProps['size'];
20
+ /** Visual variant — restricted to label variants. The `dot` arm of Badge forbids children. */
21
+ variant?: Exclude<BadgeProps['variant'], 'dot'>;
22
+ onClick?: (item: Item, status: string) => void;
23
+ className?: string;
24
+ testId?: string;
25
+ align?: CustomCellVariantProps['align'];
26
+ interactive?: boolean;
27
+ };
28
+
29
+ const defaultStatusMap: Record<string, StatusConfig> = $derived({
30
+ active: { intent: 'success', text: tt('status.active'), icon: true },
31
+ inactive: { intent: 'danger', text: tt('status.inactive'), icon: true },
32
+ pending: { intent: 'warning', text: tt('status.pending'), icon: true },
33
+ online: { intent: 'success', text: tt('status.online'), icon: true },
34
+ offline: { intent: 'neutral', text: tt('status.offline'), icon: true },
35
+ processing: { intent: 'primary', text: tt('status.processing'), icon: true },
36
+ completed: { intent: 'success', text: tt('status.completed'), icon: false },
37
+ failed: { intent: 'danger', text: tt('status.failed'), icon: false },
38
+ draft: { intent: 'neutral', text: tt('status.draft'), icon: false },
39
+ published: { intent: 'success', text: tt('status.published'), icon: true },
40
+ archived: { intent: 'neutral', text: tt('status.archived'), icon: false }
41
+ });
42
+
43
+ const staticFallbackConfig: StatusConfig = { intent: 'neutral', text: '', icon: false };
44
+
45
+ let {
46
+ item,
47
+ statusKey = 'status' as keyof Item,
48
+ statusMap = undefined as Record<string, StatusConfig> | undefined,
49
+ size = 'sm',
50
+ variant = 'filled',
51
+ onClick = undefined,
52
+ className = '',
53
+ testId = undefined,
54
+ align = 'center',
55
+ interactive = false
56
+ }: StatusBadgeProps<Item> = $props();
57
+
58
+ const mergedStatusMap = $derived({ ...defaultStatusMap, ...statusMap });
59
+ const statusValue = $derived(String(item[statusKey]) || 'default');
60
+ const fallbackConfig: StatusConfig = $derived({
61
+ ...staticFallbackConfig,
62
+ text: tt('status.unknown')
63
+ });
64
+ const config = $derived(mergedStatusMap[statusValue] || fallbackConfig);
65
+ const isClickable = $derived(Boolean(onClick && !interactive === false));
66
+
67
+ const computedTestId = $derived(() => {
68
+ if (testId) return testId;
69
+ if (item && typeof item === 'object' && 'id' in item) {
70
+ return `status-badge-${item.id}`;
71
+ }
72
+ return 'status-badge';
73
+ });
74
+
75
+ const containerStyles = $derived(
76
+ customCellVariants({
77
+ align,
78
+ interactive: isClickable,
79
+ size: 'md'
80
+ })
81
+ );
82
+
83
+ function handleClick(event: MouseEvent) {
84
+ if (onClick) {
85
+ event.stopPropagation();
86
+ onClick(item, statusValue);
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, statusValue);
95
+ }
96
+ }
97
+ }
98
+ </script>
99
+
100
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
101
+ <div
102
+ class="{containerStyles.container()} {className}"
103
+ data-testid={computedTestId()}
104
+ onclick={handleClick}
105
+ onkeydown={handleKeyDown}
106
+ role={isClickable ? 'button' : undefined}
107
+ tabindex={isClickable ? 0 : undefined}
108
+ title={`${tt('status.tooltip', { text: config.text })}${isClickable ? ` ${tt('status.clickToChange')}` : ''}`}
109
+ >
110
+ <div class={containerStyles.content()}>
111
+ <!-- Badge with max-width and truncation -->
112
+ <div class="inline-block max-w-32">
113
+ <Badge {size} {variant} intent={config.intent} class="w-full truncate">
114
+ {#if config.icon}
115
+ <div class="mr-1 h-1.5 w-1.5 shrink-0 rounded-full bg-current opacity-75"></div>
116
+ {/if}
117
+ <span class="truncate">{config.text}</span>
118
+ </Badge>
119
+ </div>
120
+ </div>
121
+ </div>
@@ -0,0 +1,44 @@
1
+ import type { BadgeProps } from '@urbicon-ui/blocks';
2
+ import { type CustomCellVariantProps } from '../variants';
3
+ export type StatusConfig = {
4
+ intent: BadgeProps['intent'];
5
+ text: string;
6
+ icon: boolean;
7
+ };
8
+ export type StatusBadgeProps<Item> = {
9
+ item: Item;
10
+ statusKey?: keyof Item;
11
+ statusMap?: Record<string, StatusConfig>;
12
+ size?: BadgeProps['size'];
13
+ /** Visual variant — restricted to label variants. The `dot` arm of Badge forbids children. */
14
+ variant?: Exclude<BadgeProps['variant'], 'dot'>;
15
+ onClick?: (item: Item, status: string) => void;
16
+ className?: string;
17
+ testId?: string;
18
+ align?: CustomCellVariantProps['align'];
19
+ interactive?: boolean;
20
+ };
21
+ declare function $$render<Item>(): {
22
+ props: StatusBadgeProps<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 StatusBadge: $$IsomorphicComponent;
43
+ type StatusBadge<Item> = InstanceType<typeof StatusBadge<Item>>;
44
+ export default StatusBadge;
@@ -0,0 +1,71 @@
1
+ <script lang="ts" generics="Item">
2
+ // Use our own Avatar component instead of Flowbite
3
+ import { Avatar } from '@urbicon-ui/blocks';
4
+ import SearchHighlight from '../features/SearchHighlight.svelte';
5
+ import { userCellVariants, type UserCellVariantProps } from '../variants';
6
+
7
+ export type UserAvatarProps<Item> = {
8
+ item: Item;
9
+ nameKey?: keyof Item;
10
+ emailKey?: keyof Item;
11
+ avatarKey?: keyof Item;
12
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
13
+ layout?: 'horizontal' | 'vertical' | 'compact';
14
+ showEmail?: boolean;
15
+ clickable?: boolean;
16
+ onClick?: (item: Item) => void;
17
+ mobile?: boolean;
18
+ } & UserCellVariantProps;
19
+
20
+ let {
21
+ item,
22
+ nameKey = 'name' as keyof Item,
23
+ emailKey = 'email' as keyof Item,
24
+ avatarKey = 'avatar' as keyof Item,
25
+ size = 'md',
26
+ layout = 'horizontal',
27
+ showEmail = true,
28
+ clickable = false,
29
+ onClick = undefined,
30
+ mobile = false
31
+ }: UserAvatarProps<Item> = $props();
32
+
33
+ // Performance-optimized values via $derived
34
+ const name = $derived.by(() => (item[nameKey] != null ? String(item[nameKey]) : ''));
35
+ const email = $derived.by(() => (item[emailKey] != null ? String(item[emailKey]) : ''));
36
+ const avatarUrl = $derived.by(() => (item[avatarKey] != null ? String(item[avatarKey]) : ''));
37
+
38
+ const avatarSize = $derived(mobile ? (size === 'lg' ? 'md' : 'sm') : size);
39
+
40
+ const styles = $derived(userCellVariants({ layout, size, clickable }));
41
+
42
+ function handleClick() {
43
+ if (clickable && onClick) {
44
+ onClick(item);
45
+ }
46
+ }
47
+ </script>
48
+
49
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
50
+ <div
51
+ class={styles.container()}
52
+ onclick={handleClick}
53
+ role={clickable ? 'button' : undefined}
54
+ tabindex={clickable ? 0 : undefined}
55
+ >
56
+ <div class={styles.avatar()}>
57
+ <Avatar size={avatarSize} src={avatarUrl} alt={name} {name} variant="circle" />
58
+ </div>
59
+
60
+ <div class={styles.content()}>
61
+ <div class={styles.name()} title={name}>
62
+ <SearchHighlight text={name} />
63
+ </div>
64
+
65
+ {#if showEmail && email}
66
+ <div class={styles.email()} title={email}>
67
+ <SearchHighlight text={email} />
68
+ </div>
69
+ {/if}
70
+ </div>
71
+ </div>
@@ -0,0 +1,37 @@
1
+ import { type UserCellVariantProps } from '../variants';
2
+ export type UserAvatarProps<Item> = {
3
+ item: Item;
4
+ nameKey?: keyof Item;
5
+ emailKey?: keyof Item;
6
+ avatarKey?: keyof Item;
7
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
8
+ layout?: 'horizontal' | 'vertical' | 'compact';
9
+ showEmail?: boolean;
10
+ clickable?: boolean;
11
+ onClick?: (item: Item) => void;
12
+ mobile?: boolean;
13
+ } & UserCellVariantProps;
14
+ declare function $$render<Item>(): {
15
+ props: UserAvatarProps<Item>;
16
+ exports: {};
17
+ bindings: "";
18
+ slots: {};
19
+ events: {};
20
+ };
21
+ declare class __sveltets_Render<Item> {
22
+ props(): ReturnType<typeof $$render<Item>>['props'];
23
+ events(): ReturnType<typeof $$render<Item>>['events'];
24
+ slots(): ReturnType<typeof $$render<Item>>['slots'];
25
+ bindings(): "";
26
+ exports(): {};
27
+ }
28
+ interface $$IsomorphicComponent {
29
+ 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']>> & {
30
+ $$bindings?: ReturnType<__sveltets_Render<Item>['bindings']>;
31
+ } & ReturnType<__sveltets_Render<Item>['exports']>;
32
+ <Item>(internal: unknown, props: ReturnType<__sveltets_Render<Item>['props']> & {}): ReturnType<__sveltets_Render<Item>['exports']>;
33
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
34
+ }
35
+ declare const UserAvatar: $$IsomorphicComponent;
36
+ type UserAvatar<Item> = InstanceType<typeof UserAvatar<Item>>;
37
+ export default UserAvatar;
@@ -0,0 +1,8 @@
1
+ export { type ActionButtonsProps, default as ActionButtons, type ExtraAction } from './ActionButtons.svelte';
2
+ export { type CopyButtonProps, default as CopyButton } from './CopyButton.svelte';
3
+ export { type CustomCellProps, default as CustomCell } from './CustomCell.svelte';
4
+ export { type DateCellProps, default as DateCell } from './DateCell.svelte';
5
+ export { default as LinkCell, type LinkCellProps } from './LinkCell.svelte';
6
+ export { default as NumberCell, type NumberCellProps } from './NumberCell.svelte';
7
+ export { default as StatusBadge, type StatusBadgeProps } from './StatusBadge.svelte';
8
+ export { default as UserAvatar, type UserAvatarProps } from './UserAvatar.svelte';
@@ -0,0 +1,9 @@
1
+ // Cell Components
2
+ export { default as ActionButtons } from './ActionButtons.svelte';
3
+ export { default as CopyButton } from './CopyButton.svelte';
4
+ export { default as CustomCell } from './CustomCell.svelte';
5
+ export { default as DateCell } from './DateCell.svelte';
6
+ export { default as LinkCell } from './LinkCell.svelte';
7
+ export { default as NumberCell } from './NumberCell.svelte';
8
+ export { default as StatusBadge } from './StatusBadge.svelte';
9
+ export { default as UserAvatar } from './UserAvatar.svelte';