@aspect-ops/exon-ui 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AspectRatio/AspectRatio.svelte +1 -0
- package/dist/components/CTASection/CTASection.svelte +298 -0
- package/dist/components/CTASection/CTASection.svelte.d.ts +15 -0
- package/dist/components/CTASection/index.d.ts +2 -0
- package/dist/components/CTASection/index.js +1 -0
- package/dist/components/Card/FlipCard.svelte +155 -0
- package/dist/components/Card/FlipCard.svelte.d.ts +13 -0
- package/dist/components/Card/index.d.ts +1 -0
- package/dist/components/Card/index.js +1 -0
- package/dist/components/Container/Container.svelte +1 -0
- package/dist/components/DataTable/DataTable.svelte +460 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +49 -0
- package/dist/components/DataTable/index.d.ts +2 -0
- package/dist/components/DataTable/index.js +1 -0
- package/dist/components/DoughnutChart/DoughnutChart.svelte +20 -2
- package/dist/components/GlobalHeader/GlobalHeader.svelte +692 -0
- package/dist/components/GlobalHeader/GlobalHeader.svelte.d.ts +3 -0
- package/dist/components/GlobalHeader/index.d.ts +2 -0
- package/dist/components/GlobalHeader/index.js +1 -0
- package/dist/components/Hero/Hero.svelte +306 -0
- package/dist/components/Hero/Hero.svelte.d.ts +18 -0
- package/dist/components/Hero/index.d.ts +2 -0
- package/dist/components/Hero/index.js +1 -0
- package/dist/components/Icon/Icon.svelte +15 -18
- package/dist/components/Icon/Icon.svelte.d.ts +2 -1
- package/dist/components/LogoCloud/LogoCloud.svelte +333 -0
- package/dist/components/LogoCloud/LogoCloud.svelte.d.ts +20 -0
- package/dist/components/LogoCloud/index.d.ts +2 -0
- package/dist/components/LogoCloud/index.js +1 -0
- package/dist/components/Menu/MenuContent.svelte +1 -0
- package/dist/components/Menu/MenuSubContent.svelte +1 -0
- package/dist/components/Mermaid/Mermaid.svelte +121 -7
- package/dist/components/Mermaid/Mermaid.svelte.d.ts +10 -0
- package/dist/components/PageHeader/PageHeader.svelte +140 -0
- package/dist/components/PageHeader/PageHeader.svelte.d.ts +30 -0
- package/dist/components/PageHeader/index.d.ts +1 -0
- package/dist/components/PageHeader/index.js +1 -0
- package/dist/components/ServiceCard/ServiceCard.svelte +359 -0
- package/dist/components/ServiceCard/ServiceCard.svelte.d.ts +16 -0
- package/dist/components/ServiceCard/index.d.ts +1 -0
- package/dist/components/ServiceCard/index.js +1 -0
- package/dist/components/SplitSection/SplitSection.svelte +194 -0
- package/dist/components/SplitSection/SplitSection.svelte.d.ts +15 -0
- package/dist/components/SplitSection/index.d.ts +1 -0
- package/dist/components/SplitSection/index.js +1 -0
- package/dist/components/StatCircle/StatCircle.svelte +172 -0
- package/dist/components/StatCircle/StatCircle.svelte.d.ts +19 -0
- package/dist/components/StatCircle/index.d.ts +1 -0
- package/dist/components/StatCircle/index.js +1 -0
- package/dist/components/StatsCard/StatsCard.svelte +301 -0
- package/dist/components/StatsCard/StatsCard.svelte.d.ts +32 -0
- package/dist/components/StatsCard/index.d.ts +2 -0
- package/dist/components/StatsCard/index.js +1 -0
- package/dist/components/StatusBadge/StatusBadge.svelte +221 -0
- package/dist/components/StatusBadge/StatusBadge.svelte.d.ts +22 -0
- package/dist/components/StatusBadge/index.d.ts +2 -0
- package/dist/components/StatusBadge/index.js +1 -0
- package/dist/components/StatusBanner/StatusBanner.svelte +325 -0
- package/dist/components/StatusBanner/StatusBanner.svelte.d.ts +13 -0
- package/dist/components/StatusBanner/index.d.ts +1 -0
- package/dist/components/StatusBanner/index.js +1 -0
- package/dist/components/TestimonialCard/TestimonialCard.svelte +290 -0
- package/dist/components/TestimonialCard/TestimonialCard.svelte.d.ts +14 -0
- package/dist/components/TestimonialCard/index.d.ts +1 -0
- package/dist/components/TestimonialCard/index.js +1 -0
- package/dist/components/Timeline/Timeline.svelte +444 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +19 -0
- package/dist/components/Timeline/index.d.ts +2 -0
- package/dist/components/Timeline/index.js +1 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.js +16 -1
- package/dist/types/data-display.d.ts +72 -0
- package/dist/types/feedback.d.ts +10 -0
- package/dist/types/index.d.ts +2 -2
- package/package.json +3 -2
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* DataTable - A sortable, customizable data table component
|
|
4
|
+
* Supports custom cell rendering, sorting, loading states, and responsive overflow
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
type SortOrder = 'asc' | 'desc';
|
|
8
|
+
type ColumnAlign = 'left' | 'center' | 'right';
|
|
9
|
+
|
|
10
|
+
interface ColumnDef {
|
|
11
|
+
/** Unique key matching the data property */
|
|
12
|
+
key: string;
|
|
13
|
+
/** Header text to display */
|
|
14
|
+
header: string;
|
|
15
|
+
/** Whether this column is sortable */
|
|
16
|
+
sortable?: boolean;
|
|
17
|
+
/** Fixed width (e.g., '100px', '10rem') */
|
|
18
|
+
width?: string;
|
|
19
|
+
/** Text alignment */
|
|
20
|
+
align?: ColumnAlign;
|
|
21
|
+
/** Custom cell renderer - receives (value, row) */
|
|
22
|
+
cell?: import('svelte').Snippet<[value: unknown, row: unknown]>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
/** Array of data objects to display */
|
|
27
|
+
data: unknown[];
|
|
28
|
+
/** Column definitions */
|
|
29
|
+
columns: ColumnDef[];
|
|
30
|
+
/** Currently sorted column key */
|
|
31
|
+
sortBy?: string;
|
|
32
|
+
/** Current sort direction */
|
|
33
|
+
sortOrder?: SortOrder;
|
|
34
|
+
/** Callback when sort changes */
|
|
35
|
+
onsort?: (column: string, order: SortOrder) => void;
|
|
36
|
+
/** Show loading skeleton */
|
|
37
|
+
loading?: boolean;
|
|
38
|
+
/** Number of skeleton rows to show when loading */
|
|
39
|
+
loadingRows?: number;
|
|
40
|
+
/** Custom empty state content */
|
|
41
|
+
empty?: import('svelte').Snippet;
|
|
42
|
+
/** Enable row hover effect */
|
|
43
|
+
rowHover?: boolean;
|
|
44
|
+
/** Enable striped rows */
|
|
45
|
+
striped?: boolean;
|
|
46
|
+
/** Make header sticky on scroll */
|
|
47
|
+
stickyHeader?: boolean;
|
|
48
|
+
/** Additional CSS classes */
|
|
49
|
+
class?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let {
|
|
53
|
+
data = [],
|
|
54
|
+
columns,
|
|
55
|
+
sortBy = $bindable(),
|
|
56
|
+
sortOrder = $bindable(),
|
|
57
|
+
onsort,
|
|
58
|
+
loading = false,
|
|
59
|
+
loadingRows = 5,
|
|
60
|
+
empty,
|
|
61
|
+
rowHover = true,
|
|
62
|
+
striped = false,
|
|
63
|
+
stickyHeader = false,
|
|
64
|
+
class: className = ''
|
|
65
|
+
}: Props = $props();
|
|
66
|
+
|
|
67
|
+
function handleSort(column: ColumnDef) {
|
|
68
|
+
if (!column.sortable) return;
|
|
69
|
+
|
|
70
|
+
let newOrder: SortOrder = 'asc';
|
|
71
|
+
if (sortBy === column.key) {
|
|
72
|
+
newOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
sortBy = column.key;
|
|
76
|
+
sortOrder = newOrder;
|
|
77
|
+
onsort?.(column.key, newOrder);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getCellValue(row: unknown, key: string): unknown {
|
|
81
|
+
if (typeof row === 'object' && row !== null) {
|
|
82
|
+
return (row as Record<string, unknown>)[key];
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isEmpty = $derived(!loading && data.length === 0);
|
|
88
|
+
const skeletonRows = $derived(Array.from({ length: loadingRows }, (_, i) => i));
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div class="data-table-container {className}" class:data-table-container--sticky={stickyHeader}>
|
|
92
|
+
<table
|
|
93
|
+
class="data-table"
|
|
94
|
+
class:data-table--striped={striped}
|
|
95
|
+
class:data-table--row-hover={rowHover}
|
|
96
|
+
>
|
|
97
|
+
<thead class="data-table__head">
|
|
98
|
+
<tr>
|
|
99
|
+
{#each columns as column}
|
|
100
|
+
<th
|
|
101
|
+
class="data-table__header data-table__header--{column.align || 'left'}"
|
|
102
|
+
class:data-table__header--sortable={column.sortable}
|
|
103
|
+
style={column.width ? `width: ${column.width}` : undefined}
|
|
104
|
+
onclick={() => handleSort(column)}
|
|
105
|
+
role={column.sortable ? 'button' : undefined}
|
|
106
|
+
tabindex={column.sortable ? 0 : undefined}
|
|
107
|
+
onkeydown={(e) => {
|
|
108
|
+
if (column.sortable && (e.key === 'Enter' || e.key === ' ')) {
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
handleSort(column);
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
aria-sort={sortBy === column.key
|
|
114
|
+
? sortOrder === 'asc'
|
|
115
|
+
? 'ascending'
|
|
116
|
+
: 'descending'
|
|
117
|
+
: undefined}
|
|
118
|
+
>
|
|
119
|
+
<div class="data-table__header-content">
|
|
120
|
+
<span>{column.header}</span>
|
|
121
|
+
{#if column.sortable}
|
|
122
|
+
<span
|
|
123
|
+
class="data-table__sort-icon"
|
|
124
|
+
class:data-table__sort-icon--active={sortBy === column.key}
|
|
125
|
+
>
|
|
126
|
+
{#if sortBy === column.key}
|
|
127
|
+
<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
128
|
+
{#if sortOrder === 'asc'}
|
|
129
|
+
<path
|
|
130
|
+
fill-rule="evenodd"
|
|
131
|
+
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
132
|
+
clip-rule="evenodd"
|
|
133
|
+
/>
|
|
134
|
+
{:else}
|
|
135
|
+
<path
|
|
136
|
+
fill-rule="evenodd"
|
|
137
|
+
d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z"
|
|
138
|
+
clip-rule="evenodd"
|
|
139
|
+
/>
|
|
140
|
+
{/if}
|
|
141
|
+
</svg>
|
|
142
|
+
{:else}
|
|
143
|
+
<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
144
|
+
<path
|
|
145
|
+
d="M5 8.5a.5.5 0 01.5-.5h9a.5.5 0 010 1h-9A.5.5 0 015 8.5zm0 3a.5.5 0 01.5-.5h9a.5.5 0 010 1h-9a.5.5 0 01-.5-.5z"
|
|
146
|
+
/>
|
|
147
|
+
</svg>
|
|
148
|
+
{/if}
|
|
149
|
+
</span>
|
|
150
|
+
{/if}
|
|
151
|
+
</div>
|
|
152
|
+
</th>
|
|
153
|
+
{/each}
|
|
154
|
+
</tr>
|
|
155
|
+
</thead>
|
|
156
|
+
<tbody class="data-table__body">
|
|
157
|
+
{#if loading}
|
|
158
|
+
{#each skeletonRows as _row}
|
|
159
|
+
<tr class="data-table__row data-table__row--skeleton">
|
|
160
|
+
{#each columns as column}
|
|
161
|
+
<td
|
|
162
|
+
class="data-table__cell data-table__cell--{column.align || 'left'}"
|
|
163
|
+
style={column.width ? `width: ${column.width}` : undefined}
|
|
164
|
+
>
|
|
165
|
+
<div class="data-table__skeleton"></div>
|
|
166
|
+
</td>
|
|
167
|
+
{/each}
|
|
168
|
+
</tr>
|
|
169
|
+
{/each}
|
|
170
|
+
{:else if isEmpty}
|
|
171
|
+
<tr class="data-table__row data-table__row--empty">
|
|
172
|
+
<td colspan={columns.length} class="data-table__cell data-table__cell--empty">
|
|
173
|
+
{#if empty}
|
|
174
|
+
{@render empty()}
|
|
175
|
+
{:else}
|
|
176
|
+
<div class="data-table__empty-default">
|
|
177
|
+
<span>No data available</span>
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
180
|
+
</td>
|
|
181
|
+
</tr>
|
|
182
|
+
{:else}
|
|
183
|
+
{#each data as row, index}
|
|
184
|
+
<tr class="data-table__row" data-row-index={index}>
|
|
185
|
+
{#each columns as column}
|
|
186
|
+
<td
|
|
187
|
+
class="data-table__cell data-table__cell--{column.align || 'left'}"
|
|
188
|
+
style={column.width ? `width: ${column.width}` : undefined}
|
|
189
|
+
>
|
|
190
|
+
{#if column.cell}
|
|
191
|
+
{@render column.cell(getCellValue(row, column.key), row)}
|
|
192
|
+
{:else}
|
|
193
|
+
{getCellValue(row, column.key) ?? '-'}
|
|
194
|
+
{/if}
|
|
195
|
+
</td>
|
|
196
|
+
{/each}
|
|
197
|
+
</tr>
|
|
198
|
+
{/each}
|
|
199
|
+
{/if}
|
|
200
|
+
</tbody>
|
|
201
|
+
</table>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<style>
|
|
205
|
+
/* Container with horizontal overflow */
|
|
206
|
+
.data-table-container {
|
|
207
|
+
width: 100%;
|
|
208
|
+
overflow-x: auto;
|
|
209
|
+
-webkit-overflow-scrolling: touch;
|
|
210
|
+
border-radius: var(--radius-lg, 0.5rem);
|
|
211
|
+
background: var(--color-bg-card, #ffffff);
|
|
212
|
+
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.data-table-container--sticky {
|
|
216
|
+
max-height: 70vh;
|
|
217
|
+
overflow-y: auto;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Table base */
|
|
221
|
+
.data-table {
|
|
222
|
+
width: 100%;
|
|
223
|
+
border-collapse: collapse;
|
|
224
|
+
font-family: inherit;
|
|
225
|
+
font-size: var(--text-sm, 0.875rem);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Header */
|
|
229
|
+
.data-table__head {
|
|
230
|
+
background: var(--color-bg-muted, #f9fafb);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.data-table-container--sticky .data-table__head {
|
|
234
|
+
position: sticky;
|
|
235
|
+
top: 0;
|
|
236
|
+
z-index: 1;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.data-table__header {
|
|
240
|
+
padding: var(--space-md, 0.75rem) var(--space-lg, 1rem);
|
|
241
|
+
font-size: var(--text-xs, 0.75rem);
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
color: var(--color-text-muted, #6b7280);
|
|
244
|
+
text-transform: uppercase;
|
|
245
|
+
letter-spacing: 0.05em;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
|
248
|
+
user-select: none;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.data-table__header--left {
|
|
252
|
+
text-align: left;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.data-table__header--center {
|
|
256
|
+
text-align: center;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.data-table__header--right {
|
|
260
|
+
text-align: right;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.data-table__header--sortable {
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
transition: background-color 0.15s ease;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.data-table__header--sortable:hover {
|
|
269
|
+
background: var(--color-bg-hover, #f3f4f6);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.data-table__header--sortable:focus-visible {
|
|
273
|
+
outline: 2px solid var(--color-primary, #3b82f6);
|
|
274
|
+
outline-offset: -2px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.data-table__header-content {
|
|
278
|
+
display: inline-flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: var(--space-xs, 0.25rem);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Sort icon */
|
|
284
|
+
.data-table__sort-icon {
|
|
285
|
+
display: inline-flex;
|
|
286
|
+
width: 1rem;
|
|
287
|
+
height: 1rem;
|
|
288
|
+
color: var(--color-text-muted, #9ca3af);
|
|
289
|
+
transition: color 0.15s ease;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.data-table__sort-icon svg {
|
|
293
|
+
width: 100%;
|
|
294
|
+
height: 100%;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.data-table__sort-icon--active {
|
|
298
|
+
color: var(--color-primary, #3b82f6);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Body */
|
|
302
|
+
.data-table__body {
|
|
303
|
+
background: var(--color-bg-card, #ffffff);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Row */
|
|
307
|
+
.data-table__row {
|
|
308
|
+
border-bottom: 1px solid var(--color-border, #e5e7eb);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.data-table__row:last-child {
|
|
312
|
+
border-bottom: none;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Row hover */
|
|
316
|
+
.data-table--row-hover
|
|
317
|
+
.data-table__row:not(.data-table__row--skeleton):not(.data-table__row--empty):hover {
|
|
318
|
+
background: var(--color-bg-hover, #f9fafb);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* Striped rows */
|
|
322
|
+
.data-table--striped .data-table__row:nth-child(even) {
|
|
323
|
+
background: var(--color-bg-muted, #f9fafb);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Cell */
|
|
327
|
+
.data-table__cell {
|
|
328
|
+
padding: var(--space-md, 0.75rem) var(--space-lg, 1rem);
|
|
329
|
+
color: var(--color-text, #1f2937);
|
|
330
|
+
vertical-align: middle;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.data-table__cell--left {
|
|
334
|
+
text-align: left;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.data-table__cell--center {
|
|
338
|
+
text-align: center;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.data-table__cell--right {
|
|
342
|
+
text-align: right;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.data-table__cell--empty {
|
|
346
|
+
padding: var(--space-2xl, 3rem) var(--space-lg, 1rem);
|
|
347
|
+
text-align: center;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Empty state */
|
|
351
|
+
.data-table__empty-default {
|
|
352
|
+
display: flex;
|
|
353
|
+
flex-direction: column;
|
|
354
|
+
align-items: center;
|
|
355
|
+
gap: var(--space-sm, 0.5rem);
|
|
356
|
+
color: var(--color-text-muted, #6b7280);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Skeleton loading */
|
|
360
|
+
.data-table__skeleton {
|
|
361
|
+
height: 1rem;
|
|
362
|
+
width: 75%;
|
|
363
|
+
background: linear-gradient(
|
|
364
|
+
90deg,
|
|
365
|
+
var(--color-bg-muted, #e5e7eb) 25%,
|
|
366
|
+
var(--color-bg-skeleton-shine, #f3f4f6) 50%,
|
|
367
|
+
var(--color-bg-muted, #e5e7eb) 75%
|
|
368
|
+
);
|
|
369
|
+
background-size: 200% 100%;
|
|
370
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
371
|
+
border-radius: var(--radius-sm, 0.25rem);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.data-table__cell--center .data-table__skeleton {
|
|
375
|
+
margin: 0 auto;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.data-table__cell--right .data-table__skeleton {
|
|
379
|
+
margin-left: auto;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@keyframes skeleton-shimmer {
|
|
383
|
+
0% {
|
|
384
|
+
background-position: 200% 0;
|
|
385
|
+
}
|
|
386
|
+
100% {
|
|
387
|
+
background-position: -200% 0;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* Dark mode support */
|
|
392
|
+
:global([data-theme='dark']) .data-table-container {
|
|
393
|
+
background: var(--color-bg-card-dark, #1f2937);
|
|
394
|
+
box-shadow: var(--shadow-sm-dark, 0 1px 2px 0 rgb(0 0 0 / 0.2));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
:global([data-theme='dark']) .data-table__head {
|
|
398
|
+
background: var(--color-bg-muted-dark, #374151);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
:global([data-theme='dark']) .data-table__header {
|
|
402
|
+
color: var(--color-text-muted-dark, #9ca3af);
|
|
403
|
+
border-bottom-color: var(--color-border-dark, #4b5563);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
:global([data-theme='dark']) .data-table__header--sortable:hover {
|
|
407
|
+
background: var(--color-bg-hover-dark, #4b5563);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
:global([data-theme='dark']) .data-table__body {
|
|
411
|
+
background: var(--color-bg-card-dark, #1f2937);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
:global([data-theme='dark']) .data-table__row {
|
|
415
|
+
border-bottom-color: var(--color-border-dark, #374151);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
:global([data-theme='dark'])
|
|
419
|
+
.data-table--row-hover
|
|
420
|
+
.data-table__row:not(.data-table__row--skeleton):not(.data-table__row--empty):hover {
|
|
421
|
+
background: var(--color-bg-hover-dark, #374151);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
:global([data-theme='dark']) .data-table--striped .data-table__row:nth-child(even) {
|
|
425
|
+
background: var(--color-bg-muted-dark, #374151);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
:global([data-theme='dark']) .data-table__cell {
|
|
429
|
+
color: var(--color-text-dark, #f9fafb);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
:global([data-theme='dark']) .data-table__empty-default {
|
|
433
|
+
color: var(--color-text-muted-dark, #9ca3af);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
:global([data-theme='dark']) .data-table__skeleton {
|
|
437
|
+
background: linear-gradient(
|
|
438
|
+
90deg,
|
|
439
|
+
var(--color-bg-muted-dark, #374151) 25%,
|
|
440
|
+
var(--color-bg-skeleton-shine-dark, #4b5563) 50%,
|
|
441
|
+
var(--color-bg-muted-dark, #374151) 75%
|
|
442
|
+
);
|
|
443
|
+
background-size: 200% 100%;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/* Mobile touch targets */
|
|
447
|
+
@media (pointer: coarse) {
|
|
448
|
+
.data-table__header {
|
|
449
|
+
padding: var(--space-md, 0.75rem) var(--space-md, 0.75rem);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.data-table__cell {
|
|
453
|
+
padding: var(--space-md, 0.75rem) var(--space-md, 0.75rem);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.data-table__header--sortable {
|
|
457
|
+
min-height: 2.75rem;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataTable - A sortable, customizable data table component
|
|
3
|
+
* Supports custom cell rendering, sorting, loading states, and responsive overflow
|
|
4
|
+
*/
|
|
5
|
+
type SortOrder = 'asc' | 'desc';
|
|
6
|
+
type ColumnAlign = 'left' | 'center' | 'right';
|
|
7
|
+
interface ColumnDef {
|
|
8
|
+
/** Unique key matching the data property */
|
|
9
|
+
key: string;
|
|
10
|
+
/** Header text to display */
|
|
11
|
+
header: string;
|
|
12
|
+
/** Whether this column is sortable */
|
|
13
|
+
sortable?: boolean;
|
|
14
|
+
/** Fixed width (e.g., '100px', '10rem') */
|
|
15
|
+
width?: string;
|
|
16
|
+
/** Text alignment */
|
|
17
|
+
align?: ColumnAlign;
|
|
18
|
+
/** Custom cell renderer - receives (value, row) */
|
|
19
|
+
cell?: import('svelte').Snippet<[value: unknown, row: unknown]>;
|
|
20
|
+
}
|
|
21
|
+
interface Props {
|
|
22
|
+
/** Array of data objects to display */
|
|
23
|
+
data: unknown[];
|
|
24
|
+
/** Column definitions */
|
|
25
|
+
columns: ColumnDef[];
|
|
26
|
+
/** Currently sorted column key */
|
|
27
|
+
sortBy?: string;
|
|
28
|
+
/** Current sort direction */
|
|
29
|
+
sortOrder?: SortOrder;
|
|
30
|
+
/** Callback when sort changes */
|
|
31
|
+
onsort?: (column: string, order: SortOrder) => void;
|
|
32
|
+
/** Show loading skeleton */
|
|
33
|
+
loading?: boolean;
|
|
34
|
+
/** Number of skeleton rows to show when loading */
|
|
35
|
+
loadingRows?: number;
|
|
36
|
+
/** Custom empty state content */
|
|
37
|
+
empty?: import('svelte').Snippet;
|
|
38
|
+
/** Enable row hover effect */
|
|
39
|
+
rowHover?: boolean;
|
|
40
|
+
/** Enable striped rows */
|
|
41
|
+
striped?: boolean;
|
|
42
|
+
/** Make header sticky on scroll */
|
|
43
|
+
stickyHeader?: boolean;
|
|
44
|
+
/** Additional CSS classes */
|
|
45
|
+
class?: string;
|
|
46
|
+
}
|
|
47
|
+
declare const DataTable: import("svelte").Component<Props, {}, "sortBy" | "sortOrder">;
|
|
48
|
+
type DataTable = ReturnType<typeof DataTable>;
|
|
49
|
+
export default DataTable;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DataTable } from './DataTable.svelte';
|
|
@@ -212,6 +212,17 @@
|
|
|
212
212
|
</text>
|
|
213
213
|
{/if}
|
|
214
214
|
{/each}
|
|
215
|
+
|
|
216
|
+
<!-- Background circle for center content -->
|
|
217
|
+
{#if showTotal}
|
|
218
|
+
<circle
|
|
219
|
+
cx={size / 2}
|
|
220
|
+
cy={size / 2}
|
|
221
|
+
r={innerRadius * 0.95}
|
|
222
|
+
fill="var(--color-bg, #ffffff)"
|
|
223
|
+
class="doughnut-chart__center-bg"
|
|
224
|
+
/>
|
|
225
|
+
{/if}
|
|
215
226
|
</svg>
|
|
216
227
|
|
|
217
228
|
<!-- Center total -->
|
|
@@ -290,6 +301,11 @@
|
|
|
290
301
|
pointer-events: none;
|
|
291
302
|
}
|
|
292
303
|
|
|
304
|
+
/* Center Background */
|
|
305
|
+
.doughnut-chart__center-bg {
|
|
306
|
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
|
307
|
+
}
|
|
308
|
+
|
|
293
309
|
/* Center */
|
|
294
310
|
.doughnut-chart__center {
|
|
295
311
|
position: absolute;
|
|
@@ -298,12 +314,13 @@
|
|
|
298
314
|
transform: translate(-50%, -50%);
|
|
299
315
|
text-align: center;
|
|
300
316
|
pointer-events: none;
|
|
317
|
+
z-index: 10;
|
|
301
318
|
}
|
|
302
319
|
|
|
303
320
|
.doughnut-chart__total {
|
|
304
321
|
display: block;
|
|
305
|
-
font-size: var(--text-
|
|
306
|
-
font-weight: var(--font-
|
|
322
|
+
font-size: var(--text-lg, 1.125rem);
|
|
323
|
+
font-weight: var(--font-semibold, 600);
|
|
307
324
|
color: var(--color-text, #1f2937);
|
|
308
325
|
line-height: 1.2;
|
|
309
326
|
}
|
|
@@ -314,6 +331,7 @@
|
|
|
314
331
|
color: var(--color-text-muted, #6b7280);
|
|
315
332
|
text-transform: uppercase;
|
|
316
333
|
letter-spacing: 0.05em;
|
|
334
|
+
margin-top: 2px;
|
|
317
335
|
}
|
|
318
336
|
|
|
319
337
|
/* Legend */
|