webcoreui 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -227
- package/astro.d.ts +115 -62
- package/astro.js +14 -0
- package/components/Avatar/Avatar.astro +9 -2
- package/components/Avatar/Avatar.svelte +3 -1
- package/components/Avatar/Avatar.tsx +4 -2
- package/components/Avatar/avatar.ts +1 -0
- package/components/Badge/Badge.astro +4 -0
- package/components/Badge/Badge.svelte +5 -1
- package/components/Badge/Badge.tsx +4 -0
- package/components/Badge/badge.module.scss +8 -0
- package/components/Badge/badge.ts +7 -3
- package/components/Breadcrumb/Breadcrumb.astro +51 -0
- package/components/Breadcrumb/Breadcrumb.svelte +45 -0
- package/components/Breadcrumb/Breadcrumb.tsx +51 -0
- package/components/Breadcrumb/breadcrumb.module.scss +26 -0
- package/components/Breadcrumb/breadcrumb.ts +12 -0
- package/components/Button/button.module.scss +6 -1
- package/components/Button/button.ts +13 -3
- package/components/Carousel/Carousel.astro +198 -0
- package/components/Carousel/Carousel.svelte +161 -0
- package/components/Carousel/Carousel.tsx +172 -0
- package/components/Carousel/carousel.module.scss +58 -0
- package/components/Carousel/carousel.ts +26 -0
- package/components/Checkbox/checkbox.ts +4 -2
- package/components/DataTable/DataTable.astro +332 -0
- package/components/DataTable/DataTable.svelte +272 -0
- package/components/DataTable/DataTable.tsx +287 -0
- package/components/DataTable/datatable.module.scss +102 -0
- package/components/DataTable/datatable.ts +41 -0
- package/components/Footer/Footer.astro +91 -0
- package/components/Footer/Footer.svelte +94 -0
- package/components/Footer/Footer.tsx +107 -0
- package/components/Footer/footer.module.scss +61 -0
- package/components/Footer/footer.ts +29 -0
- package/components/Icon/Icon.svelte +1 -1
- package/components/Icon/icon.ts +18 -1
- package/components/Icon/map.ts +14 -0
- package/components/Input/input.module.scss +6 -0
- package/components/List/List.astro +1 -1
- package/components/List/List.svelte +1 -1
- package/components/List/List.tsx +1 -2
- package/components/List/list.ts +3 -1
- package/components/Masonry/Masonry.astro +54 -0
- package/components/Masonry/Masonry.svelte +54 -0
- package/components/Masonry/Masonry.tsx +62 -0
- package/components/Masonry/masonry.module.scss +18 -0
- package/components/Masonry/masonry.ts +36 -0
- package/components/Menu/Menu.astro +1 -1
- package/components/Menu/Menu.svelte +1 -1
- package/components/Menu/Menu.tsx +1 -1
- package/components/Menu/menu.ts +4 -2
- package/components/Modal/Modal.astro +2 -0
- package/components/Modal/Modal.svelte +2 -0
- package/components/Modal/Modal.tsx +2 -0
- package/components/Modal/modal.module.scss +29 -22
- package/components/Modal/modal.ts +1 -0
- package/components/Pagination/Pagination.astro +189 -0
- package/components/Pagination/Pagination.svelte +144 -0
- package/components/Pagination/Pagination.tsx +162 -0
- package/components/Pagination/pagination.module.scss +49 -0
- package/components/Pagination/pagination.ts +35 -0
- package/components/Rating/rating.ts +3 -1
- package/components/Select/Select.astro +8 -4
- package/components/Select/Select.svelte +15 -6
- package/components/Select/Select.tsx +15 -8
- package/components/Select/select.ts +7 -2
- package/components/Sidebar/Sidebar.astro +61 -0
- package/components/Sidebar/Sidebar.svelte +50 -0
- package/components/Sidebar/Sidebar.tsx +58 -0
- package/components/Sidebar/sidebar.module.scss +43 -0
- package/components/Sidebar/sidebar.ts +21 -0
- package/components/Switch/switch.ts +4 -2
- package/components/Table/Table.svelte +1 -1
- package/components/Table/table.ts +1 -1
- package/icons/arrow-left.svg +3 -0
- package/icons/arrow-right.svg +3 -0
- package/icons/circle-close.svg +3 -0
- package/icons/components.svg +3 -0
- package/icons/file.svg +3 -0
- package/icons/home.svg +4 -0
- package/icons/order.svg +3 -0
- package/icons/sun.svg +1 -1
- package/icons.d.ts +7 -0
- package/icons.js +7 -0
- package/index.d.ts +6 -6
- package/package.json +8 -10
- package/react.d.ts +115 -62
- package/react.js +14 -0
- package/scss/resets.scss +27 -1
- package/svelte.d.ts +116 -62
- package/svelte.js +14 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
---
|
|
2
|
+
/* eslint-disable max-lines */
|
|
3
|
+
import type { DataTableProps, HeadingObject } from './datatable'
|
|
4
|
+
|
|
5
|
+
import Button from '../Button/Button.astro'
|
|
6
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
|
|
7
|
+
import Input from '../Input/Input.astro'
|
|
8
|
+
import Pagination from '../Pagination/Pagination.astro'
|
|
9
|
+
import Select from '../Select/Select.astro'
|
|
10
|
+
|
|
11
|
+
import checkIcon from '../../icons/check.svg?raw'
|
|
12
|
+
import orderIcon from '../../icons/order.svg?raw'
|
|
13
|
+
import searchIcon from '../../icons/search.svg?raw'
|
|
14
|
+
|
|
15
|
+
import styles from './datatable.module.scss'
|
|
16
|
+
|
|
17
|
+
interface Props extends DataTableProps {}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
headings,
|
|
21
|
+
filterPlaceholder = 'Filter entries',
|
|
22
|
+
showFilterIcon,
|
|
23
|
+
noResultsLabel = 'No results.',
|
|
24
|
+
itemsPerPage,
|
|
25
|
+
subText,
|
|
26
|
+
columnToggleLabel = 'Columns',
|
|
27
|
+
pagination,
|
|
28
|
+
data,
|
|
29
|
+
hover,
|
|
30
|
+
striped,
|
|
31
|
+
offsetStripe,
|
|
32
|
+
compact,
|
|
33
|
+
maxHeight,
|
|
34
|
+
className,
|
|
35
|
+
id
|
|
36
|
+
} = Astro.props
|
|
37
|
+
|
|
38
|
+
const classes = [
|
|
39
|
+
styles.table,
|
|
40
|
+
hover && styles.hover,
|
|
41
|
+
striped && styles[`striped-${striped}s`],
|
|
42
|
+
offsetStripe && styles.offset,
|
|
43
|
+
compact && styles.compact,
|
|
44
|
+
maxHeight && styles.scroll
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const footerClasses = [
|
|
48
|
+
styles.footer,
|
|
49
|
+
subText && styles.between
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
const getColumnName = (heading: HeadingObject | string | undefined) => {
|
|
53
|
+
if (!heading) {
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return ((heading as HeadingObject).name || heading as string).toLowerCase().replace(/\s/g, '')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const showColumnToggle = headings?.some(heading => {
|
|
61
|
+
return typeof heading === 'string' ? false : heading.toggleable
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const columnToggleItems = [{
|
|
65
|
+
items: headings?.length ? headings
|
|
66
|
+
.filter(heading => typeof heading !== 'string' && heading.toggleable)
|
|
67
|
+
.map(heading => ({
|
|
68
|
+
name: (heading as HeadingObject).name,
|
|
69
|
+
value: (heading as HeadingObject).name.toLowerCase(),
|
|
70
|
+
icon: checkIcon
|
|
71
|
+
})) : []
|
|
72
|
+
}]
|
|
73
|
+
|
|
74
|
+
const columnFilterItems = headings?.filter(heading => typeof heading !== 'string')
|
|
75
|
+
.filter(heading => heading.filterable)
|
|
76
|
+
.map(heading => heading.name)
|
|
77
|
+
.map(heading => getColumnName(heading))
|
|
78
|
+
|
|
79
|
+
const hasPagination = data?.length && itemsPerPage
|
|
80
|
+
? data.length > itemsPerPage
|
|
81
|
+
: false
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
<section class={className} id={id}>
|
|
85
|
+
{(!!columnFilterItems?.length || showColumnToggle) && (
|
|
86
|
+
<div class={styles.filters}>
|
|
87
|
+
{!!columnFilterItems?.length && (
|
|
88
|
+
<Input
|
|
89
|
+
type="search"
|
|
90
|
+
placeholder={filterPlaceholder}
|
|
91
|
+
data-id="w-data-table-filter"
|
|
92
|
+
data-filter={columnFilterItems}
|
|
93
|
+
>
|
|
94
|
+
<Fragment
|
|
95
|
+
set:html={searchIcon}
|
|
96
|
+
slot={showFilterIcon ? 'default' : null}
|
|
97
|
+
/>
|
|
98
|
+
</Input>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{showColumnToggle && (
|
|
102
|
+
<Select
|
|
103
|
+
name={`data-table-${id || crypto.randomUUID()}`}
|
|
104
|
+
itemGroups={columnToggleItems}
|
|
105
|
+
position="bottom-end"
|
|
106
|
+
value={columnToggleLabel}
|
|
107
|
+
updateValue={false}
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
<div
|
|
113
|
+
class:list={classes}
|
|
114
|
+
style={maxHeight ? `max-height:${maxHeight}` : undefined}
|
|
115
|
+
>
|
|
116
|
+
<table data-items-per-page={itemsPerPage} data-page={hasPagination && 1}>
|
|
117
|
+
{headings?.length && (
|
|
118
|
+
<thead>
|
|
119
|
+
<tr>
|
|
120
|
+
{headings.map(heading => (
|
|
121
|
+
<th data-name={getColumnName(heading)}>
|
|
122
|
+
<ConditionalWrapper condition={!!(heading as HeadingObject).sortable}>
|
|
123
|
+
<Button theme="flat" slot="wrapper" data-id="w-data-table-sort">
|
|
124
|
+
children
|
|
125
|
+
<Fragment set:html={orderIcon} />
|
|
126
|
+
</Button>
|
|
127
|
+
{(heading as HeadingObject).name || heading}
|
|
128
|
+
</ConditionalWrapper>
|
|
129
|
+
</th>
|
|
130
|
+
))}
|
|
131
|
+
</tr>
|
|
132
|
+
</thead>
|
|
133
|
+
)}
|
|
134
|
+
<tbody>
|
|
135
|
+
{data?.map((row, index) => (
|
|
136
|
+
<tr
|
|
137
|
+
data-page={hasPagination ? Math.ceil((index + 1) / itemsPerPage!) : undefined}
|
|
138
|
+
data-hidden={hasPagination && index >= itemsPerPage!}
|
|
139
|
+
>
|
|
140
|
+
{row.map((column, index) => (
|
|
141
|
+
<td data-name={getColumnName(headings?.[index])}>
|
|
142
|
+
<Fragment set:html={column} />
|
|
143
|
+
</td>
|
|
144
|
+
))}
|
|
145
|
+
</tr>
|
|
146
|
+
))}
|
|
147
|
+
<slot />
|
|
148
|
+
</tbody>
|
|
149
|
+
{!!columnFilterItems?.length && (
|
|
150
|
+
<tfoot data-hidden="true">
|
|
151
|
+
<tr>
|
|
152
|
+
<td colspan={data?.[0].length} class={styles['no-results']}>{noResultsLabel}</td>
|
|
153
|
+
</tr>
|
|
154
|
+
</tfoot>
|
|
155
|
+
)}
|
|
156
|
+
</table>
|
|
157
|
+
</div>
|
|
158
|
+
{(subText || hasPagination) && (
|
|
159
|
+
<div class:list={footerClasses}>
|
|
160
|
+
{subText && <span class={styles.subtext}>{subText}</span>}
|
|
161
|
+
{hasPagination && (
|
|
162
|
+
<Pagination
|
|
163
|
+
{...pagination}
|
|
164
|
+
totalPages={Math.ceil((data?.length || 0) / itemsPerPage!)}
|
|
165
|
+
className="w-data-table-pagination"
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
</section>
|
|
171
|
+
|
|
172
|
+
<script>
|
|
173
|
+
import { debounce } from '../../utils/debounce'
|
|
174
|
+
import { dispatch, listen } from '../../utils/event'
|
|
175
|
+
|
|
176
|
+
const filters = document.querySelectorAll('[data-id="w-data-table-filter"]')
|
|
177
|
+
const sorts = document.querySelectorAll('[data-id="w-data-table-sort"]')
|
|
178
|
+
|
|
179
|
+
Array.from(filters).forEach(filter => {
|
|
180
|
+
filter.addEventListener('input', debounce((event: Event) => {
|
|
181
|
+
const target = event.target as HTMLInputElement
|
|
182
|
+
const filterableColumns = target.dataset.filter
|
|
183
|
+
const table = target.closest('section')?.querySelector('table') as HTMLTableElement
|
|
184
|
+
const pagination = target.closest('section')?.querySelector('.w-data-table-pagination') as HTMLUListElement
|
|
185
|
+
const noResults = table.querySelector('tfoot') as HTMLElement
|
|
186
|
+
const tableRows = Array.from(table.querySelectorAll('tbody tr') as NodeListOf<HTMLTableRowElement>)
|
|
187
|
+
|
|
188
|
+
tableRows.forEach(row => {
|
|
189
|
+
const rowValue = Array.from(row.querySelectorAll('td'))
|
|
190
|
+
.filter(td => filterableColumns?.includes(td.dataset.name || ''))
|
|
191
|
+
.map(td => td.innerText)
|
|
192
|
+
.join('')
|
|
193
|
+
.toLowerCase()
|
|
194
|
+
|
|
195
|
+
if (rowValue.includes(target.value.toLowerCase())) {
|
|
196
|
+
row.removeAttribute('data-hidden')
|
|
197
|
+
} else {
|
|
198
|
+
row.dataset.hidden = 'true'
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const filteredRows = Array.from(
|
|
203
|
+
table?.querySelectorAll('tbody tr:not([data-hidden])') as NodeListOf<HTMLTableRowElement>
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (!filteredRows.length) {
|
|
207
|
+
noResults.removeAttribute('data-hidden')
|
|
208
|
+
} else {
|
|
209
|
+
noResults.dataset.hidden = 'true'
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (table.dataset.itemsPerPage && filteredRows.length >= Number(table.dataset.itemsPerPage)) {
|
|
213
|
+
filteredRows.forEach((row, index) => {
|
|
214
|
+
if (index >= Number(table.dataset.itemsPerPage)) {
|
|
215
|
+
row.dataset.hidden = 'true'
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (target.value) {
|
|
221
|
+
pagination.style.display = 'none'
|
|
222
|
+
} else {
|
|
223
|
+
pagination.style.display = 'flex'
|
|
224
|
+
|
|
225
|
+
tableRows.forEach(row => {
|
|
226
|
+
if (row.dataset.page !== table.dataset.page) {
|
|
227
|
+
row.dataset.hidden = 'true'
|
|
228
|
+
} else {
|
|
229
|
+
row.removeAttribute('data-hidden')
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
dispatch('dataTableFilter', {
|
|
235
|
+
results: filteredRows,
|
|
236
|
+
numberOfResults: filteredRows.length
|
|
237
|
+
})
|
|
238
|
+
}, 400))
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
Array.from(sorts).forEach(sort => {
|
|
242
|
+
let sortOrder = 1
|
|
243
|
+
|
|
244
|
+
sort.addEventListener('click', event => {
|
|
245
|
+
const target = event.target as HTMLButtonElement
|
|
246
|
+
const sortBy = target.parentElement?.dataset.name
|
|
247
|
+
const table = target.closest('section')?.querySelector('table')
|
|
248
|
+
const tableBody = table?.querySelector('tbody')
|
|
249
|
+
const sortedTableRows = Array.from(
|
|
250
|
+
table?.querySelectorAll('tbody tr') as NodeListOf<HTMLTableRowElement>
|
|
251
|
+
).sort((a, b) => {
|
|
252
|
+
let aValue: string | number = (a.querySelector(`[data-name=${sortBy}]`) as HTMLElement)
|
|
253
|
+
?.innerText.replace(/\s/g, '')
|
|
254
|
+
let bValue: string | number = (b.querySelector(`[data-name=${sortBy}]`) as HTMLElement)
|
|
255
|
+
?.innerText.replace(/\s/g, '')
|
|
256
|
+
|
|
257
|
+
if (!isNaN(aValue as any)) {
|
|
258
|
+
aValue = Number(aValue)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!isNaN(bValue as any)) {
|
|
262
|
+
bValue = Number(bValue)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return aValue > bValue
|
|
266
|
+
? sortOrder * -1
|
|
267
|
+
: sortOrder
|
|
268
|
+
}).map((row, index) => {
|
|
269
|
+
if (table?.dataset.page) {
|
|
270
|
+
row.dataset.page = `${Math.ceil((index + 1) / Number(table.dataset.itemsPerPage))}`
|
|
271
|
+
|
|
272
|
+
if (row.dataset.page !== table.dataset.page) {
|
|
273
|
+
row.dataset.hidden = 'true'
|
|
274
|
+
} else {
|
|
275
|
+
row.removeAttribute('data-hidden')
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return row
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
tableBody?.replaceChildren(...sortedTableRows)
|
|
283
|
+
|
|
284
|
+
sortOrder = sortOrder === 1 ? -1 : 1
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
listen('selectOnChange', event => {
|
|
289
|
+
const eventName = event.name.toLowerCase().replace(/\s/g, '')
|
|
290
|
+
const table = (document.querySelector(`[data-id="w-select-${event.select}"]`)
|
|
291
|
+
?.closest('section') as HTMLElement)
|
|
292
|
+
.querySelector('table') as HTMLTableElement
|
|
293
|
+
|
|
294
|
+
const affectedTableCells = Array.from(table.querySelectorAll(`[data-name=${eventName}]`)) as HTMLElement[]
|
|
295
|
+
|
|
296
|
+
const columnToggleListElement = Array.from(event.list.children)
|
|
297
|
+
.find(child => (child as HTMLLIElement).dataset.name === event.name) as HTMLLIElement
|
|
298
|
+
const svgIcon = columnToggleListElement.children[0] as HTMLElement
|
|
299
|
+
|
|
300
|
+
svgIcon.style.opacity = svgIcon.style.opacity === '0'
|
|
301
|
+
? '1'
|
|
302
|
+
: '0'
|
|
303
|
+
|
|
304
|
+
if (svgIcon.style.opacity === '0') {
|
|
305
|
+
affectedTableCells.forEach(cell => cell.dataset.hidden = 'true')
|
|
306
|
+
} else {
|
|
307
|
+
affectedTableCells.forEach(cell => cell.removeAttribute('data-hidden'))
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
listen('paginate', event => {
|
|
312
|
+
const table = event.target
|
|
313
|
+
.closest('section')
|
|
314
|
+
.querySelector('table')
|
|
315
|
+
|
|
316
|
+
if (!table) {
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const tableRows = Array.from(table.querySelectorAll('tbody tr') as NodeListOf<HTMLTableRowElement>)
|
|
321
|
+
|
|
322
|
+
table.dataset.page = event.page
|
|
323
|
+
|
|
324
|
+
tableRows.forEach(row => {
|
|
325
|
+
if (Number(row.dataset.page) === event.page) {
|
|
326
|
+
row.removeAttribute('data-hidden')
|
|
327
|
+
} else {
|
|
328
|
+
row.dataset.hidden = 'true'
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
</script>
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HeadingObject, SvelteDataTableProps } from './datatable'
|
|
3
|
+
|
|
4
|
+
import Button from '../Button/Button.svelte'
|
|
5
|
+
import Input from '../Input/Input.svelte'
|
|
6
|
+
import Pagination from '../Pagination/Pagination.svelte'
|
|
7
|
+
import Select from '../Select/Select.svelte'
|
|
8
|
+
|
|
9
|
+
import { classNames } from '../../utils/classNames'
|
|
10
|
+
import { debounce } from '../../utils/debounce'
|
|
11
|
+
|
|
12
|
+
import checkIcon from '../../icons/check.svg?raw'
|
|
13
|
+
import orderIcon from '../../icons/order.svg?raw'
|
|
14
|
+
import searchIcon from '../../icons/search.svg?raw'
|
|
15
|
+
|
|
16
|
+
import styles from './datatable.module.scss'
|
|
17
|
+
|
|
18
|
+
import type { ListEventType } from '../List/list'
|
|
19
|
+
|
|
20
|
+
export let headings: SvelteDataTableProps['headings'] = []
|
|
21
|
+
export let filterPlaceholder: SvelteDataTableProps['filterPlaceholder'] = 'Filter entries'
|
|
22
|
+
export let showFilterIcon: SvelteDataTableProps['showFilterIcon'] = false
|
|
23
|
+
export let noResultsLabel: SvelteDataTableProps['noResultsLabel'] = 'No results.'
|
|
24
|
+
export let itemsPerPage: SvelteDataTableProps['itemsPerPage'] = null
|
|
25
|
+
export let subText: SvelteDataTableProps['subText'] = ''
|
|
26
|
+
export let columnToggleLabel: SvelteDataTableProps['columnToggleLabel'] = 'Columns'
|
|
27
|
+
export let pagination: SvelteDataTableProps['pagination'] = {}
|
|
28
|
+
export let data: SvelteDataTableProps['data'] = []
|
|
29
|
+
export let hover: SvelteDataTableProps['hover'] = false
|
|
30
|
+
export let striped: SvelteDataTableProps['striped'] = null
|
|
31
|
+
export let offsetStripe: SvelteDataTableProps['offsetStripe'] = false
|
|
32
|
+
export let compact: SvelteDataTableProps['compact'] = false
|
|
33
|
+
export let maxHeight: SvelteDataTableProps['maxHeight'] = ''
|
|
34
|
+
export let className: SvelteDataTableProps['className'] = ''
|
|
35
|
+
export let id: SvelteDataTableProps['id'] = ''
|
|
36
|
+
export let onFilter: SvelteDataTableProps['onFilter'] = () => {}
|
|
37
|
+
|
|
38
|
+
let filteredData: any = data
|
|
39
|
+
let toggledData: any = filteredData
|
|
40
|
+
let filteredHeadings: any = headings
|
|
41
|
+
let page: number = 1
|
|
42
|
+
let hasActiveFilter: boolean = false
|
|
43
|
+
let sortOrder = 1
|
|
44
|
+
|
|
45
|
+
const classes = classNames([
|
|
46
|
+
styles.table,
|
|
47
|
+
hover && styles.hover,
|
|
48
|
+
striped && styles[`striped-${striped}s`],
|
|
49
|
+
offsetStripe && styles.offset,
|
|
50
|
+
compact && styles.compact,
|
|
51
|
+
maxHeight && styles.scroll
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const footerClasses = classNames([
|
|
55
|
+
styles.footer,
|
|
56
|
+
subText && styles.between
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
const showColumnToggle = headings?.some(heading => {
|
|
60
|
+
return typeof heading === 'string' ? false : heading.toggleable
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const columnToggleItems = [{
|
|
64
|
+
items: headings?.length ? headings
|
|
65
|
+
.filter(heading => typeof heading !== 'string' && heading.toggleable)
|
|
66
|
+
.map(heading => ({
|
|
67
|
+
icon: checkIcon,
|
|
68
|
+
name: (heading as HeadingObject).name,
|
|
69
|
+
value: String(headings.findIndex(h => {
|
|
70
|
+
return (h as HeadingObject).name === (heading as HeadingObject).name
|
|
71
|
+
}))
|
|
72
|
+
})) : []
|
|
73
|
+
}]
|
|
74
|
+
|
|
75
|
+
const columnFilterIndexes = headings?.map(heading => (heading as HeadingObject).filterable)
|
|
76
|
+
.map((heading, index) => heading ? index : null)
|
|
77
|
+
.filter(heading => heading !== null) || []
|
|
78
|
+
|
|
79
|
+
const hasPagination = data?.length && itemsPerPage
|
|
80
|
+
? data.length > itemsPerPage
|
|
81
|
+
: false
|
|
82
|
+
|
|
83
|
+
const filter = debounce((event: Event) => {
|
|
84
|
+
const target = event.target as HTMLInputElement
|
|
85
|
+
|
|
86
|
+
hasActiveFilter = !!target.value
|
|
87
|
+
|
|
88
|
+
filteredData = toggledData?.filter((row: string[]) => {
|
|
89
|
+
const rowValue = row.filter((_, index) => columnFilterIndexes.includes(index))
|
|
90
|
+
.join('')
|
|
91
|
+
.toLowerCase()
|
|
92
|
+
|
|
93
|
+
return rowValue.includes(target.value.toLowerCase())
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
onFilter?.({
|
|
97
|
+
results: filteredData,
|
|
98
|
+
numberOfResults: filteredData.length
|
|
99
|
+
})
|
|
100
|
+
}, 400)
|
|
101
|
+
|
|
102
|
+
const toggleColumns = (event: ListEventType) => {
|
|
103
|
+
const columnToggleListElement = Array.from(event.list.children)
|
|
104
|
+
.find(child => (child as HTMLLIElement).dataset.name === event.name) as HTMLLIElement
|
|
105
|
+
const svgIcon = columnToggleListElement.children[0] as HTMLElement
|
|
106
|
+
|
|
107
|
+
svgIcon.style.opacity = svgIcon.style.opacity === '0'
|
|
108
|
+
? '1'
|
|
109
|
+
: '0'
|
|
110
|
+
|
|
111
|
+
if (svgIcon.style.opacity === '0') {
|
|
112
|
+
filteredData = (hasActiveFilter ? data : filteredData)?.map((row: string[]) => {
|
|
113
|
+
return row.map((column, index) => index === Number(event.value) ? null : column)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
filteredHeadings = filteredHeadings.map((heading: HeadingObject | string) => {
|
|
117
|
+
return ((heading as HeadingObject)?.name || heading) === event.name ? null : heading
|
|
118
|
+
})
|
|
119
|
+
} else {
|
|
120
|
+
filteredData = (hasActiveFilter ? data : filteredData)?.map((row: string[], x: number) => {
|
|
121
|
+
return row.map((column, y) => y === Number(event.value) ? data?.[x][y] : column)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
filteredHeadings = filteredHeadings.map((heading: HeadingObject | string, index: number) => {
|
|
125
|
+
return ((headings?.[index] as HeadingObject)?.name || headings?.[index]) === event.name
|
|
126
|
+
? headings?.[index]
|
|
127
|
+
: heading
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
hasActiveFilter = false
|
|
132
|
+
toggledData = filteredData
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const sort = (index: number) => {
|
|
136
|
+
filteredData = filteredData.sort((a: string[], b: string[]) => {
|
|
137
|
+
let aValue: string | number = a[index]
|
|
138
|
+
let bValue: string | number = b[index]
|
|
139
|
+
|
|
140
|
+
if (!isNaN(aValue as any)) {
|
|
141
|
+
aValue = Number(aValue)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!isNaN(bValue as any)) {
|
|
145
|
+
bValue = Number(bValue)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return aValue > bValue
|
|
149
|
+
? sortOrder * -1
|
|
150
|
+
: sortOrder
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
sortOrder = sortOrder === 1 ? -1 : 1
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
$: isNextPage = (index: number) => {
|
|
157
|
+
if (hasPagination && itemsPerPage && !hasActiveFilter) {
|
|
158
|
+
const currentPage = Math.ceil((index + 1) / itemsPerPage)
|
|
159
|
+
|
|
160
|
+
return currentPage !== page ? 'true' : undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (hasActiveFilter && itemsPerPage) {
|
|
164
|
+
return index >= itemsPerPage ? 'true' : undefined
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return undefined
|
|
168
|
+
}
|
|
169
|
+
</script>
|
|
170
|
+
|
|
171
|
+
<section class={className || null} id={id || null}>
|
|
172
|
+
{#if columnFilterIndexes?.length || showColumnToggle}
|
|
173
|
+
<div class={styles.filters}>
|
|
174
|
+
{#if columnFilterIndexes?.length}
|
|
175
|
+
{#if showFilterIcon}
|
|
176
|
+
<Input
|
|
177
|
+
type="search"
|
|
178
|
+
placeholder={filterPlaceholder}
|
|
179
|
+
onInput={filter}
|
|
180
|
+
>
|
|
181
|
+
{@html searchIcon}
|
|
182
|
+
</Input>
|
|
183
|
+
{:else}
|
|
184
|
+
<Input
|
|
185
|
+
type="search"
|
|
186
|
+
placeholder={filterPlaceholder}
|
|
187
|
+
onInput={filter}
|
|
188
|
+
/>
|
|
189
|
+
{/if}
|
|
190
|
+
{/if}
|
|
191
|
+
{#if showColumnToggle}
|
|
192
|
+
<Select
|
|
193
|
+
name={`data-table-${id || crypto.randomUUID()}`}
|
|
194
|
+
itemGroups={columnToggleItems}
|
|
195
|
+
position="bottom-end"
|
|
196
|
+
value={columnToggleLabel}
|
|
197
|
+
onChange={toggleColumns}
|
|
198
|
+
updateValue={false}
|
|
199
|
+
/>
|
|
200
|
+
{/if}
|
|
201
|
+
</div>
|
|
202
|
+
{/if}
|
|
203
|
+
|
|
204
|
+
<div
|
|
205
|
+
class={classes}
|
|
206
|
+
style={maxHeight ? `max-height:${maxHeight}` : undefined}
|
|
207
|
+
>
|
|
208
|
+
<table>
|
|
209
|
+
{#if filteredHeadings?.length}
|
|
210
|
+
<thead>
|
|
211
|
+
<tr>
|
|
212
|
+
{#each filteredHeadings as heading, index}
|
|
213
|
+
{#if heading}
|
|
214
|
+
<th>
|
|
215
|
+
{#if heading.sortable}
|
|
216
|
+
<Button theme="flat" slot="wrapper" onClick={() => sort(index)}>
|
|
217
|
+
{heading.name || heading}
|
|
218
|
+
{@html orderIcon}
|
|
219
|
+
</Button>
|
|
220
|
+
{:else}
|
|
221
|
+
{heading.name || heading}
|
|
222
|
+
{/if}
|
|
223
|
+
</th>
|
|
224
|
+
{/if}
|
|
225
|
+
{/each}
|
|
226
|
+
</tr>
|
|
227
|
+
</thead>
|
|
228
|
+
{/if}
|
|
229
|
+
|
|
230
|
+
<tbody>
|
|
231
|
+
{#if filteredData?.length}
|
|
232
|
+
{#each filteredData as row, index}
|
|
233
|
+
<tr data-hidden={isNextPage(index)}>
|
|
234
|
+
{#each row as column}
|
|
235
|
+
{#if column}
|
|
236
|
+
<td>
|
|
237
|
+
{@html column}
|
|
238
|
+
</td>
|
|
239
|
+
{/if}
|
|
240
|
+
{/each}
|
|
241
|
+
</tr>
|
|
242
|
+
{/each}
|
|
243
|
+
{/if}
|
|
244
|
+
<slot />
|
|
245
|
+
</tbody>
|
|
246
|
+
{#if columnFilterIndexes?.length && !filteredData.length}
|
|
247
|
+
<tfoot>
|
|
248
|
+
<tr>
|
|
249
|
+
<td colspan={data?.[0].length} class={styles['no-results']}>
|
|
250
|
+
{noResultsLabel}
|
|
251
|
+
</td>
|
|
252
|
+
</tr>
|
|
253
|
+
</tfoot>
|
|
254
|
+
{/if}
|
|
255
|
+
</table>
|
|
256
|
+
</div>
|
|
257
|
+
{#if subText || hasPagination}
|
|
258
|
+
<div class={footerClasses}>
|
|
259
|
+
{#if subText}
|
|
260
|
+
<span class={styles.subtext}>{subText}</span>
|
|
261
|
+
{/if}
|
|
262
|
+
{#if hasPagination && itemsPerPage && !hasActiveFilter}
|
|
263
|
+
<Pagination
|
|
264
|
+
{...pagination}
|
|
265
|
+
totalPages={Math.ceil((data?.length || 0) / itemsPerPage)}
|
|
266
|
+
currentPage={page}
|
|
267
|
+
onChange={event => page = event.page}
|
|
268
|
+
/>
|
|
269
|
+
{/if}
|
|
270
|
+
</div>
|
|
271
|
+
{/if}
|
|
272
|
+
</section>
|