@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.
- package/README.md +153 -0
- package/dist/cells/ActionButtons.svelte +224 -0
- package/dist/cells/ActionButtons.svelte.d.ts +74 -0
- package/dist/cells/CopyButton.svelte +89 -0
- package/dist/cells/CopyButton.svelte.d.ts +33 -0
- package/dist/cells/CustomCell.svelte +136 -0
- package/dist/cells/CustomCell.svelte.d.ts +44 -0
- package/dist/cells/DateCell.svelte +194 -0
- package/dist/cells/DateCell.svelte.d.ts +39 -0
- package/dist/cells/LinkCell.svelte +240 -0
- package/dist/cells/LinkCell.svelte.d.ts +42 -0
- package/dist/cells/NumberCell.svelte +225 -0
- package/dist/cells/NumberCell.svelte.d.ts +47 -0
- package/dist/cells/StatusBadge.svelte +121 -0
- package/dist/cells/StatusBadge.svelte.d.ts +44 -0
- package/dist/cells/UserAvatar.svelte +71 -0
- package/dist/cells/UserAvatar.svelte.d.ts +37 -0
- package/dist/cells/index.d.ts +8 -0
- package/dist/cells/index.js +9 -0
- package/dist/core/EmptyState.svelte +161 -0
- package/dist/core/EmptyState.svelte.d.ts +16 -0
- package/dist/core/ErrorState.svelte +158 -0
- package/dist/core/ErrorState.svelte.d.ts +15 -0
- package/dist/core/GroupedRow.svelte +239 -0
- package/dist/core/GroupedRow.svelte.d.ts +18 -0
- package/dist/core/LoadingState.svelte +75 -0
- package/dist/core/LoadingState.svelte.d.ts +14 -0
- package/dist/core/MobileCard.svelte +151 -0
- package/dist/core/MobileCard.svelte.d.ts +15 -0
- package/dist/core/TableCell.svelte +105 -0
- package/dist/core/TableCell.svelte.d.ts +14 -0
- package/dist/core/TableDesktop.svelte +480 -0
- package/dist/core/TableDesktop.svelte.d.ts +26 -0
- package/dist/core/TableHead.svelte +314 -0
- package/dist/core/TableHead.svelte.d.ts +7 -0
- package/dist/core/TableMobile.svelte +112 -0
- package/dist/core/TableMobile.svelte.d.ts +13 -0
- package/dist/core/TableProvider.svelte +271 -0
- package/dist/core/TableProvider.svelte.d.ts +40 -0
- package/dist/core/TableRow.svelte +171 -0
- package/dist/core/TableRow.svelte.d.ts +16 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +14 -0
- package/dist/core/sticky-context.svelte.d.ts +48 -0
- package/dist/core/sticky-context.svelte.js +88 -0
- package/dist/core/table/Table.svelte +304 -0
- package/dist/core/table/Table.svelte.d.ts +26 -0
- package/dist/core/table/index.d.ts +448 -0
- package/dist/core/table/index.js +1 -0
- package/dist/core/table-style-context.d.ts +66 -0
- package/dist/core/table-style-context.js +26 -0
- package/dist/factories/ColumnValidation.d.ts +49 -0
- package/dist/factories/ColumnValidation.js +188 -0
- package/dist/factories/TableColumns.d.ts +97 -0
- package/dist/factories/TableColumns.js +262 -0
- package/dist/factories/TypedColumnBuilder.d.ts +41 -0
- package/dist/factories/TypedColumnBuilder.js +72 -0
- package/dist/factories/index.d.ts +12 -0
- package/dist/factories/index.js +13 -0
- package/dist/features/HeaderMenu.svelte +236 -0
- package/dist/features/HeaderMenu.svelte.d.ts +8 -0
- package/dist/features/LiveUpdateBanner.svelte +66 -0
- package/dist/features/LiveUpdateBanner.svelte.d.ts +6 -0
- package/dist/features/SearchHighlight.svelte +21 -0
- package/dist/features/SearchHighlight.svelte.d.ts +8 -0
- package/dist/features/SmartFilterBar/ChipsField.svelte +104 -0
- package/dist/features/SmartFilterBar/ChipsField.svelte.d.ts +5 -0
- package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte +84 -0
- package/dist/features/SmartFilterBar/ColumnVisibilityMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/FilterMenu.svelte +367 -0
- package/dist/features/SmartFilterBar/FilterMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/GroupingMenu.svelte +82 -0
- package/dist/features/SmartFilterBar/GroupingMenu.svelte.d.ts +3 -0
- package/dist/features/SmartFilterBar/SmartFilterBar.svelte +109 -0
- package/dist/features/SmartFilterBar/SmartFilterBar.svelte.d.ts +11 -0
- package/dist/features/SmartFilterBar/SummaryMenu.svelte +118 -0
- package/dist/features/SmartFilterBar/SummaryMenu.svelte.d.ts +3 -0
- package/dist/features/SummaryRow.svelte +97 -0
- package/dist/features/SummaryRow.svelte.d.ts +8 -0
- package/dist/features/index.d.ts +4 -0
- package/dist/features/index.js +4 -0
- package/dist/i18n/index.d.ts +366 -0
- package/dist/i18n/index.js +21 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +41 -0
- package/dist/stores/TableStore.svelte.d.ts +192 -0
- package/dist/stores/TableStore.svelte.js +362 -0
- package/dist/stores/concerns/index.d.ts +15 -0
- package/dist/stores/concerns/index.js +14 -0
- package/dist/stores/concerns/types.d.ts +31 -0
- package/dist/stores/concerns/types.js +1 -0
- package/dist/stores/concerns/useColumnOrder.svelte.d.ts +16 -0
- package/dist/stores/concerns/useColumnOrder.svelte.js +81 -0
- package/dist/stores/concerns/useColumnVisibility.svelte.d.ts +16 -0
- package/dist/stores/concerns/useColumnVisibility.svelte.js +58 -0
- package/dist/stores/concerns/useExpansion.svelte.d.ts +9 -0
- package/dist/stores/concerns/useExpansion.svelte.js +32 -0
- package/dist/stores/concerns/useFiltering.svelte.d.ts +20 -0
- package/dist/stores/concerns/useFiltering.svelte.js +109 -0
- package/dist/stores/concerns/useFocusManagement.svelte.d.ts +15 -0
- package/dist/stores/concerns/useFocusManagement.svelte.js +52 -0
- package/dist/stores/concerns/useGrouping.svelte.d.ts +15 -0
- package/dist/stores/concerns/useGrouping.svelte.js +86 -0
- package/dist/stores/concerns/useLiveUpdates.svelte.d.ts +45 -0
- package/dist/stores/concerns/useLiveUpdates.svelte.js +175 -0
- package/dist/stores/concerns/usePagination.svelte.d.ts +18 -0
- package/dist/stores/concerns/usePagination.svelte.js +54 -0
- package/dist/stores/concerns/usePersistence.svelte.d.ts +36 -0
- package/dist/stores/concerns/usePersistence.svelte.js +167 -0
- package/dist/stores/concerns/useRemoteData.svelte.d.ts +21 -0
- package/dist/stores/concerns/useRemoteData.svelte.js +64 -0
- package/dist/stores/concerns/useSearch.svelte.d.ts +8 -0
- package/dist/stores/concerns/useSearch.svelte.js +16 -0
- package/dist/stores/concerns/useSelection.svelte.d.ts +21 -0
- package/dist/stores/concerns/useSelection.svelte.js +110 -0
- package/dist/stores/concerns/useSorting.svelte.d.ts +11 -0
- package/dist/stores/concerns/useSorting.svelte.js +70 -0
- package/dist/stores/concerns/useSummary.svelte.d.ts +18 -0
- package/dist/stores/concerns/useSummary.svelte.js +96 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/style/index.css +137 -0
- package/dist/style/index.d.ts +2 -0
- package/dist/style/index.js +2 -0
- package/dist/style/table-theme.css +131 -0
- package/dist/style/themes/comfortable.css +20 -0
- package/dist/style/themes/compact.css +20 -0
- package/dist/translations/de.d.ts +177 -0
- package/dist/translations/de.js +176 -0
- package/dist/translations/en.d.ts +177 -0
- package/dist/translations/en.js +176 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/tableTypes.d.ts +262 -0
- package/dist/types/tableTypes.js +1 -0
- package/dist/utils/index.d.ts +165 -0
- package/dist/utils/index.js +330 -0
- package/dist/utils/sticky-measure.d.ts +54 -0
- package/dist/utils/sticky-measure.js +107 -0
- package/dist/utils/virtualizer.d.ts +43 -0
- package/dist/utils/virtualizer.js +43 -0
- package/dist/variants/index.d.ts +11 -0
- package/dist/variants/index.js +15 -0
- package/dist/variants/table-cells.variants.d.ts +827 -0
- package/dist/variants/table-cells.variants.js +627 -0
- package/dist/variants/table-features.variants.d.ts +547 -0
- package/dist/variants/table-features.variants.js +412 -0
- package/dist/variants/table-states.variants.d.ts +594 -0
- package/dist/variants/table-states.variants.js +394 -0
- package/dist/variants/table.system.d.ts +301 -0
- package/dist/variants/table.system.js +314 -0
- package/dist/variants/table.variants.d.ts +428 -0
- package/dist/variants/table.variants.js +360 -0
- package/package.json +93 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useTableI18n } from '../i18n';
|
|
3
|
+
import { emptyStateVariants, tableRowVariants, type EmptyStateVariantProps } from '../variants';
|
|
4
|
+
import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
|
|
5
|
+
|
|
6
|
+
const tt = useTableI18n();
|
|
7
|
+
|
|
8
|
+
export type EmptyStateProps = {
|
|
9
|
+
message?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
actionText?: string;
|
|
13
|
+
onAction?: () => void;
|
|
14
|
+
colSpan?: number;
|
|
15
|
+
class?: string;
|
|
16
|
+
testId?: string;
|
|
17
|
+
useI18n?: boolean;
|
|
18
|
+
size?: EmptyStateVariantProps['size'];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
message = undefined,
|
|
23
|
+
description = undefined,
|
|
24
|
+
icon = undefined,
|
|
25
|
+
actionText = undefined,
|
|
26
|
+
onAction = undefined,
|
|
27
|
+
colSpan = 1,
|
|
28
|
+
class: className = '',
|
|
29
|
+
testId = 'empty-state',
|
|
30
|
+
useI18n = true,
|
|
31
|
+
size = 'md'
|
|
32
|
+
}: EmptyStateProps = $props();
|
|
33
|
+
|
|
34
|
+
const styleConfig = getTableStyleConfig();
|
|
35
|
+
|
|
36
|
+
// Smart message with I18n fallback
|
|
37
|
+
const displayMessage = $derived.by(() => {
|
|
38
|
+
if (message) return message;
|
|
39
|
+
|
|
40
|
+
if (useI18n) {
|
|
41
|
+
return tt('data.empty');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return 'No data found';
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Smart action text with I18n fallback
|
|
48
|
+
const displayActionText = $derived.by(() => {
|
|
49
|
+
if (actionText) return actionText;
|
|
50
|
+
|
|
51
|
+
if (useI18n && onAction) {
|
|
52
|
+
return tt('data.refresh');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return actionText;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// TV Styles
|
|
59
|
+
const styles = $derived(emptyStateVariants({ size }));
|
|
60
|
+
const rowStyles = $derived(tableRowVariants({ size }));
|
|
61
|
+
|
|
62
|
+
// Event handlers
|
|
63
|
+
function handleAction() {
|
|
64
|
+
if (onAction) {
|
|
65
|
+
onAction();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
70
|
+
if (onAction && (event.key === 'Enter' || event.key === ' ')) {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
onAction();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<tr class={rowStyles.row()} data-testid={testId}>
|
|
78
|
+
<td colspan={colSpan} class={rowStyles.cell()}>
|
|
79
|
+
<div
|
|
80
|
+
class={resolveSlotClass(
|
|
81
|
+
styles.container(),
|
|
82
|
+
styleConfig.slotClasses.emptyState,
|
|
83
|
+
styleConfig.unstyled,
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{#if icon}
|
|
88
|
+
<div class={styles.icon()}>
|
|
89
|
+
{#if icon === 'search'}
|
|
90
|
+
<svg
|
|
91
|
+
class={styles.iconSvg()}
|
|
92
|
+
viewBox="0 0 24 24"
|
|
93
|
+
fill="none"
|
|
94
|
+
stroke="currentColor"
|
|
95
|
+
stroke-linecap="round"
|
|
96
|
+
stroke-linejoin="round"
|
|
97
|
+
>
|
|
98
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
99
|
+
<path d="M21 21L16.65 16.65"></path>
|
|
100
|
+
</svg>
|
|
101
|
+
{:else if icon === 'database'}
|
|
102
|
+
<svg
|
|
103
|
+
class={styles.iconSvg()}
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
fill="none"
|
|
106
|
+
stroke="currentColor"
|
|
107
|
+
stroke-linecap="round"
|
|
108
|
+
stroke-linejoin="round"
|
|
109
|
+
>
|
|
110
|
+
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
|
|
111
|
+
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
|
|
112
|
+
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
|
|
113
|
+
</svg>
|
|
114
|
+
{:else if icon === 'inbox'}
|
|
115
|
+
<svg
|
|
116
|
+
class={styles.iconSvg()}
|
|
117
|
+
viewBox="0 0 24 24"
|
|
118
|
+
fill="none"
|
|
119
|
+
stroke="currentColor"
|
|
120
|
+
stroke-linecap="round"
|
|
121
|
+
stroke-linejoin="round"
|
|
122
|
+
>
|
|
123
|
+
<polyline points="22,12 18,12 15,21 9,21 6,12 2,12"></polyline>
|
|
124
|
+
<path
|
|
125
|
+
d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"
|
|
126
|
+
></path>
|
|
127
|
+
</svg>
|
|
128
|
+
{:else}
|
|
129
|
+
<!-- Consumer-provided SVG markup; they are responsible for
|
|
130
|
+
sanitisation (same contract as the other icon slots). -->
|
|
131
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
132
|
+
{@html icon}
|
|
133
|
+
{/if}
|
|
134
|
+
</div>
|
|
135
|
+
{/if}
|
|
136
|
+
|
|
137
|
+
<div class={styles.content()}>
|
|
138
|
+
<h3 class={styles.title()}>
|
|
139
|
+
{displayMessage}
|
|
140
|
+
</h3>
|
|
141
|
+
|
|
142
|
+
{#if description}
|
|
143
|
+
<p class={styles.description()}>
|
|
144
|
+
{description}
|
|
145
|
+
</p>
|
|
146
|
+
{/if}
|
|
147
|
+
|
|
148
|
+
{#if displayActionText && onAction}
|
|
149
|
+
<button
|
|
150
|
+
class={styles.action()}
|
|
151
|
+
onclick={handleAction}
|
|
152
|
+
onkeydown={handleKeyDown}
|
|
153
|
+
type="button"
|
|
154
|
+
>
|
|
155
|
+
{displayActionText}
|
|
156
|
+
</button>
|
|
157
|
+
{/if}
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</td>
|
|
161
|
+
</tr>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type EmptyStateVariantProps } from '../variants';
|
|
2
|
+
export type EmptyStateProps = {
|
|
3
|
+
message?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
actionText?: string;
|
|
7
|
+
onAction?: () => void;
|
|
8
|
+
colSpan?: number;
|
|
9
|
+
class?: string;
|
|
10
|
+
testId?: string;
|
|
11
|
+
useI18n?: boolean;
|
|
12
|
+
size?: EmptyStateVariantProps['size'];
|
|
13
|
+
};
|
|
14
|
+
declare const EmptyState: import("svelte").Component<EmptyStateProps, {}, "">;
|
|
15
|
+
type EmptyState = ReturnType<typeof EmptyState>;
|
|
16
|
+
export default EmptyState;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useTableI18n } from '../i18n';
|
|
3
|
+
import { errorStateVariants, tableRowVariants, type ErrorStateVariantProps } from '../variants';
|
|
4
|
+
import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
|
|
5
|
+
|
|
6
|
+
const tt = useTableI18n();
|
|
7
|
+
|
|
8
|
+
export type ErrorStateProps = {
|
|
9
|
+
title?: string;
|
|
10
|
+
message?: string;
|
|
11
|
+
details?: string;
|
|
12
|
+
retryText?: string;
|
|
13
|
+
onRetry?: () => void;
|
|
14
|
+
colSpan?: number;
|
|
15
|
+
className?: string;
|
|
16
|
+
testId?: string;
|
|
17
|
+
size?: ErrorStateVariantProps['size'];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
title = tt('error.loadingError'),
|
|
22
|
+
message = tt('error.genericMessage'),
|
|
23
|
+
details = undefined,
|
|
24
|
+
retryText = tt('error.retry'),
|
|
25
|
+
onRetry = undefined,
|
|
26
|
+
colSpan = 1,
|
|
27
|
+
className = '',
|
|
28
|
+
testId = 'error-state',
|
|
29
|
+
size = 'md'
|
|
30
|
+
}: ErrorStateProps = $props();
|
|
31
|
+
|
|
32
|
+
const styleConfig = getTableStyleConfig();
|
|
33
|
+
|
|
34
|
+
let showDetails = $state(false);
|
|
35
|
+
|
|
36
|
+
// TV Styles
|
|
37
|
+
const styles = $derived(errorStateVariants({ size, detailsExpanded: showDetails }));
|
|
38
|
+
const rowStyles = $derived(tableRowVariants({ size }));
|
|
39
|
+
|
|
40
|
+
// Event handlers
|
|
41
|
+
function handleRetry() {
|
|
42
|
+
if (onRetry) {
|
|
43
|
+
onRetry();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function toggleDetails() {
|
|
48
|
+
showDetails = !showDetails;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
52
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
const target = event.target as HTMLElement;
|
|
55
|
+
|
|
56
|
+
if (target.classList.contains('retry-button')) {
|
|
57
|
+
handleRetry();
|
|
58
|
+
} else if (target.classList.contains('details-toggle')) {
|
|
59
|
+
toggleDetails();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<tr class={rowStyles.row()} data-testid={testId}>
|
|
66
|
+
<td colspan={colSpan} class={rowStyles.cell()}>
|
|
67
|
+
<div
|
|
68
|
+
class={resolveSlotClass(
|
|
69
|
+
styles.container(),
|
|
70
|
+
styleConfig.slotClasses.errorState,
|
|
71
|
+
styleConfig.unstyled,
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<div class={styles.icon()}>
|
|
76
|
+
<svg
|
|
77
|
+
class={styles.iconSvg()}
|
|
78
|
+
viewBox="0 0 24 24"
|
|
79
|
+
fill="none"
|
|
80
|
+
stroke="currentColor"
|
|
81
|
+
stroke-linecap="round"
|
|
82
|
+
stroke-linejoin="round"
|
|
83
|
+
>
|
|
84
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
85
|
+
<line x1="15" y1="9" x2="9" y2="15"></line>
|
|
86
|
+
<line x1="9" y1="9" x2="15" y2="15"></line>
|
|
87
|
+
</svg>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class={styles.content()}>
|
|
91
|
+
<h3 class={styles.title()}>
|
|
92
|
+
{title}
|
|
93
|
+
</h3>
|
|
94
|
+
|
|
95
|
+
<p class={styles.message()}>
|
|
96
|
+
{message}
|
|
97
|
+
</p>
|
|
98
|
+
|
|
99
|
+
{#if details}
|
|
100
|
+
<div class={styles.details()}>
|
|
101
|
+
<button
|
|
102
|
+
class="{styles.detailsToggle()} details-toggle"
|
|
103
|
+
onclick={toggleDetails}
|
|
104
|
+
onkeydown={handleKeyDown}
|
|
105
|
+
type="button"
|
|
106
|
+
aria-expanded={showDetails}
|
|
107
|
+
>
|
|
108
|
+
<span>{tt('actions.showDetails')}</span>
|
|
109
|
+
<svg
|
|
110
|
+
class={styles.detailsIcon()}
|
|
111
|
+
width="16"
|
|
112
|
+
height="16"
|
|
113
|
+
viewBox="0 0 24 24"
|
|
114
|
+
fill="none"
|
|
115
|
+
stroke="currentColor"
|
|
116
|
+
stroke-width="2"
|
|
117
|
+
stroke-linecap="round"
|
|
118
|
+
stroke-linejoin="round"
|
|
119
|
+
>
|
|
120
|
+
<polyline points="6,9 12,15 18,9"></polyline>
|
|
121
|
+
</svg>
|
|
122
|
+
</button>
|
|
123
|
+
|
|
124
|
+
{#if showDetails}
|
|
125
|
+
<div class={styles.detailsContent()}>
|
|
126
|
+
<pre class={styles.detailsText()}>{details}</pre>
|
|
127
|
+
</div>
|
|
128
|
+
{/if}
|
|
129
|
+
</div>
|
|
130
|
+
{/if}
|
|
131
|
+
|
|
132
|
+
{#if onRetry}
|
|
133
|
+
<button
|
|
134
|
+
class="{styles.retryButton()} retry-button"
|
|
135
|
+
onclick={handleRetry}
|
|
136
|
+
onkeydown={handleKeyDown}
|
|
137
|
+
type="button"
|
|
138
|
+
>
|
|
139
|
+
<svg
|
|
140
|
+
class={styles.retryIcon()}
|
|
141
|
+
viewBox="0 0 24 24"
|
|
142
|
+
fill="none"
|
|
143
|
+
stroke="currentColor"
|
|
144
|
+
stroke-width="2"
|
|
145
|
+
stroke-linecap="round"
|
|
146
|
+
stroke-linejoin="round"
|
|
147
|
+
>
|
|
148
|
+
<polyline points="23,4 23,10 17,10"></polyline>
|
|
149
|
+
<polyline points="1,20 1,14 7,14"></polyline>
|
|
150
|
+
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
|
|
151
|
+
</svg>
|
|
152
|
+
{retryText}
|
|
153
|
+
</button>
|
|
154
|
+
{/if}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</td>
|
|
158
|
+
</tr>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ErrorStateVariantProps } from '../variants';
|
|
2
|
+
export type ErrorStateProps = {
|
|
3
|
+
title?: string;
|
|
4
|
+
message?: string;
|
|
5
|
+
details?: string;
|
|
6
|
+
retryText?: string;
|
|
7
|
+
onRetry?: () => void;
|
|
8
|
+
colSpan?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
testId?: string;
|
|
11
|
+
size?: ErrorStateVariantProps['size'];
|
|
12
|
+
};
|
|
13
|
+
declare const ErrorState: import("svelte").Component<ErrorStateProps, {}, "">;
|
|
14
|
+
type ErrorState = ReturnType<typeof ErrorState>;
|
|
15
|
+
export default ErrorState;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { slide } from 'svelte/transition';
|
|
3
|
+
import {
|
|
4
|
+
resolveIcon,
|
|
5
|
+
Checkbox,
|
|
6
|
+
ChevronDownIcon as ChevronDownIconDefault
|
|
7
|
+
} from '@urbicon-ui/blocks';
|
|
8
|
+
import { getTableContext } from '../stores/TableStore.svelte';
|
|
9
|
+
import { useTableI18n } from '../i18n';
|
|
10
|
+
|
|
11
|
+
const ChevronDownIcon = resolveIcon('chevronDown', ChevronDownIconDefault);
|
|
12
|
+
import { groupHeaderVariants, type GroupHeaderVariantProps } from '../variants';
|
|
13
|
+
import { tableRowVariants } from '../variants';
|
|
14
|
+
import TableCell from './TableCell.svelte';
|
|
15
|
+
import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
|
|
16
|
+
import { getStickyContext } from './sticky-context.svelte';
|
|
17
|
+
import { resolveColumnId } from '../utils';
|
|
18
|
+
import type { Column, TableItem } from '../types/tableTypes';
|
|
19
|
+
import type { Snippet } from 'svelte';
|
|
20
|
+
|
|
21
|
+
const tt = useTableI18n();
|
|
22
|
+
|
|
23
|
+
export type GroupedRowProps = {
|
|
24
|
+
groupName: string;
|
|
25
|
+
items: TableItem[];
|
|
26
|
+
expandable?: boolean;
|
|
27
|
+
expandedRowContent?: Snippet<[item: TableItem]>;
|
|
28
|
+
cell?: Snippet<[item: TableItem, value: unknown, column: Column]>;
|
|
29
|
+
size?: GroupHeaderVariantProps['size'];
|
|
30
|
+
class?: string;
|
|
31
|
+
testId?: string;
|
|
32
|
+
groupHeaderContent?: Snippet<[groupName: string, items: TableItem[], isExpanded: boolean]>;
|
|
33
|
+
onRowClick?: (item: TableItem) => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
groupName,
|
|
38
|
+
items,
|
|
39
|
+
expandable = false,
|
|
40
|
+
expandedRowContent = undefined,
|
|
41
|
+
cell = undefined,
|
|
42
|
+
size = 'md',
|
|
43
|
+
class: className = '',
|
|
44
|
+
testId = undefined,
|
|
45
|
+
groupHeaderContent,
|
|
46
|
+
onRowClick = undefined
|
|
47
|
+
}: GroupedRowProps = $props();
|
|
48
|
+
|
|
49
|
+
// Table context
|
|
50
|
+
const tableContext = getTableContext();
|
|
51
|
+
const { state: tableState, toggleGroupExpand, toggleExpand, isItemExpanded } = tableContext;
|
|
52
|
+
|
|
53
|
+
// Reactive computations
|
|
54
|
+
const computedTestId = $derived.by(() => {
|
|
55
|
+
if (testId) return testId;
|
|
56
|
+
return `grouped-row-${groupName.replace(/\s+/g, '-').toLowerCase()}`;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const isExpanded = $derived.by(() => {
|
|
60
|
+
return !tableState.collapsedGroups.has(groupName);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
let selectable = $derived(tableState.selectionMode !== 'none');
|
|
64
|
+
const styleConfig = getTableStyleConfig();
|
|
65
|
+
const stickyContext = getStickyContext();
|
|
66
|
+
|
|
67
|
+
const colSpan = $derived.by(() => {
|
|
68
|
+
let count = tableState.columns.length;
|
|
69
|
+
if (expandable) count++; // For expand column
|
|
70
|
+
if (tableState.groupByKey) count++; // For group indentation column
|
|
71
|
+
if (selectable) count++; // For selection checkbox column
|
|
72
|
+
return count;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const displayGroupName = $derived.by(() => {
|
|
76
|
+
if (groupName === '' || groupName === null || groupName === undefined) {
|
|
77
|
+
return tt('group.noGroup');
|
|
78
|
+
}
|
|
79
|
+
return String(groupName);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const itemCountText = $derived.by(() => {
|
|
83
|
+
const count = items.length;
|
|
84
|
+
return `(${count} ${count === 1 ? tt('group.item') : tt('group.items')})`;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Tailwind-Variants styling
|
|
88
|
+
const styles = $derived(groupHeaderVariants({ size, sticky: stickyContext.mode.group }));
|
|
89
|
+
|
|
90
|
+
// TableRow styles for individual items
|
|
91
|
+
const rowStyles = $derived(
|
|
92
|
+
tableRowVariants({
|
|
93
|
+
state: 'default',
|
|
94
|
+
size
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Event handlers
|
|
99
|
+
function handleToggleGroup() {
|
|
100
|
+
toggleGroupExpand(groupName);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
104
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
handleToggleGroup();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleItemToggleExpand(e: MouseEvent, row: TableItem) {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
if (!expandable) return;
|
|
113
|
+
const candidate = row.id ?? row.__index;
|
|
114
|
+
if (typeof candidate === 'string' || typeof candidate === 'number') {
|
|
115
|
+
toggleExpand(candidate);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<!-- Group Header Row -->
|
|
121
|
+
<tr
|
|
122
|
+
class={resolveSlotClass(
|
|
123
|
+
styles.row(),
|
|
124
|
+
styleConfig.slotClasses.groupHeader,
|
|
125
|
+
styleConfig.unstyled,
|
|
126
|
+
className
|
|
127
|
+
)}
|
|
128
|
+
data-testid={computedTestId}
|
|
129
|
+
id={`grouped-item-${groupName}`}
|
|
130
|
+
>
|
|
131
|
+
<td
|
|
132
|
+
colspan={colSpan}
|
|
133
|
+
class={styles.cell()}
|
|
134
|
+
onclick={handleToggleGroup}
|
|
135
|
+
onkeydown={handleKeyDown}
|
|
136
|
+
role="button"
|
|
137
|
+
tabindex="0"
|
|
138
|
+
aria-expanded={isExpanded}
|
|
139
|
+
aria-label="{displayGroupName} {isExpanded
|
|
140
|
+
? tt('header.collapseAllGroups')
|
|
141
|
+
: tt('header.expandAllGroups')}"
|
|
142
|
+
>
|
|
143
|
+
<div class={styles.content()}>
|
|
144
|
+
<!-- Expand/Collapse Icon -->
|
|
145
|
+
<div class={styles.chevron()}>
|
|
146
|
+
<ChevronDownIcon class={styles.chevron()} size={16} />
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- Group Name and Count -->
|
|
150
|
+
<div class={styles.content()}>
|
|
151
|
+
<span class={styles.title()}>
|
|
152
|
+
{displayGroupName}
|
|
153
|
+
</span>
|
|
154
|
+
<span class={styles.count()}>
|
|
155
|
+
{itemCountText}
|
|
156
|
+
</span>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Custom header content or actions -->
|
|
160
|
+
<div class={styles.actions()}>
|
|
161
|
+
{#if groupHeaderContent}
|
|
162
|
+
{@render groupHeaderContent(groupName, items, isExpanded)}
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</td>
|
|
167
|
+
</tr>
|
|
168
|
+
|
|
169
|
+
<!-- Group Content Rows -->
|
|
170
|
+
{#if isExpanded}
|
|
171
|
+
{#each items as item, index (item.id ?? index)}
|
|
172
|
+
{@const rowItemId =
|
|
173
|
+
typeof item.id === 'string' || typeof item.id === 'number' ? item.id : index}
|
|
174
|
+
{@const isRowExpanded = isItemExpanded(rowItemId)}
|
|
175
|
+
{@const isRowSelected = selectable && tableContext.isSelected(rowItemId)}
|
|
176
|
+
<tr
|
|
177
|
+
class={rowStyles.row()}
|
|
178
|
+
transition:slide={{ duration: 150 }}
|
|
179
|
+
aria-selected={selectable ? isRowSelected : undefined}
|
|
180
|
+
data-testid={`grouped-item-${rowItemId}`}
|
|
181
|
+
>
|
|
182
|
+
{#if selectable}
|
|
183
|
+
<td class="{rowStyles.cell()} w-12" onclick={(e) => e.stopPropagation()}>
|
|
184
|
+
<div class="flex h-full w-full items-center justify-center">
|
|
185
|
+
<Checkbox
|
|
186
|
+
checked={isRowSelected}
|
|
187
|
+
onchange={() => tableContext.toggleItem(rowItemId)}
|
|
188
|
+
aria-label={isRowSelected ? tt('selection.deselectRow') : tt('selection.selectRow')}
|
|
189
|
+
size="sm"
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
</td>
|
|
193
|
+
{/if}
|
|
194
|
+
|
|
195
|
+
{#if tableState.groupByKey}
|
|
196
|
+
<td class={rowStyles.cell()} aria-hidden="true"></td>
|
|
197
|
+
{/if}
|
|
198
|
+
|
|
199
|
+
{#if expandable}
|
|
200
|
+
<td class="{rowStyles.cell()} w-10">
|
|
201
|
+
<div class="flex h-full w-full items-center justify-center px-2 py-2">
|
|
202
|
+
<button
|
|
203
|
+
class="table-expand-button rounded-modify flex h-6 w-6 items-center justify-center transition-transform duration-(--blocks-duration-fast) {isRowExpanded
|
|
204
|
+
? 'rotate-180'
|
|
205
|
+
: ''}"
|
|
206
|
+
onclick={(e) => handleItemToggleExpand(e, item)}
|
|
207
|
+
aria-label={tt('actions.showDetails')}
|
|
208
|
+
data-testid={`expand-button-${rowItemId}`}
|
|
209
|
+
>
|
|
210
|
+
<ChevronDownIcon class="h-4 w-4" />
|
|
211
|
+
</button>
|
|
212
|
+
</div>
|
|
213
|
+
</td>
|
|
214
|
+
{/if}
|
|
215
|
+
|
|
216
|
+
{#each tableContext.orderedColumns as column (resolveColumnId(column))}
|
|
217
|
+
<TableCell
|
|
218
|
+
{item}
|
|
219
|
+
{column}
|
|
220
|
+
{cell}
|
|
221
|
+
{size}
|
|
222
|
+
cellClass={rowStyles.cell()}
|
|
223
|
+
testIdPrefix="grouped-cell"
|
|
224
|
+
/>
|
|
225
|
+
{/each}
|
|
226
|
+
</tr>
|
|
227
|
+
|
|
228
|
+
<!-- Expanded row content -->
|
|
229
|
+
{#if isRowExpanded && expandedRowContent}
|
|
230
|
+
<tr data-testid={`expanded-row-${rowItemId}`} class="border-b-0">
|
|
231
|
+
<td colspan={colSpan} class="p-0">
|
|
232
|
+
<div class="bg-surface-elevated/50 px-6 py-4" transition:slide={{ duration: 150 }}>
|
|
233
|
+
{@render expandedRowContent(item)}
|
|
234
|
+
</div>
|
|
235
|
+
</td>
|
|
236
|
+
</tr>
|
|
237
|
+
{/if}
|
|
238
|
+
{/each}
|
|
239
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type GroupHeaderVariantProps } from '../variants';
|
|
2
|
+
import type { Column, TableItem } from '../types/tableTypes';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
export type GroupedRowProps = {
|
|
5
|
+
groupName: string;
|
|
6
|
+
items: TableItem[];
|
|
7
|
+
expandable?: boolean;
|
|
8
|
+
expandedRowContent?: Snippet<[item: TableItem]>;
|
|
9
|
+
cell?: Snippet<[item: TableItem, value: unknown, column: Column]>;
|
|
10
|
+
size?: GroupHeaderVariantProps['size'];
|
|
11
|
+
class?: string;
|
|
12
|
+
testId?: string;
|
|
13
|
+
groupHeaderContent?: Snippet<[groupName: string, items: TableItem[], isExpanded: boolean]>;
|
|
14
|
+
onRowClick?: (item: TableItem) => void;
|
|
15
|
+
};
|
|
16
|
+
declare const GroupedRow: import("svelte").Component<GroupedRowProps, {}, "">;
|
|
17
|
+
type GroupedRow = ReturnType<typeof GroupedRow>;
|
|
18
|
+
export default GroupedRow;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useTableI18n } from '../i18n';
|
|
3
|
+
import { Spinner } from '@urbicon-ui/blocks';
|
|
4
|
+
import {
|
|
5
|
+
loadingStateVariants,
|
|
6
|
+
tableRowVariants,
|
|
7
|
+
type LoadingStateVariantProps
|
|
8
|
+
} from '../variants';
|
|
9
|
+
import { getTableStyleConfig, resolveSlotClass } from './table-style-context';
|
|
10
|
+
|
|
11
|
+
const tt = useTableI18n();
|
|
12
|
+
|
|
13
|
+
export type LoadingStateProps = {
|
|
14
|
+
text?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
showSpinner?: boolean;
|
|
17
|
+
colSpan?: number;
|
|
18
|
+
class?: string;
|
|
19
|
+
testId?: string;
|
|
20
|
+
useI18n?: boolean;
|
|
21
|
+
size?: LoadingStateVariantProps['size'];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
text = undefined,
|
|
26
|
+
description = undefined,
|
|
27
|
+
showSpinner = true,
|
|
28
|
+
colSpan = 1,
|
|
29
|
+
class: className = '',
|
|
30
|
+
testId = 'loading-state',
|
|
31
|
+
useI18n = true,
|
|
32
|
+
size = 'md'
|
|
33
|
+
}: LoadingStateProps = $props();
|
|
34
|
+
|
|
35
|
+
const SPINNER_SIZE_MAP = { sm: 'sm', md: 'md', lg: 'lg' } as const;
|
|
36
|
+
|
|
37
|
+
const displayText = $derived.by(() => {
|
|
38
|
+
if (text) return text;
|
|
39
|
+
if (useI18n) return tt('data.loading');
|
|
40
|
+
return 'Loading data...';
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const styles = $derived(loadingStateVariants({ size }));
|
|
44
|
+
const rowStyles = $derived(tableRowVariants({ size }));
|
|
45
|
+
const styleConfig = getTableStyleConfig();
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<tr class={rowStyles.row()} data-testid={testId}>
|
|
49
|
+
<td colspan={colSpan} class={rowStyles.cell()}>
|
|
50
|
+
<div
|
|
51
|
+
class={resolveSlotClass(
|
|
52
|
+
styles.container(),
|
|
53
|
+
styleConfig.slotClasses.loadingState,
|
|
54
|
+
styleConfig.unstyled,
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{#if showSpinner}
|
|
59
|
+
<Spinner size={SPINNER_SIZE_MAP[size]} intent="primary" label={displayText} />
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
<div class={styles.content()}>
|
|
63
|
+
<h3 class={styles.text()}>
|
|
64
|
+
{displayText}
|
|
65
|
+
</h3>
|
|
66
|
+
|
|
67
|
+
{#if description}
|
|
68
|
+
<p class={styles.description()}>
|
|
69
|
+
{description}
|
|
70
|
+
</p>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</td>
|
|
75
|
+
</tr>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type LoadingStateVariantProps } from '../variants';
|
|
2
|
+
export type LoadingStateProps = {
|
|
3
|
+
text?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
showSpinner?: boolean;
|
|
6
|
+
colSpan?: number;
|
|
7
|
+
class?: string;
|
|
8
|
+
testId?: string;
|
|
9
|
+
useI18n?: boolean;
|
|
10
|
+
size?: LoadingStateVariantProps['size'];
|
|
11
|
+
};
|
|
12
|
+
declare const LoadingState: import("svelte").Component<LoadingStateProps, {}, "">;
|
|
13
|
+
type LoadingState = ReturnType<typeof LoadingState>;
|
|
14
|
+
export default LoadingState;
|