@rkosafo/cai.components 0.0.3 → 0.0.6
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 +8 -8
- package/dist/baseEditor/index.svelte +32 -32
- package/dist/builders/filters/FilterBuilder.svelte +638 -638
- package/dist/forms/FormCheckbox/FormCheckbox.svelte +53 -53
- package/dist/forms/FormDatepicker/FormDatepicker.svelte +159 -159
- package/dist/forms/FormInput/FormInput.svelte +87 -87
- package/dist/forms/FormRadio/FormRadio.svelte +53 -53
- package/dist/forms/FormSelect/FormSelect.svelte +86 -86
- package/dist/forms/FormTextarea/FormTextarea.svelte +77 -77
- package/dist/forms/checkbox/Checkbox.svelte +82 -82
- package/dist/forms/checkbox/CheckboxButton.svelte +92 -92
- package/dist/forms/datepicker/Datepicker.svelte +706 -706
- package/dist/forms/form/Form.svelte +69 -69
- package/dist/forms/input/Input.svelte +363 -363
- package/dist/forms/label/Label.svelte +38 -38
- package/dist/forms/radio/Radio.svelte +48 -48
- package/dist/forms/radio/RadioButton.svelte +22 -22
- package/dist/forms/select/Select.svelte +50 -50
- package/dist/forms/textarea/Textarea.svelte +165 -165
- package/dist/layout/TF/Content/Content.svelte +28 -28
- package/dist/layout/TF/Header/Header.svelte +159 -159
- package/dist/layout/TF/Sidebar/Sidebar.svelte +74 -74
- package/dist/layout/TF/Wrapper/Wrapper.svelte +17 -17
- package/dist/themes/ThemeProvider.svelte +20 -20
- package/dist/typography/heading/Heading.svelte +35 -35
- package/dist/ui/accordion/Accordion.svelte +49 -49
- package/dist/ui/accordion/AccordionItem.svelte +173 -173
- package/dist/ui/alert/Alert.svelte +83 -83
- package/dist/ui/alertDialog/AlertDialog.svelte +40 -40
- package/dist/ui/avatar/Avatar.svelte +77 -77
- package/dist/ui/buttons/Button.svelte +102 -102
- package/dist/ui/buttons/GradientButton.svelte +59 -59
- package/dist/ui/datatable/Datatable.svelte +516 -516
- package/dist/ui/drawer/Drawer.svelte +280 -280
- package/dist/ui/dropdown/Dropdown.svelte +36 -36
- package/dist/ui/dropdown/DropdownDivider.svelte +11 -11
- package/dist/ui/dropdown/DropdownGroup.svelte +14 -14
- package/dist/ui/dropdown/DropdownHeader.svelte +14 -14
- package/dist/ui/dropdown/DropdownItem.svelte +52 -52
- package/dist/ui/footer/Footer.svelte +15 -15
- package/dist/ui/footer/FooterBrand.svelte +37 -37
- package/dist/ui/footer/FooterCopyright.svelte +45 -45
- package/dist/ui/footer/FooterIcon.svelte +22 -22
- package/dist/ui/footer/FooterLink.svelte +33 -33
- package/dist/ui/footer/FooterLinkGroup.svelte +13 -13
- package/dist/ui/indicator/Indicator.svelte +42 -42
- package/dist/ui/modal/Modal.svelte +265 -265
- package/dist/ui/notificationList/NotificationList.svelte +123 -123
- package/dist/ui/pageLoader/PageLoader.svelte +10 -10
- package/dist/ui/paginate/Paginate.svelte +96 -96
- package/dist/ui/tab/Tab.svelte +65 -65
- package/dist/ui/table/Table.svelte +385 -385
- package/dist/ui/tableLoader/TableLoader.svelte +24 -24
- package/dist/ui/toolbar/Toolbar.svelte +59 -59
- package/dist/ui/toolbar/ToolbarButton.svelte +56 -56
- package/dist/ui/toolbar/ToolbarGroup.svelte +43 -43
- package/dist/utils/Popper.svelte +257 -257
- package/dist/utils/closeButton/CloseButton.svelte +88 -88
- package/dist/utils/singleSelection.svelte.js +48 -48
- package/dist/youtube/index.svelte +12 -12
- package/package.json +7 -3
|
@@ -1,385 +1,385 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Dropdown, DropdownItem, type TableProps } from '../../index.js';
|
|
3
|
-
import { clickOutsideAction } from '../../utils/svelte-legos.js';
|
|
4
|
-
import { createTable, Render, Subscribe } from 'svelte-headless-table';
|
|
5
|
-
import {
|
|
6
|
-
addColumnOrder,
|
|
7
|
-
addHiddenColumns,
|
|
8
|
-
addResizedColumns,
|
|
9
|
-
addSelectedRows,
|
|
10
|
-
addSortBy
|
|
11
|
-
} from 'svelte-headless-table/plugins';
|
|
12
|
-
import { writable } from 'svelte/store';
|
|
13
|
-
|
|
14
|
-
let {
|
|
15
|
-
data = [],
|
|
16
|
-
headerColor = 'white',
|
|
17
|
-
height = 400,
|
|
18
|
-
bgWhite = false,
|
|
19
|
-
headerTextColor = 'black',
|
|
20
|
-
tableColumns = [],
|
|
21
|
-
showActions = false,
|
|
22
|
-
showCheckBox = false,
|
|
23
|
-
showViewDetails = true,
|
|
24
|
-
showEdit = false,
|
|
25
|
-
showMiniWidth = false,
|
|
26
|
-
rowClickable = false,
|
|
27
|
-
hideWhiteSpace = true,
|
|
28
|
-
hiddenColumns = $bindable([]),
|
|
29
|
-
sortedColumns = $bindable([]),
|
|
30
|
-
initialSortKeys = [],
|
|
31
|
-
actionLists = [],
|
|
32
|
-
showIndex = false,
|
|
33
|
-
selectedRows = $bindable([]),
|
|
34
|
-
selectAllChecked = $bindable(false),
|
|
35
|
-
showDelete = false,
|
|
36
|
-
handleCheckbox,
|
|
37
|
-
onActionClicked,
|
|
38
|
-
onDelete,
|
|
39
|
-
onEdit,
|
|
40
|
-
onView,
|
|
41
|
-
onRowClicked
|
|
42
|
-
}: TableProps = $props();
|
|
43
|
-
|
|
44
|
-
let dropdown = $state(-1);
|
|
45
|
-
let openDropDown = $state(false);
|
|
46
|
-
let tableData = writable(data);
|
|
47
|
-
|
|
48
|
-
$effect(() => {
|
|
49
|
-
tableData.set(data);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const table = createTable(tableData, {
|
|
53
|
-
sort: addSortBy({
|
|
54
|
-
disableMultiSort: false,
|
|
55
|
-
serverSide: true,
|
|
56
|
-
initialSortKeys: initialSortKeys
|
|
57
|
-
}),
|
|
58
|
-
colOrder: addColumnOrder(),
|
|
59
|
-
select: addSelectedRows(),
|
|
60
|
-
hideCols: addHiddenColumns({
|
|
61
|
-
initialHiddenColumnIds: hiddenColumns
|
|
62
|
-
}),
|
|
63
|
-
resize: addResizedColumns()
|
|
64
|
-
});
|
|
65
|
-
const columns = tableColumns.map((x: any) => table.column({ ...x }));
|
|
66
|
-
const { headerRows, flatColumns, rows, tableAttrs, tableBodyAttrs, pluginStates, pageRows } =
|
|
67
|
-
table.createViewModel(columns);
|
|
68
|
-
// $allColumns = flatColumns;
|
|
69
|
-
const { hiddenColumnIds } = pluginStates.hideCols;
|
|
70
|
-
|
|
71
|
-
$effect(() => hiddenColumnIds.set(hiddenColumns));
|
|
72
|
-
|
|
73
|
-
const { sortKeys } = pluginStates.sort;
|
|
74
|
-
$effect(() => {
|
|
75
|
-
sortedColumns = $sortKeys;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
function handleRowCheckboxChange(checked: boolean, row: any) {
|
|
79
|
-
let updatedSelectedRows;
|
|
80
|
-
|
|
81
|
-
if (checked) {
|
|
82
|
-
updatedSelectedRows = [...selectedRows, row];
|
|
83
|
-
} else {
|
|
84
|
-
updatedSelectedRows = selectedRows.filter((item) => item !== row);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
selectedRows = updatedSelectedRows;
|
|
88
|
-
if (handleCheckbox) handleCheckbox(updatedSelectedRows);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const handleSelectAllChange = () => {
|
|
92
|
-
if (selectAllChecked) {
|
|
93
|
-
selectedRows = [];
|
|
94
|
-
if (handleCheckbox) handleCheckbox([]);
|
|
95
|
-
} else {
|
|
96
|
-
const allRows = data.map((x) => x);
|
|
97
|
-
selectedRows = allRows;
|
|
98
|
-
if (handleCheckbox) handleCheckbox(allRows);
|
|
99
|
-
}
|
|
100
|
-
selectAllChecked = !selectAllChecked;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
function toggleSubmenu(index: any) {
|
|
104
|
-
dropdown = dropdown === index ? -1 : index;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function closeDropDown() {
|
|
108
|
-
openDropDown = false;
|
|
109
|
-
dropdown = -1;
|
|
110
|
-
}
|
|
111
|
-
function getRow(row: any) {
|
|
112
|
-
const { original } = row;
|
|
113
|
-
return original;
|
|
114
|
-
}
|
|
115
|
-
</script>
|
|
116
|
-
|
|
117
|
-
<div class="h-full w-full flex-auto">
|
|
118
|
-
<div class="grid grid-cols-1 gap-2">
|
|
119
|
-
<div
|
|
120
|
-
class=" scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch h-full w-full overflow-x-auto"
|
|
121
|
-
>
|
|
122
|
-
<table {...$tableAttrs} class="mb-6 w-full pb-6">
|
|
123
|
-
<thead
|
|
124
|
-
class=" sticky top-0"
|
|
125
|
-
class:bg-[#92cbdf]={headerColor === 'sky'}
|
|
126
|
-
class:bg-green-300={headerColor === 'green'}
|
|
127
|
-
class:bg-[#CF9B14]={headerColor === 'orange'}
|
|
128
|
-
class:bg-[#cbc3fa]={headerColor === 'purple'}
|
|
129
|
-
class:bg-blue-300={headerColor === 'blue'}
|
|
130
|
-
class:bg-white={headerColor === 'white'}
|
|
131
|
-
class:text-gray-700={headerTextColor === 'gray'}
|
|
132
|
-
class:text-white={headerTextColor === 'white'}
|
|
133
|
-
class:text-black={headerTextColor === 'black'}
|
|
134
|
-
class:border-b-2={headerColor === 'white'}
|
|
135
|
-
>
|
|
136
|
-
{#each $headerRows as headerRow (headerRow.id)}
|
|
137
|
-
<Subscribe rowAttrs={headerRow.attrs()}>
|
|
138
|
-
{#snippet children({ rowAttrs })}
|
|
139
|
-
<tr {...rowAttrs}>
|
|
140
|
-
{#if showCheckBox}
|
|
141
|
-
<th
|
|
142
|
-
class="px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
143
|
-
>
|
|
144
|
-
<input
|
|
145
|
-
class="cursor-pointer"
|
|
146
|
-
type="checkbox"
|
|
147
|
-
checked={selectAllChecked}
|
|
148
|
-
onchange={handleSelectAllChange}
|
|
149
|
-
/>
|
|
150
|
-
</th>
|
|
151
|
-
{/if}
|
|
152
|
-
{#if showActions}
|
|
153
|
-
<th
|
|
154
|
-
class="px-4 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
155
|
-
>Actions</th
|
|
156
|
-
>
|
|
157
|
-
{/if}
|
|
158
|
-
{#if showIndex}
|
|
159
|
-
<th
|
|
160
|
-
class="px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
161
|
-
>#</th
|
|
162
|
-
>
|
|
163
|
-
{/if}
|
|
164
|
-
{#each headerRow.cells as cell (cell.id)}
|
|
165
|
-
<Subscribe attrs={cell.attrs()} props={cell.props()}>
|
|
166
|
-
{#snippet children({ attrs, props })}
|
|
167
|
-
<!-- use:props.resize : apply to th -->
|
|
168
|
-
<th
|
|
169
|
-
{...attrs}
|
|
170
|
-
onclick={props.sort.toggle}
|
|
171
|
-
class="relative cursor-pointer px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
172
|
-
>
|
|
173
|
-
<div class="flex items-center gap-1">
|
|
174
|
-
<Render of={cell.render()} />
|
|
175
|
-
{#if props.sort?.order === 'asc'}
|
|
176
|
-
<iconify-icon icon="ph:caret-up" class="text-gray-500"></iconify-icon>
|
|
177
|
-
{:else if props.sort?.order === 'desc'}
|
|
178
|
-
<iconify-icon icon="ph:caret-down" class="text-gray-500"
|
|
179
|
-
></iconify-icon>
|
|
180
|
-
{:else if props.sort?.disabled == false}
|
|
181
|
-
<iconify-icon icon="ph:caret-up-down" class="text-gray-500"
|
|
182
|
-
></iconify-icon>
|
|
183
|
-
{/if}
|
|
184
|
-
<!-- {#if !props.resize.disabled}
|
|
185
|
-
<div class="resizer" use:props.resize.drag />
|
|
186
|
-
{/if} -->
|
|
187
|
-
</div>
|
|
188
|
-
</th>
|
|
189
|
-
{/snippet}
|
|
190
|
-
</Subscribe>
|
|
191
|
-
{/each}
|
|
192
|
-
</tr>
|
|
193
|
-
{/snippet}
|
|
194
|
-
</Subscribe>
|
|
195
|
-
{/each}
|
|
196
|
-
</thead>
|
|
197
|
-
<tbody
|
|
198
|
-
{...$tableBodyAttrs}
|
|
199
|
-
class="divide-y"
|
|
200
|
-
class:bg-white={bgWhite}
|
|
201
|
-
use:clickOutsideAction
|
|
202
|
-
onclickoutside={closeDropDown}
|
|
203
|
-
>
|
|
204
|
-
{#each $pageRows as row, index (row.id)}
|
|
205
|
-
<Subscribe rowAttrs={row.attrs()}>
|
|
206
|
-
{#snippet children({ rowAttrs })}
|
|
207
|
-
<tr
|
|
208
|
-
{...rowAttrs}
|
|
209
|
-
class:bg-slate-100={index % 2 !== 0}
|
|
210
|
-
class:cursor-pointer={rowClickable}
|
|
211
|
-
class:hover:bg-gray-100={rowClickable}
|
|
212
|
-
class="dark:bg-gray-900 dark:text-white dark:hover:bg-gray-800"
|
|
213
|
-
onclick={(e) => {
|
|
214
|
-
if (rowClickable) {
|
|
215
|
-
const target = e.target as HTMLElement | null;
|
|
216
|
-
if (
|
|
217
|
-
target &&
|
|
218
|
-
(target.localName === 'iconify-icon' || target.localName === 'button')
|
|
219
|
-
) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
onRowClicked && onRowClicked(getRow(row));
|
|
223
|
-
closeDropDown();
|
|
224
|
-
}
|
|
225
|
-
}}
|
|
226
|
-
>
|
|
227
|
-
{#if showCheckBox}
|
|
228
|
-
<td class="px-6 py-2">
|
|
229
|
-
<input
|
|
230
|
-
type="checkbox"
|
|
231
|
-
class="cursor-pointer"
|
|
232
|
-
onchange={(e) => {
|
|
233
|
-
const checked = e?.target?.checked;
|
|
234
|
-
handleRowCheckboxChange(checked, getRow(row));
|
|
235
|
-
}}
|
|
236
|
-
checked={selectedRows.find((x) => x.id === getRow(row).id) ?? false}
|
|
237
|
-
/>
|
|
238
|
-
</td>
|
|
239
|
-
{/if}
|
|
240
|
-
{#if showActions}
|
|
241
|
-
<td class="px-5 py-2">
|
|
242
|
-
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
243
|
-
<button
|
|
244
|
-
class="dropdown-{row.id} grid h-7 w-7 place-content-center rounded-[5px] border border-gray-300 bg-gray-200 hover:bg-gray-300 dark:bg-gray-900 dark:hover:bg-gray-700"
|
|
245
|
-
>
|
|
246
|
-
<iconify-icon class="dots-menu dark:text-white" icon="tabler:dots-vertical"
|
|
247
|
-
></iconify-icon>
|
|
248
|
-
</button>
|
|
249
|
-
<!-- {#if dropdown === index && openDropDown} -->
|
|
250
|
-
<Dropdown
|
|
251
|
-
triggeredBy=".dropdown-{row.id}"
|
|
252
|
-
placement="right"
|
|
253
|
-
class="list-none"
|
|
254
|
-
>
|
|
255
|
-
{#each actionLists as { name, icon, otherClasses, visible }}
|
|
256
|
-
{#if visible}
|
|
257
|
-
{#if visible(getRow(row))}
|
|
258
|
-
<DropdownItem
|
|
259
|
-
data-dropdown-id={`dropdown-${row.id}`}
|
|
260
|
-
class="flex w-full items-center gap-1 px-4 py-2 text-sm whitespace-nowrap hover:bg-gray-100 {otherClasses}"
|
|
261
|
-
onclick={(e) => {
|
|
262
|
-
onActionClicked && onActionClicked({ name, row: getRow(row) });
|
|
263
|
-
closeDropDown();
|
|
264
|
-
e.stopPropagation();
|
|
265
|
-
}}
|
|
266
|
-
>
|
|
267
|
-
{#if icon}
|
|
268
|
-
<iconify-icon {icon} style="font-size: 18px;"></iconify-icon>
|
|
269
|
-
{/if}
|
|
270
|
-
{name}
|
|
271
|
-
</DropdownItem>
|
|
272
|
-
{/if}
|
|
273
|
-
{:else}
|
|
274
|
-
<DropdownItem
|
|
275
|
-
data-dropdown-id={`dropdown-${row.id}`}
|
|
276
|
-
class=" flex w-full items-center gap-1 px-4 py-2 text-sm whitespace-nowrap hover:bg-gray-100 {otherClasses}"
|
|
277
|
-
onclick={(e) => {
|
|
278
|
-
onActionClicked && onActionClicked({ name, row: getRow(row) });
|
|
279
|
-
closeDropDown();
|
|
280
|
-
e.stopPropagation();
|
|
281
|
-
}}
|
|
282
|
-
>
|
|
283
|
-
{#if icon}
|
|
284
|
-
<iconify-icon {icon} style="font-size: 18px;"></iconify-icon>
|
|
285
|
-
{/if}
|
|
286
|
-
{name}
|
|
287
|
-
</DropdownItem>
|
|
288
|
-
{/if}
|
|
289
|
-
{:else}
|
|
290
|
-
{#if showViewDetails}
|
|
291
|
-
<DropdownItem
|
|
292
|
-
data-dropdown-id={`dropdown-${row.id}`}
|
|
293
|
-
class="flex items-center px-4 py-2 gap-1 hover:bg-gray-100 w-full text-sm"
|
|
294
|
-
onclick={(e) => {
|
|
295
|
-
onView && onView(getRow(row));
|
|
296
|
-
closeDropDown();
|
|
297
|
-
e.stopPropagation();
|
|
298
|
-
}}
|
|
299
|
-
>
|
|
300
|
-
<iconify-icon icon="ion:eye" style="font-size: 18px;"
|
|
301
|
-
></iconify-icon>View Details
|
|
302
|
-
</DropdownItem>
|
|
303
|
-
{/if}
|
|
304
|
-
|
|
305
|
-
{#if showEdit}
|
|
306
|
-
<DropdownItem
|
|
307
|
-
data-dropdown-id={`dropdown-${row.id}`}
|
|
308
|
-
class="flex items-center px-4 py-2 gap-1 hover:bg-gray-100 w-full text-sm"
|
|
309
|
-
onclick={(e) => {
|
|
310
|
-
onEdit && onEdit(getRow(row));
|
|
311
|
-
closeDropDown();
|
|
312
|
-
e.stopPropagation();
|
|
313
|
-
}}
|
|
314
|
-
>
|
|
315
|
-
<iconify-icon icon="ri:edit-2-fill" style="font-size: 18px;"
|
|
316
|
-
></iconify-icon>Edit Details
|
|
317
|
-
</DropdownItem>
|
|
318
|
-
{/if}
|
|
319
|
-
{#if showDelete}
|
|
320
|
-
<DropdownItem
|
|
321
|
-
data-dropdown-id={`dropdown-${row.id}`}
|
|
322
|
-
class="flex items-center px-4 py-2 gap-1 text-red-600 hover:bg-red-100 w-full text-sm"
|
|
323
|
-
onclick={(e) => {
|
|
324
|
-
onDelete && onDelete(getRow(row));
|
|
325
|
-
closeDropDown();
|
|
326
|
-
e.stopPropagation();
|
|
327
|
-
}}
|
|
328
|
-
>
|
|
329
|
-
<iconify-icon icon="mdi:trash" style="font-size: 18px;"
|
|
330
|
-
></iconify-icon>Delete Details
|
|
331
|
-
</DropdownItem>
|
|
332
|
-
{/if}
|
|
333
|
-
{/each}
|
|
334
|
-
</Dropdown>
|
|
335
|
-
|
|
336
|
-
<!-- {/if} -->
|
|
337
|
-
</td>
|
|
338
|
-
{/if}
|
|
339
|
-
{#if showIndex}
|
|
340
|
-
<td class="px-6 py-2">
|
|
341
|
-
{index + 1}
|
|
342
|
-
</td>
|
|
343
|
-
{/if}
|
|
344
|
-
{#each row.cells as cell (cell.id)}
|
|
345
|
-
<Subscribe attrs={cell.attrs()}>
|
|
346
|
-
{#snippet children({ attrs })}
|
|
347
|
-
<td
|
|
348
|
-
{...attrs}
|
|
349
|
-
class="px-6 py-2 text-left text-sm rtl:text-right"
|
|
350
|
-
class:whitespace-nowrap={hideWhiteSpace}
|
|
351
|
-
>
|
|
352
|
-
<Render of={cell.render()} />
|
|
353
|
-
</td>
|
|
354
|
-
{/snippet}
|
|
355
|
-
</Subscribe>
|
|
356
|
-
{/each}
|
|
357
|
-
</tr>
|
|
358
|
-
{/snippet}
|
|
359
|
-
</Subscribe>
|
|
360
|
-
{/each}
|
|
361
|
-
</tbody>
|
|
362
|
-
</table>
|
|
363
|
-
<div class="h-48"></div>
|
|
364
|
-
|
|
365
|
-
{#if !$pageRows.length}
|
|
366
|
-
<div class="flex h-72 w-full items-center justify-center">
|
|
367
|
-
<div class="rounded-md bg-yellow-200 px-6 py-4">No records found</div>
|
|
368
|
-
</div>
|
|
369
|
-
{/if}
|
|
370
|
-
</div>
|
|
371
|
-
</div>
|
|
372
|
-
</div>
|
|
373
|
-
|
|
374
|
-
<style>
|
|
375
|
-
.resizer {
|
|
376
|
-
position: absolute;
|
|
377
|
-
top: 0;
|
|
378
|
-
bottom: 0;
|
|
379
|
-
right: -4px;
|
|
380
|
-
width: 8px;
|
|
381
|
-
background: #6194d9;
|
|
382
|
-
cursor: col-resize;
|
|
383
|
-
z-index: 1;
|
|
384
|
-
}
|
|
385
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Dropdown, DropdownItem, type TableProps } from '../../index.js';
|
|
3
|
+
import { clickOutsideAction } from '../../utils/svelte-legos.js';
|
|
4
|
+
import { createTable, Render, Subscribe } from 'svelte-headless-table';
|
|
5
|
+
import {
|
|
6
|
+
addColumnOrder,
|
|
7
|
+
addHiddenColumns,
|
|
8
|
+
addResizedColumns,
|
|
9
|
+
addSelectedRows,
|
|
10
|
+
addSortBy
|
|
11
|
+
} from 'svelte-headless-table/plugins';
|
|
12
|
+
import { writable } from 'svelte/store';
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
data = [],
|
|
16
|
+
headerColor = 'white',
|
|
17
|
+
height = 400,
|
|
18
|
+
bgWhite = false,
|
|
19
|
+
headerTextColor = 'black',
|
|
20
|
+
tableColumns = [],
|
|
21
|
+
showActions = false,
|
|
22
|
+
showCheckBox = false,
|
|
23
|
+
showViewDetails = true,
|
|
24
|
+
showEdit = false,
|
|
25
|
+
showMiniWidth = false,
|
|
26
|
+
rowClickable = false,
|
|
27
|
+
hideWhiteSpace = true,
|
|
28
|
+
hiddenColumns = $bindable([]),
|
|
29
|
+
sortedColumns = $bindable([]),
|
|
30
|
+
initialSortKeys = [],
|
|
31
|
+
actionLists = [],
|
|
32
|
+
showIndex = false,
|
|
33
|
+
selectedRows = $bindable([]),
|
|
34
|
+
selectAllChecked = $bindable(false),
|
|
35
|
+
showDelete = false,
|
|
36
|
+
handleCheckbox,
|
|
37
|
+
onActionClicked,
|
|
38
|
+
onDelete,
|
|
39
|
+
onEdit,
|
|
40
|
+
onView,
|
|
41
|
+
onRowClicked
|
|
42
|
+
}: TableProps = $props();
|
|
43
|
+
|
|
44
|
+
let dropdown = $state(-1);
|
|
45
|
+
let openDropDown = $state(false);
|
|
46
|
+
let tableData = writable(data);
|
|
47
|
+
|
|
48
|
+
$effect(() => {
|
|
49
|
+
tableData.set(data);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const table = createTable(tableData, {
|
|
53
|
+
sort: addSortBy({
|
|
54
|
+
disableMultiSort: false,
|
|
55
|
+
serverSide: true,
|
|
56
|
+
initialSortKeys: initialSortKeys
|
|
57
|
+
}),
|
|
58
|
+
colOrder: addColumnOrder(),
|
|
59
|
+
select: addSelectedRows(),
|
|
60
|
+
hideCols: addHiddenColumns({
|
|
61
|
+
initialHiddenColumnIds: hiddenColumns
|
|
62
|
+
}),
|
|
63
|
+
resize: addResizedColumns()
|
|
64
|
+
});
|
|
65
|
+
const columns = tableColumns.map((x: any) => table.column({ ...x }));
|
|
66
|
+
const { headerRows, flatColumns, rows, tableAttrs, tableBodyAttrs, pluginStates, pageRows } =
|
|
67
|
+
table.createViewModel(columns);
|
|
68
|
+
// $allColumns = flatColumns;
|
|
69
|
+
const { hiddenColumnIds } = pluginStates.hideCols;
|
|
70
|
+
|
|
71
|
+
$effect(() => hiddenColumnIds.set(hiddenColumns));
|
|
72
|
+
|
|
73
|
+
const { sortKeys } = pluginStates.sort;
|
|
74
|
+
$effect(() => {
|
|
75
|
+
sortedColumns = $sortKeys;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function handleRowCheckboxChange(checked: boolean, row: any) {
|
|
79
|
+
let updatedSelectedRows;
|
|
80
|
+
|
|
81
|
+
if (checked) {
|
|
82
|
+
updatedSelectedRows = [...selectedRows, row];
|
|
83
|
+
} else {
|
|
84
|
+
updatedSelectedRows = selectedRows.filter((item) => item !== row);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
selectedRows = updatedSelectedRows;
|
|
88
|
+
if (handleCheckbox) handleCheckbox(updatedSelectedRows);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const handleSelectAllChange = () => {
|
|
92
|
+
if (selectAllChecked) {
|
|
93
|
+
selectedRows = [];
|
|
94
|
+
if (handleCheckbox) handleCheckbox([]);
|
|
95
|
+
} else {
|
|
96
|
+
const allRows = data.map((x) => x);
|
|
97
|
+
selectedRows = allRows;
|
|
98
|
+
if (handleCheckbox) handleCheckbox(allRows);
|
|
99
|
+
}
|
|
100
|
+
selectAllChecked = !selectAllChecked;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function toggleSubmenu(index: any) {
|
|
104
|
+
dropdown = dropdown === index ? -1 : index;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function closeDropDown() {
|
|
108
|
+
openDropDown = false;
|
|
109
|
+
dropdown = -1;
|
|
110
|
+
}
|
|
111
|
+
function getRow(row: any) {
|
|
112
|
+
const { original } = row;
|
|
113
|
+
return original;
|
|
114
|
+
}
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<div class="h-full w-full flex-auto">
|
|
118
|
+
<div class="grid grid-cols-1 gap-2">
|
|
119
|
+
<div
|
|
120
|
+
class=" scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch h-full w-full overflow-x-auto"
|
|
121
|
+
>
|
|
122
|
+
<table {...$tableAttrs} class="mb-6 w-full pb-6">
|
|
123
|
+
<thead
|
|
124
|
+
class=" sticky top-0"
|
|
125
|
+
class:bg-[#92cbdf]={headerColor === 'sky'}
|
|
126
|
+
class:bg-green-300={headerColor === 'green'}
|
|
127
|
+
class:bg-[#CF9B14]={headerColor === 'orange'}
|
|
128
|
+
class:bg-[#cbc3fa]={headerColor === 'purple'}
|
|
129
|
+
class:bg-blue-300={headerColor === 'blue'}
|
|
130
|
+
class:bg-white={headerColor === 'white'}
|
|
131
|
+
class:text-gray-700={headerTextColor === 'gray'}
|
|
132
|
+
class:text-white={headerTextColor === 'white'}
|
|
133
|
+
class:text-black={headerTextColor === 'black'}
|
|
134
|
+
class:border-b-2={headerColor === 'white'}
|
|
135
|
+
>
|
|
136
|
+
{#each $headerRows as headerRow (headerRow.id)}
|
|
137
|
+
<Subscribe rowAttrs={headerRow.attrs()}>
|
|
138
|
+
{#snippet children({ rowAttrs })}
|
|
139
|
+
<tr {...rowAttrs}>
|
|
140
|
+
{#if showCheckBox}
|
|
141
|
+
<th
|
|
142
|
+
class="px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
143
|
+
>
|
|
144
|
+
<input
|
|
145
|
+
class="cursor-pointer"
|
|
146
|
+
type="checkbox"
|
|
147
|
+
checked={selectAllChecked}
|
|
148
|
+
onchange={handleSelectAllChange}
|
|
149
|
+
/>
|
|
150
|
+
</th>
|
|
151
|
+
{/if}
|
|
152
|
+
{#if showActions}
|
|
153
|
+
<th
|
|
154
|
+
class="px-4 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
155
|
+
>Actions</th
|
|
156
|
+
>
|
|
157
|
+
{/if}
|
|
158
|
+
{#if showIndex}
|
|
159
|
+
<th
|
|
160
|
+
class="px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
161
|
+
>#</th
|
|
162
|
+
>
|
|
163
|
+
{/if}
|
|
164
|
+
{#each headerRow.cells as cell (cell.id)}
|
|
165
|
+
<Subscribe attrs={cell.attrs()} props={cell.props()}>
|
|
166
|
+
{#snippet children({ attrs, props })}
|
|
167
|
+
<!-- use:props.resize : apply to th -->
|
|
168
|
+
<th
|
|
169
|
+
{...attrs}
|
|
170
|
+
onclick={props.sort.toggle}
|
|
171
|
+
class="relative cursor-pointer px-6 py-2.5 text-left text-sm font-semibold whitespace-nowrap rtl:text-right"
|
|
172
|
+
>
|
|
173
|
+
<div class="flex items-center gap-1">
|
|
174
|
+
<Render of={cell.render()} />
|
|
175
|
+
{#if props.sort?.order === 'asc'}
|
|
176
|
+
<iconify-icon icon="ph:caret-up" class="text-gray-500"></iconify-icon>
|
|
177
|
+
{:else if props.sort?.order === 'desc'}
|
|
178
|
+
<iconify-icon icon="ph:caret-down" class="text-gray-500"
|
|
179
|
+
></iconify-icon>
|
|
180
|
+
{:else if props.sort?.disabled == false}
|
|
181
|
+
<iconify-icon icon="ph:caret-up-down" class="text-gray-500"
|
|
182
|
+
></iconify-icon>
|
|
183
|
+
{/if}
|
|
184
|
+
<!-- {#if !props.resize.disabled}
|
|
185
|
+
<div class="resizer" use:props.resize.drag />
|
|
186
|
+
{/if} -->
|
|
187
|
+
</div>
|
|
188
|
+
</th>
|
|
189
|
+
{/snippet}
|
|
190
|
+
</Subscribe>
|
|
191
|
+
{/each}
|
|
192
|
+
</tr>
|
|
193
|
+
{/snippet}
|
|
194
|
+
</Subscribe>
|
|
195
|
+
{/each}
|
|
196
|
+
</thead>
|
|
197
|
+
<tbody
|
|
198
|
+
{...$tableBodyAttrs}
|
|
199
|
+
class="divide-y"
|
|
200
|
+
class:bg-white={bgWhite}
|
|
201
|
+
use:clickOutsideAction
|
|
202
|
+
onclickoutside={closeDropDown}
|
|
203
|
+
>
|
|
204
|
+
{#each $pageRows as row, index (row.id)}
|
|
205
|
+
<Subscribe rowAttrs={row.attrs()}>
|
|
206
|
+
{#snippet children({ rowAttrs })}
|
|
207
|
+
<tr
|
|
208
|
+
{...rowAttrs}
|
|
209
|
+
class:bg-slate-100={index % 2 !== 0}
|
|
210
|
+
class:cursor-pointer={rowClickable}
|
|
211
|
+
class:hover:bg-gray-100={rowClickable}
|
|
212
|
+
class="dark:bg-gray-900 dark:text-white dark:hover:bg-gray-800"
|
|
213
|
+
onclick={(e) => {
|
|
214
|
+
if (rowClickable) {
|
|
215
|
+
const target = e.target as HTMLElement | null;
|
|
216
|
+
if (
|
|
217
|
+
target &&
|
|
218
|
+
(target.localName === 'iconify-icon' || target.localName === 'button')
|
|
219
|
+
) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
onRowClicked && onRowClicked(getRow(row));
|
|
223
|
+
closeDropDown();
|
|
224
|
+
}
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
{#if showCheckBox}
|
|
228
|
+
<td class="px-6 py-2">
|
|
229
|
+
<input
|
|
230
|
+
type="checkbox"
|
|
231
|
+
class="cursor-pointer"
|
|
232
|
+
onchange={(e) => {
|
|
233
|
+
const checked = e?.target?.checked;
|
|
234
|
+
handleRowCheckboxChange(checked, getRow(row));
|
|
235
|
+
}}
|
|
236
|
+
checked={selectedRows.find((x) => x.id === getRow(row).id) ?? false}
|
|
237
|
+
/>
|
|
238
|
+
</td>
|
|
239
|
+
{/if}
|
|
240
|
+
{#if showActions}
|
|
241
|
+
<td class="px-5 py-2">
|
|
242
|
+
<!-- svelte-ignore a11y_consider_explicit_label -->
|
|
243
|
+
<button
|
|
244
|
+
class="dropdown-{row.id} grid h-7 w-7 place-content-center rounded-[5px] border border-gray-300 bg-gray-200 hover:bg-gray-300 dark:bg-gray-900 dark:hover:bg-gray-700"
|
|
245
|
+
>
|
|
246
|
+
<iconify-icon class="dots-menu dark:text-white" icon="tabler:dots-vertical"
|
|
247
|
+
></iconify-icon>
|
|
248
|
+
</button>
|
|
249
|
+
<!-- {#if dropdown === index && openDropDown} -->
|
|
250
|
+
<Dropdown
|
|
251
|
+
triggeredBy=".dropdown-{row.id}"
|
|
252
|
+
placement="right"
|
|
253
|
+
class="list-none"
|
|
254
|
+
>
|
|
255
|
+
{#each actionLists as { name, icon, otherClasses, visible }}
|
|
256
|
+
{#if visible}
|
|
257
|
+
{#if visible(getRow(row))}
|
|
258
|
+
<DropdownItem
|
|
259
|
+
data-dropdown-id={`dropdown-${row.id}`}
|
|
260
|
+
class="flex w-full items-center gap-1 px-4 py-2 text-sm whitespace-nowrap hover:bg-gray-100 {otherClasses}"
|
|
261
|
+
onclick={(e) => {
|
|
262
|
+
onActionClicked && onActionClicked({ name, row: getRow(row) });
|
|
263
|
+
closeDropDown();
|
|
264
|
+
e.stopPropagation();
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
{#if icon}
|
|
268
|
+
<iconify-icon {icon} style="font-size: 18px;"></iconify-icon>
|
|
269
|
+
{/if}
|
|
270
|
+
{name}
|
|
271
|
+
</DropdownItem>
|
|
272
|
+
{/if}
|
|
273
|
+
{:else}
|
|
274
|
+
<DropdownItem
|
|
275
|
+
data-dropdown-id={`dropdown-${row.id}`}
|
|
276
|
+
class=" flex w-full items-center gap-1 px-4 py-2 text-sm whitespace-nowrap hover:bg-gray-100 {otherClasses}"
|
|
277
|
+
onclick={(e) => {
|
|
278
|
+
onActionClicked && onActionClicked({ name, row: getRow(row) });
|
|
279
|
+
closeDropDown();
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
{#if icon}
|
|
284
|
+
<iconify-icon {icon} style="font-size: 18px;"></iconify-icon>
|
|
285
|
+
{/if}
|
|
286
|
+
{name}
|
|
287
|
+
</DropdownItem>
|
|
288
|
+
{/if}
|
|
289
|
+
{:else}
|
|
290
|
+
{#if showViewDetails}
|
|
291
|
+
<DropdownItem
|
|
292
|
+
data-dropdown-id={`dropdown-${row.id}`}
|
|
293
|
+
class="flex items-center px-4 py-2 gap-1 hover:bg-gray-100 w-full text-sm"
|
|
294
|
+
onclick={(e) => {
|
|
295
|
+
onView && onView(getRow(row));
|
|
296
|
+
closeDropDown();
|
|
297
|
+
e.stopPropagation();
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
<iconify-icon icon="ion:eye" style="font-size: 18px;"
|
|
301
|
+
></iconify-icon>View Details
|
|
302
|
+
</DropdownItem>
|
|
303
|
+
{/if}
|
|
304
|
+
|
|
305
|
+
{#if showEdit}
|
|
306
|
+
<DropdownItem
|
|
307
|
+
data-dropdown-id={`dropdown-${row.id}`}
|
|
308
|
+
class="flex items-center px-4 py-2 gap-1 hover:bg-gray-100 w-full text-sm"
|
|
309
|
+
onclick={(e) => {
|
|
310
|
+
onEdit && onEdit(getRow(row));
|
|
311
|
+
closeDropDown();
|
|
312
|
+
e.stopPropagation();
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<iconify-icon icon="ri:edit-2-fill" style="font-size: 18px;"
|
|
316
|
+
></iconify-icon>Edit Details
|
|
317
|
+
</DropdownItem>
|
|
318
|
+
{/if}
|
|
319
|
+
{#if showDelete}
|
|
320
|
+
<DropdownItem
|
|
321
|
+
data-dropdown-id={`dropdown-${row.id}`}
|
|
322
|
+
class="flex items-center px-4 py-2 gap-1 text-red-600 hover:bg-red-100 w-full text-sm"
|
|
323
|
+
onclick={(e) => {
|
|
324
|
+
onDelete && onDelete(getRow(row));
|
|
325
|
+
closeDropDown();
|
|
326
|
+
e.stopPropagation();
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
<iconify-icon icon="mdi:trash" style="font-size: 18px;"
|
|
330
|
+
></iconify-icon>Delete Details
|
|
331
|
+
</DropdownItem>
|
|
332
|
+
{/if}
|
|
333
|
+
{/each}
|
|
334
|
+
</Dropdown>
|
|
335
|
+
|
|
336
|
+
<!-- {/if} -->
|
|
337
|
+
</td>
|
|
338
|
+
{/if}
|
|
339
|
+
{#if showIndex}
|
|
340
|
+
<td class="px-6 py-2">
|
|
341
|
+
{index + 1}
|
|
342
|
+
</td>
|
|
343
|
+
{/if}
|
|
344
|
+
{#each row.cells as cell (cell.id)}
|
|
345
|
+
<Subscribe attrs={cell.attrs()}>
|
|
346
|
+
{#snippet children({ attrs })}
|
|
347
|
+
<td
|
|
348
|
+
{...attrs}
|
|
349
|
+
class="px-6 py-2 text-left text-sm rtl:text-right"
|
|
350
|
+
class:whitespace-nowrap={hideWhiteSpace}
|
|
351
|
+
>
|
|
352
|
+
<Render of={cell.render()} />
|
|
353
|
+
</td>
|
|
354
|
+
{/snippet}
|
|
355
|
+
</Subscribe>
|
|
356
|
+
{/each}
|
|
357
|
+
</tr>
|
|
358
|
+
{/snippet}
|
|
359
|
+
</Subscribe>
|
|
360
|
+
{/each}
|
|
361
|
+
</tbody>
|
|
362
|
+
</table>
|
|
363
|
+
<div class="h-48"></div>
|
|
364
|
+
|
|
365
|
+
{#if !$pageRows.length}
|
|
366
|
+
<div class="flex h-72 w-full items-center justify-center">
|
|
367
|
+
<div class="rounded-md bg-yellow-200 px-6 py-4">No records found</div>
|
|
368
|
+
</div>
|
|
369
|
+
{/if}
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<style>
|
|
375
|
+
.resizer {
|
|
376
|
+
position: absolute;
|
|
377
|
+
top: 0;
|
|
378
|
+
bottom: 0;
|
|
379
|
+
right: -4px;
|
|
380
|
+
width: 8px;
|
|
381
|
+
background: #6194d9;
|
|
382
|
+
cursor: col-resize;
|
|
383
|
+
z-index: 1;
|
|
384
|
+
}
|
|
385
|
+
</style>
|