@isoftdata/svelte-table 2.11.0 → 2.11.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 +388 -388
- package/dist/DraggableRows.svelte +285 -285
- package/dist/Pagination.svelte +303 -303
- package/dist/Table.svelte +1165 -1165
- package/dist/Td.svelte +171 -171
- package/dist/TreeRow.svelte +170 -170
- package/package.json +1 -1
|
@@ -1,285 +1,285 @@
|
|
|
1
|
-
<script
|
|
2
|
-
lang="ts"
|
|
3
|
-
generics="Row extends UuidRowProps"
|
|
4
|
-
>
|
|
5
|
-
import type { Snippet } from 'svelte'
|
|
6
|
-
import type { ClassValue } from 'svelte/elements'
|
|
7
|
-
import type { StringKeyOfType } from './tree-utility'
|
|
8
|
-
|
|
9
|
-
import type Table from './Table.svelte'
|
|
10
|
-
import type { Column, UuidRowProps } from './'
|
|
11
|
-
import Td from './Td.svelte'
|
|
12
|
-
import { flip } from 'svelte/animate'
|
|
13
|
-
import { getContext, tick } from 'svelte'
|
|
14
|
-
import Button from '@isoftdata/svelte-button'
|
|
15
|
-
|
|
16
|
-
type RankProperty = StringKeyOfType<Row, number | null>
|
|
17
|
-
|
|
18
|
-
interface Props {
|
|
19
|
-
/** Columns definition */
|
|
20
|
-
columns: Array<Column>
|
|
21
|
-
/** This should be the same value passed to the table's `rows` prop. You should `bind:` this so changes to the rank column propagate back to the table component.*/
|
|
22
|
-
rows: Array<Row>
|
|
23
|
-
/** This should be the same value passed to the table's `currentPageRows` prop. */
|
|
24
|
-
currentPageRows: Array<Row & { originalIndex: number; uuid: string }>
|
|
25
|
-
/** The property of the column containing the "rank" or "order" of the row */
|
|
26
|
-
rankProperty: RankProperty
|
|
27
|
-
/** The table component this component is in. */
|
|
28
|
-
table: Table<Row> | undefined
|
|
29
|
-
/** Used to discriminate what is being dragged/dropped. If you have two draggable tables on the same page, consider changing this.*/
|
|
30
|
-
dataType?: string
|
|
31
|
-
/** Whether to show the rank next to the drag target*/
|
|
32
|
-
showRank?: boolean
|
|
33
|
-
/** Any classes to apply to every `tr`*/
|
|
34
|
-
class?: ClassValue
|
|
35
|
-
/** A function that computes any classes you want to add conditionally based on the state of the current row*/
|
|
36
|
-
computeClass?: (row: Row) => ClassValue
|
|
37
|
-
/** A function that computes if a row should be enabled for reordering(drag or stepper buttons) */
|
|
38
|
-
computeReorderability?: ((row: Row) => boolean) | undefined
|
|
39
|
-
/** The rank the list starts at. Defaults to 1. */
|
|
40
|
-
firstRank?: number
|
|
41
|
-
// Snippets
|
|
42
|
-
prepend?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
43
|
-
children?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
44
|
-
append?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
45
|
-
// Callbacks
|
|
46
|
-
rowClick?: (context: { row: Row & { originalIndex: number; uuid: string } }) => void
|
|
47
|
-
drop?: (context: { rows: [Row, Row] }) => void
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let {
|
|
51
|
-
columns,
|
|
52
|
-
rows = $bindable(),
|
|
53
|
-
currentPageRows,
|
|
54
|
-
rankProperty,
|
|
55
|
-
table = $bindable(),
|
|
56
|
-
dataType = 'application/table-row',
|
|
57
|
-
showRank = false,
|
|
58
|
-
class: classNames = '',
|
|
59
|
-
computeClass = () => '',
|
|
60
|
-
computeReorderability = undefined,
|
|
61
|
-
firstRank = 1,
|
|
62
|
-
prepend,
|
|
63
|
-
children,
|
|
64
|
-
append,
|
|
65
|
-
rowClick,
|
|
66
|
-
drop,
|
|
67
|
-
}: Props = $props()
|
|
68
|
-
|
|
69
|
-
let drugRowIndex: number | null = $state(null)
|
|
70
|
-
let hoveredRowIndex: number | null = $state(null)
|
|
71
|
-
let animate = $state(true)
|
|
72
|
-
|
|
73
|
-
let maxRank = $derived(rows.length + firstRank - 1)
|
|
74
|
-
|
|
75
|
-
const idProp = getContext<'uuid' | keyof Row>('idProp')
|
|
76
|
-
|
|
77
|
-
function getRank(row: Row): number {
|
|
78
|
-
return row[rankProperty] as number
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function setRank(row: Row, rank: number) {
|
|
82
|
-
row[rankProperty] = rank as Row[RankProperty]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function relatedTargetIsInTarget(relatedTarget: EventTarget | null, target: EventTarget | null): boolean {
|
|
86
|
-
return (
|
|
87
|
-
!!relatedTarget &&
|
|
88
|
-
!!target &&
|
|
89
|
-
relatedTarget instanceof Node &&
|
|
90
|
-
target instanceof Node &&
|
|
91
|
-
(relatedTarget === target || target.contains(relatedTarget))
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function normalizeRanks() {
|
|
96
|
-
rows.sort((a, b) => getRank(a) - getRank(b))
|
|
97
|
-
rows.forEach((row, index) => {
|
|
98
|
-
setRank(row, index + firstRank)
|
|
99
|
-
})
|
|
100
|
-
rows = rows
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function dragStart(event: DragEvent, row: Row & { originalIndex: number }) {
|
|
104
|
-
if (!event.dataTransfer) {
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
event.dataTransfer.setData(dataType, JSON.stringify(row))
|
|
108
|
-
// Show the whole row while dragging
|
|
109
|
-
if (event.target instanceof Element) {
|
|
110
|
-
const tr = event.target.closest('tr')
|
|
111
|
-
if (tr) {
|
|
112
|
-
event.dataTransfer.setDragImage(tr, 15, 15)
|
|
113
|
-
drugRowIndex = row.originalIndex
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
event.dataTransfer.effectAllowed = 'move'
|
|
117
|
-
event.dataTransfer.dropEffect = 'move'
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function dragEnd() {
|
|
121
|
-
drugRowIndex = null
|
|
122
|
-
hoveredRowIndex = null
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function dragEnter(event: DragEvent, row: Row & { originalIndex: number }) {
|
|
126
|
-
if (event.dataTransfer?.types.includes(dataType)) {
|
|
127
|
-
event.preventDefault()
|
|
128
|
-
if (event.target instanceof Element) {
|
|
129
|
-
const tr = event.target.closest('tr')
|
|
130
|
-
if (tr && !relatedTargetIsInTarget(event.relatedTarget, tr)) {
|
|
131
|
-
hoveredRowIndex = row.originalIndex
|
|
132
|
-
tr.scrollIntoView({ behavior: 'instant', block: 'nearest' })
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function dragLeave(event: DragEvent) {
|
|
139
|
-
if (event.dataTransfer?.types.includes(dataType)) {
|
|
140
|
-
event.preventDefault()
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function onDrop(event: DragEvent) {
|
|
145
|
-
if (table && event.dataTransfer?.types.includes(dataType)) {
|
|
146
|
-
event.preventDefault()
|
|
147
|
-
|
|
148
|
-
const hoveredRow = typeof hoveredRowIndex === 'number' ? rows[hoveredRowIndex] : null
|
|
149
|
-
const drugRow = typeof drugRowIndex === 'number' ? rows[drugRowIndex] : null
|
|
150
|
-
if (drugRow && hoveredRow) {
|
|
151
|
-
// This doesn't allow us to drop something at the end of the list, maybe fix that later if it causes enough grief.
|
|
152
|
-
const newRank = getRank(hoveredRow)
|
|
153
|
-
|
|
154
|
-
setRank(drugRow, newRank)
|
|
155
|
-
setRank(hoveredRow, newRank + 1)
|
|
156
|
-
|
|
157
|
-
normalizeRanks()
|
|
158
|
-
table.sortColumn = columns.find(column => column.property === rankProperty)
|
|
159
|
-
table.sortDirection ??= 'ASC'
|
|
160
|
-
|
|
161
|
-
drop?.({ rows: [drugRow, hoveredRow] })
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
await tick()
|
|
165
|
-
drugRowIndex = null
|
|
166
|
-
hoveredRowIndex = null
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function rankButtonClick(rowIndex: number, direction: 1 | -1) {
|
|
170
|
-
const row = rows[rowIndex]
|
|
171
|
-
const rank = getRank(row)
|
|
172
|
-
const newRank = rank + direction
|
|
173
|
-
|
|
174
|
-
if (newRank < firstRank || newRank > maxRank) {
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const otherRowIndex = rows.findIndex(row => row[rankProperty] === newRank)
|
|
179
|
-
if (otherRowIndex === -1) {
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
const otherRowRank = getRank(rows[otherRowIndex])
|
|
183
|
-
setRank(rows[otherRowIndex], rank)
|
|
184
|
-
setRank(rows[rowIndex], otherRowRank)
|
|
185
|
-
normalizeRanks()
|
|
186
|
-
drop?.({ rows: [rows[otherRowIndex], rows[rowIndex]] })
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export async function withoutAnimation(callback: () => void | Promise<void>) {
|
|
190
|
-
animate = false
|
|
191
|
-
await callback()
|
|
192
|
-
animate = true
|
|
193
|
-
}
|
|
194
|
-
</script>
|
|
195
|
-
|
|
196
|
-
{#each currentPageRows as row (row[idProp])}
|
|
197
|
-
{@const rank = getRank(row)}
|
|
198
|
-
{@const reorderable = computeReorderability ? computeReorderability(row) : typeof row[rankProperty] === 'number'}
|
|
199
|
-
<tr
|
|
200
|
-
id="row-{row.id}"
|
|
201
|
-
data-original-index={row.originalIndex}
|
|
202
|
-
class={[classNames, computeClass(row)]}
|
|
203
|
-
class:hovered-row-asc={table?.sortDirection === 'ASC' && hoveredRowIndex === row.originalIndex}
|
|
204
|
-
class:hovered-row-desc={table?.sortDirection === 'DESC' && hoveredRowIndex === row.originalIndex}
|
|
205
|
-
class:text-muted={drugRowIndex === row.originalIndex}
|
|
206
|
-
animate:flip={{ duration: distance => (animate && distance > 5 ? 200 : 0) }}
|
|
207
|
-
ondrop={onDrop}
|
|
208
|
-
ondragover={event => event.preventDefault()}
|
|
209
|
-
ondragenter={event => dragEnter(event, row)}
|
|
210
|
-
ondragleave={dragLeave}
|
|
211
|
-
onclick={() => rowClick?.({ row })}
|
|
212
|
-
>
|
|
213
|
-
{@render prepend?.({ row })}
|
|
214
|
-
<Td property={rankProperty}>
|
|
215
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
216
|
-
<div
|
|
217
|
-
draggable={reorderable ? 'true' : 'false'}
|
|
218
|
-
style:cursor={reorderable ? 'grab' : 'not-allowed'}
|
|
219
|
-
class="d-none d-md-block"
|
|
220
|
-
ondragstart={event => (reorderable ? dragStart(event, row) : undefined)}
|
|
221
|
-
ondragend={dragEnd}
|
|
222
|
-
>
|
|
223
|
-
<i
|
|
224
|
-
class="fas fa-bars"
|
|
225
|
-
class:text-muted={!reorderable}
|
|
226
|
-
></i>
|
|
227
|
-
{#if showRank}
|
|
228
|
-
{rank}
|
|
229
|
-
{/if}
|
|
230
|
-
</div>
|
|
231
|
-
<div class="btn-group d-md-none">
|
|
232
|
-
<Button
|
|
233
|
-
outline
|
|
234
|
-
size="sm"
|
|
235
|
-
iconClass="arrow-up"
|
|
236
|
-
colorGreyDisabled={false}
|
|
237
|
-
disabled={rank <= firstRank || !reorderable}
|
|
238
|
-
onclick={() => rankButtonClick(row.originalIndex, -1)}
|
|
239
|
-
/>
|
|
240
|
-
{#if showRank}
|
|
241
|
-
<Button
|
|
242
|
-
outline
|
|
243
|
-
size="sm"
|
|
244
|
-
colorGreyDisabled={false}
|
|
245
|
-
disabled
|
|
246
|
-
>
|
|
247
|
-
{rank}
|
|
248
|
-
</Button>
|
|
249
|
-
{/if}
|
|
250
|
-
|
|
251
|
-
<Button
|
|
252
|
-
outline
|
|
253
|
-
size="sm"
|
|
254
|
-
iconClass="arrow-down"
|
|
255
|
-
colorGreyDisabled={false}
|
|
256
|
-
disabled={rank >= maxRank || !reorderable}
|
|
257
|
-
onclick={() => rankButtonClick(row.originalIndex, 1)}
|
|
258
|
-
/>
|
|
259
|
-
</div>
|
|
260
|
-
{#if showRank}
|
|
261
|
-
<span class="sr-only">{row[rankProperty]}</span>
|
|
262
|
-
{/if}
|
|
263
|
-
</Td>
|
|
264
|
-
{@render children?.({ row })}
|
|
265
|
-
{@render append?.({ row })}
|
|
266
|
-
</tr>
|
|
267
|
-
{/each}
|
|
268
|
-
|
|
269
|
-
<style>
|
|
270
|
-
.hovered-row-asc {
|
|
271
|
-
border-top: 5px solid var(--dark, var(--bs-dark, black));
|
|
272
|
-
}
|
|
273
|
-
.hovered-row-desc {
|
|
274
|
-
border-bottom: 5px solid var(--dark, var(--bs-dark, black));
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/* Dark Mode! (BS5 Only) */
|
|
278
|
-
:global([data-bs-theme='dark']) .hovered-row-asc {
|
|
279
|
-
border-top: 5px solid var(--bs-light, white);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
:global([data-bs-theme='dark']) .hovered-row-desc {
|
|
283
|
-
border-bottom: 5px solid var(--bs-light, white);
|
|
284
|
-
}
|
|
285
|
-
</style>
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
generics="Row extends UuidRowProps"
|
|
4
|
+
>
|
|
5
|
+
import type { Snippet } from 'svelte'
|
|
6
|
+
import type { ClassValue } from 'svelte/elements'
|
|
7
|
+
import type { StringKeyOfType } from './tree-utility'
|
|
8
|
+
|
|
9
|
+
import type Table from './Table.svelte'
|
|
10
|
+
import type { Column, UuidRowProps } from './'
|
|
11
|
+
import Td from './Td.svelte'
|
|
12
|
+
import { flip } from 'svelte/animate'
|
|
13
|
+
import { getContext, tick } from 'svelte'
|
|
14
|
+
import Button from '@isoftdata/svelte-button'
|
|
15
|
+
|
|
16
|
+
type RankProperty = StringKeyOfType<Row, number | null>
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
/** Columns definition */
|
|
20
|
+
columns: Array<Column>
|
|
21
|
+
/** This should be the same value passed to the table's `rows` prop. You should `bind:` this so changes to the rank column propagate back to the table component.*/
|
|
22
|
+
rows: Array<Row>
|
|
23
|
+
/** This should be the same value passed to the table's `currentPageRows` prop. */
|
|
24
|
+
currentPageRows: Array<Row & { originalIndex: number; uuid: string }>
|
|
25
|
+
/** The property of the column containing the "rank" or "order" of the row */
|
|
26
|
+
rankProperty: RankProperty
|
|
27
|
+
/** The table component this component is in. */
|
|
28
|
+
table: Table<Row> | undefined
|
|
29
|
+
/** Used to discriminate what is being dragged/dropped. If you have two draggable tables on the same page, consider changing this.*/
|
|
30
|
+
dataType?: string
|
|
31
|
+
/** Whether to show the rank next to the drag target*/
|
|
32
|
+
showRank?: boolean
|
|
33
|
+
/** Any classes to apply to every `tr`*/
|
|
34
|
+
class?: ClassValue
|
|
35
|
+
/** A function that computes any classes you want to add conditionally based on the state of the current row*/
|
|
36
|
+
computeClass?: (row: Row) => ClassValue
|
|
37
|
+
/** A function that computes if a row should be enabled for reordering(drag or stepper buttons) */
|
|
38
|
+
computeReorderability?: ((row: Row) => boolean) | undefined
|
|
39
|
+
/** The rank the list starts at. Defaults to 1. */
|
|
40
|
+
firstRank?: number
|
|
41
|
+
// Snippets
|
|
42
|
+
prepend?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
43
|
+
children?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
44
|
+
append?: Snippet<[{ row: Row & { originalIndex: number; uuid: string } }]>
|
|
45
|
+
// Callbacks
|
|
46
|
+
rowClick?: (context: { row: Row & { originalIndex: number; uuid: string } }) => void
|
|
47
|
+
drop?: (context: { rows: [Row, Row] }) => void
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let {
|
|
51
|
+
columns,
|
|
52
|
+
rows = $bindable(),
|
|
53
|
+
currentPageRows,
|
|
54
|
+
rankProperty,
|
|
55
|
+
table = $bindable(),
|
|
56
|
+
dataType = 'application/table-row',
|
|
57
|
+
showRank = false,
|
|
58
|
+
class: classNames = '',
|
|
59
|
+
computeClass = () => '',
|
|
60
|
+
computeReorderability = undefined,
|
|
61
|
+
firstRank = 1,
|
|
62
|
+
prepend,
|
|
63
|
+
children,
|
|
64
|
+
append,
|
|
65
|
+
rowClick,
|
|
66
|
+
drop,
|
|
67
|
+
}: Props = $props()
|
|
68
|
+
|
|
69
|
+
let drugRowIndex: number | null = $state(null)
|
|
70
|
+
let hoveredRowIndex: number | null = $state(null)
|
|
71
|
+
let animate = $state(true)
|
|
72
|
+
|
|
73
|
+
let maxRank = $derived(rows.length + firstRank - 1)
|
|
74
|
+
|
|
75
|
+
const idProp = getContext<'uuid' | keyof Row>('idProp')
|
|
76
|
+
|
|
77
|
+
function getRank(row: Row): number {
|
|
78
|
+
return row[rankProperty] as number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function setRank(row: Row, rank: number) {
|
|
82
|
+
row[rankProperty] = rank as Row[RankProperty]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function relatedTargetIsInTarget(relatedTarget: EventTarget | null, target: EventTarget | null): boolean {
|
|
86
|
+
return (
|
|
87
|
+
!!relatedTarget &&
|
|
88
|
+
!!target &&
|
|
89
|
+
relatedTarget instanceof Node &&
|
|
90
|
+
target instanceof Node &&
|
|
91
|
+
(relatedTarget === target || target.contains(relatedTarget))
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizeRanks() {
|
|
96
|
+
rows.sort((a, b) => getRank(a) - getRank(b))
|
|
97
|
+
rows.forEach((row, index) => {
|
|
98
|
+
setRank(row, index + firstRank)
|
|
99
|
+
})
|
|
100
|
+
rows = rows
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function dragStart(event: DragEvent, row: Row & { originalIndex: number }) {
|
|
104
|
+
if (!event.dataTransfer) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
event.dataTransfer.setData(dataType, JSON.stringify(row))
|
|
108
|
+
// Show the whole row while dragging
|
|
109
|
+
if (event.target instanceof Element) {
|
|
110
|
+
const tr = event.target.closest('tr')
|
|
111
|
+
if (tr) {
|
|
112
|
+
event.dataTransfer.setDragImage(tr, 15, 15)
|
|
113
|
+
drugRowIndex = row.originalIndex
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
event.dataTransfer.effectAllowed = 'move'
|
|
117
|
+
event.dataTransfer.dropEffect = 'move'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function dragEnd() {
|
|
121
|
+
drugRowIndex = null
|
|
122
|
+
hoveredRowIndex = null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function dragEnter(event: DragEvent, row: Row & { originalIndex: number }) {
|
|
126
|
+
if (event.dataTransfer?.types.includes(dataType)) {
|
|
127
|
+
event.preventDefault()
|
|
128
|
+
if (event.target instanceof Element) {
|
|
129
|
+
const tr = event.target.closest('tr')
|
|
130
|
+
if (tr && !relatedTargetIsInTarget(event.relatedTarget, tr)) {
|
|
131
|
+
hoveredRowIndex = row.originalIndex
|
|
132
|
+
tr.scrollIntoView({ behavior: 'instant', block: 'nearest' })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function dragLeave(event: DragEvent) {
|
|
139
|
+
if (event.dataTransfer?.types.includes(dataType)) {
|
|
140
|
+
event.preventDefault()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function onDrop(event: DragEvent) {
|
|
145
|
+
if (table && event.dataTransfer?.types.includes(dataType)) {
|
|
146
|
+
event.preventDefault()
|
|
147
|
+
|
|
148
|
+
const hoveredRow = typeof hoveredRowIndex === 'number' ? rows[hoveredRowIndex] : null
|
|
149
|
+
const drugRow = typeof drugRowIndex === 'number' ? rows[drugRowIndex] : null
|
|
150
|
+
if (drugRow && hoveredRow) {
|
|
151
|
+
// This doesn't allow us to drop something at the end of the list, maybe fix that later if it causes enough grief.
|
|
152
|
+
const newRank = getRank(hoveredRow)
|
|
153
|
+
|
|
154
|
+
setRank(drugRow, newRank)
|
|
155
|
+
setRank(hoveredRow, newRank + 1)
|
|
156
|
+
|
|
157
|
+
normalizeRanks()
|
|
158
|
+
table.sortColumn = columns.find(column => column.property === rankProperty)
|
|
159
|
+
table.sortDirection ??= 'ASC'
|
|
160
|
+
|
|
161
|
+
drop?.({ rows: [drugRow, hoveredRow] })
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
await tick()
|
|
165
|
+
drugRowIndex = null
|
|
166
|
+
hoveredRowIndex = null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function rankButtonClick(rowIndex: number, direction: 1 | -1) {
|
|
170
|
+
const row = rows[rowIndex]
|
|
171
|
+
const rank = getRank(row)
|
|
172
|
+
const newRank = rank + direction
|
|
173
|
+
|
|
174
|
+
if (newRank < firstRank || newRank > maxRank) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const otherRowIndex = rows.findIndex(row => row[rankProperty] === newRank)
|
|
179
|
+
if (otherRowIndex === -1) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
const otherRowRank = getRank(rows[otherRowIndex])
|
|
183
|
+
setRank(rows[otherRowIndex], rank)
|
|
184
|
+
setRank(rows[rowIndex], otherRowRank)
|
|
185
|
+
normalizeRanks()
|
|
186
|
+
drop?.({ rows: [rows[otherRowIndex], rows[rowIndex]] })
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function withoutAnimation(callback: () => void | Promise<void>) {
|
|
190
|
+
animate = false
|
|
191
|
+
await callback()
|
|
192
|
+
animate = true
|
|
193
|
+
}
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
{#each currentPageRows as row (row[idProp])}
|
|
197
|
+
{@const rank = getRank(row)}
|
|
198
|
+
{@const reorderable = computeReorderability ? computeReorderability(row) : typeof row[rankProperty] === 'number'}
|
|
199
|
+
<tr
|
|
200
|
+
id="row-{row.id}"
|
|
201
|
+
data-original-index={row.originalIndex}
|
|
202
|
+
class={[classNames, computeClass(row)]}
|
|
203
|
+
class:hovered-row-asc={table?.sortDirection === 'ASC' && hoveredRowIndex === row.originalIndex}
|
|
204
|
+
class:hovered-row-desc={table?.sortDirection === 'DESC' && hoveredRowIndex === row.originalIndex}
|
|
205
|
+
class:text-muted={drugRowIndex === row.originalIndex}
|
|
206
|
+
animate:flip={{ duration: distance => (animate && distance > 5 ? 200 : 0) }}
|
|
207
|
+
ondrop={onDrop}
|
|
208
|
+
ondragover={event => event.preventDefault()}
|
|
209
|
+
ondragenter={event => dragEnter(event, row)}
|
|
210
|
+
ondragleave={dragLeave}
|
|
211
|
+
onclick={() => rowClick?.({ row })}
|
|
212
|
+
>
|
|
213
|
+
{@render prepend?.({ row })}
|
|
214
|
+
<Td property={rankProperty}>
|
|
215
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
216
|
+
<div
|
|
217
|
+
draggable={reorderable ? 'true' : 'false'}
|
|
218
|
+
style:cursor={reorderable ? 'grab' : 'not-allowed'}
|
|
219
|
+
class="d-none d-md-block"
|
|
220
|
+
ondragstart={event => (reorderable ? dragStart(event, row) : undefined)}
|
|
221
|
+
ondragend={dragEnd}
|
|
222
|
+
>
|
|
223
|
+
<i
|
|
224
|
+
class="fas fa-bars"
|
|
225
|
+
class:text-muted={!reorderable}
|
|
226
|
+
></i>
|
|
227
|
+
{#if showRank}
|
|
228
|
+
{rank}
|
|
229
|
+
{/if}
|
|
230
|
+
</div>
|
|
231
|
+
<div class="btn-group d-md-none">
|
|
232
|
+
<Button
|
|
233
|
+
outline
|
|
234
|
+
size="sm"
|
|
235
|
+
iconClass="arrow-up"
|
|
236
|
+
colorGreyDisabled={false}
|
|
237
|
+
disabled={rank <= firstRank || !reorderable}
|
|
238
|
+
onclick={() => rankButtonClick(row.originalIndex, -1)}
|
|
239
|
+
/>
|
|
240
|
+
{#if showRank}
|
|
241
|
+
<Button
|
|
242
|
+
outline
|
|
243
|
+
size="sm"
|
|
244
|
+
colorGreyDisabled={false}
|
|
245
|
+
disabled
|
|
246
|
+
>
|
|
247
|
+
{rank}
|
|
248
|
+
</Button>
|
|
249
|
+
{/if}
|
|
250
|
+
|
|
251
|
+
<Button
|
|
252
|
+
outline
|
|
253
|
+
size="sm"
|
|
254
|
+
iconClass="arrow-down"
|
|
255
|
+
colorGreyDisabled={false}
|
|
256
|
+
disabled={rank >= maxRank || !reorderable}
|
|
257
|
+
onclick={() => rankButtonClick(row.originalIndex, 1)}
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
{#if showRank}
|
|
261
|
+
<span class="sr-only">{row[rankProperty]}</span>
|
|
262
|
+
{/if}
|
|
263
|
+
</Td>
|
|
264
|
+
{@render children?.({ row })}
|
|
265
|
+
{@render append?.({ row })}
|
|
266
|
+
</tr>
|
|
267
|
+
{/each}
|
|
268
|
+
|
|
269
|
+
<style>
|
|
270
|
+
.hovered-row-asc {
|
|
271
|
+
border-top: 5px solid var(--dark, var(--bs-dark, black));
|
|
272
|
+
}
|
|
273
|
+
.hovered-row-desc {
|
|
274
|
+
border-bottom: 5px solid var(--dark, var(--bs-dark, black));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* Dark Mode! (BS5 Only) */
|
|
278
|
+
:global([data-bs-theme='dark']) .hovered-row-asc {
|
|
279
|
+
border-top: 5px solid var(--bs-light, white);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
:global([data-bs-theme='dark']) .hovered-row-desc {
|
|
283
|
+
border-bottom: 5px solid var(--bs-light, white);
|
|
284
|
+
}
|
|
285
|
+
</style>
|