@mrintel/villain-ui 0.2.2 → 0.6.3
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/LICENSE +21 -21
- package/README.md +3490 -1296
- package/dist/components/buttons/Button.svelte +27 -0
- package/dist/components/buttons/Button.svelte.d.ts +14 -0
- package/dist/components/buttons/ButtonGroup.svelte +17 -0
- package/dist/components/buttons/ButtonGroup.svelte.d.ts +8 -0
- package/dist/components/buttons/FloatingActionButton.svelte +20 -0
- package/dist/components/buttons/FloatingActionButton.svelte.d.ts +12 -0
- package/dist/components/buttons/IconButton.svelte +23 -0
- package/dist/components/buttons/IconButton.svelte.d.ts +14 -0
- package/dist/components/buttons/LinkButton.svelte +24 -0
- package/dist/components/buttons/LinkButton.svelte.d.ts +15 -0
- package/dist/components/buttons/buttonClasses.d.ts +15 -0
- package/dist/components/buttons/buttonClasses.js +15 -0
- package/dist/components/buttons/index.d.ts +5 -0
- package/dist/components/buttons/index.js +5 -0
- package/dist/components/cards/Card.svelte +60 -0
- package/dist/components/cards/Card.svelte.d.ts +15 -0
- package/dist/components/cards/Container.svelte +17 -0
- package/dist/components/cards/Container.svelte.d.ts +10 -0
- package/dist/components/cards/Divider.svelte +36 -0
- package/dist/components/cards/Divider.svelte.d.ts +11 -0
- package/dist/components/cards/Grid.svelte +55 -0
- package/dist/components/cards/Grid.svelte.d.ts +10 -0
- package/dist/components/cards/Panel.svelte +18 -0
- package/dist/components/cards/Panel.svelte.d.ts +11 -0
- package/dist/components/cards/SectionHeader.svelte +24 -0
- package/dist/components/cards/SectionHeader.svelte.d.ts +12 -0
- package/dist/components/cards/index.d.ts +6 -0
- package/dist/components/cards/index.js +6 -0
- package/dist/components/data/Avatar.svelte +48 -0
- package/dist/components/data/Avatar.svelte.d.ts +10 -0
- package/dist/components/data/Badge.svelte +45 -0
- package/dist/components/data/Badge.svelte.d.ts +14 -0
- package/dist/components/data/CalendarGrid.svelte +433 -0
- package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
- package/dist/components/data/CalendarGrid.types.d.ts +7 -0
- package/dist/components/data/CalendarGrid.types.js +1 -0
- package/dist/components/data/CodeBlock.svelte +119 -0
- package/dist/components/data/CodeBlock.svelte.d.ts +40 -0
- package/dist/components/data/List.svelte +87 -0
- package/dist/components/data/List.svelte.d.ts +15 -0
- package/dist/components/data/Pagination.svelte +121 -0
- package/dist/components/data/Pagination.svelte.d.ts +14 -0
- package/dist/components/data/Sparkline.svelte +117 -0
- package/dist/components/data/Sparkline.svelte.d.ts +43 -0
- package/dist/components/data/Stat.svelte +92 -0
- package/dist/components/data/Stat.svelte.d.ts +11 -0
- package/dist/components/data/Table.svelte +443 -0
- package/dist/components/data/Table.svelte.d.ts +30 -0
- package/dist/components/data/Table.types.d.ts +14 -0
- package/dist/components/data/Table.types.js +1 -0
- package/dist/components/data/Tag.svelte +51 -0
- package/dist/components/data/Tag.svelte.d.ts +13 -0
- package/dist/components/data/index.d.ts +12 -0
- package/dist/components/data/index.js +10 -0
- package/dist/components/forms/Checkbox.svelte +39 -0
- package/dist/components/forms/Checkbox.svelte.d.ts +12 -0
- package/dist/components/forms/DatePicker.svelte +61 -0
- package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
- package/dist/components/forms/DateTimePicker.svelte +63 -0
- package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
- package/dist/components/forms/FileUpload.svelte +136 -0
- package/dist/components/forms/FileUpload.svelte.d.ts +23 -0
- package/dist/components/forms/Input.svelte +282 -0
- package/dist/components/forms/Input.svelte.d.ts +19 -0
- package/dist/components/forms/InputGroup.svelte +7 -0
- package/dist/components/forms/InputGroup.svelte.d.ts +20 -0
- package/dist/components/forms/RadioGroup.svelte +77 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
- package/dist/components/forms/RangeSlider.svelte +90 -0
- package/dist/components/forms/RangeSlider.svelte.d.ts +14 -0
- package/dist/components/forms/Select.svelte +106 -0
- package/dist/components/forms/Select.svelte.d.ts +18 -0
- package/dist/components/forms/Switch.svelte +44 -0
- package/dist/components/forms/Switch.svelte.d.ts +12 -0
- package/dist/components/forms/Textarea.svelte +52 -0
- package/dist/components/forms/Textarea.svelte.d.ts +15 -0
- package/dist/components/forms/TimePicker.svelte +63 -0
- package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
- package/dist/components/forms/formClasses.d.ts +3 -0
- package/dist/components/forms/formClasses.js +3 -0
- package/dist/components/forms/index.d.ts +12 -0
- package/dist/components/forms/index.js +12 -0
- package/dist/components/navigation/Breadcrumbs.svelte +56 -0
- package/dist/components/navigation/Breadcrumbs.svelte.d.ts +15 -0
- package/dist/components/navigation/ContextMenu.svelte +133 -0
- package/dist/components/navigation/ContextMenu.svelte.d.ts +18 -0
- package/dist/components/navigation/DropdownMenu.svelte +139 -0
- package/dist/components/navigation/DropdownMenu.svelte.d.ts +17 -0
- package/dist/components/navigation/Menu.svelte +72 -0
- package/dist/components/navigation/Menu.svelte.d.ts +15 -0
- package/dist/components/navigation/Navbar.svelte +111 -0
- package/dist/components/navigation/Navbar.svelte.d.ts +15 -0
- package/dist/components/navigation/Sidebar.svelte +236 -0
- package/dist/components/navigation/Sidebar.svelte.d.ts +12 -0
- package/dist/components/navigation/Tabs.svelte +86 -0
- package/dist/components/navigation/Tabs.svelte.d.ts +19 -0
- package/dist/components/navigation/index.d.ts +7 -0
- package/dist/components/navigation/index.js +7 -0
- package/dist/components/overlays/Alert.svelte +81 -0
- package/dist/components/overlays/Alert.svelte.d.ts +15 -0
- package/dist/components/overlays/CommandPalette.svelte +182 -0
- package/dist/components/overlays/CommandPalette.svelte.d.ts +16 -0
- package/dist/components/overlays/Drawer.svelte +158 -0
- package/dist/components/overlays/Drawer.svelte.d.ts +16 -0
- package/dist/components/overlays/Dropdown.svelte +62 -0
- package/dist/components/overlays/Dropdown.svelte.d.ts +11 -0
- package/dist/components/overlays/Modal.svelte +125 -0
- package/dist/components/overlays/Modal.svelte.d.ts +15 -0
- package/dist/components/overlays/Popover.svelte +106 -0
- package/dist/components/overlays/Popover.svelte.d.ts +11 -0
- package/dist/components/overlays/ProgressBar.svelte +29 -0
- package/dist/components/overlays/ProgressBar.svelte.d.ts +10 -0
- package/dist/components/overlays/SkeletonLoader.svelte +66 -0
- package/dist/components/overlays/SkeletonLoader.svelte.d.ts +9 -0
- package/dist/components/overlays/Spinner.svelte +33 -0
- package/dist/components/overlays/Spinner.svelte.d.ts +7 -0
- package/dist/components/overlays/Toast.svelte +111 -0
- package/dist/components/overlays/Toast.svelte.d.ts +16 -0
- package/dist/components/overlays/Tooltip.svelte +94 -0
- package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
- package/dist/components/overlays/index.d.ts +11 -0
- package/dist/components/overlays/index.js +11 -0
- package/dist/components/typography/Code.svelte +10 -0
- package/dist/components/typography/Code.svelte.d.ts +6 -0
- package/dist/components/typography/Heading.svelte +15 -0
- package/dist/components/typography/Heading.svelte.d.ts +10 -0
- package/dist/components/typography/Text.svelte +21 -0
- package/dist/components/typography/Text.svelte.d.ts +10 -0
- package/dist/components/typography/index.d.ts +3 -0
- package/dist/components/typography/index.js +3 -0
- package/dist/components/utilities/Accordion.svelte +54 -0
- package/dist/components/utilities/Accordion.svelte.d.ts +17 -0
- package/dist/components/utilities/Carousel.svelte +124 -0
- package/dist/components/utilities/Carousel.svelte.d.ts +16 -0
- package/dist/components/utilities/Collapse.svelte +46 -0
- package/dist/components/utilities/Collapse.svelte.d.ts +10 -0
- package/dist/components/utilities/Hero.svelte +42 -0
- package/dist/components/utilities/Hero.svelte.d.ts +10 -0
- package/dist/components/utilities/Portal.svelte +47 -0
- package/dist/components/utilities/Portal.svelte.d.ts +21 -0
- package/dist/components/utilities/ScrollArea.svelte +33 -0
- package/dist/components/utilities/ScrollArea.svelte.d.ts +8 -0
- package/dist/components/utilities/SystemConsole.svelte +310 -0
- package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
- package/dist/components/utilities/SystemInterface.svelte +726 -0
- package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
- package/dist/components/utilities/index.d.ts +9 -0
- package/dist/components/utilities/index.js +8 -0
- package/dist/components/utilities/utilities.types.d.ts +46 -0
- package/dist/components/utilities/utilities.types.js +4 -0
- package/dist/index.d.ts +60 -175
- package/dist/index.js +24 -4560
- package/dist/lib/internal/id.d.ts +12 -0
- package/dist/lib/internal/id.js +15 -0
- package/dist/theme.css +2821 -0
- package/package.json +83 -75
- package/dist/index.css +0 -1
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
<script lang="ts">import Checkbox from '../forms/Checkbox.svelte';
|
|
2
|
+
let { columns, data, striped = false, hoverable = true, compact = false, stickyHeader = false, loading = false, loadingMessage = 'Loading...', emptyMessage = 'No data available', emptyState, filterFn, onSort, onRowClick, selectable = false, selectedKeys = $bindable(new Set()), rowKey = 'id', onSelectionChange, class: className = '', children, ...restProps } = $props();
|
|
3
|
+
// Determine if we're in dynamic mode or manual markup mode
|
|
4
|
+
const isDynamicMode = $derived(columns && data);
|
|
5
|
+
// Apply user-defined filter if provided
|
|
6
|
+
const filteredData = $derived.by(() => {
|
|
7
|
+
if (!isDynamicMode || !data)
|
|
8
|
+
return [];
|
|
9
|
+
if (!filterFn)
|
|
10
|
+
return data;
|
|
11
|
+
return data.filter(filterFn);
|
|
12
|
+
});
|
|
13
|
+
// Track current sort state
|
|
14
|
+
let sortColumn = $state(null);
|
|
15
|
+
let sortDirection = $state(null);
|
|
16
|
+
function handleSort(columnKey) {
|
|
17
|
+
if (!onSort)
|
|
18
|
+
return;
|
|
19
|
+
// Cycle through: null -> asc -> desc -> null
|
|
20
|
+
if (sortColumn !== columnKey) {
|
|
21
|
+
sortColumn = columnKey;
|
|
22
|
+
sortDirection = 'asc';
|
|
23
|
+
}
|
|
24
|
+
else if (sortDirection === 'asc') {
|
|
25
|
+
sortDirection = 'desc';
|
|
26
|
+
}
|
|
27
|
+
else if (sortDirection === 'desc') {
|
|
28
|
+
sortColumn = null;
|
|
29
|
+
sortDirection = null;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
sortDirection = 'asc';
|
|
33
|
+
}
|
|
34
|
+
onSort(columnKey, sortDirection);
|
|
35
|
+
}
|
|
36
|
+
function handleRowClick(row) {
|
|
37
|
+
if (onRowClick) {
|
|
38
|
+
onRowClick(row);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Selection logic
|
|
42
|
+
function getRowKey(row, index) {
|
|
43
|
+
return row[rowKey] ?? index;
|
|
44
|
+
}
|
|
45
|
+
const allSelected = $derived.by(() => {
|
|
46
|
+
if (!selectable || filteredData.length === 0)
|
|
47
|
+
return false;
|
|
48
|
+
return filteredData.every((row, i) => selectedKeys.has(getRowKey(row, i)));
|
|
49
|
+
});
|
|
50
|
+
const someSelected = $derived.by(() => {
|
|
51
|
+
if (!selectable || filteredData.length === 0)
|
|
52
|
+
return false;
|
|
53
|
+
const selectedCount = filteredData.filter((row, i) => selectedKeys.has(getRowKey(row, i))).length;
|
|
54
|
+
return selectedCount > 0 && selectedCount < filteredData.length;
|
|
55
|
+
});
|
|
56
|
+
function toggleSelectAll() {
|
|
57
|
+
if (allSelected) {
|
|
58
|
+
// Deselect all
|
|
59
|
+
selectedKeys = new Set();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Select all filtered rows
|
|
63
|
+
selectedKeys = new Set(filteredData.map((row, i) => getRowKey(row, i)));
|
|
64
|
+
}
|
|
65
|
+
notifySelectionChange();
|
|
66
|
+
}
|
|
67
|
+
function toggleRowSelection(row, index) {
|
|
68
|
+
const key = getRowKey(row, index);
|
|
69
|
+
const newSet = new Set(selectedKeys);
|
|
70
|
+
if (newSet.has(key)) {
|
|
71
|
+
newSet.delete(key);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
newSet.add(key);
|
|
75
|
+
}
|
|
76
|
+
selectedKeys = newSet;
|
|
77
|
+
notifySelectionChange();
|
|
78
|
+
}
|
|
79
|
+
function isRowSelected(row, index) {
|
|
80
|
+
return selectedKeys.has(getRowKey(row, index));
|
|
81
|
+
}
|
|
82
|
+
function notifySelectionChange() {
|
|
83
|
+
if (onSelectionChange) {
|
|
84
|
+
const selectedRows = filteredData.filter((row, i) => selectedKeys.has(getRowKey(row, i)));
|
|
85
|
+
onSelectionChange(selectedKeys, selectedRows);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<div class="panel-raised rounded-[var(--radius-xl)] overflow-hidden {className}" {...restProps}>
|
|
91
|
+
<table class="mrdv-table w-full" class:striped class:hoverable class:compact class:sticky-header={stickyHeader}>
|
|
92
|
+
{#if isDynamicMode}
|
|
93
|
+
<!-- Dynamic mode: render from columns and data -->
|
|
94
|
+
<thead>
|
|
95
|
+
<tr>
|
|
96
|
+
{#if selectable}
|
|
97
|
+
<th class="select-cell">
|
|
98
|
+
<Checkbox
|
|
99
|
+
checked={allSelected}
|
|
100
|
+
onchange={toggleSelectAll}
|
|
101
|
+
class={someSelected ? 'indeterminate' : ''}
|
|
102
|
+
/>
|
|
103
|
+
</th>
|
|
104
|
+
{/if}
|
|
105
|
+
{#each columns as column}
|
|
106
|
+
<th
|
|
107
|
+
style:text-align={column.align || 'left'}
|
|
108
|
+
class:sortable={column.sortable && onSort}
|
|
109
|
+
class:sorted={sortColumn === column.key}
|
|
110
|
+
onclick={() => column.sortable && handleSort(column.key)}
|
|
111
|
+
>
|
|
112
|
+
<div class="th-content">
|
|
113
|
+
{column.label}
|
|
114
|
+
{#if column.sortable && onSort}
|
|
115
|
+
<span class="sort-indicator">
|
|
116
|
+
{#if sortColumn === column.key}
|
|
117
|
+
{#if sortDirection === 'asc'}
|
|
118
|
+
<svg
|
|
119
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
120
|
+
width="16"
|
|
121
|
+
height="16"
|
|
122
|
+
viewBox="0 0 24 24"
|
|
123
|
+
fill="none"
|
|
124
|
+
stroke="currentColor"
|
|
125
|
+
stroke-width="2"
|
|
126
|
+
stroke-linecap="round"
|
|
127
|
+
stroke-linejoin="round"
|
|
128
|
+
>
|
|
129
|
+
<path d="m18 15-6-6-6 6" />
|
|
130
|
+
</svg>
|
|
131
|
+
{:else if sortDirection === 'desc'}
|
|
132
|
+
<svg
|
|
133
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
134
|
+
width="16"
|
|
135
|
+
height="16"
|
|
136
|
+
viewBox="0 0 24 24"
|
|
137
|
+
fill="none"
|
|
138
|
+
stroke="currentColor"
|
|
139
|
+
stroke-width="2"
|
|
140
|
+
stroke-linecap="round"
|
|
141
|
+
stroke-linejoin="round"
|
|
142
|
+
>
|
|
143
|
+
<path d="m6 9 6 6 6-6" />
|
|
144
|
+
</svg>
|
|
145
|
+
{/if}
|
|
146
|
+
{:else}
|
|
147
|
+
<svg
|
|
148
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
149
|
+
width="16"
|
|
150
|
+
height="16"
|
|
151
|
+
viewBox="0 0 24 24"
|
|
152
|
+
fill="none"
|
|
153
|
+
stroke="currentColor"
|
|
154
|
+
stroke-width="2"
|
|
155
|
+
stroke-linecap="round"
|
|
156
|
+
stroke-linejoin="round"
|
|
157
|
+
opacity="0.3"
|
|
158
|
+
>
|
|
159
|
+
<path d="m7 15 5 5 5-5" />
|
|
160
|
+
<path d="m7 9 5-5 5 5" />
|
|
161
|
+
</svg>
|
|
162
|
+
{/if}
|
|
163
|
+
</span>
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
</th>
|
|
167
|
+
{/each}
|
|
168
|
+
</tr>
|
|
169
|
+
</thead>
|
|
170
|
+
<tbody>
|
|
171
|
+
{#if loading}
|
|
172
|
+
<!-- Loading state -->
|
|
173
|
+
<tr class="state-row">
|
|
174
|
+
<td colspan={(columns?.length || 0) + (selectable ? 1 : 0)} class="text-center">
|
|
175
|
+
<div class="loading-container">
|
|
176
|
+
<svg
|
|
177
|
+
class="loading-spinner"
|
|
178
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
179
|
+
width="24"
|
|
180
|
+
height="24"
|
|
181
|
+
viewBox="0 0 24 24"
|
|
182
|
+
fill="none"
|
|
183
|
+
stroke="currentColor"
|
|
184
|
+
stroke-width="2"
|
|
185
|
+
stroke-linecap="round"
|
|
186
|
+
stroke-linejoin="round"
|
|
187
|
+
>
|
|
188
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
189
|
+
</svg>
|
|
190
|
+
<span>{loadingMessage}</span>
|
|
191
|
+
</div>
|
|
192
|
+
</td>
|
|
193
|
+
</tr>
|
|
194
|
+
{:else if filteredData.length === 0}
|
|
195
|
+
<!-- Empty state -->
|
|
196
|
+
<tr class="state-row">
|
|
197
|
+
<td colspan={(columns?.length || 0) + (selectable ? 1 : 0)} class="text-center">
|
|
198
|
+
{#if emptyState}
|
|
199
|
+
{@render emptyState()}
|
|
200
|
+
{:else}
|
|
201
|
+
<div class="empty-container">
|
|
202
|
+
<svg
|
|
203
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
204
|
+
width="48"
|
|
205
|
+
height="48"
|
|
206
|
+
viewBox="0 0 24 24"
|
|
207
|
+
fill="none"
|
|
208
|
+
stroke="currentColor"
|
|
209
|
+
stroke-width="1.5"
|
|
210
|
+
stroke-linecap="round"
|
|
211
|
+
stroke-linejoin="round"
|
|
212
|
+
opacity="0.3"
|
|
213
|
+
>
|
|
214
|
+
<circle cx="11" cy="11" r="8" />
|
|
215
|
+
<path d="m21 21-4.3-4.3" />
|
|
216
|
+
</svg>
|
|
217
|
+
<p>{emptyMessage}</p>
|
|
218
|
+
</div>
|
|
219
|
+
{/if}
|
|
220
|
+
</td>
|
|
221
|
+
</tr>
|
|
222
|
+
{:else}
|
|
223
|
+
<!-- Data rows -->
|
|
224
|
+
{#each filteredData as row, index}
|
|
225
|
+
<tr
|
|
226
|
+
class:clickable={onRowClick}
|
|
227
|
+
class:selected={selectable && isRowSelected(row, index)}
|
|
228
|
+
onclick={() => handleRowClick(row)}
|
|
229
|
+
>
|
|
230
|
+
{#if selectable}
|
|
231
|
+
<td class="select-cell" onclick={(e) => e.stopPropagation()}>
|
|
232
|
+
<Checkbox
|
|
233
|
+
checked={isRowSelected(row, index)}
|
|
234
|
+
onchange={() => toggleRowSelection(row, index)}
|
|
235
|
+
/>
|
|
236
|
+
</td>
|
|
237
|
+
{/if}
|
|
238
|
+
{#each columns as column}
|
|
239
|
+
<td style:text-align={column.align || 'left'}>
|
|
240
|
+
{#if restProps[`cell_${column.key}`]}
|
|
241
|
+
{@render restProps[`cell_${column.key}`]({ value: row[column.key], row })}
|
|
242
|
+
{:else if column.render}
|
|
243
|
+
{@html column.render(row[column.key], row)}
|
|
244
|
+
{:else}
|
|
245
|
+
{row[column.key]}
|
|
246
|
+
{/if}
|
|
247
|
+
</td>
|
|
248
|
+
{/each}
|
|
249
|
+
</tr>
|
|
250
|
+
{/each}
|
|
251
|
+
{/if}
|
|
252
|
+
</tbody>
|
|
253
|
+
{:else}
|
|
254
|
+
<!-- Manual markup mode: use children snippet -->
|
|
255
|
+
{@render children?.()}
|
|
256
|
+
{/if}
|
|
257
|
+
</table>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<style>
|
|
261
|
+
.mrdv-table :global(thead) {
|
|
262
|
+
background: var(--color-base-2);
|
|
263
|
+
border-bottom: 2px solid var(--color-border-strong);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.mrdv-table :global(thead th) {
|
|
267
|
+
color: var(--color-text);
|
|
268
|
+
font-weight: 600;
|
|
269
|
+
text-transform: uppercase;
|
|
270
|
+
letter-spacing: 0.05em;
|
|
271
|
+
text-align: left;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.mrdv-table.compact :global(th),
|
|
275
|
+
.mrdv-table.compact :global(td) {
|
|
276
|
+
padding: 0.5rem 1rem;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.mrdv-table:not(.compact) :global(th),
|
|
280
|
+
.mrdv-table:not(.compact) :global(td) {
|
|
281
|
+
padding: 1rem 1.5rem;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.mrdv-table :global(tbody) {
|
|
285
|
+
position: relative;
|
|
286
|
+
z-index: 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.mrdv-table :global(tbody tr) {
|
|
290
|
+
border-bottom: 1px solid var(--color-border);
|
|
291
|
+
transition: all var(--duration-200) var(--ease-luxe);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.mrdv-table.hoverable :global(tbody tr:hover) {
|
|
295
|
+
background: var(--color-accent-overlay-5);
|
|
296
|
+
transform: translateY(-1px);
|
|
297
|
+
box-shadow:
|
|
298
|
+
0 2px 8px var(--color-shadow-overlay-20),
|
|
299
|
+
var(--shadow-accent-glow);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.mrdv-table.striped :global(tbody tr:nth-child(even)) {
|
|
303
|
+
background: var(--color-neutral-overlay-2);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.mrdv-table.striped.hoverable :global(tbody tr:nth-child(even):hover) {
|
|
307
|
+
background: var(--color-accent-overlay-5);
|
|
308
|
+
transform: translateY(-1px);
|
|
309
|
+
box-shadow:
|
|
310
|
+
0 2px 8px var(--color-shadow-overlay-20),
|
|
311
|
+
var(--shadow-accent-glow);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.mrdv-table :global(th),
|
|
315
|
+
.mrdv-table :global(td) {
|
|
316
|
+
text-align: left;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Sortable column styles */
|
|
320
|
+
.mrdv-table th.sortable {
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
user-select: none;
|
|
323
|
+
transition: all var(--duration-200) var(--ease-luxe);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.mrdv-table th.sortable:hover {
|
|
327
|
+
background: var(--color-accent-overlay-5);
|
|
328
|
+
color: var(--color-accent);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.mrdv-table th.sorted {
|
|
332
|
+
color: var(--color-accent);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.th-content {
|
|
336
|
+
display: flex;
|
|
337
|
+
align-items: center;
|
|
338
|
+
gap: 0.5rem;
|
|
339
|
+
justify-content: space-between;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.sort-indicator {
|
|
343
|
+
display: flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
flex-shrink: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Clickable row styles */
|
|
349
|
+
.mrdv-table tbody tr.clickable {
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Selection styles */
|
|
354
|
+
.select-cell {
|
|
355
|
+
width: 3rem;
|
|
356
|
+
padding-left: 1rem !important;
|
|
357
|
+
padding-right: 0.5rem !important;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.mrdv-table tbody tr.selected {
|
|
361
|
+
background: var(--color-accent-overlay-10);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.mrdv-table tbody tr.selected:hover {
|
|
365
|
+
background: var(--color-accent-overlay-15);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.mrdv-table.striped tbody tr.selected:nth-child(even) {
|
|
369
|
+
background: var(--color-accent-overlay-10);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Indeterminate checkbox styling */
|
|
373
|
+
.select-cell :global(.indeterminate input[type="checkbox"]) {
|
|
374
|
+
background: var(--color-accent);
|
|
375
|
+
border-color: var(--color-accent);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.select-cell :global(.indeterminate input[type="checkbox"])::after {
|
|
379
|
+
content: '';
|
|
380
|
+
position: absolute;
|
|
381
|
+
left: 50%;
|
|
382
|
+
top: 50%;
|
|
383
|
+
transform: translate(-50%, -50%);
|
|
384
|
+
width: 10px;
|
|
385
|
+
height: 2px;
|
|
386
|
+
background: white;
|
|
387
|
+
border-radius: 1px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Sticky header */
|
|
391
|
+
.mrdv-table.sticky-header thead {
|
|
392
|
+
position: sticky;
|
|
393
|
+
top: 0;
|
|
394
|
+
z-index: 10;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* Loading state */
|
|
398
|
+
.loading-container {
|
|
399
|
+
display: flex;
|
|
400
|
+
align-items: center;
|
|
401
|
+
justify-content: center;
|
|
402
|
+
gap: 0.75rem;
|
|
403
|
+
padding: 3rem 1rem;
|
|
404
|
+
color: var(--color-text-secondary);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.loading-spinner {
|
|
408
|
+
animation: spin 1s linear infinite;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
@keyframes spin {
|
|
412
|
+
from {
|
|
413
|
+
transform: rotate(0deg);
|
|
414
|
+
}
|
|
415
|
+
to {
|
|
416
|
+
transform: rotate(360deg);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Empty state */
|
|
421
|
+
.empty-container {
|
|
422
|
+
display: flex;
|
|
423
|
+
flex-direction: column;
|
|
424
|
+
align-items: center;
|
|
425
|
+
justify-content: center;
|
|
426
|
+
gap: 1rem;
|
|
427
|
+
padding: 3rem 1rem;
|
|
428
|
+
color: var(--color-text-muted);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.empty-container p {
|
|
432
|
+
margin: 0;
|
|
433
|
+
font-size: 0.875rem;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/* State row */
|
|
437
|
+
.state-row td {
|
|
438
|
+
border-bottom: none !important;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.text-center {
|
|
442
|
+
text-align: center;
|
|
443
|
+
}</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { Column, SortDirection, RowKey } from './Table.types';
|
|
3
|
+
interface Props {
|
|
4
|
+
columns?: Column[];
|
|
5
|
+
data?: Record<string, any>[];
|
|
6
|
+
striped?: boolean;
|
|
7
|
+
hoverable?: boolean;
|
|
8
|
+
compact?: boolean;
|
|
9
|
+
stickyHeader?: boolean;
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
loadingMessage?: string;
|
|
12
|
+
emptyMessage?: string;
|
|
13
|
+
emptyState?: Snippet;
|
|
14
|
+
filterFn?: (row: Record<string, any>) => boolean;
|
|
15
|
+
onSort?: (columnKey: string, direction: SortDirection) => void;
|
|
16
|
+
onRowClick?: (row: Record<string, any>) => void;
|
|
17
|
+
selectable?: boolean;
|
|
18
|
+
selectedKeys?: Set<RowKey>;
|
|
19
|
+
rowKey?: string;
|
|
20
|
+
onSelectionChange?: (selectedKeys: Set<RowKey>, rows: Record<string, any>[]) => void;
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
[key: `cell_${string}`]: Snippet<[{
|
|
23
|
+
value: any;
|
|
24
|
+
row: any;
|
|
25
|
+
}]> | undefined;
|
|
26
|
+
class?: string;
|
|
27
|
+
}
|
|
28
|
+
declare const Table: import("svelte").Component<Props, {}, "selectedKeys">;
|
|
29
|
+
type Table = ReturnType<typeof Table>;
|
|
30
|
+
export default Table;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SortDirection = 'asc' | 'desc' | null;
|
|
2
|
+
export interface Column {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
align?: 'left' | 'center' | 'right';
|
|
6
|
+
sortable?: boolean;
|
|
7
|
+
render?: (value: any, row: any) => any;
|
|
8
|
+
}
|
|
9
|
+
export type RowKey = string | number;
|
|
10
|
+
export interface SelectionState {
|
|
11
|
+
selectedKeys: Set<RowKey>;
|
|
12
|
+
allSelected: boolean;
|
|
13
|
+
someSelected: boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script lang="ts">"use strict";
|
|
2
|
+
let { variant = 'default', size = 'md', dismissible = false, onDismiss, ondismiss, children, icon } = $props();
|
|
3
|
+
const onDismissCallback = $derived(onDismiss ?? ondismiss);
|
|
4
|
+
const variantClasses = {
|
|
5
|
+
default: 'bg-[var(--color-base-2)] text-[var(--color-text-soft)] border-[var(--color-border)]',
|
|
6
|
+
accent: 'bg-[var(--color-secondary-overlay-10)] text-[var(--color-accent-soft)] border-[var(--color-accent)] shadow-[0_0_12px_var(--color-secondary-overlay-20)]',
|
|
7
|
+
info: 'bg-[var(--color-info-overlay-10)] text-[var(--color-info)] border-[var(--color-info)] shadow-[0_0_12px_var(--color-info-overlay-20)]'
|
|
8
|
+
};
|
|
9
|
+
const sizeClasses = {
|
|
10
|
+
sm: 'px-2 py-0.5 text-xs gap-1',
|
|
11
|
+
md: 'px-3 py-1.5 text-sm gap-2',
|
|
12
|
+
lg: 'px-4 py-2 text-base gap-2'
|
|
13
|
+
};
|
|
14
|
+
const classes = $derived(`inline-flex items-center rounded-[var(--radius-pill)] border font-[var(--font-body)] font-medium transition-all duration-300 hover:scale-[1.02] ${sizeClasses[size]} ${variantClasses[variant]}`);
|
|
15
|
+
function handleDismiss() {
|
|
16
|
+
onDismissCallback?.();
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<span class={classes}>
|
|
21
|
+
{#if icon}
|
|
22
|
+
<span class="inline-flex items-center justify-center">
|
|
23
|
+
{@render icon()}
|
|
24
|
+
</span>
|
|
25
|
+
{/if}
|
|
26
|
+
{@render children?.()}
|
|
27
|
+
|
|
28
|
+
{#if dismissible}
|
|
29
|
+
<button
|
|
30
|
+
class="ml-1 opacity-60 hover:opacity-100 hover:text-[var(--color-text)] transition-all duration-300"
|
|
31
|
+
onclick={handleDismiss}
|
|
32
|
+
aria-label="Remove tag"
|
|
33
|
+
>
|
|
34
|
+
<svg
|
|
35
|
+
width="14"
|
|
36
|
+
height="14"
|
|
37
|
+
viewBox="0 0 14 14"
|
|
38
|
+
fill="none"
|
|
39
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
40
|
+
>
|
|
41
|
+
<path
|
|
42
|
+
d="M10.5 3.5L3.5 10.5M3.5 3.5L10.5 10.5"
|
|
43
|
+
stroke="currentColor"
|
|
44
|
+
stroke-width="1.5"
|
|
45
|
+
stroke-linecap="round"
|
|
46
|
+
stroke-linejoin="round"
|
|
47
|
+
/>
|
|
48
|
+
</svg>
|
|
49
|
+
</button>
|
|
50
|
+
{/if}
|
|
51
|
+
</span>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
variant?: 'default' | 'accent' | 'info';
|
|
3
|
+
size?: 'sm' | 'md' | 'lg';
|
|
4
|
+
dismissible?: boolean;
|
|
5
|
+
onDismiss?: () => void;
|
|
6
|
+
/** @deprecated Use onDismiss */
|
|
7
|
+
ondismiss?: () => void;
|
|
8
|
+
children?: import('svelte').Snippet;
|
|
9
|
+
icon?: import('svelte').Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const Tag: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Tag = ReturnType<typeof Tag>;
|
|
13
|
+
export default Tag;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { default as Table } from './Table.svelte';
|
|
2
|
+
export type { Column as TableColumn, SortDirection, RowKey, SelectionState } from './Table.types';
|
|
3
|
+
export { default as Pagination } from './Pagination.svelte';
|
|
4
|
+
export { default as Badge } from './Badge.svelte';
|
|
5
|
+
export { default as Tag } from './Tag.svelte';
|
|
6
|
+
export { default as List } from './List.svelte';
|
|
7
|
+
export { default as Avatar } from './Avatar.svelte';
|
|
8
|
+
export { default as CodeBlock } from './CodeBlock.svelte';
|
|
9
|
+
export { default as Stat } from './Stat.svelte';
|
|
10
|
+
export { default as CalendarGrid } from './CalendarGrid.svelte';
|
|
11
|
+
export type { CalendarEvent } from './CalendarGrid.types';
|
|
12
|
+
export { default as Sparkline } from './Sparkline.svelte';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as Table } from './Table.svelte';
|
|
2
|
+
export { default as Pagination } from './Pagination.svelte';
|
|
3
|
+
export { default as Badge } from './Badge.svelte';
|
|
4
|
+
export { default as Tag } from './Tag.svelte';
|
|
5
|
+
export { default as List } from './List.svelte';
|
|
6
|
+
export { default as Avatar } from './Avatar.svelte';
|
|
7
|
+
export { default as CodeBlock } from './CodeBlock.svelte';
|
|
8
|
+
export { default as Stat } from './Stat.svelte';
|
|
9
|
+
export { default as CalendarGrid } from './CalendarGrid.svelte';
|
|
10
|
+
export { default as Sparkline } from './Sparkline.svelte';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">import { createId } from '../../lib/internal/id.js';
|
|
2
|
+
let { checked = $bindable(false), disabled = false, label, id = createId('checkbox'), onchange, iconBefore, class: className = '' } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''} {className}">
|
|
6
|
+
<input
|
|
7
|
+
type="checkbox"
|
|
8
|
+
{id}
|
|
9
|
+
{disabled}
|
|
10
|
+
bind:checked
|
|
11
|
+
onchange={onchange}
|
|
12
|
+
class="w-6 h-6 rounded-sm border-2 border-border-strong bg-transparent appearance-none transition-all duration-200 ease-luxe cursor-pointer checked:bg-accent checked:border-accent checked:accent-glow focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {disabled ? 'cursor-not-allowed' : ''}"
|
|
13
|
+
/>
|
|
14
|
+
{#if iconBefore}
|
|
15
|
+
<span class="inline-flex items-center justify-center text-text-soft">
|
|
16
|
+
{@render iconBefore()}
|
|
17
|
+
</span>
|
|
18
|
+
{/if}
|
|
19
|
+
{#if label}
|
|
20
|
+
<span class="text-text text-sm select-none">
|
|
21
|
+
{label}
|
|
22
|
+
</span>
|
|
23
|
+
{/if}
|
|
24
|
+
</label>
|
|
25
|
+
|
|
26
|
+
<style>
|
|
27
|
+
input[type="checkbox"]:checked::after {
|
|
28
|
+
content: '';
|
|
29
|
+
position: absolute;
|
|
30
|
+
left: 50%;
|
|
31
|
+
top: 50%;
|
|
32
|
+
transform: translate(-50%, -50%);
|
|
33
|
+
width: 12px;
|
|
34
|
+
height: 12px;
|
|
35
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 6L5 8.5L9.5 3.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
36
|
+
background-size: contain;
|
|
37
|
+
background-repeat: no-repeat;
|
|
38
|
+
background-position: center;
|
|
39
|
+
}</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface Props {
|
|
2
|
+
checked?: boolean;
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
label?: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
onchange?: (event: Event) => void;
|
|
7
|
+
iconBefore?: import('svelte').Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
|
|
11
|
+
type Checkbox = ReturnType<typeof Checkbox>;
|
|
12
|
+
export default Checkbox;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">import { createId } from '../../lib/internal/id.js';
|
|
2
|
+
import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
|
|
3
|
+
let { value = $bindable(''), min, max, placeholder, disabled = false, error = false, label, id = createId('datepicker'), onchange, class: className = '', } = $props();
|
|
4
|
+
const errorClasses = $derived(error ? 'error-state' : '');
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if label}
|
|
8
|
+
<div>
|
|
9
|
+
<label for={id} class="text-text-soft text-sm mb-2 block">
|
|
10
|
+
{label}
|
|
11
|
+
</label>
|
|
12
|
+
<input
|
|
13
|
+
type="date"
|
|
14
|
+
{id}
|
|
15
|
+
{placeholder}
|
|
16
|
+
{disabled}
|
|
17
|
+
{min}
|
|
18
|
+
{max}
|
|
19
|
+
bind:value
|
|
20
|
+
{onchange}
|
|
21
|
+
class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
|
|
22
|
+
? disabledClasses
|
|
23
|
+
: ''} {className}"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
{:else}
|
|
27
|
+
<input
|
|
28
|
+
type="date"
|
|
29
|
+
{id}
|
|
30
|
+
{placeholder}
|
|
31
|
+
{disabled}
|
|
32
|
+
{min}
|
|
33
|
+
{max}
|
|
34
|
+
bind:value
|
|
35
|
+
{onchange}
|
|
36
|
+
class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
|
|
37
|
+
? disabledClasses
|
|
38
|
+
: ''} {className}"
|
|
39
|
+
/>
|
|
40
|
+
{/if}
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
input[type='date'] {
|
|
44
|
+
min-height: 3rem;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
input[type='date']::-webkit-calendar-picker-indicator {
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
filter: invert(0.7);
|
|
51
|
+
transition: filter 120ms var(--ease-sharp);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
input[type='date']:hover::-webkit-calendar-picker-indicator {
|
|
55
|
+
filter: invert(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
input[type='date']:disabled::-webkit-calendar-picker-indicator {
|
|
59
|
+
cursor: not-allowed;
|
|
60
|
+
opacity: 0.4;
|
|
61
|
+
}</style>
|