poe-svelte-ui-lib 1.6.2 → 1.6.3
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/dist/Input/Input.svelte +68 -77
- package/dist/Input/Input.svelte.d.ts +1 -1
- package/dist/Table/Table.svelte +154 -117
- package/dist/Table/Table.svelte.d.ts +1 -1
- package/dist/Table/TableProps.svelte +442 -399
- package/dist/Table/TableProps.svelte.d.ts +1 -1
- package/dist/Tabs/TabsProps.svelte +18 -16
- package/dist/libIcons/ButtonAdd.svelte +2 -7
- package/dist/libIcons/ButtonRemove.svelte +8 -0
- package/dist/libIcons/ButtonRemove.svelte.d.ts +18 -0
- package/dist/locales/translations.js +221 -218
- package/dist/options.d.ts +5 -0
- package/dist/options.js +161 -157
- package/dist/types.d.ts +30 -23
- package/dist/types.js +1 -1
- package/package.json +2 -2
package/dist/Table/Table.svelte
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
<!-- $lib/ElementsUI/Table.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
|
-
import { get } from
|
|
4
|
-
import type { ITableHeader, ITableProps } from
|
|
5
|
-
import { fade, fly } from
|
|
6
|
-
import { twMerge } from
|
|
7
|
-
import { onMount } from
|
|
8
|
-
import ButtonClear from
|
|
3
|
+
import { get } from "svelte/store"
|
|
4
|
+
import type { ISelectOption, ITableHeader, ITableProps } from "../types"
|
|
5
|
+
import { fade, fly, slide } from "svelte/transition"
|
|
6
|
+
import { twMerge } from "tailwind-merge"
|
|
7
|
+
import { onMount } from "svelte"
|
|
8
|
+
import ButtonClear from "../libIcons/ButtonClear.svelte"
|
|
9
|
+
import { t } from "../locales/i18n"
|
|
9
10
|
|
|
10
11
|
let {
|
|
11
12
|
id = crypto.randomUUID(),
|
|
12
|
-
wrapperClass =
|
|
13
|
-
label = { name:
|
|
13
|
+
wrapperClass = "",
|
|
14
|
+
label = { name: "", class: "" },
|
|
14
15
|
body = $bindable(),
|
|
15
16
|
header = [],
|
|
16
|
-
footer =
|
|
17
|
-
dataBuffer = { stashData: false, rowsAmmount: 10, clearButton: false, clearClass:
|
|
18
|
-
type =
|
|
17
|
+
footer = "",
|
|
18
|
+
dataBuffer = { stashData: false, rowsAmmount: 10, clearButton: false, clearClass: "" },
|
|
19
|
+
type = "table",
|
|
19
20
|
outline = false,
|
|
20
21
|
cursor = null,
|
|
21
22
|
loader,
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
/* Сортировка */
|
|
34
35
|
let sortState: {
|
|
35
36
|
key: string | null
|
|
36
|
-
direction:
|
|
37
|
+
direction: "asc" | "desc" | null
|
|
37
38
|
} = {
|
|
38
39
|
key: null,
|
|
39
40
|
direction: null,
|
|
@@ -42,42 +43,42 @@
|
|
|
42
43
|
let isAutoscroll = $state(false)
|
|
43
44
|
|
|
44
45
|
const logTypeOptions = [
|
|
45
|
-
{ id: crypto.randomUUID(), name:
|
|
46
|
-
{ id: crypto.randomUUID(), name:
|
|
47
|
-
{ id: crypto.randomUUID(), name:
|
|
46
|
+
{ id: crypto.randomUUID(), name: "Error", value: "error", color: "bg-(--red-color)" },
|
|
47
|
+
{ id: crypto.randomUUID(), name: "Warning", value: "warning", color: "bg-(--yellow-color)" },
|
|
48
|
+
{ id: crypto.randomUUID(), name: "Info", value: "info", color: "bg-(--gray-color)" },
|
|
48
49
|
]
|
|
49
|
-
let logType = $state([
|
|
50
|
+
let logType = $state(["error", "warning"])
|
|
50
51
|
|
|
51
52
|
/* Сортировка столбцов */
|
|
52
53
|
const sortRows = (key: string) => {
|
|
53
54
|
if (sortState.key === key) {
|
|
54
|
-
sortState.direction = sortState.direction ===
|
|
55
|
+
sortState.direction = sortState.direction === "asc" ? "desc" : "asc"
|
|
55
56
|
} else {
|
|
56
57
|
sortState.key = key
|
|
57
|
-
sortState.direction =
|
|
58
|
+
sortState.direction = "asc"
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
body = [...body].sort((a, b) => {
|
|
61
62
|
const aValue = a[key]
|
|
62
63
|
const bValue = b[key]
|
|
63
|
-
if (typeof aValue ===
|
|
64
|
-
return sortState.direction ===
|
|
64
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
65
|
+
return sortState.direction === "asc" ? aValue - bValue : bValue - aValue
|
|
65
66
|
}
|
|
66
67
|
if (aValue instanceof Date && bValue instanceof Date) {
|
|
67
|
-
return sortState.direction ===
|
|
68
|
+
return sortState.direction === "asc" ? aValue.getTime() - bValue.getTime() : bValue.getTime() - aValue.getTime()
|
|
68
69
|
}
|
|
69
70
|
const strA = String(aValue).toLowerCase()
|
|
70
71
|
const strB = String(bValue).toLowerCase()
|
|
71
|
-
const numA = strA.match(/\d+/g)?.[0] ||
|
|
72
|
-
const numB = strB.match(/\d+/g)?.[0] ||
|
|
72
|
+
const numA = strA.match(/\d+/g)?.[0] || ""
|
|
73
|
+
const numB = strB.match(/\d+/g)?.[0] || ""
|
|
73
74
|
if (numA && numB) {
|
|
74
75
|
const numCompare = parseInt(numA, 10) - parseInt(numB, 10)
|
|
75
76
|
if (numCompare !== 0) {
|
|
76
|
-
return sortState.direction ===
|
|
77
|
+
return sortState.direction === "asc" ? numCompare : -numCompare
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
const stringCompare = strA.localeCompare(strB)
|
|
80
|
-
return sortState.direction ===
|
|
81
|
+
return sortState.direction === "asc" ? stringCompare : -stringCompare
|
|
81
82
|
})
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -113,18 +114,33 @@
|
|
|
113
114
|
if (button.onClick) button.onClick(row)
|
|
114
115
|
else if (button.eventHandler && onClick) {
|
|
115
116
|
let value: Record<string, boolean | string | number | number[] | object | null> = {}
|
|
116
|
-
button.eventHandler.Variables.forEach((v: string) =>
|
|
117
|
-
value[v] = row[v]
|
|
118
|
-
})
|
|
117
|
+
button.eventHandler.Variables.forEach((v: string) => (value[v] = row[v]))
|
|
119
118
|
button.eventHandler.Value = JSON.stringify(value)
|
|
120
119
|
onClick(button.eventHandler)
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
let
|
|
123
|
+
let isDropdownOpen: number | null = $state(null)
|
|
124
|
+
let changedOptions: { index: number; options: ISelectOption<string | number>[] | null }[] = $state([])
|
|
125
|
+
|
|
126
|
+
const selectOption = (index: number, options: ISelectOption<string | number>[], option: ISelectOption<string | number>, event: MouseEvent) => {
|
|
127
|
+
event.stopPropagation()
|
|
128
|
+
|
|
129
|
+
const existingItem = changedOptions.find(item => item.index === index)
|
|
130
|
+
|
|
131
|
+
if (existingItem) {
|
|
132
|
+
existingItem.options = [option, ...options.filter(opt => opt.id !== option.id)]
|
|
133
|
+
} else {
|
|
134
|
+
changedOptions = [...changedOptions, { index, options: [option, ...options.filter(opt => opt.id !== option.id)] }]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
isDropdownOpen = null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let copiedCell = $state({ x: "", y: -1 })
|
|
125
141
|
let tooltip = $state({
|
|
126
142
|
show: false,
|
|
127
|
-
text:
|
|
143
|
+
text: "",
|
|
128
144
|
x: 0,
|
|
129
145
|
y: 0,
|
|
130
146
|
})
|
|
@@ -133,14 +149,14 @@
|
|
|
133
149
|
modalData = {
|
|
134
150
|
isOpen: true,
|
|
135
151
|
rawData: text,
|
|
136
|
-
formattedData: formatting ? formatting(text) : (text ??
|
|
152
|
+
formattedData: formatting ? formatting(text) : (text ?? ""),
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
|
|
140
156
|
const showTooltip = (event: MouseEvent, text: string, formatting?: (text: string) => string) => {
|
|
141
157
|
tooltip = {
|
|
142
158
|
show: true,
|
|
143
|
-
text: formatting ? formatting(text) : (text ??
|
|
159
|
+
text: formatting ? formatting(text) : (text ?? ""),
|
|
144
160
|
x: event.clientX,
|
|
145
161
|
y: event.clientY,
|
|
146
162
|
}
|
|
@@ -153,23 +169,23 @@
|
|
|
153
169
|
/* Для работы этой проверки в описании столбцов таблицы нужно явно указать что строка будет пустая при отсутствии иконки в БД -
|
|
154
170
|
src: (row) => (row.icon ? `data:image/png;base64,${row.icon}` : '') */
|
|
155
171
|
const hasImage = (column: ITableHeader<any>, row: any): boolean => {
|
|
156
|
-
const src = typeof column.image?.src ===
|
|
172
|
+
const src = typeof column.image?.src === "function" ? column.image.src(row) : column.image?.src
|
|
157
173
|
return !!src
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
$effect(() => {
|
|
161
177
|
const currentType = type
|
|
162
|
-
if (currentType ===
|
|
178
|
+
if (currentType === "logger") {
|
|
163
179
|
header = [
|
|
164
180
|
{
|
|
165
|
-
key:
|
|
166
|
-
label: { name:
|
|
167
|
-
width:
|
|
181
|
+
key: "color",
|
|
182
|
+
label: { name: "Type" },
|
|
183
|
+
width: "3rem",
|
|
168
184
|
} as ITableHeader<any>,
|
|
169
185
|
{
|
|
170
|
-
key:
|
|
171
|
-
label: { name:
|
|
172
|
-
width:
|
|
186
|
+
key: "data",
|
|
187
|
+
label: { name: "Data" },
|
|
188
|
+
width: "calc(100% - 3rem)",
|
|
173
189
|
} as ITableHeader<any>,
|
|
174
190
|
]
|
|
175
191
|
}
|
|
@@ -179,14 +195,14 @@
|
|
|
179
195
|
})
|
|
180
196
|
|
|
181
197
|
$effect(() => {
|
|
182
|
-
if (body && type ==
|
|
198
|
+
if (body && type == "logger") {
|
|
183
199
|
if (Array.isArray(body)) {
|
|
184
200
|
for (let i = 0; i < body.length; i++) {
|
|
185
201
|
buffer = [
|
|
186
202
|
...buffer,
|
|
187
203
|
{
|
|
188
204
|
type: Object.entries(body[i])[0][1] as string,
|
|
189
|
-
color: `<div class='size-6 rounded-full ${logTypeOptions.find(
|
|
205
|
+
color: `<div class='size-6 rounded-full ${logTypeOptions.find(o => o.value == Object.entries(body[i])[0][1])?.color}'></div>`,
|
|
190
206
|
data: Object.entries(body[i])[1][1] as string,
|
|
191
207
|
},
|
|
192
208
|
]
|
|
@@ -196,7 +212,7 @@
|
|
|
196
212
|
...buffer,
|
|
197
213
|
{
|
|
198
214
|
type: Object.entries(body)[0][1] as string,
|
|
199
|
-
color: `<div class='size-6 rounded-full ${logTypeOptions.find(
|
|
215
|
+
color: `<div class='size-6 rounded-full ${logTypeOptions.find(o => o.value == Object.entries(body)[0][1])?.color}'></div>`,
|
|
200
216
|
data: Object.entries(body)[1][1] as string,
|
|
201
217
|
},
|
|
202
218
|
]
|
|
@@ -211,7 +227,7 @@
|
|
|
211
227
|
})
|
|
212
228
|
|
|
213
229
|
$effect(() => {
|
|
214
|
-
if (body && dataBuffer.stashData && type ==
|
|
230
|
+
if (body && dataBuffer.stashData && type == "table") {
|
|
215
231
|
if (Array.isArray(body)) {
|
|
216
232
|
for (let i = 0; i < body.length; i++) {
|
|
217
233
|
buffer = [...buffer, body[i]]
|
|
@@ -227,28 +243,28 @@
|
|
|
227
243
|
|
|
228
244
|
onMount(() => {
|
|
229
245
|
if (autoscroll) {
|
|
230
|
-
container?.addEventListener(
|
|
246
|
+
container?.addEventListener("scroll", handleAutoScroll)
|
|
231
247
|
scrollToBottom()
|
|
232
248
|
}
|
|
233
249
|
|
|
234
|
-
if (type ===
|
|
250
|
+
if (type === "logger") {
|
|
235
251
|
header = [
|
|
236
252
|
{
|
|
237
|
-
key:
|
|
238
|
-
label: { name:
|
|
239
|
-
width:
|
|
253
|
+
key: "color",
|
|
254
|
+
label: { name: "Type" },
|
|
255
|
+
width: "3rem",
|
|
240
256
|
} as ITableHeader<any>,
|
|
241
257
|
{
|
|
242
|
-
key:
|
|
243
|
-
label: { name:
|
|
244
|
-
width:
|
|
258
|
+
key: "data",
|
|
259
|
+
label: { name: "Data" },
|
|
260
|
+
width: "calc(100% - 3rem)",
|
|
245
261
|
} as ITableHeader<any>,
|
|
246
262
|
]
|
|
247
263
|
}
|
|
248
264
|
|
|
249
265
|
return () => {
|
|
250
266
|
if (autoscroll) {
|
|
251
|
-
container?.removeEventListener(
|
|
267
|
+
container?.removeEventListener("scroll", handleAutoScroll)
|
|
252
268
|
}
|
|
253
269
|
}
|
|
254
270
|
})
|
|
@@ -256,13 +272,12 @@
|
|
|
256
272
|
|
|
257
273
|
<div
|
|
258
274
|
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
259
|
-
class={twMerge(`bg-blue flex h-full w-full items-center ${type ==
|
|
260
|
-
>
|
|
275
|
+
class={twMerge(`bg-blue flex h-full w-full items-center ${type == "logger" ? "gap-2" : ""} flex-col overflow-hidden`, wrapperClass)}>
|
|
261
276
|
{#if label.name}
|
|
262
277
|
<h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
263
278
|
{/if}
|
|
264
279
|
|
|
265
|
-
{#if type ==
|
|
280
|
+
{#if type == "logger"}
|
|
266
281
|
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="flex w-[50%] justify-center rounded-full">
|
|
267
282
|
{#each logTypeOptions as option, index}
|
|
268
283
|
<button
|
|
@@ -271,20 +286,17 @@
|
|
|
271
286
|
select-none hover:shadow-md
|
|
272
287
|
${
|
|
273
288
|
logType.includes(option.value) && logType !== null
|
|
274
|
-
?
|
|
275
|
-
:
|
|
289
|
+
? "z-10 py-1 shadow-[0_0_10px_var(--shadow-color)] hover:shadow-[0_0_15px_var(--shadow-color)]"
|
|
290
|
+
: ""
|
|
276
291
|
}
|
|
277
|
-
${logTypeOptions.length > 0 && index === 0 ?
|
|
278
|
-
index === logTypeOptions.length - 1 ? 'rounded-r-2xl' : ''
|
|
279
|
-
} ${option.color}`)}
|
|
292
|
+
${logTypeOptions.length > 0 && index === 0 ? "rounded-l-2xl" : ""} ${index === logTypeOptions.length - 1 ? "rounded-r-2xl" : ""} ${option.color}`)}
|
|
280
293
|
onclick={() => {
|
|
281
294
|
if (logType.includes(option.value)) {
|
|
282
|
-
logType = logType.filter(
|
|
295
|
+
logType = logType.filter(type => type !== option.value)
|
|
283
296
|
} else {
|
|
284
297
|
logType.push(option.value)
|
|
285
298
|
}
|
|
286
|
-
}}
|
|
287
|
-
>
|
|
299
|
+
}}>
|
|
288
300
|
<span class="flex flex-row items-center justify-center gap-4">
|
|
289
301
|
{#if option}
|
|
290
302
|
<div class="flex-1">
|
|
@@ -300,29 +312,26 @@
|
|
|
300
312
|
<div
|
|
301
313
|
class="relative flex h-full w-full flex-col overflow-hidden rounded-xl border shadow-sm transition duration-200 hover:shadow-md {outline
|
|
302
314
|
? ' border-(--border-color)'
|
|
303
|
-
: 'border-transparent'} "
|
|
304
|
-
>
|
|
315
|
+
: 'border-transparent'} ">
|
|
305
316
|
<!-- Table Header -->
|
|
306
|
-
<div class="grid font-semibold" style={`grid-template-columns: ${header.map(
|
|
317
|
+
<div class="grid font-semibold" style={`grid-template-columns: ${header.map(c => c.width || "minmax(0, 1fr)").join(" ")};`}>
|
|
307
318
|
{#each header as column, index (column)}
|
|
308
319
|
<div
|
|
309
320
|
class={twMerge(
|
|
310
|
-
`items-center justify-center border-l ${outline && index !== 0 ?
|
|
311
|
-
column.align ===
|
|
312
|
-
?
|
|
313
|
-
: column.align ===
|
|
314
|
-
?
|
|
315
|
-
:
|
|
321
|
+
`items-center justify-center border-l ${outline && index !== 0 ? " border-(--border-color)" : "border-transparent"} ${
|
|
322
|
+
column.align === "center"
|
|
323
|
+
? "flex justify-center text-center"
|
|
324
|
+
: column.align === "right"
|
|
325
|
+
? "flex justify-end text-right"
|
|
326
|
+
: "flex justify-start text-left"
|
|
316
327
|
} gap-1 bg-(--bg-color) p-2 text-left`,
|
|
317
328
|
column.label?.class,
|
|
318
|
-
)}
|
|
319
|
-
>
|
|
329
|
+
)}>
|
|
320
330
|
<span>{column.label?.name}</span>
|
|
321
331
|
{#if column.sortable}
|
|
322
332
|
<button
|
|
323
333
|
class="inline-block cursor-pointer font-bold transition-transform duration-75 hover:scale-110 active:scale-95"
|
|
324
|
-
onclick={() => sortRows(column.key as string)}
|
|
325
|
-
>
|
|
334
|
+
onclick={() => sortRows(column.key as string)}>
|
|
326
335
|
↑↓
|
|
327
336
|
</button>
|
|
328
337
|
{/if}
|
|
@@ -332,11 +341,10 @@
|
|
|
332
341
|
{#if dataBuffer.clearButton}
|
|
333
342
|
<button
|
|
334
343
|
class={twMerge(
|
|
335
|
-
|
|
344
|
+
"absolute right-2 bg-(--back-color) rounded-full p-1 cursor-pointer [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full",
|
|
336
345
|
dataBuffer.clearClass,
|
|
337
346
|
)}
|
|
338
|
-
onclick={clearBuffer}
|
|
339
|
-
>
|
|
347
|
+
onclick={clearBuffer}>
|
|
340
348
|
<ButtonClear />
|
|
341
349
|
</button>
|
|
342
350
|
{/if}
|
|
@@ -344,14 +352,14 @@
|
|
|
344
352
|
{#if body || buffer}
|
|
345
353
|
{@const isSliced = buffer.length - (dataBuffer.rowsAmmount ?? 10) > 0 ? buffer.length - ((dataBuffer.rowsAmmount ?? 10) % 2) !== 0 : false}
|
|
346
354
|
{@const rows =
|
|
347
|
-
type ==
|
|
348
|
-
? buffer.filter(
|
|
355
|
+
type == "logger"
|
|
356
|
+
? buffer.filter(str => logType.includes(str.type)).slice(-(dataBuffer.rowsAmmount ?? 10))
|
|
349
357
|
: dataBuffer.stashData
|
|
350
358
|
? buffer.slice(-(dataBuffer.rowsAmmount ?? 10))
|
|
351
359
|
: body}
|
|
352
360
|
<!-- Table Body с прокруткой -->
|
|
353
361
|
<div class="flex-1 overflow-y-auto bg-(--container-color)/50" bind:this={container} onscroll={handleScroll}>
|
|
354
|
-
<div class="grid min-w-0" style={`grid-template-columns: ${header.map(
|
|
362
|
+
<div class="grid min-w-0" style={`grid-template-columns: ${header.map(c => c.width || "minmax(0, 1fr)").join(" ")};`}>
|
|
355
363
|
{#each rows as row, index (row)}
|
|
356
364
|
{#each header as column, j (column)}
|
|
357
365
|
<div
|
|
@@ -364,35 +372,70 @@
|
|
|
364
372
|
: 'flex justify-start text-left'}
|
|
365
373
|
border-t
|
|
366
374
|
{j !== 0 ? ' border-l ' : ''}
|
|
367
|
-
{outline ? 'border-(--border-color)' : 'border-transparent'} "
|
|
368
|
-
|
|
369
|
-
{#if column.buttons}
|
|
375
|
+
{outline ? 'border-(--border-color)' : 'border-transparent'} ">
|
|
376
|
+
{#if column.action?.type == "buttons" && column.action?.buttons}
|
|
370
377
|
<div class="flex w-full flex-col gap-1">
|
|
371
|
-
{#each column.buttons as button (button)}
|
|
378
|
+
{#each column.action?.buttons as button (button)}
|
|
372
379
|
<button
|
|
373
380
|
class="{twMerge(`cursor-pointer rounded-full
|
|
374
381
|
px-4 py-1 font-semibold shadow-sm transition-shadow duration-200 outline-none select-none hover:shadow-md
|
|
375
382
|
${typeof button.class === 'function' ? button.class(row) : button.class}`)} bg-(--bg-color)"
|
|
376
|
-
onclick={() => buttonClick(row, button)}
|
|
377
|
-
|
|
378
|
-
{typeof button.name === 'function' ? button.name(row) : button.name}
|
|
383
|
+
onclick={() => buttonClick(row, button)}>
|
|
384
|
+
{typeof button.name === "function" ? button.name(row) : button.name}
|
|
379
385
|
</button>
|
|
380
386
|
{/each}
|
|
381
387
|
</div>
|
|
388
|
+
{:else if column.action?.type == "select" && column.action?.select}
|
|
389
|
+
{@const currentOptions = changedOptions.find(item => item.index === index)?.options}
|
|
390
|
+
{@const defaultOptions = Array.isArray(row[column.key])
|
|
391
|
+
? row[column.key].map((option: string | number) => ({
|
|
392
|
+
id: crypto.randomUUID(),
|
|
393
|
+
value: option,
|
|
394
|
+
name: option,
|
|
395
|
+
class: "",
|
|
396
|
+
disabled: false,
|
|
397
|
+
}))
|
|
398
|
+
: []}
|
|
399
|
+
{@const options = currentOptions || defaultOptions}
|
|
400
|
+
<div class="relative w-full">
|
|
401
|
+
<button
|
|
402
|
+
class={`w-full rounded-2xl border border-(--blue-color) bg-(--back-color) p-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition duration-200
|
|
403
|
+
cursor-pointer hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]`}
|
|
404
|
+
onclick={() => (isDropdownOpen = isDropdownOpen === index ? null : index)}>
|
|
405
|
+
{options[0]?.name || $t("common.select_tag")}
|
|
406
|
+
</button>
|
|
407
|
+
|
|
408
|
+
{#if isDropdownOpen === index}
|
|
409
|
+
<div
|
|
410
|
+
class="absolute top-full left-1/2 z-50 -translate-x-1/2 rounded-b-2xl shadow-[0_0_3px_rgb(0_0_0_/0.25)]"
|
|
411
|
+
style="width: calc(100% - 1.8rem);"
|
|
412
|
+
transition:slide={{ duration: 250 }}>
|
|
413
|
+
{#each options as option, option_index (option.id)}
|
|
414
|
+
<button
|
|
415
|
+
id={option.id}
|
|
416
|
+
value={option?.value ? String(option.value) : ""}
|
|
417
|
+
class={twMerge(
|
|
418
|
+
`flex h-full w-full cursor-pointer items-center justify-center p-1 inset-shadow-[0_10px_10px_-15px_rgb(0_0_0_/0.5)] duration-250 hover:bg-(--field-color)! bg-(--back-color)
|
|
419
|
+
${option_index === options.length - 1 ? "rounded-b-2xl" : ""} `,
|
|
420
|
+
option.class,
|
|
421
|
+
)}
|
|
422
|
+
onclick={e => selectOption(index, options, option, e)}>
|
|
423
|
+
{option.name}
|
|
424
|
+
</button>
|
|
425
|
+
{/each}
|
|
426
|
+
</div>
|
|
427
|
+
{/if}
|
|
428
|
+
</div>
|
|
382
429
|
{:else if column.image?.src || column.image?.defaultIcon}
|
|
383
|
-
<div
|
|
384
|
-
class="flex items-center justify-center"
|
|
385
|
-
style={`width: ${column.image.width || '5rem'}; height: ${column.image.height || '5rem'};`}
|
|
386
|
-
>
|
|
430
|
+
<div class="flex items-center justify-center" style={`width: ${column.image.width || "5rem"}; height: ${column.image.height || "5rem"};`}>
|
|
387
431
|
{#if hasImage(column, row)}
|
|
388
432
|
<img
|
|
389
|
-
src={typeof column.image?.src ===
|
|
390
|
-
alt={column.image.alt ??
|
|
391
|
-
class={twMerge(`h-full w-full object-cover ${column.image.class ||
|
|
392
|
-
loading="lazy"
|
|
393
|
-
/>
|
|
433
|
+
src={typeof column.image?.src === "function" ? column.image.src(row) : column.image?.src || ""}
|
|
434
|
+
alt={column.image.alt ?? "Image"}
|
|
435
|
+
class={twMerge(`h-full w-full object-cover ${column.image.class || ""}`)}
|
|
436
|
+
loading="lazy" />
|
|
394
437
|
{:else if column.image.defaultIcon}
|
|
395
|
-
{#if typeof column.image.defaultIcon ===
|
|
438
|
+
{#if typeof column.image.defaultIcon === "string"}
|
|
396
439
|
{@html column.image.defaultIcon}
|
|
397
440
|
{:else}
|
|
398
441
|
<column.image.defaultIcon />
|
|
@@ -402,25 +445,23 @@
|
|
|
402
445
|
{:else}
|
|
403
446
|
<div
|
|
404
447
|
class=" w-full max-w-full wrap-break-word {column.overflow?.truncated ? 'truncate' : ' whitespace-normal'}"
|
|
405
|
-
onmouseenter={column.overflow?.truncated ?
|
|
448
|
+
onmouseenter={column.overflow?.truncated ? e => showTooltip(e, row[column.key], column.overflow?.formatting) : undefined}
|
|
406
449
|
onmouseleave={column.overflow?.truncated ? hideTooltip : undefined}
|
|
407
450
|
onmousemove={column.overflow?.truncated
|
|
408
|
-
?
|
|
451
|
+
? e => {
|
|
409
452
|
tooltip.x = e.clientX
|
|
410
453
|
tooltip.y = e.clientY
|
|
411
454
|
}
|
|
412
455
|
: undefined}
|
|
413
456
|
role="columnheader"
|
|
414
|
-
tabindex={null}
|
|
415
|
-
>
|
|
457
|
+
tabindex={null}>
|
|
416
458
|
{#if column.overflow?.modal}
|
|
417
459
|
<button
|
|
418
460
|
class="w-full cursor-pointer overflow-hidden text-left text-ellipsis whitespace-nowrap"
|
|
419
|
-
onclick={
|
|
461
|
+
onclick={e => {
|
|
420
462
|
e.stopPropagation()
|
|
421
463
|
showModal(row[column.key], column.overflow?.formatting)
|
|
422
|
-
}}
|
|
423
|
-
>
|
|
464
|
+
}}>
|
|
424
465
|
{@html row[column.key]}
|
|
425
466
|
</button>
|
|
426
467
|
{:else}
|
|
@@ -434,28 +475,25 @@
|
|
|
434
475
|
{#if column.overflow?.copy}
|
|
435
476
|
<button
|
|
436
477
|
class="mx-2 flex cursor-pointer border-none bg-transparent text-2xl"
|
|
437
|
-
onclick={
|
|
478
|
+
onclick={e => {
|
|
438
479
|
e.preventDefault()
|
|
439
480
|
navigator.clipboard.writeText(row[column.key])
|
|
440
481
|
copiedCell = { x: column.key as string, y: index }
|
|
441
|
-
setTimeout(() => (copiedCell = { x:
|
|
482
|
+
setTimeout(() => (copiedCell = { x: "", y: -1 }), 1000)
|
|
442
483
|
}}
|
|
443
|
-
aria-label="Копировать текст"
|
|
444
|
-
>
|
|
484
|
+
aria-label="Копировать текст">
|
|
445
485
|
<div class=" size-5 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
|
|
446
486
|
{#if copiedCell.y === index && copiedCell.x === column.key}
|
|
447
487
|
<div
|
|
448
488
|
class="absolute top-1/2 right-3.5 -translate-y-1/2 transform rounded-md bg-(--green-color) px-1.5 py-1 shadow-lg"
|
|
449
|
-
transition:fade={{ duration: 200 }}
|
|
450
|
-
>
|
|
489
|
+
transition:fade={{ duration: 200 }}>
|
|
451
490
|
✓
|
|
452
491
|
</div>
|
|
453
492
|
{:else}
|
|
454
493
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
455
494
|
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
456
495
|
<path
|
|
457
|
-
d="M6 11c0-2.828 0-4.243.879-5.121C7.757 5 9.172 5 12 5h3c2.828 0 4.243 0 5.121.879C21 6.757 21 8.172 21 11v5c0 2.828 0 4.243-.879 5.121C19.243 22 17.828 22 15 22h-3c-2.828 0-4.243 0-5.121-.879C6 20.243 6 18.828 6 16z"
|
|
458
|
-
/>
|
|
496
|
+
d="M6 11c0-2.828 0-4.243.879-5.121C7.757 5 9.172 5 12 5h3c2.828 0 4.243 0 5.121.879C21 6.757 21 8.172 21 11v5c0 2.828 0 4.243-.879 5.121C19.243 22 17.828 22 15 22h-3c-2.828 0-4.243 0-5.121-.879C6 20.243 6 18.828 6 16z" />
|
|
459
497
|
<path d="M6 19a3 3 0 0 1-3-3v-6c0-3.771 0-5.657 1.172-6.828S7.229 2 11 2h4a3 3 0 0 1 3 3" />
|
|
460
498
|
</g>
|
|
461
499
|
</svg>
|
|
@@ -477,8 +515,7 @@
|
|
|
477
515
|
style="background: color-mix(in srgb, var(--yellow-color) 30%, var(--back-color)); transform: translateX(-50%); left: {tooltip.x +
|
|
478
516
|
10}px; top: {tooltip.y + 10}px;"
|
|
479
517
|
transition:fly={{ y: 10, duration: 200 }}
|
|
480
|
-
role="tooltip"
|
|
481
|
-
>
|
|
518
|
+
role="tooltip">
|
|
482
519
|
{@html tooltip.text}
|
|
483
520
|
</div>
|
|
484
521
|
{/if}
|