@warkypublic/svelix 0.1.39 → 0.1.41
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/BetterMenu/BetterMenuAsyncButton.svelte +41 -24
- package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte.d.ts +1 -1
- package/dist/components/BetterMenu/types.d.ts +2 -1
- package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +2 -3
- package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +2 -3
- package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +2 -3
- package/dist/components/Former/FormerButtonArea.svelte +3 -2
- package/dist/components/Former/FormerDrawer.svelte +130 -124
- package/dist/components/Former/FormerDrawer.svelte.d.ts +1 -1
- package/dist/components/FormerControllers/InlineWrapper.svelte +4 -34
- package/dist/components/Gridler/adapters/GridlerResolveSpecAdapter.d.ts +1 -0
- package/dist/components/Gridler/adapters/GridlerResolveSpecAdapter.js +6 -3
- package/dist/components/Gridler/adapters/GridlerRestHeaderSpecAdapter.d.ts +1 -0
- package/dist/components/Gridler/adapters/GridlerRestHeaderSpecAdapter.js +6 -3
- package/dist/components/Gridler/components/GridlerFull.svelte +2 -0
- package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +340 -0
- package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte.d.ts +3 -0
- package/dist/components/Gridler/components/GridlerSearch.svelte +2 -4
- package/dist/components/Gridler/components/GridlerSearchToggle.svelte +2 -4
- package/dist/components/Gridler/types.d.ts +19 -0
- package/dist/components/Icons/IconAdd.svelte +8 -0
- package/dist/components/Icons/IconAdd.svelte.d.ts +7 -0
- package/dist/components/Icons/IconAlertCircle.svelte +9 -0
- package/dist/components/Icons/IconAlertCircle.svelte.d.ts +7 -0
- package/dist/components/Icons/IconAlertTriangle.svelte +9 -0
- package/dist/components/Icons/IconAlertTriangle.svelte.d.ts +7 -0
- package/dist/components/Icons/IconCamera.svelte +8 -0
- package/dist/components/Icons/IconCamera.svelte.d.ts +7 -0
- package/dist/components/Icons/IconClose.svelte +7 -0
- package/dist/components/Icons/IconClose.svelte.d.ts +7 -0
- package/dist/components/Icons/IconEdit.svelte +8 -0
- package/dist/components/Icons/IconEdit.svelte.d.ts +7 -0
- package/dist/components/Icons/IconFilter.svelte +7 -0
- package/dist/components/Icons/IconFilter.svelte.d.ts +7 -0
- package/dist/components/Icons/IconSearch.svelte +8 -0
- package/dist/components/Icons/IconSearch.svelte.d.ts +7 -0
- package/dist/components/Icons/IconSort.svelte +11 -0
- package/dist/components/Icons/IconSort.svelte.d.ts +8 -0
- package/dist/components/Icons/IconTrash.svelte +10 -0
- package/dist/components/Icons/IconTrash.svelte.d.ts +7 -0
- package/dist/components/Icons/index.d.ts +11 -0
- package/dist/components/Icons/index.js +11 -0
- package/dist/components/Icons/strings.d.ts +11 -0
- package/dist/components/Icons/strings.js +15 -0
- package/dist/components/Screenshot/Screenshot.svelte +2 -15
- package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +4 -13
- package/dist/components/Types/generic_grid.d.ts +18 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/llm/COMPONENT_GUIDE.md +98 -0
- package/llm/README.md +93 -0
- package/package.json +46 -47
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import GridlerFull from './GridlerFull.svelte';
|
|
3
|
+
import FormerDrawer from '../../Former/FormerDrawer.svelte';
|
|
4
|
+
import TextInputCtrl from '../../FormerControllers/TextInputCtrl.svelte';
|
|
5
|
+
import NumberInputCtrl from '../../FormerControllers/NumberInputCtrl.svelte';
|
|
6
|
+
import NativeSelectCtrl from '../../FormerControllers/NativeSelectCtrl.svelte';
|
|
7
|
+
import type { FormRequestType } from '../../Former/types';
|
|
8
|
+
import type { GridlerContextMenuItem } from '../types';
|
|
9
|
+
import IconEdit from '../../Icons/IconEdit.svelte';
|
|
10
|
+
import IconTrash from '../../Icons/IconTrash.svelte';
|
|
11
|
+
import { iconAddSvg, iconEditSvg, iconTrashSvg } from '../../Icons/strings';
|
|
12
|
+
|
|
13
|
+
interface EmployeeRow {
|
|
14
|
+
id: number;
|
|
15
|
+
name: string;
|
|
16
|
+
email: string;
|
|
17
|
+
department: string;
|
|
18
|
+
salary: number;
|
|
19
|
+
status: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DEPARTMENTS = ['Engineering', 'Marketing', 'Sales', 'HR', 'Finance', 'Operations'];
|
|
23
|
+
const STATUSES = ['Active', 'Pending', 'Inactive'];
|
|
24
|
+
const FIRST = ['Alice', 'Bob', 'Carol', 'David', 'Eve', 'Frank', 'Grace', 'Henry'];
|
|
25
|
+
const LAST = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia'];
|
|
26
|
+
|
|
27
|
+
function makeRow(i: number): EmployeeRow {
|
|
28
|
+
const first = FIRST[i % FIRST.length];
|
|
29
|
+
const last = LAST[Math.floor(i / FIRST.length) % LAST.length];
|
|
30
|
+
return {
|
|
31
|
+
id: i + 1,
|
|
32
|
+
name: `${first} ${last}`,
|
|
33
|
+
email: `${first.toLowerCase()}.${last.toLowerCase()}@example.com`,
|
|
34
|
+
department: DEPARTMENTS[i % DEPARTMENTS.length],
|
|
35
|
+
salary: 40000 + (i % 60) * 1000,
|
|
36
|
+
status: STATUSES[i % STATUSES.length],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const columns = [
|
|
41
|
+
{ id: 'id', title: 'ID', width: 70 },
|
|
42
|
+
{ id: 'name', title: 'Name', width: 180 },
|
|
43
|
+
{ id: 'email', title: 'Email', width: 220 },
|
|
44
|
+
{ id: 'department', title: 'Department', width: 150 },
|
|
45
|
+
{ id: 'salary', title: 'Salary', width: 110 },
|
|
46
|
+
{ id: 'status', title: 'Status', width: 110 },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const departmentOptions = DEPARTMENTS.map((d) => ({ label: d, value: d }));
|
|
50
|
+
const statusOptions = STATUSES.map((s) => ({ label: s, value: s }));
|
|
51
|
+
|
|
52
|
+
let nextId = 51;
|
|
53
|
+
let rows = $state<Record<string, unknown>[]>(
|
|
54
|
+
Array.from({ length: 50 }, (_, i) => makeRow(i) as unknown as Record<string, unknown>),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// ── Drawer state ─────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
let drawerOpen = $state(false);
|
|
60
|
+
let formMode = $state<FormRequestType>('update');
|
|
61
|
+
let formValues = $state<EmployeeRow | undefined>(undefined);
|
|
62
|
+
let lastSaved = $state<EmployeeRow | undefined>(undefined);
|
|
63
|
+
|
|
64
|
+
function openForm(mode: FormRequestType, row?: EmployeeRow) {
|
|
65
|
+
formMode = mode;
|
|
66
|
+
formValues = row ? { ...row } : { id: 0, name: '', email: '', department: '', salary: 50000, status: 'Active' };
|
|
67
|
+
drawerOpen = true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const drawerTitle = $derived(
|
|
71
|
+
formMode === 'insert' ? 'Add Employee' : formMode === 'delete' ? 'Delete Employee' : 'Edit Employee',
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// ── Selection tracking ────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
let selectedItems = $state<Record<string, unknown>[]>([]);
|
|
77
|
+
|
|
78
|
+
function firstSelected(): EmployeeRow | undefined {
|
|
79
|
+
const item = selectedItems[0];
|
|
80
|
+
return item ? (item as unknown as EmployeeRow) : undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── Burger menu items ─────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
const menuItems: GridlerContextMenuItem[] = [
|
|
86
|
+
{ id: 'add', label: 'Add', icon: iconAddSvg, kind: 'item', onselect: () => openForm('insert') },
|
|
87
|
+
{ id: 'sep1', label: '', kind: 'separator' },
|
|
88
|
+
{
|
|
89
|
+
id: 'edit',
|
|
90
|
+
label: 'Edit Selected',
|
|
91
|
+
icon: iconEditSvg,
|
|
92
|
+
kind: 'item',
|
|
93
|
+
onselect: () => {
|
|
94
|
+
const row = firstSelected();
|
|
95
|
+
if (row) openForm('update', row);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'delete',
|
|
100
|
+
label: 'Delete Selected',
|
|
101
|
+
icon: iconTrashSvg,
|
|
102
|
+
kind: 'item',
|
|
103
|
+
onselect: () => {
|
|
104
|
+
const row = firstSelected();
|
|
105
|
+
if (row) openForm('delete', row);
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// ── Context menu (right-click on row) ─────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
let ctxVisible = $state(false);
|
|
113
|
+
let ctxX = $state(0);
|
|
114
|
+
let ctxY = $state(0);
|
|
115
|
+
let ctxRow = $state<EmployeeRow | undefined>(undefined);
|
|
116
|
+
let wrapperEl = $state<HTMLElement | null>(null);
|
|
117
|
+
let lastMousePos = { x: 0, y: 0 };
|
|
118
|
+
|
|
119
|
+
// Capture phase fires before the canvas handler, so lastMousePos is fresh when
|
|
120
|
+
// onRowContextMenu calls showContextMenu.
|
|
121
|
+
$effect(() => {
|
|
122
|
+
if (!wrapperEl) return;
|
|
123
|
+
const handler = (e: MouseEvent) => { lastMousePos = { x: e.clientX, y: e.clientY }; };
|
|
124
|
+
wrapperEl.addEventListener('contextmenu', handler, true);
|
|
125
|
+
return () => wrapperEl?.removeEventListener('contextmenu', handler, true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
function showContextMenu(rowData: Record<string, unknown> | undefined) {
|
|
129
|
+
if (!rowData) return;
|
|
130
|
+
ctxRow = rowData as unknown as EmployeeRow;
|
|
131
|
+
ctxX = lastMousePos.x;
|
|
132
|
+
ctxY = lastMousePos.y;
|
|
133
|
+
ctxVisible = true;
|
|
134
|
+
|
|
135
|
+
const close = () => {
|
|
136
|
+
ctxVisible = false;
|
|
137
|
+
document.removeEventListener('click', close, true);
|
|
138
|
+
document.removeEventListener('keydown', closeOnEsc, true);
|
|
139
|
+
};
|
|
140
|
+
const closeOnEsc = (e: KeyboardEvent) => {
|
|
141
|
+
if (e.key === 'Escape') close();
|
|
142
|
+
};
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
document.addEventListener('click', close, true);
|
|
145
|
+
document.addEventListener('keydown', closeOnEsc, true);
|
|
146
|
+
}, 0);
|
|
147
|
+
}
|
|
148
|
+
</script>
|
|
149
|
+
|
|
150
|
+
<div class="p-4 space-y-2" bind:this={wrapperEl}>
|
|
151
|
+
<p class="text-sm text-surface-600-300-token">
|
|
152
|
+
Double-click a row to edit · Right-click for context menu · Use ☰ for Add / Edit / Delete
|
|
153
|
+
</p>
|
|
154
|
+
|
|
155
|
+
<GridlerFull
|
|
156
|
+
{columns}
|
|
157
|
+
data={rows}
|
|
158
|
+
height={480}
|
|
159
|
+
uniqueID="id"
|
|
160
|
+
rowMarkers="number"
|
|
161
|
+
{menuItems}
|
|
162
|
+
onRowDblClick={(_row, rowData) => rowData && openForm('update', rowData as unknown as EmployeeRow)}
|
|
163
|
+
onRowContextMenu={(_row, rowData) => showContextMenu(rowData)}
|
|
164
|
+
onSelectedItemsChange={(items) => (selectedItems = items)}
|
|
165
|
+
/>
|
|
166
|
+
|
|
167
|
+
<!-- Context menu popup -->
|
|
168
|
+
{#if ctxVisible}
|
|
169
|
+
<div
|
|
170
|
+
role="menu"
|
|
171
|
+
class="ctx-menu"
|
|
172
|
+
style="left:{ctxX}px;top:{ctxY}px;"
|
|
173
|
+
>
|
|
174
|
+
<button
|
|
175
|
+
role="menuitem"
|
|
176
|
+
class="ctx-item"
|
|
177
|
+
onclick={() => { ctxVisible = false; openForm('update', ctxRow); }}
|
|
178
|
+
>
|
|
179
|
+
<IconEdit size={15} />
|
|
180
|
+
Edit
|
|
181
|
+
</button>
|
|
182
|
+
<button
|
|
183
|
+
role="menuitem"
|
|
184
|
+
class="ctx-item ctx-item--danger"
|
|
185
|
+
onclick={() => { ctxVisible = false; openForm('delete', ctxRow); }}
|
|
186
|
+
>
|
|
187
|
+
<IconTrash size={15} />
|
|
188
|
+
Delete
|
|
189
|
+
</button>
|
|
190
|
+
</div>
|
|
191
|
+
{/if}
|
|
192
|
+
|
|
193
|
+
<FormerDrawer
|
|
194
|
+
bind:opened={drawerOpen}
|
|
195
|
+
bind:values={formValues}
|
|
196
|
+
request={formMode}
|
|
197
|
+
uniqueKeyField="id"
|
|
198
|
+
layout={{ title: drawerTitle, buttonArea: 'bottom' }}
|
|
199
|
+
onAPICall={async (_mode, _req, data) => {
|
|
200
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
201
|
+
return data as EmployeeRow;
|
|
202
|
+
}}
|
|
203
|
+
afterSave={(data) => {
|
|
204
|
+
const saved = data as EmployeeRow;
|
|
205
|
+
lastSaved = saved;
|
|
206
|
+
if (formMode === 'insert') {
|
|
207
|
+
saved.id = nextId++;
|
|
208
|
+
rows = [...rows, saved as unknown as Record<string, unknown>];
|
|
209
|
+
} else if (formMode === 'delete') {
|
|
210
|
+
rows = rows.filter((r) => r['id'] !== saved.id);
|
|
211
|
+
} else {
|
|
212
|
+
const rec = saved as unknown as Record<string, unknown>;
|
|
213
|
+
rows = rows.map((r) => (r['id'] === saved.id ? { ...rec } : r));
|
|
214
|
+
}
|
|
215
|
+
}}
|
|
216
|
+
onClose={() => { drawerOpen = false; }}
|
|
217
|
+
>
|
|
218
|
+
{#snippet children(state)}
|
|
219
|
+
{@const isReadonly = state.request === 'delete'}
|
|
220
|
+
<div class="p-6 space-y-4">
|
|
221
|
+
<TextInputCtrl
|
|
222
|
+
label="Name"
|
|
223
|
+
name="name"
|
|
224
|
+
value={state.values?.name ?? ''}
|
|
225
|
+
required
|
|
226
|
+
disabled={isReadonly}
|
|
227
|
+
onchange={(v) => state.setState('values', { ...state.values, name: v })}
|
|
228
|
+
/>
|
|
229
|
+
<TextInputCtrl
|
|
230
|
+
label="Email"
|
|
231
|
+
name="email"
|
|
232
|
+
type="email"
|
|
233
|
+
value={state.values?.email ?? ''}
|
|
234
|
+
required
|
|
235
|
+
disabled={isReadonly}
|
|
236
|
+
onchange={(v) => state.setState('values', { ...state.values, email: v })}
|
|
237
|
+
/>
|
|
238
|
+
<div class="grid grid-cols-2 gap-4">
|
|
239
|
+
<NativeSelectCtrl
|
|
240
|
+
label="Department"
|
|
241
|
+
name="department"
|
|
242
|
+
value={state.values?.department ?? ''}
|
|
243
|
+
options={departmentOptions}
|
|
244
|
+
disabled={isReadonly}
|
|
245
|
+
onchange={(v) => state.setState('values', { ...state.values, department: v })}
|
|
246
|
+
/>
|
|
247
|
+
<NativeSelectCtrl
|
|
248
|
+
label="Status"
|
|
249
|
+
name="status"
|
|
250
|
+
value={state.values?.status ?? ''}
|
|
251
|
+
options={statusOptions}
|
|
252
|
+
disabled={isReadonly}
|
|
253
|
+
onchange={(v) => state.setState('values', { ...state.values, status: v })}
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
<NumberInputCtrl
|
|
257
|
+
label="Salary"
|
|
258
|
+
name="salary"
|
|
259
|
+
value={state.values?.salary}
|
|
260
|
+
min={0}
|
|
261
|
+
disabled={isReadonly}
|
|
262
|
+
onchange={(v) => state.setState('values', { ...state.values, salary: v })}
|
|
263
|
+
/>
|
|
264
|
+
</div>
|
|
265
|
+
{/snippet}
|
|
266
|
+
</FormerDrawer>
|
|
267
|
+
|
|
268
|
+
{#if lastSaved}
|
|
269
|
+
<div class="card p-4">
|
|
270
|
+
<p class="font-semibold text-sm mb-2">Last saved:</p>
|
|
271
|
+
<pre class="text-xs overflow-auto">{JSON.stringify(lastSaved, null, 2)}</pre>
|
|
272
|
+
</div>
|
|
273
|
+
{/if}
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<style>
|
|
277
|
+
.ctx-menu {
|
|
278
|
+
position: fixed;
|
|
279
|
+
z-index: 9999;
|
|
280
|
+
min-width: 160px;
|
|
281
|
+
background: #fff;
|
|
282
|
+
border: 1px solid #e2e8f0;
|
|
283
|
+
border-radius: 8px;
|
|
284
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
285
|
+
padding: 4px;
|
|
286
|
+
font-family: system-ui, sans-serif;
|
|
287
|
+
font-size: 13px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
:global(.dark) .ctx-menu {
|
|
291
|
+
background: #1e293b;
|
|
292
|
+
border-color: #334155;
|
|
293
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.ctx-item {
|
|
297
|
+
display: flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
gap: 9px;
|
|
300
|
+
width: 100%;
|
|
301
|
+
padding: 7px 10px;
|
|
302
|
+
background: none;
|
|
303
|
+
border: none;
|
|
304
|
+
border-radius: 5px;
|
|
305
|
+
text-align: left;
|
|
306
|
+
cursor: pointer;
|
|
307
|
+
color: #1e293b;
|
|
308
|
+
font-size: inherit;
|
|
309
|
+
font-family: inherit;
|
|
310
|
+
transition: background 120ms;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
:global(.dark) .ctx-item {
|
|
314
|
+
color: #e2e8f0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.ctx-item:hover {
|
|
318
|
+
background: #f1f5f9;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
:global(.dark) .ctx-item:hover {
|
|
322
|
+
background: #334155;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.ctx-item--danger {
|
|
326
|
+
color: #dc2626;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
:global(.dark) .ctx-item--danger {
|
|
330
|
+
color: #f87171;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.ctx-item--danger:hover {
|
|
334
|
+
background: #fef2f2;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
:global(.dark) .ctx-item--danger:hover {
|
|
338
|
+
background: #450a0a;
|
|
339
|
+
}
|
|
340
|
+
</style>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
// @ts-nocheck
|
|
3
|
+
import IconClose from '../../Icons/IconClose.svelte';
|
|
3
4
|
interface Props {
|
|
4
5
|
value: string;
|
|
5
6
|
onValueChange: (v: string) => void;
|
|
@@ -35,10 +36,7 @@
|
|
|
35
36
|
aria-label="Clear search"
|
|
36
37
|
onclick={() => { onValueChange(''); inputRef?.focus(); }}
|
|
37
38
|
>
|
|
38
|
-
<
|
|
39
|
-
<line x1="1" y1="1" x2="9" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
40
|
-
<line x1="9" y1="1" x2="1" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
41
|
-
</svg>
|
|
39
|
+
<IconClose size={10} />
|
|
42
40
|
</button>
|
|
43
41
|
{/if}
|
|
44
42
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import GridlerSearch from './GridlerSearch.svelte';
|
|
3
|
+
import IconSearch from '../../Icons/IconSearch.svelte';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
show: boolean;
|
|
@@ -19,10 +20,7 @@
|
|
|
19
20
|
aria-label={show ? "Close search" : "Open search"}
|
|
20
21
|
onclick={onToggle}
|
|
21
22
|
>
|
|
22
|
-
<
|
|
23
|
-
<circle cx="5.5" cy="5.5" r="4" stroke="currentColor" stroke-width="1.5" />
|
|
24
|
-
<line x1="9" y1="9" x2="13" y2="13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
25
|
-
</svg>
|
|
23
|
+
<IconSearch size={14} />
|
|
26
24
|
</button>
|
|
27
25
|
|
|
28
26
|
{#if show}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GridColumn, GridCommonProps, GridColumnSortOrder, GridColumnFilters } from '../Types/generic_grid';
|
|
2
|
+
import type { Options } from '@warkypublic/resolvespec-js';
|
|
2
3
|
export type { GridColumnSortOrder, GridColumnFilters };
|
|
3
4
|
export type Item = [col: number, row: number];
|
|
4
5
|
export interface GridlerAdapterConfig {
|
|
@@ -8,6 +9,23 @@ export interface GridlerAdapterConfig {
|
|
|
8
9
|
entity: string;
|
|
9
10
|
/** Row field whose value is passed as cursor_forward on subsequent requests. Defaults to 'id'. */
|
|
10
11
|
uniqueID?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Default ResolveSpec options applied to every request. Grid-controlled sort, filters, limit,
|
|
14
|
+
* cursor, and columns always take precedence over values set here.
|
|
15
|
+
*
|
|
16
|
+
* Key fields:
|
|
17
|
+
* - `preload` — eager-load relations. Each entry specifies `relation`, optional `columns`,
|
|
18
|
+
* `filters`, `sort`, `limit`, `recursive`, `sql_joins`, etc.
|
|
19
|
+
* - `omit_columns` — columns to exclude from the response.
|
|
20
|
+
* - `computedColumns` — server-side computed expressions: `{ name, expression }`.
|
|
21
|
+
* - `customOperators` — raw SQL WHERE fragments (AND-combined): `{ name, sql }`.
|
|
22
|
+
* - `parameters` — named query parameters passed to the server: `{ name, value, sequence? }`.
|
|
23
|
+
* - `filters` — default server-side filters applied before any grid filters.
|
|
24
|
+
* - `sort` — default sort order, overridden when the user sorts a column.
|
|
25
|
+
* - `fetch_row_number` — column name whose row number is returned in metadata.
|
|
26
|
+
* - `offset` — static row offset (cursor pagination is used by the adapter; avoid mixing).
|
|
27
|
+
*/
|
|
28
|
+
extraOptions?: Partial<Options>;
|
|
11
29
|
}
|
|
12
30
|
export interface GridlerPageResult {
|
|
13
31
|
data: Record<string, unknown>[];
|
|
@@ -152,6 +170,7 @@ export interface GridlerContextMenuItem {
|
|
|
152
170
|
id: string;
|
|
153
171
|
label: string;
|
|
154
172
|
disabled?: boolean;
|
|
173
|
+
icon?: string;
|
|
155
174
|
/** Use 'separator' to render a divider line. */
|
|
156
175
|
kind?: 'item' | 'separator';
|
|
157
176
|
onselect?: () => void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<circle cx="12" cy="12" r="9"/>
|
|
7
|
+
<path d="M12 8v8M8 12h8"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<circle cx="12" cy="12" r="10"/>
|
|
7
|
+
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
8
|
+
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="m10.29 3.86-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.71-3.14l-8-14a2 2 0 0 0-3.42 0z"/>
|
|
7
|
+
<line x1="12" y1="9" x2="12" y2="13"/>
|
|
8
|
+
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/>
|
|
7
|
+
<circle cx="12" cy="13" r="3"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="M18 6 6 18M6 6l12 12"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
7
|
+
<path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="M3 5a1 1 0 0 1 1-1h16a1 1 0 0 1 .8 1.6L14 13.5V19a1 1 0 0 1-1.45.9l-3-1.5A1 1 0 0 1 9 17.5v-4L3.2 5.6A1 1 0 0 1 3 5Z"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<circle cx="11" cy="11" r="7"/>
|
|
7
|
+
<path d="m21 21-4.35-4.35"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { direction: 'asc' | 'desc'; size?: number; class?: string; }
|
|
3
|
+
const { direction, size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" class={cls} aria-hidden="true">
|
|
6
|
+
{#if direction === 'asc'}
|
|
7
|
+
<path d="M7 14l5-5 5 5H7z"/>
|
|
8
|
+
{:else}
|
|
9
|
+
<path d="M7 10l5 5 5-5H7z"/>
|
|
10
|
+
{/if}
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props { size?: number; class?: string; }
|
|
3
|
+
const { size = 16, class: cls = '' }: Props = $props();
|
|
4
|
+
</script>
|
|
5
|
+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
|
|
6
|
+
<path d="M3 6h18"/>
|
|
7
|
+
<path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/>
|
|
8
|
+
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
|
|
9
|
+
<path d="M10 11v6M14 11v6"/>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as IconAdd } from './IconAdd.svelte';
|
|
2
|
+
export { default as IconAlertCircle } from './IconAlertCircle.svelte';
|
|
3
|
+
export { default as IconAlertTriangle } from './IconAlertTriangle.svelte';
|
|
4
|
+
export { default as IconCamera } from './IconCamera.svelte';
|
|
5
|
+
export { default as IconClose } from './IconClose.svelte';
|
|
6
|
+
export { default as IconEdit } from './IconEdit.svelte';
|
|
7
|
+
export { default as IconFilter } from './IconFilter.svelte';
|
|
8
|
+
export { default as IconSearch } from './IconSearch.svelte';
|
|
9
|
+
export { default as IconSort } from './IconSort.svelte';
|
|
10
|
+
export { default as IconTrash } from './IconTrash.svelte';
|
|
11
|
+
export * from './strings';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as IconAdd } from './IconAdd.svelte';
|
|
2
|
+
export { default as IconAlertCircle } from './IconAlertCircle.svelte';
|
|
3
|
+
export { default as IconAlertTriangle } from './IconAlertTriangle.svelte';
|
|
4
|
+
export { default as IconCamera } from './IconCamera.svelte';
|
|
5
|
+
export { default as IconClose } from './IconClose.svelte';
|
|
6
|
+
export { default as IconEdit } from './IconEdit.svelte';
|
|
7
|
+
export { default as IconFilter } from './IconFilter.svelte';
|
|
8
|
+
export { default as IconSearch } from './IconSearch.svelte';
|
|
9
|
+
export { default as IconSort } from './IconSort.svelte';
|
|
10
|
+
export { default as IconTrash } from './IconTrash.svelte';
|
|
11
|
+
export * from './strings';
|