twintrinsic 0.0.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/LICENSE +674 -0
- package/README.md +150 -0
- package/dist/App/App.svelte +54 -0
- package/dist/App/App.svelte.d.ts +65 -0
- package/dist/Section.svelte +25 -0
- package/dist/Section.svelte.d.ts +34 -0
- package/dist/actions/clickOutside.d.ts +9 -0
- package/dist/actions/clickOutside.js +19 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/components/Accordion/Accordion.svelte +75 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +39 -0
- package/dist/components/Accordion/AccordionItem.svelte +150 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +30 -0
- package/dist/components/App/App.story.md +8 -0
- package/dist/components/App/App.story.svelte +170 -0
- package/dist/components/App/App.story.svelte.d.ts +22 -0
- package/dist/components/App/App.svelte +77 -0
- package/dist/components/App/App.svelte.d.ts +66 -0
- package/dist/components/App/Split.svelte +346 -0
- package/dist/components/App/Split.svelte.d.ts +54 -0
- package/dist/components/App/index.d.ts +2 -0
- package/dist/components/App/index.js +3 -0
- package/dist/components/AppHeader/AppHeader.svelte +439 -0
- package/dist/components/AppHeader/AppHeader.svelte.d.ts +24 -0
- package/dist/components/Avatar/Avatar.svelte +300 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +48 -0
- package/dist/components/Avatar/AvatarGroup.svelte +185 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +46 -0
- package/dist/components/Badge/Badge.svelte +186 -0
- package/dist/components/Badge/Badge.svelte.d.ts +51 -0
- package/dist/components/BottomBar/BottomBar.svelte +146 -0
- package/dist/components/BottomBar/BottomBar.svelte.d.ts +38 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte +77 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte.d.ts +42 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte +171 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte.d.ts +38 -0
- package/dist/components/Button/Button.svelte +252 -0
- package/dist/components/Button/Button.svelte.d.ts +80 -0
- package/dist/components/Button/ButtonGroup.svelte +127 -0
- package/dist/components/Button/ButtonGroup.svelte.d.ts +44 -0
- package/dist/components/Card/Card.svelte +152 -0
- package/dist/components/Card/Card.svelte.d.ts +55 -0
- package/dist/components/Carousel/Carousel.svelte +461 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +79 -0
- package/dist/components/Carousel/CarouselItem.svelte +149 -0
- package/dist/components/Carousel/CarouselItem.svelte.d.ts +35 -0
- package/dist/components/Chip/Chip.svelte +288 -0
- package/dist/components/Chip/Chip.svelte.d.ts +71 -0
- package/dist/components/Chip/ChipGroup.svelte +190 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +71 -0
- package/dist/components/CodeBlock/CodeBlock.svelte +356 -0
- package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +44 -0
- package/dist/components/CodeBlock/index.d.ts +1 -0
- package/dist/components/CodeBlock/index.js +1 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte +145 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte.d.ts +44 -0
- package/dist/components/CodeEditor/CodeEditor.svelte +229 -0
- package/dist/components/CodeEditor/CodeEditor.svelte.d.ts +23 -0
- package/dist/components/Combobox/Combobox.svelte +279 -0
- package/dist/components/Combobox/Combobox.svelte.d.ts +34 -0
- package/dist/components/Container/Container.svelte +45 -0
- package/dist/components/Container/Container.svelte.d.ts +36 -0
- package/dist/components/DataTable/DataTable.svelte +879 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +102 -0
- package/dist/components/Form/AutoComplete.svelte +357 -0
- package/dist/components/Form/AutoComplete.svelte.d.ts +73 -0
- package/dist/components/Form/Calendar.svelte +429 -0
- package/dist/components/Form/Calendar.svelte.d.ts +53 -0
- package/dist/components/Form/Checkbox.svelte +196 -0
- package/dist/components/Form/Checkbox.svelte.d.ts +50 -0
- package/dist/components/Form/ColorPicker.svelte +396 -0
- package/dist/components/Form/ColorPicker.svelte.d.ts +43 -0
- package/dist/components/Form/Combobox.svelte +645 -0
- package/dist/components/Form/Combobox.svelte.d.ts +93 -0
- package/dist/components/Form/Dropdown.svelte +773 -0
- package/dist/components/Form/Dropdown.svelte.d.ts +81 -0
- package/dist/components/Form/FileUpload.svelte +796 -0
- package/dist/components/Form/FileUpload.svelte.d.ts +78 -0
- package/dist/components/Form/FloatLabel.svelte +245 -0
- package/dist/components/Form/FloatLabel.svelte.d.ts +44 -0
- package/dist/components/Form/Form.svelte +281 -0
- package/dist/components/Form/Form.svelte.d.ts +54 -0
- package/dist/components/Form/FormField.svelte +218 -0
- package/dist/components/Form/FormField.svelte.d.ts +47 -0
- package/dist/components/Form/Input.svelte +340 -0
- package/dist/components/Form/Input.svelte.d.ts +79 -0
- package/dist/components/Form/InputSwitch.svelte +189 -0
- package/dist/components/Form/InputSwitch.svelte.d.ts +46 -0
- package/dist/components/Form/InvalidState.svelte +97 -0
- package/dist/components/Form/InvalidState.svelte.d.ts +37 -0
- package/dist/components/Form/Knob.svelte +537 -0
- package/dist/components/Form/Knob.svelte.d.ts +78 -0
- package/dist/components/Form/ListInput.svelte +469 -0
- package/dist/components/Form/ListInput.svelte.d.ts +70 -0
- package/dist/components/Form/Listbox.svelte +513 -0
- package/dist/components/Form/Listbox.svelte.d.ts +74 -0
- package/dist/components/Form/NumberInput.svelte +452 -0
- package/dist/components/Form/NumberInput.svelte.d.ts +82 -0
- package/dist/components/Form/Radio.svelte +192 -0
- package/dist/components/Form/Radio.svelte.d.ts +53 -0
- package/dist/components/Form/RadioGroup.svelte +155 -0
- package/dist/components/Form/RadioGroup.svelte.d.ts +48 -0
- package/dist/components/Form/Rating.svelte +380 -0
- package/dist/components/Form/Rating.svelte.d.ts +64 -0
- package/dist/components/Form/Select.svelte +436 -0
- package/dist/components/Form/Select.svelte.d.ts +49 -0
- package/dist/components/Form/SelectGroup.svelte +34 -0
- package/dist/components/Form/SelectGroup.svelte.d.ts +33 -0
- package/dist/components/Form/Slider.svelte +622 -0
- package/dist/components/Form/Slider.svelte.d.ts +73 -0
- package/dist/components/Form/Switch.svelte +192 -0
- package/dist/components/Form/Switch.svelte.d.ts +46 -0
- package/dist/components/Form/TextInput.svelte +274 -0
- package/dist/components/Form/TextInput.svelte.d.ts +74 -0
- package/dist/components/Form/Textarea.svelte +207 -0
- package/dist/components/Form/Textarea.svelte.d.ts +62 -0
- package/dist/components/Icon/Icon.svelte +140 -0
- package/dist/components/Icon/Icon.svelte.d.ts +25 -0
- package/dist/components/Icon/index.d.ts +1 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Lazy/Lazy.svelte +158 -0
- package/dist/components/Lazy/Lazy.svelte.d.ts +42 -0
- package/dist/components/Masonry/Masonry.svelte +299 -0
- package/dist/components/Masonry/Masonry.svelte.d.ts +55 -0
- package/dist/components/Menu/Menu/Menu.svelte +65 -0
- package/dist/components/Menu/Menu/Menu.svelte.d.ts +17 -0
- package/dist/components/Menu/Menu/MenuItem.svelte +90 -0
- package/dist/components/Menu/Menu/MenuItem.svelte.d.ts +27 -0
- package/dist/components/Modal/Modal.svelte +334 -0
- package/dist/components/Modal/Modal.svelte.d.ts +55 -0
- package/dist/components/Panel/Card.svelte +141 -0
- package/dist/components/Panel/Card.svelte.d.ts +52 -0
- package/dist/components/Panel/Hero/Hero.story.md +9 -0
- package/dist/components/Panel/Hero/Hero.story.svelte +49 -0
- package/dist/components/Panel/Hero/Hero.story.svelte.d.ts +21 -0
- package/dist/components/Panel/Hero/Hero.svelte +24 -0
- package/dist/components/Panel/Hero/Hero.svelte.d.ts +32 -0
- package/dist/components/Panel/LazyPanel.svelte +110 -0
- package/dist/components/Panel/LazyPanel.svelte.d.ts +46 -0
- package/dist/components/Panel/Panel.svelte +205 -0
- package/dist/components/Panel/Panel.svelte.d.ts +23 -0
- package/dist/components/Progress/Progress.svelte +220 -0
- package/dist/components/Progress/Progress.svelte.d.ts +61 -0
- package/dist/components/Separator/Separator.svelte +109 -0
- package/dist/components/Separator/Separator.svelte.d.ts +35 -0
- package/dist/components/Sidebar/Sidebar.svelte +213 -0
- package/dist/components/Sidebar/Sidebar.svelte.d.ts +60 -0
- package/dist/components/Skeleton/Skeleton.svelte +170 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +48 -0
- package/dist/components/Stepper/Stepper.svelte +111 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +54 -0
- package/dist/components/Stepper/StepperStep.svelte +369 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +63 -0
- package/dist/components/Table/Table.svelte +167 -0
- package/dist/components/Table/Table.svelte.d.ts +56 -0
- package/dist/components/Table/TableBody.svelte +41 -0
- package/dist/components/Table/TableBody.svelte.d.ts +33 -0
- package/dist/components/Table/TableCell.svelte +76 -0
- package/dist/components/Table/TableCell.svelte.d.ts +36 -0
- package/dist/components/Table/TableHead.svelte +41 -0
- package/dist/components/Table/TableHead.svelte.d.ts +32 -0
- package/dist/components/Table/TableHeader.svelte +148 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +42 -0
- package/dist/components/Table/TableRow.svelte +99 -0
- package/dist/components/Table/TableRow.svelte.d.ts +40 -0
- package/dist/components/Tabs/Tab.svelte +145 -0
- package/dist/components/Tabs/Tab.svelte.d.ts +36 -0
- package/dist/components/Tabs/TabList.svelte +60 -0
- package/dist/components/Tabs/TabList.svelte.d.ts +32 -0
- package/dist/components/Tabs/TabPanel.svelte +118 -0
- package/dist/components/Tabs/TabPanel.svelte.d.ts +38 -0
- package/dist/components/Tabs/Tabs.svelte +287 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +50 -0
- package/dist/components/Tag/Tag.svelte +260 -0
- package/dist/components/Tag/Tag.svelte.d.ts +54 -0
- package/dist/components/Tag/TagGroup.svelte +147 -0
- package/dist/components/Tag/TagGroup.svelte.d.ts +62 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte +93 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte.d.ts +12 -0
- package/dist/components/Timeline/Timeline.svelte +144 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +48 -0
- package/dist/components/Timeline/TimelineItem.svelte +391 -0
- package/dist/components/Timeline/TimelineItem.svelte.d.ts +63 -0
- package/dist/components/Toast/Toast.svelte +313 -0
- package/dist/components/Toast/Toast.svelte.d.ts +44 -0
- package/dist/components/Toast/toastStore.d.ts +40 -0
- package/dist/components/Toast/toastStore.js +293 -0
- package/dist/components/Tooltip/Tooltip.svelte +282 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +55 -0
- package/dist/components/Tree/Tree.svelte +129 -0
- package/dist/components/Tree/Tree.svelte.d.ts +61 -0
- package/dist/components/Tree/TreeNode.svelte +332 -0
- package/dist/components/Tree/TreeNode.svelte.d.ts +55 -0
- package/dist/components/icons/TwintrinsicLogo.svelte +73 -0
- package/dist/components/icons/TwintrinsicLogo.svelte.d.ts +17 -0
- package/dist/components/icons/twintrinsic-source.svg +73 -0
- package/dist/components/icons/twintrinsic.svg +38 -0
- package/dist/docs/EventsTable.svelte +86 -0
- package/dist/docs/EventsTable.svelte.d.ts +27 -0
- package/dist/docs/PropsTable.svelte +103 -0
- package/dist/docs/PropsTable.svelte.d.ts +28 -0
- package/dist/docs/index.d.ts +2 -0
- package/dist/docs/index.js +2 -0
- package/dist/helpers/detectLanguage.d.ts +6 -0
- package/dist/helpers/detectLanguage.js +60 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +94 -0
- package/dist/twintrinsic.css +347 -0
- package/package.json +98 -0
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
DataTable - A component for displaying tabular data with advanced features.
|
|
4
|
+
Provides sorting, filtering, pagination, and selection capabilities with proper accessibility.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<DataTable
|
|
9
|
+
data={users}
|
|
10
|
+
columns={[
|
|
11
|
+
{ field: 'id', header: 'ID' },
|
|
12
|
+
{ field: 'name', header: 'Name' },
|
|
13
|
+
{ field: 'email', header: 'Email' }
|
|
14
|
+
]}
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
<DataTable
|
|
18
|
+
data={products}
|
|
19
|
+
columns={columns}
|
|
20
|
+
sortable
|
|
21
|
+
filterable
|
|
22
|
+
pageable
|
|
23
|
+
selectable
|
|
24
|
+
onsort={handleSort}
|
|
25
|
+
onfilter={handleFilter}
|
|
26
|
+
onpage={handlePage}
|
|
27
|
+
onselect={handleSelect}
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
30
|
+
-->
|
|
31
|
+
<script>
|
|
32
|
+
import { setContext } from "svelte"
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
/** @type {string} - Additional CSS classes */
|
|
36
|
+
class: className = "",
|
|
37
|
+
|
|
38
|
+
/** @type {string} - HTML id for accessibility */
|
|
39
|
+
id = crypto.randomUUID(),
|
|
40
|
+
|
|
41
|
+
/** @type {Array} - Data to display */
|
|
42
|
+
data = [],
|
|
43
|
+
|
|
44
|
+
/** @type {Array} - Column definitions */
|
|
45
|
+
columns = [],
|
|
46
|
+
|
|
47
|
+
/** @type {boolean} - Whether to enable sorting */
|
|
48
|
+
sortable = false,
|
|
49
|
+
|
|
50
|
+
/** @type {boolean} - Whether to enable filtering */
|
|
51
|
+
filterable = false,
|
|
52
|
+
|
|
53
|
+
/** @type {boolean} - Whether to enable pagination */
|
|
54
|
+
pageable = false,
|
|
55
|
+
|
|
56
|
+
/** @type {boolean} - Whether to enable row selection */
|
|
57
|
+
selectable = false,
|
|
58
|
+
|
|
59
|
+
/** @type {boolean} - Whether to enable multiple row selection */
|
|
60
|
+
multiSelect = false,
|
|
61
|
+
|
|
62
|
+
/** @type {Array} - Selected row keys */
|
|
63
|
+
selected = [],
|
|
64
|
+
|
|
65
|
+
/** @type {string} - Key field for row identification */
|
|
66
|
+
keyField = "id",
|
|
67
|
+
|
|
68
|
+
/** @type {number} - Current page (1-based) */
|
|
69
|
+
page = 1,
|
|
70
|
+
|
|
71
|
+
/** @type {number} - Number of rows per page */
|
|
72
|
+
pageSize = 10,
|
|
73
|
+
|
|
74
|
+
/** @type {Array} - Available page size options */
|
|
75
|
+
pageSizeOptions = [5, 10, 20, 50, 100],
|
|
76
|
+
|
|
77
|
+
/** @type {string} - Field to sort by */
|
|
78
|
+
sortField,
|
|
79
|
+
|
|
80
|
+
/** @type {string} - Sort order (asc, desc) */
|
|
81
|
+
sortOrder = "asc",
|
|
82
|
+
|
|
83
|
+
/** @type {Object} - Filter values by field */
|
|
84
|
+
filters = {},
|
|
85
|
+
|
|
86
|
+
/** @type {boolean} - Whether to show a loading indicator */
|
|
87
|
+
loading = false,
|
|
88
|
+
|
|
89
|
+
/** @type {string} - Text to display when there is no data */
|
|
90
|
+
emptyMessage = "No data available",
|
|
91
|
+
|
|
92
|
+
/** @type {string} - ARIA label for the table */
|
|
93
|
+
ariaLabel = "Data table",
|
|
94
|
+
|
|
95
|
+
/** @type {Function} - Custom row class function */
|
|
96
|
+
rowClass,
|
|
97
|
+
|
|
98
|
+
/** @type {Function} - Custom cell formatter */
|
|
99
|
+
cellFormatter,
|
|
100
|
+
|
|
101
|
+
/** @type {boolean} - Whether to enable responsive mode */
|
|
102
|
+
responsive = true,
|
|
103
|
+
|
|
104
|
+
/** @type {boolean} - Whether to enable striped rows */
|
|
105
|
+
striped = false,
|
|
106
|
+
|
|
107
|
+
/** @type {boolean} - Whether to enable hoverable rows */
|
|
108
|
+
hoverable = true,
|
|
109
|
+
|
|
110
|
+
/** @type {boolean} - Whether to show a border */
|
|
111
|
+
bordered = false,
|
|
112
|
+
|
|
113
|
+
/** @type {boolean} - Whether to make the header sticky */
|
|
114
|
+
stickyHeader = false,
|
|
115
|
+
|
|
116
|
+
/** @type {boolean} - Whether to enable compact mode */
|
|
117
|
+
compact = false,
|
|
118
|
+
|
|
119
|
+
/** @type {(event: CustomEvent) => void} - Sort event handler */
|
|
120
|
+
onsort,
|
|
121
|
+
/** @type {(event: CustomEvent) => void} - Filter event handler */
|
|
122
|
+
onfilter,
|
|
123
|
+
/** @type {(event: CustomEvent) => void} - Page event handler */
|
|
124
|
+
onpage,
|
|
125
|
+
/** @type {(event: CustomEvent) => void} - Select event handler */
|
|
126
|
+
onselect,
|
|
127
|
+
} = $props()
|
|
128
|
+
|
|
129
|
+
// Component state
|
|
130
|
+
let currentPage = $state(1)
|
|
131
|
+
let currentPageSize = $state(10)
|
|
132
|
+
let currentSortField = $state("")
|
|
133
|
+
let currentSortOrder = $state("asc")
|
|
134
|
+
let currentFilters = $state({})
|
|
135
|
+
let selectedRows = $state([])
|
|
136
|
+
let allSelected = $state(false)
|
|
137
|
+
let tableElement = $state()
|
|
138
|
+
|
|
139
|
+
// Update state when props change
|
|
140
|
+
$effect(() => {
|
|
141
|
+
currentPage = page
|
|
142
|
+
currentPageSize = pageSize
|
|
143
|
+
currentSortField = sortField || ""
|
|
144
|
+
currentSortOrder = sortOrder || "asc"
|
|
145
|
+
currentFilters = filters || {}
|
|
146
|
+
selectedRows = Array.isArray(selected) ? [...selected] : []
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Provide context for child components
|
|
150
|
+
$effect(() => {
|
|
151
|
+
setContext("dataTable", {
|
|
152
|
+
sortable,
|
|
153
|
+
filterable,
|
|
154
|
+
selectable,
|
|
155
|
+
multiSelect,
|
|
156
|
+
keyField,
|
|
157
|
+
getSortField: () => currentSortField,
|
|
158
|
+
getSortOrder: () => currentSortOrder,
|
|
159
|
+
getFilters: () => currentFilters,
|
|
160
|
+
getSelected: () => selectedRows,
|
|
161
|
+
isSelected: (key) => selectedRows.includes(key),
|
|
162
|
+
toggleSort: (field) => handleSort(field),
|
|
163
|
+
setFilter: (field, value) => handleFilter(field, value),
|
|
164
|
+
toggleSelection: (key) => toggleRowSelection(key),
|
|
165
|
+
selectAll: () => toggleSelectAll(),
|
|
166
|
+
cellFormatter,
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Computed values
|
|
171
|
+
const totalRecords = $derived(data.length)
|
|
172
|
+
const totalPages = $derived(Math.max(1, Math.ceil(totalRecords / currentPageSize)))
|
|
173
|
+
const startIndex = $derived((currentPage - 1) * currentPageSize)
|
|
174
|
+
const endIndex = $derived(Math.min(startIndex + currentPageSize, totalRecords))
|
|
175
|
+
|
|
176
|
+
// Process data with sorting, filtering, and pagination
|
|
177
|
+
const processedData = $derived.by(() => {
|
|
178
|
+
let result = [...data]
|
|
179
|
+
|
|
180
|
+
// Apply filters
|
|
181
|
+
if (filterable && Object.keys(currentFilters).length > 0) {
|
|
182
|
+
result = result.filter((item) => {
|
|
183
|
+
return Object.entries(currentFilters).every(([field, filterValue]) => {
|
|
184
|
+
if (!filterValue) return true
|
|
185
|
+
|
|
186
|
+
const value = item[field]
|
|
187
|
+
if (value === undefined || value === null) return false
|
|
188
|
+
|
|
189
|
+
return String(value).toLowerCase().includes(String(filterValue).toLowerCase())
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Apply sorting
|
|
195
|
+
if (sortable && currentSortField) {
|
|
196
|
+
result.sort((a, b) => {
|
|
197
|
+
const valueA = a[currentSortField]
|
|
198
|
+
const valueB = b[currentSortField]
|
|
199
|
+
|
|
200
|
+
// Handle null/undefined values
|
|
201
|
+
if (valueA === undefined || valueA === null) return currentSortOrder === "asc" ? -1 : 1
|
|
202
|
+
if (valueB === undefined || valueB === null) return currentSortOrder === "asc" ? 1 : -1
|
|
203
|
+
|
|
204
|
+
// Compare based on type
|
|
205
|
+
if (typeof valueA === "string" && typeof valueB === "string") {
|
|
206
|
+
return currentSortOrder === "asc"
|
|
207
|
+
? valueA.localeCompare(valueB)
|
|
208
|
+
: valueB.localeCompare(valueA)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return currentSortOrder === "asc" ? valueA - valueB : valueB - valueA
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Get total after filtering
|
|
216
|
+
const filteredTotal = result.length
|
|
217
|
+
|
|
218
|
+
// Apply pagination
|
|
219
|
+
if (pageable) {
|
|
220
|
+
result = result.slice(startIndex, endIndex)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
rows: result,
|
|
225
|
+
filteredTotal,
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Handles sorting
|
|
231
|
+
* @param {string} field - Field to sort by
|
|
232
|
+
*/
|
|
233
|
+
function handleSort(field) {
|
|
234
|
+
if (!sortable) return
|
|
235
|
+
|
|
236
|
+
if (currentSortField === field) {
|
|
237
|
+
// Toggle order if same field
|
|
238
|
+
currentSortOrder = currentSortOrder === "asc" ? "desc" : "asc"
|
|
239
|
+
} else {
|
|
240
|
+
// Set new field and default to ascending
|
|
241
|
+
currentSortField = field
|
|
242
|
+
currentSortOrder = "asc"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
onsort?.(new CustomEvent("sort", { detail: { field: currentSortField, order: currentSortOrder } }))
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Handles filtering
|
|
250
|
+
* @param {string} field - Field to filter
|
|
251
|
+
* @param {string} value - Filter value
|
|
252
|
+
*/
|
|
253
|
+
function handleFilter(field, value) {
|
|
254
|
+
if (!filterable) return
|
|
255
|
+
|
|
256
|
+
if (value) {
|
|
257
|
+
currentFilters = { ...currentFilters, [field]: value }
|
|
258
|
+
} else {
|
|
259
|
+
// Remove filter if value is empty
|
|
260
|
+
const { [field]: removed, ...rest } = currentFilters
|
|
261
|
+
currentFilters = rest
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Reset to first page when filtering
|
|
265
|
+
currentPage = 1
|
|
266
|
+
|
|
267
|
+
onfilter?.(new CustomEvent("filter", { detail: { filters: currentFilters } }))
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Handles page change
|
|
272
|
+
* @param {number} newPage - New page number
|
|
273
|
+
*/
|
|
274
|
+
function handlePageChange(newPage) {
|
|
275
|
+
if (!pageable) return
|
|
276
|
+
|
|
277
|
+
if (newPage >= 1 && newPage <= totalPages) {
|
|
278
|
+
currentPage = newPage
|
|
279
|
+
onpage?.(new CustomEvent("page", { detail: { page: currentPage, pageSize: currentPageSize } }))
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Handles page size change
|
|
285
|
+
* @param {Event} event - Change event
|
|
286
|
+
*/
|
|
287
|
+
function handlePageSizeChange(event) {
|
|
288
|
+
if (!pageable) return
|
|
289
|
+
|
|
290
|
+
const newPageSize = Number(event.target.value)
|
|
291
|
+
currentPageSize = newPageSize
|
|
292
|
+
|
|
293
|
+
// Adjust current page to maintain position
|
|
294
|
+
const newTotalPages = Math.max(1, Math.ceil(totalRecords / newPageSize))
|
|
295
|
+
if (currentPage > newTotalPages) {
|
|
296
|
+
currentPage = newTotalPages
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
onpage?.(new CustomEvent("page", { detail: { page: currentPage, pageSize: currentPageSize } }))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Toggles selection of a row
|
|
304
|
+
* @param {string|number} key - Row key
|
|
305
|
+
*/
|
|
306
|
+
function toggleRowSelection(key) {
|
|
307
|
+
if (!selectable) return
|
|
308
|
+
|
|
309
|
+
if (selectedRows.includes(key)) {
|
|
310
|
+
// Remove if already selected
|
|
311
|
+
selectedRows = selectedRows.filter((k) => k !== key)
|
|
312
|
+
allSelected = false
|
|
313
|
+
} else {
|
|
314
|
+
// Add if not selected
|
|
315
|
+
if (multiSelect) {
|
|
316
|
+
selectedRows = [...selectedRows, key]
|
|
317
|
+
|
|
318
|
+
// Check if all visible rows are now selected
|
|
319
|
+
const visibleKeys = processedData.rows.map((row) => row[keyField])
|
|
320
|
+
allSelected = visibleKeys.every((k) => selectedRows.includes(k))
|
|
321
|
+
} else {
|
|
322
|
+
selectedRows = [key]
|
|
323
|
+
allSelected = false
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
onselect?.(new CustomEvent("select", { detail: { selected: selectedRows } }))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Toggles selection of all rows
|
|
332
|
+
*/
|
|
333
|
+
function toggleSelectAll() {
|
|
334
|
+
if (!selectable || !multiSelect) return
|
|
335
|
+
|
|
336
|
+
if (allSelected) {
|
|
337
|
+
// Deselect all visible rows
|
|
338
|
+
const visibleKeys = processedData.rows.map((row) => row[keyField])
|
|
339
|
+
selectedRows = selectedRows.filter((key) => !visibleKeys.includes(key))
|
|
340
|
+
allSelected = false
|
|
341
|
+
} else {
|
|
342
|
+
// Select all visible rows
|
|
343
|
+
const visibleKeys = processedData.rows.map((row) => row[keyField])
|
|
344
|
+
const newSelected = [...selectedRows]
|
|
345
|
+
|
|
346
|
+
visibleKeys.forEach((key) => {
|
|
347
|
+
if (!newSelected.includes(key)) {
|
|
348
|
+
newSelected.push(key)
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
selectedRows = newSelected
|
|
353
|
+
allSelected = true
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
onselect?.(new CustomEvent("select", { detail: { selected: selectedRows } }))
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Gets CSS classes for a row
|
|
361
|
+
* @param {Object} row - Row data
|
|
362
|
+
* @param {number} index - Row index
|
|
363
|
+
* @returns {string} - CSS classes
|
|
364
|
+
*/
|
|
365
|
+
function getRowClasses(row, index) {
|
|
366
|
+
const isSelected = selectedRows.includes(row[keyField])
|
|
367
|
+
const classes = [
|
|
368
|
+
"data-table-row",
|
|
369
|
+
isSelected ? "data-table-row-selected" : "",
|
|
370
|
+
striped && index % 2 === 1 ? "data-table-row-striped" : "",
|
|
371
|
+
hoverable ? "data-table-row-hoverable" : "",
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
if (rowClass && typeof rowClass === "function") {
|
|
375
|
+
const customClass = rowClass(row, index)
|
|
376
|
+
if (customClass) {
|
|
377
|
+
classes.push(customClass)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return classes.filter(Boolean).join(" ")
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Formats a cell value
|
|
386
|
+
* @param {any} value - Cell value
|
|
387
|
+
* @param {Object} column - Column definition
|
|
388
|
+
* @param {Object} row - Row data
|
|
389
|
+
* @returns {string} - Formatted value
|
|
390
|
+
*/
|
|
391
|
+
function formatCell(value, column, row) {
|
|
392
|
+
if (column.formatter) {
|
|
393
|
+
return column.formatter(value, row)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (cellFormatter) {
|
|
397
|
+
return cellFormatter(value, column, row)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (value === null || value === undefined) {
|
|
401
|
+
return ""
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return String(value)
|
|
405
|
+
}
|
|
406
|
+
</script>
|
|
407
|
+
|
|
408
|
+
<div
|
|
409
|
+
class="
|
|
410
|
+
data-table-wrapper
|
|
411
|
+
{responsive ? 'data-table-responsive' : ''}
|
|
412
|
+
{className}
|
|
413
|
+
"
|
|
414
|
+
>
|
|
415
|
+
<div class="data-table-container">
|
|
416
|
+
<table
|
|
417
|
+
{id}
|
|
418
|
+
class="
|
|
419
|
+
data-table
|
|
420
|
+
{bordered ? 'data-table-bordered' : ''}
|
|
421
|
+
{compact ? 'data-table-compact' : ''}
|
|
422
|
+
{stickyHeader ? 'data-table-sticky-header' : ''}
|
|
423
|
+
"
|
|
424
|
+
aria-label={ariaLabel}
|
|
425
|
+
aria-busy={loading}
|
|
426
|
+
bind:this={tableElement}
|
|
427
|
+
>
|
|
428
|
+
<thead class="data-table-header">
|
|
429
|
+
<tr>
|
|
430
|
+
{#if selectable}
|
|
431
|
+
<th class="data-table-selection-cell">
|
|
432
|
+
{#if multiSelect}
|
|
433
|
+
<div class="data-table-checkbox">
|
|
434
|
+
<input
|
|
435
|
+
type="checkbox"
|
|
436
|
+
checked={allSelected}
|
|
437
|
+
onchange={toggleSelectAll}
|
|
438
|
+
aria-label="Select all rows"
|
|
439
|
+
/>
|
|
440
|
+
</div>
|
|
441
|
+
{/if}
|
|
442
|
+
</th>
|
|
443
|
+
{/if}
|
|
444
|
+
|
|
445
|
+
{#each columns as column, colIndex}
|
|
446
|
+
<th
|
|
447
|
+
class="
|
|
448
|
+
data-table-header-cell
|
|
449
|
+
{column.sortable !== false && sortable ? 'data-table-sortable' : ''}
|
|
450
|
+
{column.class || ''}
|
|
451
|
+
"
|
|
452
|
+
style={column.style || ''}
|
|
453
|
+
onclick={() => column.sortable !== false && sortable && handleSort(column.field)}
|
|
454
|
+
aria-sort={currentSortField === column.field
|
|
455
|
+
? (currentSortOrder === 'asc' ? 'ascending' : 'descending')
|
|
456
|
+
: undefined}
|
|
457
|
+
>
|
|
458
|
+
<div class="data-table-header-content">
|
|
459
|
+
<span class="data-table-header-text">
|
|
460
|
+
{column.header || column.field}
|
|
461
|
+
</span>
|
|
462
|
+
|
|
463
|
+
{#if column.sortable !== false && sortable}
|
|
464
|
+
<span class="data-table-sort-icon">
|
|
465
|
+
{#if currentSortField === column.field}
|
|
466
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
467
|
+
<path
|
|
468
|
+
stroke-linecap="round"
|
|
469
|
+
stroke-linejoin="round"
|
|
470
|
+
stroke-width="2"
|
|
471
|
+
d={currentSortOrder === 'asc'
|
|
472
|
+
? "M5 15l7-7 7 7"
|
|
473
|
+
: "M19 9l-7 7-7-7"}
|
|
474
|
+
></path>
|
|
475
|
+
</svg>
|
|
476
|
+
{:else}
|
|
477
|
+
<svg class="w-4 h-4 opacity-0 hover:parent:opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
478
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"></path>
|
|
479
|
+
</svg>
|
|
480
|
+
{/if}
|
|
481
|
+
</span>
|
|
482
|
+
{/if}
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
{#if column.filterable !== false && filterable}
|
|
486
|
+
<div class="data-table-filter">
|
|
487
|
+
<input
|
|
488
|
+
type="text"
|
|
489
|
+
placeholder="Filter..."
|
|
490
|
+
value={currentFilters[column.field] || ''}
|
|
491
|
+
oninput={(e) => handleFilter(column.field, e.target.value)}
|
|
492
|
+
onclick={(e) => e.stopPropagation()}
|
|
493
|
+
aria-label={`Filter by ${column.header || column.field}`}
|
|
494
|
+
class="data-table-filter-input"
|
|
495
|
+
/>
|
|
496
|
+
</div>
|
|
497
|
+
{/if}
|
|
498
|
+
</th>
|
|
499
|
+
{/each}
|
|
500
|
+
</tr>
|
|
501
|
+
</thead>
|
|
502
|
+
|
|
503
|
+
<tbody class="data-table-body">
|
|
504
|
+
{#if loading}
|
|
505
|
+
<tr class="data-table-loading-row">
|
|
506
|
+
<td colspan={columns.length + (selectable ? 1 : 0)} class="data-table-loading-cell">
|
|
507
|
+
<div class="data-table-loading">
|
|
508
|
+
<svg class="data-table-spinner" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
509
|
+
<circle class="data-table-spinner-track" cx="12" cy="12" r="10" />
|
|
510
|
+
<circle class="data-table-spinner-path" cx="12" cy="12" r="10" />
|
|
511
|
+
</svg>
|
|
512
|
+
<span>Loading...</span>
|
|
513
|
+
</div>
|
|
514
|
+
</td>
|
|
515
|
+
</tr>
|
|
516
|
+
{:else if processedData.rows.length === 0}
|
|
517
|
+
<tr class="data-table-empty-row">
|
|
518
|
+
<td colspan={columns.length + (selectable ? 1 : 0)} class="data-table-empty-cell">
|
|
519
|
+
{emptyMessage}
|
|
520
|
+
</td>
|
|
521
|
+
</tr>
|
|
522
|
+
{:else}
|
|
523
|
+
{#each processedData.rows as row, rowIndex}
|
|
524
|
+
<tr
|
|
525
|
+
class={getRowClasses(row, rowIndex)}
|
|
526
|
+
onclick={() => selectable && toggleRowSelection(row[keyField])}
|
|
527
|
+
aria-selected={selectable && selectedRows.includes(row[keyField])}
|
|
528
|
+
>
|
|
529
|
+
{#if selectable}
|
|
530
|
+
<td class="data-table-selection-cell">
|
|
531
|
+
<div class="data-table-checkbox">
|
|
532
|
+
<input
|
|
533
|
+
type={multiSelect ? 'checkbox' : 'radio'}
|
|
534
|
+
checked={selectedRows.includes(row[keyField])}
|
|
535
|
+
onchange={() => toggleRowSelection(row[keyField])}
|
|
536
|
+
name={multiSelect ? undefined : `${id}-selection`}
|
|
537
|
+
aria-label={`Select row ${rowIndex + 1}`}
|
|
538
|
+
onclick={(e) => e.stopPropagation()}
|
|
539
|
+
/>
|
|
540
|
+
</div>
|
|
541
|
+
</td>
|
|
542
|
+
{/if}
|
|
543
|
+
|
|
544
|
+
{#each columns as column, colIndex}
|
|
545
|
+
<td
|
|
546
|
+
class="
|
|
547
|
+
data-table-cell
|
|
548
|
+
{column.cellClass || ''}
|
|
549
|
+
"
|
|
550
|
+
style={column.cellStyle || ''}
|
|
551
|
+
data-label={responsive ? (column.header || column.field) : undefined}
|
|
552
|
+
>
|
|
553
|
+
{#if column.template}
|
|
554
|
+
{@html column.template(row[column.field])}
|
|
555
|
+
{:else}
|
|
556
|
+
{formatCell(row[column.field], column, row)}
|
|
557
|
+
{/if}
|
|
558
|
+
</td>
|
|
559
|
+
{/each}
|
|
560
|
+
</tr>
|
|
561
|
+
{/each}
|
|
562
|
+
{/if}
|
|
563
|
+
</tbody>
|
|
564
|
+
</table>
|
|
565
|
+
</div>
|
|
566
|
+
|
|
567
|
+
{#if pageable && totalRecords > 0}
|
|
568
|
+
<div class="data-table-pagination">
|
|
569
|
+
<div class="data-table-pagination-info">
|
|
570
|
+
Showing {startIndex + 1} to {endIndex} of {processedData.filteredTotal} entries
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
<div class="data-table-pagination-controls">
|
|
574
|
+
<div class="data-table-page-size">
|
|
575
|
+
<label>
|
|
576
|
+
<span>Rows per page:</span>
|
|
577
|
+
<select
|
|
578
|
+
value={currentPageSize}
|
|
579
|
+
onchange={handlePageSizeChange}
|
|
580
|
+
aria-label="Rows per page"
|
|
581
|
+
>
|
|
582
|
+
{#each pageSizeOptions as option}
|
|
583
|
+
<option value={option}>{option}</option>
|
|
584
|
+
{/each}
|
|
585
|
+
</select>
|
|
586
|
+
</label>
|
|
587
|
+
</div>
|
|
588
|
+
|
|
589
|
+
<div class="data-table-page-controls">
|
|
590
|
+
<button
|
|
591
|
+
type="button"
|
|
592
|
+
class="data-table-page-button"
|
|
593
|
+
disabled={currentPage === 1}
|
|
594
|
+
onclick={() => handlePageChange(1)}
|
|
595
|
+
aria-label="First page"
|
|
596
|
+
>
|
|
597
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
598
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7"></path>
|
|
599
|
+
</svg>
|
|
600
|
+
</button>
|
|
601
|
+
|
|
602
|
+
<button
|
|
603
|
+
type="button"
|
|
604
|
+
class="data-table-page-button"
|
|
605
|
+
disabled={currentPage === 1}
|
|
606
|
+
onclick={() => handlePageChange(currentPage - 1)}
|
|
607
|
+
aria-label="Previous page"
|
|
608
|
+
>
|
|
609
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
610
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
|
611
|
+
</svg>
|
|
612
|
+
</button>
|
|
613
|
+
|
|
614
|
+
<span class="data-table-page-info">
|
|
615
|
+
Page {currentPage} of {totalPages}
|
|
616
|
+
</span>
|
|
617
|
+
|
|
618
|
+
<button
|
|
619
|
+
type="button"
|
|
620
|
+
class="data-table-page-button"
|
|
621
|
+
disabled={currentPage === totalPages}
|
|
622
|
+
onclick={() => handlePageChange(currentPage + 1)}
|
|
623
|
+
aria-label="Next page"
|
|
624
|
+
>
|
|
625
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
626
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
627
|
+
</svg>
|
|
628
|
+
</button>
|
|
629
|
+
|
|
630
|
+
<button
|
|
631
|
+
type="button"
|
|
632
|
+
class="data-table-page-button"
|
|
633
|
+
disabled={currentPage === totalPages}
|
|
634
|
+
onclick={() => handlePageChange(totalPages)}
|
|
635
|
+
aria-label="Last page"
|
|
636
|
+
>
|
|
637
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
638
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7"></path>
|
|
639
|
+
</svg>
|
|
640
|
+
</button>
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
{/if}
|
|
645
|
+
</div>
|
|
646
|
+
|
|
647
|
+
<style>
|
|
648
|
+
@reference "../../twintrinsic.css";
|
|
649
|
+
|
|
650
|
+
.data-table-wrapper {
|
|
651
|
+
@apply w-full;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.data-table-container {
|
|
655
|
+
@apply w-full overflow-x-auto;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.data-table {
|
|
659
|
+
@apply w-full border-collapse;
|
|
660
|
+
@apply text-text dark:text-text;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.data-table-bordered {
|
|
664
|
+
@apply border border-border dark:border-border;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.data-table-bordered th,
|
|
668
|
+
.data-table-bordered td {
|
|
669
|
+
@apply border border-border dark:border-border;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.data-table-compact th,
|
|
673
|
+
.data-table-compact td {
|
|
674
|
+
@apply py-1 px-2;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.data-table-sticky-header thead {
|
|
678
|
+
@apply sticky top-0;
|
|
679
|
+
@apply z-10;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.data-table-header {
|
|
683
|
+
@apply bg-surface dark:bg-surface;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.data-table-header-cell {
|
|
687
|
+
@apply py-3 px-4;
|
|
688
|
+
@apply font-medium text-left;
|
|
689
|
+
@apply whitespace-nowrap;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.data-table-sortable {
|
|
693
|
+
@apply cursor-pointer;
|
|
694
|
+
@apply hover:bg-hover dark:hover:bg-hover;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.data-table-header-content {
|
|
698
|
+
@apply flex items-center;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.data-table-header-text {
|
|
702
|
+
@apply flex-grow;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.data-table-sort-icon {
|
|
706
|
+
@apply ml-1 flex-shrink-0;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.data-table-filter {
|
|
710
|
+
@apply mt-2;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.data-table-filter-input {
|
|
714
|
+
@apply w-full px-2 py-1;
|
|
715
|
+
@apply bg-background dark:bg-background;
|
|
716
|
+
@apply border border-border dark:border-border;
|
|
717
|
+
@apply rounded-md;
|
|
718
|
+
@apply text-sm;
|
|
719
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.data-table-body {
|
|
723
|
+
@apply bg-background dark:bg-background;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.data-table-row {
|
|
727
|
+
@apply border-t border-border dark:border-border;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.data-table-row-striped {
|
|
731
|
+
@apply bg-muted/5 dark:bg-muted/5;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.data-table-row-hoverable {
|
|
735
|
+
@apply hover:bg-hover dark:hover:bg-hover;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
.data-table-row-selected {
|
|
739
|
+
@apply bg-primary-50 dark:bg-primary-900/20;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.data-table-cell {
|
|
743
|
+
@apply py-3 px-4;
|
|
744
|
+
@apply align-middle;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.data-table-selection-cell {
|
|
748
|
+
@apply w-10 py-3 px-4;
|
|
749
|
+
@apply text-center;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.data-table-checkbox {
|
|
753
|
+
@apply flex items-center justify-center;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.data-table-checkbox input {
|
|
757
|
+
@apply w-4 h-4;
|
|
758
|
+
@apply text-primary-500 dark:text-primary-500;
|
|
759
|
+
@apply border border-border dark:border-border;
|
|
760
|
+
@apply rounded;
|
|
761
|
+
@apply focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.data-table-loading-cell,
|
|
765
|
+
.data-table-empty-cell {
|
|
766
|
+
@apply py-8 px-4;
|
|
767
|
+
@apply text-center;
|
|
768
|
+
@apply text-muted dark:text-muted;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.data-table-loading {
|
|
772
|
+
@apply flex flex-col items-center justify-center;
|
|
773
|
+
@apply space-y-2;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.data-table-spinner {
|
|
777
|
+
@apply w-8 h-8;
|
|
778
|
+
@apply animate-spin;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.data-table-spinner-track {
|
|
782
|
+
@apply opacity-25;
|
|
783
|
+
@apply stroke-current;
|
|
784
|
+
@apply fill-none;
|
|
785
|
+
@apply stroke-2;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.data-table-spinner-path {
|
|
789
|
+
@apply opacity-75;
|
|
790
|
+
@apply stroke-current;
|
|
791
|
+
@apply fill-none;
|
|
792
|
+
@apply stroke-2;
|
|
793
|
+
stroke-dasharray: 60;
|
|
794
|
+
stroke-dashoffset: 45;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.data-table-pagination {
|
|
798
|
+
@apply mt-4;
|
|
799
|
+
@apply flex flex-col sm:flex-row items-center justify-between;
|
|
800
|
+
@apply text-sm;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.data-table-pagination-info {
|
|
804
|
+
@apply mb-2 sm:mb-0;
|
|
805
|
+
@apply text-muted dark:text-muted;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.data-table-pagination-controls {
|
|
809
|
+
@apply flex flex-col sm:flex-row items-center;
|
|
810
|
+
@apply space-y-2 sm:space-y-0 sm:space-x-4;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.data-table-page-size {
|
|
814
|
+
@apply flex items-center;
|
|
815
|
+
@apply space-x-2;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.data-table-page-size select {
|
|
819
|
+
@apply px-2 py-1;
|
|
820
|
+
@apply bg-background dark:bg-background;
|
|
821
|
+
@apply border border-border dark:border-border;
|
|
822
|
+
@apply rounded-md;
|
|
823
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.data-table-page-controls {
|
|
827
|
+
@apply flex items-center;
|
|
828
|
+
@apply space-x-1;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.data-table-page-button {
|
|
832
|
+
@apply p-1;
|
|
833
|
+
@apply bg-background dark:bg-background;
|
|
834
|
+
@apply border border-border dark:border-border;
|
|
835
|
+
@apply rounded-md;
|
|
836
|
+
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400;
|
|
837
|
+
@apply hover:bg-hover dark:hover:bg-hover;
|
|
838
|
+
@apply transition-colors duration-150;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.data-table-page-button:disabled {
|
|
842
|
+
@apply opacity-50 cursor-not-allowed;
|
|
843
|
+
@apply hover:bg-background dark:hover:bg-background;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.data-table-page-info {
|
|
847
|
+
@apply px-2;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/* Responsive styles */
|
|
851
|
+
@media (max-width: 640px) {
|
|
852
|
+
.data-table-responsive thead {
|
|
853
|
+
@apply hidden;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.data-table-responsive tbody tr {
|
|
857
|
+
@apply block border-b border-border dark:border-border;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.data-table-responsive tbody td {
|
|
861
|
+
@apply block text-right;
|
|
862
|
+
@apply py-2 px-3;
|
|
863
|
+
@apply border-none;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.data-table-responsive tbody td::before {
|
|
867
|
+
content: attr(data-label);
|
|
868
|
+
@apply float-left font-medium;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.data-table-responsive .data-table-selection-cell {
|
|
872
|
+
@apply w-full text-left;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.data-table-responsive .data-table-checkbox {
|
|
876
|
+
@apply justify-start;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
</style>
|