webcoreui 0.5.0 → 0.6.1
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 +7 -0
- package/astro.d.ts +115 -68
- package/astro.js +8 -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.ts +13 -3
- package/components/Carousel/Carousel.astro +16 -7
- package/components/Carousel/Carousel.svelte +15 -5
- package/components/Carousel/Carousel.tsx +15 -5
- package/components/Carousel/carousel.module.scss +4 -1
- package/components/Carousel/carousel.ts +1 -1
- package/components/Checkbox/checkbox.ts +4 -2
- package/components/DataTable/DataTable.astro +2 -2
- package/components/DataTable/DataTable.tsx +287 -287
- 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 +8 -0
- 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 +2 -0
- package/components/Pagination/Pagination.svelte +6 -3
- package/components/Pagination/Pagination.tsx +5 -3
- package/components/Pagination/pagination.ts +1 -0
- package/components/Rating/rating.ts +3 -1
- 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/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/sun.svg +1 -1
- package/icons.d.ts +4 -0
- package/icons.js +4 -0
- package/index.d.ts +8 -0
- package/index.js +1 -0
- package/package.json +9 -10
- package/react.d.ts +115 -68
- package/react.js +8 -0
- package/scss/config/typography.scss +3 -1
- package/scss/global/utility.scss +1 -1
- package/svelte.d.ts +116 -68
- package/svelte.js +8 -0
- package/utils/DOMUtils.ts +28 -0
- package/scss/global/elements.scss +0 -31
|
@@ -1,287 +1,287 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import type { HeadingObject, ReactDataTableProps } from './datatable'
|
|
3
|
-
|
|
4
|
-
import Button from '../Button/Button.tsx'
|
|
5
|
-
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
|
|
6
|
-
import Input from '../Input/Input.tsx'
|
|
7
|
-
import Pagination from '../Pagination/Pagination.tsx'
|
|
8
|
-
import Select from '../Select/Select.tsx'
|
|
9
|
-
|
|
10
|
-
import { classNames } from '../../utils/classNames'
|
|
11
|
-
import { debounce } from '../../utils/debounce'
|
|
12
|
-
|
|
13
|
-
import checkIcon from '../../icons/check.svg?raw'
|
|
14
|
-
import orderIcon from '../../icons/order.svg?raw'
|
|
15
|
-
import searchIcon from '../../icons/search.svg?raw'
|
|
16
|
-
|
|
17
|
-
import styles from './datatable.module.scss'
|
|
18
|
-
|
|
19
|
-
import type { ListEventType } from '../List/list'
|
|
20
|
-
|
|
21
|
-
// eslint-disable-next-line complexity
|
|
22
|
-
const DataTable = ({
|
|
23
|
-
headings,
|
|
24
|
-
filterPlaceholder = 'Filter entries',
|
|
25
|
-
showFilterIcon,
|
|
26
|
-
noResultsLabel = 'No results.',
|
|
27
|
-
itemsPerPage,
|
|
28
|
-
subText,
|
|
29
|
-
columnToggleLabel = 'Columns',
|
|
30
|
-
pagination,
|
|
31
|
-
data,
|
|
32
|
-
hover,
|
|
33
|
-
striped,
|
|
34
|
-
offsetStripe,
|
|
35
|
-
compact,
|
|
36
|
-
maxHeight,
|
|
37
|
-
className,
|
|
38
|
-
id,
|
|
39
|
-
onFilter,
|
|
40
|
-
children
|
|
41
|
-
}: ReactDataTableProps) => {
|
|
42
|
-
const [filteredData, setFilteredData] = useState<any>(data)
|
|
43
|
-
const [toggledData, setToggledData] = useState(filteredData)
|
|
44
|
-
const [filteredHeadings, setFilteredHeadings] = useState<any>(headings)
|
|
45
|
-
const [page, setPage] = useState(1)
|
|
46
|
-
const [hasActiveFilter, setHasActiveFilter] = useState(false)
|
|
47
|
-
const [sortOrder, setSortOrder] = useState(1)
|
|
48
|
-
|
|
49
|
-
const classes = classNames([
|
|
50
|
-
styles.table,
|
|
51
|
-
hover && styles.hover,
|
|
52
|
-
striped && styles[`striped-${striped}s`],
|
|
53
|
-
offsetStripe && styles.offset,
|
|
54
|
-
compact && styles.compact,
|
|
55
|
-
maxHeight && styles.scroll
|
|
56
|
-
])
|
|
57
|
-
|
|
58
|
-
const footerClasses = classNames([
|
|
59
|
-
styles.footer,
|
|
60
|
-
subText && styles.between
|
|
61
|
-
])
|
|
62
|
-
|
|
63
|
-
const styleVariables = {
|
|
64
|
-
...(maxHeight && {
|
|
65
|
-
} as React.CSSProperties
|
|
66
|
-
|
|
67
|
-
const showColumnToggle = headings?.some(heading => {
|
|
68
|
-
return typeof heading === 'string' ? false : heading.toggleable
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const columnToggleItems = [{
|
|
72
|
-
items: headings?.length ? headings
|
|
73
|
-
.filter(heading => typeof heading !== 'string' && heading.toggleable)
|
|
74
|
-
.map(heading => ({
|
|
75
|
-
icon: checkIcon,
|
|
76
|
-
name: (heading as HeadingObject).name,
|
|
77
|
-
value: String(headings.findIndex(h => {
|
|
78
|
-
return (h as HeadingObject).name === (heading as HeadingObject).name
|
|
79
|
-
}))
|
|
80
|
-
})) : []
|
|
81
|
-
}]
|
|
82
|
-
|
|
83
|
-
const columnFilterIndexes = headings?.map(heading => (heading as HeadingObject).filterable)
|
|
84
|
-
.map((heading, index) => heading ? index : null)
|
|
85
|
-
.filter(heading => heading !== null) || []
|
|
86
|
-
|
|
87
|
-
const hasPagination = data?.length && itemsPerPage
|
|
88
|
-
? data.length > itemsPerPage
|
|
89
|
-
: false
|
|
90
|
-
|
|
91
|
-
const filter = debounce((event: Event) => {
|
|
92
|
-
const target = event.target as HTMLInputElement
|
|
93
|
-
|
|
94
|
-
setHasActiveFilter(!!target.value)
|
|
95
|
-
|
|
96
|
-
setFilteredData(toggledData?.filter((row: string[]) => {
|
|
97
|
-
const rowValue = row.filter((_, index) => columnFilterIndexes.includes(index))
|
|
98
|
-
.join('')
|
|
99
|
-
.toLowerCase()
|
|
100
|
-
|
|
101
|
-
return rowValue.includes(target.value.toLowerCase())
|
|
102
|
-
}))
|
|
103
|
-
|
|
104
|
-
onFilter?.({
|
|
105
|
-
results: filteredData,
|
|
106
|
-
numberOfResults: filteredData.length
|
|
107
|
-
})
|
|
108
|
-
}, 400)
|
|
109
|
-
|
|
110
|
-
const toggleColumns = (event: ListEventType) => {
|
|
111
|
-
const columnToggleListElement = Array.from(event.list.children)
|
|
112
|
-
.find(child => (child as HTMLLIElement).dataset.name === event.name) as HTMLLIElement
|
|
113
|
-
const svgIcon = columnToggleListElement.children[0] as HTMLElement
|
|
114
|
-
let mappedData
|
|
115
|
-
|
|
116
|
-
svgIcon.style.opacity = svgIcon.style.opacity === '0'
|
|
117
|
-
? '1'
|
|
118
|
-
: '0'
|
|
119
|
-
|
|
120
|
-
if (svgIcon.style.opacity === '0') {
|
|
121
|
-
mappedData = (hasActiveFilter ? data : filteredData)?.map((row: string[]) => {
|
|
122
|
-
return row.map((column, index) => index === Number(event.value) ? null : column)
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
setFilteredData(mappedData)
|
|
126
|
-
|
|
127
|
-
setFilteredHeadings(filteredHeadings.map((heading: HeadingObject | string) => {
|
|
128
|
-
return ((heading as HeadingObject)?.name || heading) === event.name ? null : heading
|
|
129
|
-
}))
|
|
130
|
-
} else {
|
|
131
|
-
mappedData = (hasActiveFilter ? data : filteredData)?.map((row: string[], x: number) => {
|
|
132
|
-
return row.map((column, y) => y === Number(event.value) ? data?.[x][y] : column)
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
setFilteredData(mappedData)
|
|
136
|
-
|
|
137
|
-
setFilteredHeadings(filteredHeadings.map((heading: HeadingObject | string, index: number) => {
|
|
138
|
-
return ((headings?.[index] as HeadingObject)?.name || headings?.[index]) === event.name
|
|
139
|
-
? headings?.[index]
|
|
140
|
-
: heading
|
|
141
|
-
}))
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
setToggledData(mappedData)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const sort = (index: number) => {
|
|
148
|
-
const sortedData = filteredData.sort((a: string[], b: string[]) => {
|
|
149
|
-
let aValue: string | number = a[index]
|
|
150
|
-
let bValue: string | number = b[index]
|
|
151
|
-
|
|
152
|
-
if (!isNaN(aValue as any)) {
|
|
153
|
-
aValue = Number(aValue)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!isNaN(bValue as any)) {
|
|
157
|
-
bValue = Number(bValue)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return aValue > bValue
|
|
161
|
-
? sortOrder * -1
|
|
162
|
-
: sortOrder
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
setFilteredData(sortedData)
|
|
166
|
-
setSortOrder(sortOrder === 1 ? -1 : 1)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const isNextPage = (index: number) => {
|
|
170
|
-
if (hasPagination && itemsPerPage && !hasActiveFilter) {
|
|
171
|
-
const currentPage = Math.ceil((index + 1) / itemsPerPage)
|
|
172
|
-
|
|
173
|
-
return currentPage !== page ? 'true' : undefined
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (hasActiveFilter && itemsPerPage) {
|
|
177
|
-
return index >= itemsPerPage ? 'true' : undefined
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return undefined
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return (
|
|
184
|
-
<section className={className} id={id}>
|
|
185
|
-
{(!!columnFilterIndexes?.length || showColumnToggle) && (
|
|
186
|
-
<div className={styles.filters}>
|
|
187
|
-
{!!columnFilterIndexes?.length && (
|
|
188
|
-
<Input
|
|
189
|
-
type="search"
|
|
190
|
-
placeholder={filterPlaceholder}
|
|
191
|
-
onInput={filter}
|
|
192
|
-
>
|
|
193
|
-
{showFilterIcon && (
|
|
194
|
-
<span dangerouslySetInnerHTML={{ __html: searchIcon }} />
|
|
195
|
-
)}
|
|
196
|
-
</Input>
|
|
197
|
-
)}
|
|
198
|
-
{showColumnToggle && (
|
|
199
|
-
<Select
|
|
200
|
-
name={`data-table-${id || crypto.randomUUID()}`}
|
|
201
|
-
itemGroups={columnToggleItems}
|
|
202
|
-
position="bottom-end"
|
|
203
|
-
value={columnToggleLabel}
|
|
204
|
-
onChange={toggleColumns}
|
|
205
|
-
updateValue={false}
|
|
206
|
-
/>
|
|
207
|
-
)}
|
|
208
|
-
</div>
|
|
209
|
-
)}
|
|
210
|
-
|
|
211
|
-
<div className={classes} style={styleVariables}>
|
|
212
|
-
<table>
|
|
213
|
-
{!!filteredHeadings?.length && (
|
|
214
|
-
<thead>
|
|
215
|
-
<tr>
|
|
216
|
-
{filteredHeadings?.map((heading: HeadingObject | string, index: number) => {
|
|
217
|
-
if (!heading) {
|
|
218
|
-
return null
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
<th key={index}>
|
|
223
|
-
<ConditionalWrapper
|
|
224
|
-
condition={!!(heading as HeadingObject).sortable}
|
|
225
|
-
wrapper={children => (
|
|
226
|
-
<Button theme="flat" slot="wrapper" onClick={() => sort(index)}>
|
|
227
|
-
{children}
|
|
228
|
-
<span dangerouslySetInnerHTML={{ __html: orderIcon }} />
|
|
229
|
-
</Button>
|
|
230
|
-
)}
|
|
231
|
-
>
|
|
232
|
-
{(heading as HeadingObject).name || heading as string}
|
|
233
|
-
</ConditionalWrapper>
|
|
234
|
-
</th>
|
|
235
|
-
)
|
|
236
|
-
})}
|
|
237
|
-
</tr>
|
|
238
|
-
</thead>
|
|
239
|
-
)}
|
|
240
|
-
|
|
241
|
-
<tbody>
|
|
242
|
-
{filteredData?.map((row: string[], rowIndex: number) => (
|
|
243
|
-
<tr key={rowIndex} data-hidden={isNextPage(rowIndex)}>
|
|
244
|
-
{row.filter(Boolean).map((column, columnIndex) => (
|
|
245
|
-
<td
|
|
246
|
-
key={columnIndex}
|
|
247
|
-
dangerouslySetInnerHTML={{ __html: column }}
|
|
248
|
-
/>
|
|
249
|
-
))}
|
|
250
|
-
</tr>
|
|
251
|
-
))}
|
|
252
|
-
{children}
|
|
253
|
-
</tbody>
|
|
254
|
-
{!filteredData?.length && (
|
|
255
|
-
<tfoot>
|
|
256
|
-
<tr>
|
|
257
|
-
<td
|
|
258
|
-
colSpan={data?.[0].length}
|
|
259
|
-
className={styles['no-results']}
|
|
260
|
-
>
|
|
261
|
-
{noResultsLabel}
|
|
262
|
-
</td>
|
|
263
|
-
</tr>
|
|
264
|
-
</tfoot>
|
|
265
|
-
)}
|
|
266
|
-
</table>
|
|
267
|
-
</div>
|
|
268
|
-
{(subText || hasPagination) && (
|
|
269
|
-
<div className={footerClasses}>
|
|
270
|
-
{subText && (
|
|
271
|
-
<span className={styles.subtext}>{subText}</span>
|
|
272
|
-
)}
|
|
273
|
-
{(hasPagination && itemsPerPage && !hasActiveFilter) && (
|
|
274
|
-
<Pagination
|
|
275
|
-
{...pagination}
|
|
276
|
-
totalPages={Math.ceil((data?.length || 0) / itemsPerPage)}
|
|
277
|
-
currentPage={page}
|
|
278
|
-
onChange={event => setPage(event.page)}
|
|
279
|
-
/>
|
|
280
|
-
)}
|
|
281
|
-
</div>
|
|
282
|
-
)}
|
|
283
|
-
</section>
|
|
284
|
-
)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export default DataTable
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import type { HeadingObject, ReactDataTableProps } from './datatable'
|
|
3
|
+
|
|
4
|
+
import Button from '../Button/Button.tsx'
|
|
5
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
|
|
6
|
+
import Input from '../Input/Input.tsx'
|
|
7
|
+
import Pagination from '../Pagination/Pagination.tsx'
|
|
8
|
+
import Select from '../Select/Select.tsx'
|
|
9
|
+
|
|
10
|
+
import { classNames } from '../../utils/classNames'
|
|
11
|
+
import { debounce } from '../../utils/debounce'
|
|
12
|
+
|
|
13
|
+
import checkIcon from '../../icons/check.svg?raw'
|
|
14
|
+
import orderIcon from '../../icons/order.svg?raw'
|
|
15
|
+
import searchIcon from '../../icons/search.svg?raw'
|
|
16
|
+
|
|
17
|
+
import styles from './datatable.module.scss'
|
|
18
|
+
|
|
19
|
+
import type { ListEventType } from '../List/list'
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line complexity
|
|
22
|
+
const DataTable = ({
|
|
23
|
+
headings,
|
|
24
|
+
filterPlaceholder = 'Filter entries',
|
|
25
|
+
showFilterIcon,
|
|
26
|
+
noResultsLabel = 'No results.',
|
|
27
|
+
itemsPerPage,
|
|
28
|
+
subText,
|
|
29
|
+
columnToggleLabel = 'Columns',
|
|
30
|
+
pagination,
|
|
31
|
+
data,
|
|
32
|
+
hover,
|
|
33
|
+
striped,
|
|
34
|
+
offsetStripe,
|
|
35
|
+
compact,
|
|
36
|
+
maxHeight,
|
|
37
|
+
className,
|
|
38
|
+
id,
|
|
39
|
+
onFilter,
|
|
40
|
+
children
|
|
41
|
+
}: ReactDataTableProps) => {
|
|
42
|
+
const [filteredData, setFilteredData] = useState<any>(data)
|
|
43
|
+
const [toggledData, setToggledData] = useState(filteredData)
|
|
44
|
+
const [filteredHeadings, setFilteredHeadings] = useState<any>(headings)
|
|
45
|
+
const [page, setPage] = useState(1)
|
|
46
|
+
const [hasActiveFilter, setHasActiveFilter] = useState(false)
|
|
47
|
+
const [sortOrder, setSortOrder] = useState(1)
|
|
48
|
+
|
|
49
|
+
const classes = classNames([
|
|
50
|
+
styles.table,
|
|
51
|
+
hover && styles.hover,
|
|
52
|
+
striped && styles[`striped-${striped}s`],
|
|
53
|
+
offsetStripe && styles.offset,
|
|
54
|
+
compact && styles.compact,
|
|
55
|
+
maxHeight && styles.scroll
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
const footerClasses = classNames([
|
|
59
|
+
styles.footer,
|
|
60
|
+
subText && styles.between
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
const styleVariables = {
|
|
64
|
+
...(maxHeight && { maxHeight })
|
|
65
|
+
} as React.CSSProperties
|
|
66
|
+
|
|
67
|
+
const showColumnToggle = headings?.some(heading => {
|
|
68
|
+
return typeof heading === 'string' ? false : heading.toggleable
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const columnToggleItems = [{
|
|
72
|
+
items: headings?.length ? headings
|
|
73
|
+
.filter(heading => typeof heading !== 'string' && heading.toggleable)
|
|
74
|
+
.map(heading => ({
|
|
75
|
+
icon: checkIcon,
|
|
76
|
+
name: (heading as HeadingObject).name,
|
|
77
|
+
value: String(headings.findIndex(h => {
|
|
78
|
+
return (h as HeadingObject).name === (heading as HeadingObject).name
|
|
79
|
+
}))
|
|
80
|
+
})) : []
|
|
81
|
+
}]
|
|
82
|
+
|
|
83
|
+
const columnFilterIndexes = headings?.map(heading => (heading as HeadingObject).filterable)
|
|
84
|
+
.map((heading, index) => heading ? index : null)
|
|
85
|
+
.filter(heading => heading !== null) || []
|
|
86
|
+
|
|
87
|
+
const hasPagination = data?.length && itemsPerPage
|
|
88
|
+
? data.length > itemsPerPage
|
|
89
|
+
: false
|
|
90
|
+
|
|
91
|
+
const filter = debounce((event: Event) => {
|
|
92
|
+
const target = event.target as HTMLInputElement
|
|
93
|
+
|
|
94
|
+
setHasActiveFilter(!!target.value)
|
|
95
|
+
|
|
96
|
+
setFilteredData(toggledData?.filter((row: string[]) => {
|
|
97
|
+
const rowValue = row.filter((_, index) => columnFilterIndexes.includes(index))
|
|
98
|
+
.join('')
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
|
|
101
|
+
return rowValue.includes(target.value.toLowerCase())
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
onFilter?.({
|
|
105
|
+
results: filteredData,
|
|
106
|
+
numberOfResults: filteredData.length
|
|
107
|
+
})
|
|
108
|
+
}, 400)
|
|
109
|
+
|
|
110
|
+
const toggleColumns = (event: ListEventType) => {
|
|
111
|
+
const columnToggleListElement = Array.from(event.list.children)
|
|
112
|
+
.find(child => (child as HTMLLIElement).dataset.name === event.name) as HTMLLIElement
|
|
113
|
+
const svgIcon = columnToggleListElement.children[0] as HTMLElement
|
|
114
|
+
let mappedData
|
|
115
|
+
|
|
116
|
+
svgIcon.style.opacity = svgIcon.style.opacity === '0'
|
|
117
|
+
? '1'
|
|
118
|
+
: '0'
|
|
119
|
+
|
|
120
|
+
if (svgIcon.style.opacity === '0') {
|
|
121
|
+
mappedData = (hasActiveFilter ? data : filteredData)?.map((row: string[]) => {
|
|
122
|
+
return row.map((column, index) => index === Number(event.value) ? null : column)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
setFilteredData(mappedData)
|
|
126
|
+
|
|
127
|
+
setFilteredHeadings(filteredHeadings.map((heading: HeadingObject | string) => {
|
|
128
|
+
return ((heading as HeadingObject)?.name || heading) === event.name ? null : heading
|
|
129
|
+
}))
|
|
130
|
+
} else {
|
|
131
|
+
mappedData = (hasActiveFilter ? data : filteredData)?.map((row: string[], x: number) => {
|
|
132
|
+
return row.map((column, y) => y === Number(event.value) ? data?.[x][y] : column)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
setFilteredData(mappedData)
|
|
136
|
+
|
|
137
|
+
setFilteredHeadings(filteredHeadings.map((heading: HeadingObject | string, index: number) => {
|
|
138
|
+
return ((headings?.[index] as HeadingObject)?.name || headings?.[index]) === event.name
|
|
139
|
+
? headings?.[index]
|
|
140
|
+
: heading
|
|
141
|
+
}))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setToggledData(mappedData)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const sort = (index: number) => {
|
|
148
|
+
const sortedData = filteredData.sort((a: string[], b: string[]) => {
|
|
149
|
+
let aValue: string | number = a[index]
|
|
150
|
+
let bValue: string | number = b[index]
|
|
151
|
+
|
|
152
|
+
if (!isNaN(aValue as any)) {
|
|
153
|
+
aValue = Number(aValue)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!isNaN(bValue as any)) {
|
|
157
|
+
bValue = Number(bValue)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return aValue > bValue
|
|
161
|
+
? sortOrder * -1
|
|
162
|
+
: sortOrder
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
setFilteredData(sortedData)
|
|
166
|
+
setSortOrder(sortOrder === 1 ? -1 : 1)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const isNextPage = (index: number) => {
|
|
170
|
+
if (hasPagination && itemsPerPage && !hasActiveFilter) {
|
|
171
|
+
const currentPage = Math.ceil((index + 1) / itemsPerPage)
|
|
172
|
+
|
|
173
|
+
return currentPage !== page ? 'true' : undefined
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (hasActiveFilter && itemsPerPage) {
|
|
177
|
+
return index >= itemsPerPage ? 'true' : undefined
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return undefined
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<section className={className} id={id}>
|
|
185
|
+
{(!!columnFilterIndexes?.length || showColumnToggle) && (
|
|
186
|
+
<div className={styles.filters}>
|
|
187
|
+
{!!columnFilterIndexes?.length && (
|
|
188
|
+
<Input
|
|
189
|
+
type="search"
|
|
190
|
+
placeholder={filterPlaceholder}
|
|
191
|
+
onInput={filter}
|
|
192
|
+
>
|
|
193
|
+
{showFilterIcon && (
|
|
194
|
+
<span dangerouslySetInnerHTML={{ __html: searchIcon }} />
|
|
195
|
+
)}
|
|
196
|
+
</Input>
|
|
197
|
+
)}
|
|
198
|
+
{showColumnToggle && (
|
|
199
|
+
<Select
|
|
200
|
+
name={`data-table-${id || crypto.randomUUID()}`}
|
|
201
|
+
itemGroups={columnToggleItems}
|
|
202
|
+
position="bottom-end"
|
|
203
|
+
value={columnToggleLabel}
|
|
204
|
+
onChange={toggleColumns}
|
|
205
|
+
updateValue={false}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
<div className={classes} style={styleVariables}>
|
|
212
|
+
<table>
|
|
213
|
+
{!!filteredHeadings?.length && (
|
|
214
|
+
<thead>
|
|
215
|
+
<tr>
|
|
216
|
+
{filteredHeadings?.map((heading: HeadingObject | string, index: number) => {
|
|
217
|
+
if (!heading) {
|
|
218
|
+
return null
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<th key={index}>
|
|
223
|
+
<ConditionalWrapper
|
|
224
|
+
condition={!!(heading as HeadingObject).sortable}
|
|
225
|
+
wrapper={children => (
|
|
226
|
+
<Button theme="flat" slot="wrapper" onClick={() => sort(index)}>
|
|
227
|
+
{children}
|
|
228
|
+
<span dangerouslySetInnerHTML={{ __html: orderIcon }} />
|
|
229
|
+
</Button>
|
|
230
|
+
)}
|
|
231
|
+
>
|
|
232
|
+
{(heading as HeadingObject).name || heading as string}
|
|
233
|
+
</ConditionalWrapper>
|
|
234
|
+
</th>
|
|
235
|
+
)
|
|
236
|
+
})}
|
|
237
|
+
</tr>
|
|
238
|
+
</thead>
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
<tbody>
|
|
242
|
+
{filteredData?.map((row: string[], rowIndex: number) => (
|
|
243
|
+
<tr key={rowIndex} data-hidden={isNextPage(rowIndex)}>
|
|
244
|
+
{row.filter(Boolean).map((column, columnIndex) => (
|
|
245
|
+
<td
|
|
246
|
+
key={columnIndex}
|
|
247
|
+
dangerouslySetInnerHTML={{ __html: column }}
|
|
248
|
+
/>
|
|
249
|
+
))}
|
|
250
|
+
</tr>
|
|
251
|
+
))}
|
|
252
|
+
{children}
|
|
253
|
+
</tbody>
|
|
254
|
+
{!filteredData?.length && (
|
|
255
|
+
<tfoot>
|
|
256
|
+
<tr>
|
|
257
|
+
<td
|
|
258
|
+
colSpan={data?.[0].length}
|
|
259
|
+
className={styles['no-results']}
|
|
260
|
+
>
|
|
261
|
+
{noResultsLabel}
|
|
262
|
+
</td>
|
|
263
|
+
</tr>
|
|
264
|
+
</tfoot>
|
|
265
|
+
)}
|
|
266
|
+
</table>
|
|
267
|
+
</div>
|
|
268
|
+
{(subText || hasPagination) && (
|
|
269
|
+
<div className={footerClasses}>
|
|
270
|
+
{subText && (
|
|
271
|
+
<span className={styles.subtext}>{subText}</span>
|
|
272
|
+
)}
|
|
273
|
+
{(hasPagination && itemsPerPage && !hasActiveFilter) && (
|
|
274
|
+
<Pagination
|
|
275
|
+
{...pagination}
|
|
276
|
+
totalPages={Math.ceil((data?.length || 0) / itemsPerPage)}
|
|
277
|
+
currentPage={page}
|
|
278
|
+
onChange={event => setPage(event.page)}
|
|
279
|
+
/>
|
|
280
|
+
)}
|
|
281
|
+
</div>
|
|
282
|
+
)}
|
|
283
|
+
</section>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export default DataTable
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { FooterProps } from './footer'
|
|
3
|
+
|
|
4
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
|
|
5
|
+
|
|
6
|
+
import styles from './footer.module.scss'
|
|
7
|
+
|
|
8
|
+
interface Props extends FooterProps {}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
logo,
|
|
12
|
+
columns,
|
|
13
|
+
subText,
|
|
14
|
+
className,
|
|
15
|
+
wrapperClassName,
|
|
16
|
+
subTextClassName
|
|
17
|
+
} = Astro.props
|
|
18
|
+
|
|
19
|
+
const classes = [
|
|
20
|
+
styles.footer,
|
|
21
|
+
className
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const containerClasses = [
|
|
25
|
+
styles.container,
|
|
26
|
+
wrapperClassName
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const subTextClasses = [
|
|
30
|
+
styles.subtext,
|
|
31
|
+
subTextClassName
|
|
32
|
+
]
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
<footer class:list={classes}>
|
|
36
|
+
<div class:list={containerClasses}>
|
|
37
|
+
<ConditionalWrapper condition={!!(logo?.url || logo?.html)}>
|
|
38
|
+
<div slot="wrapper" class={styles.wrapper}>children</div>
|
|
39
|
+
{logo?.url && (
|
|
40
|
+
<a href="/">
|
|
41
|
+
<img
|
|
42
|
+
src={logo.url}
|
|
43
|
+
alt={logo.alt || 'Logo'}
|
|
44
|
+
width={logo.width}
|
|
45
|
+
height={logo.height}
|
|
46
|
+
loading={logo.eager ? undefined : 'lazy'}
|
|
47
|
+
/>
|
|
48
|
+
</a>
|
|
49
|
+
)}
|
|
50
|
+
|
|
51
|
+
{logo?.html && (
|
|
52
|
+
<a href="/" aria-label={logo.alt || 'Logo'}>
|
|
53
|
+
<Fragment set:html={logo.html} />
|
|
54
|
+
</a>
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
{!!columns?.length && (
|
|
58
|
+
<ConditionalWrapper condition={columns.length > 1}>
|
|
59
|
+
<div slot="wrapper" class={styles.columns}>children</div>
|
|
60
|
+
{columns.map(column => (
|
|
61
|
+
<ConditionalWrapper condition={!!column.title}>
|
|
62
|
+
<div slot="wrapper">children</div>
|
|
63
|
+
{column.title && (
|
|
64
|
+
<strong class={styles['column-title']}>{column.title}</strong>
|
|
65
|
+
)}
|
|
66
|
+
<ul class={styles.column}>
|
|
67
|
+
{column.items.map(item => (
|
|
68
|
+
<li>
|
|
69
|
+
<a
|
|
70
|
+
href={item.href}
|
|
71
|
+
target={item.target}
|
|
72
|
+
class:list={[item.active && styles.active]}
|
|
73
|
+
>
|
|
74
|
+
{item.name}
|
|
75
|
+
</a>
|
|
76
|
+
</li>
|
|
77
|
+
))}
|
|
78
|
+
</ul>
|
|
79
|
+
</ConditionalWrapper>
|
|
80
|
+
))}
|
|
81
|
+
</ConditionalWrapper>
|
|
82
|
+
)}
|
|
83
|
+
</ConditionalWrapper>
|
|
84
|
+
{(subText || Astro.slots.has('default')) && (
|
|
85
|
+
<div class:list={subTextClasses}>
|
|
86
|
+
{subText && <span set:html={subText} />}
|
|
87
|
+
<slot />
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
</footer>
|