poe-svelte-ui-lib 1.6.1 → 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 -118
- 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,13 +227,12 @@
|
|
|
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]]
|
|
218
234
|
}
|
|
219
235
|
} else buffer = [...buffer, body]
|
|
220
|
-
buffer = [...buffer, body]
|
|
221
236
|
if (buffer.length > (dataBuffer.rowsAmmount ?? 10)) {
|
|
222
237
|
buffer = buffer.slice(-(dataBuffer.rowsAmmount ?? 10))
|
|
223
238
|
}
|
|
@@ -228,28 +243,28 @@
|
|
|
228
243
|
|
|
229
244
|
onMount(() => {
|
|
230
245
|
if (autoscroll) {
|
|
231
|
-
container?.addEventListener(
|
|
246
|
+
container?.addEventListener("scroll", handleAutoScroll)
|
|
232
247
|
scrollToBottom()
|
|
233
248
|
}
|
|
234
249
|
|
|
235
|
-
if (type ===
|
|
250
|
+
if (type === "logger") {
|
|
236
251
|
header = [
|
|
237
252
|
{
|
|
238
|
-
key:
|
|
239
|
-
label: { name:
|
|
240
|
-
width:
|
|
253
|
+
key: "color",
|
|
254
|
+
label: { name: "Type" },
|
|
255
|
+
width: "3rem",
|
|
241
256
|
} as ITableHeader<any>,
|
|
242
257
|
{
|
|
243
|
-
key:
|
|
244
|
-
label: { name:
|
|
245
|
-
width:
|
|
258
|
+
key: "data",
|
|
259
|
+
label: { name: "Data" },
|
|
260
|
+
width: "calc(100% - 3rem)",
|
|
246
261
|
} as ITableHeader<any>,
|
|
247
262
|
]
|
|
248
263
|
}
|
|
249
264
|
|
|
250
265
|
return () => {
|
|
251
266
|
if (autoscroll) {
|
|
252
|
-
container?.removeEventListener(
|
|
267
|
+
container?.removeEventListener("scroll", handleAutoScroll)
|
|
253
268
|
}
|
|
254
269
|
}
|
|
255
270
|
})
|
|
@@ -257,13 +272,12 @@
|
|
|
257
272
|
|
|
258
273
|
<div
|
|
259
274
|
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
260
|
-
class={twMerge(`bg-blue flex h-full w-full items-center ${type ==
|
|
261
|
-
>
|
|
275
|
+
class={twMerge(`bg-blue flex h-full w-full items-center ${type == "logger" ? "gap-2" : ""} flex-col overflow-hidden`, wrapperClass)}>
|
|
262
276
|
{#if label.name}
|
|
263
277
|
<h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
264
278
|
{/if}
|
|
265
279
|
|
|
266
|
-
{#if type ==
|
|
280
|
+
{#if type == "logger"}
|
|
267
281
|
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="flex w-[50%] justify-center rounded-full">
|
|
268
282
|
{#each logTypeOptions as option, index}
|
|
269
283
|
<button
|
|
@@ -272,20 +286,17 @@
|
|
|
272
286
|
select-none hover:shadow-md
|
|
273
287
|
${
|
|
274
288
|
logType.includes(option.value) && logType !== null
|
|
275
|
-
?
|
|
276
|
-
:
|
|
289
|
+
? "z-10 py-1 shadow-[0_0_10px_var(--shadow-color)] hover:shadow-[0_0_15px_var(--shadow-color)]"
|
|
290
|
+
: ""
|
|
277
291
|
}
|
|
278
|
-
${logTypeOptions.length > 0 && index === 0 ?
|
|
279
|
-
index === logTypeOptions.length - 1 ? 'rounded-r-2xl' : ''
|
|
280
|
-
} ${option.color}`)}
|
|
292
|
+
${logTypeOptions.length > 0 && index === 0 ? "rounded-l-2xl" : ""} ${index === logTypeOptions.length - 1 ? "rounded-r-2xl" : ""} ${option.color}`)}
|
|
281
293
|
onclick={() => {
|
|
282
294
|
if (logType.includes(option.value)) {
|
|
283
|
-
logType = logType.filter(
|
|
295
|
+
logType = logType.filter(type => type !== option.value)
|
|
284
296
|
} else {
|
|
285
297
|
logType.push(option.value)
|
|
286
298
|
}
|
|
287
|
-
}}
|
|
288
|
-
>
|
|
299
|
+
}}>
|
|
289
300
|
<span class="flex flex-row items-center justify-center gap-4">
|
|
290
301
|
{#if option}
|
|
291
302
|
<div class="flex-1">
|
|
@@ -301,29 +312,26 @@
|
|
|
301
312
|
<div
|
|
302
313
|
class="relative flex h-full w-full flex-col overflow-hidden rounded-xl border shadow-sm transition duration-200 hover:shadow-md {outline
|
|
303
314
|
? ' border-(--border-color)'
|
|
304
|
-
: 'border-transparent'} "
|
|
305
|
-
>
|
|
315
|
+
: 'border-transparent'} ">
|
|
306
316
|
<!-- Table Header -->
|
|
307
|
-
<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(" ")};`}>
|
|
308
318
|
{#each header as column, index (column)}
|
|
309
319
|
<div
|
|
310
320
|
class={twMerge(
|
|
311
|
-
`items-center justify-center border-l ${outline && index !== 0 ?
|
|
312
|
-
column.align ===
|
|
313
|
-
?
|
|
314
|
-
: column.align ===
|
|
315
|
-
?
|
|
316
|
-
:
|
|
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"
|
|
317
327
|
} gap-1 bg-(--bg-color) p-2 text-left`,
|
|
318
328
|
column.label?.class,
|
|
319
|
-
)}
|
|
320
|
-
>
|
|
329
|
+
)}>
|
|
321
330
|
<span>{column.label?.name}</span>
|
|
322
331
|
{#if column.sortable}
|
|
323
332
|
<button
|
|
324
333
|
class="inline-block cursor-pointer font-bold transition-transform duration-75 hover:scale-110 active:scale-95"
|
|
325
|
-
onclick={() => sortRows(column.key as string)}
|
|
326
|
-
>
|
|
334
|
+
onclick={() => sortRows(column.key as string)}>
|
|
327
335
|
↑↓
|
|
328
336
|
</button>
|
|
329
337
|
{/if}
|
|
@@ -333,11 +341,10 @@
|
|
|
333
341
|
{#if dataBuffer.clearButton}
|
|
334
342
|
<button
|
|
335
343
|
class={twMerge(
|
|
336
|
-
|
|
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",
|
|
337
345
|
dataBuffer.clearClass,
|
|
338
346
|
)}
|
|
339
|
-
onclick={clearBuffer}
|
|
340
|
-
>
|
|
347
|
+
onclick={clearBuffer}>
|
|
341
348
|
<ButtonClear />
|
|
342
349
|
</button>
|
|
343
350
|
{/if}
|
|
@@ -345,14 +352,14 @@
|
|
|
345
352
|
{#if body || buffer}
|
|
346
353
|
{@const isSliced = buffer.length - (dataBuffer.rowsAmmount ?? 10) > 0 ? buffer.length - ((dataBuffer.rowsAmmount ?? 10) % 2) !== 0 : false}
|
|
347
354
|
{@const rows =
|
|
348
|
-
type ==
|
|
349
|
-
? buffer.filter(
|
|
355
|
+
type == "logger"
|
|
356
|
+
? buffer.filter(str => logType.includes(str.type)).slice(-(dataBuffer.rowsAmmount ?? 10))
|
|
350
357
|
: dataBuffer.stashData
|
|
351
358
|
? buffer.slice(-(dataBuffer.rowsAmmount ?? 10))
|
|
352
359
|
: body}
|
|
353
360
|
<!-- Table Body с прокруткой -->
|
|
354
361
|
<div class="flex-1 overflow-y-auto bg-(--container-color)/50" bind:this={container} onscroll={handleScroll}>
|
|
355
|
-
<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(" ")};`}>
|
|
356
363
|
{#each rows as row, index (row)}
|
|
357
364
|
{#each header as column, j (column)}
|
|
358
365
|
<div
|
|
@@ -365,35 +372,70 @@
|
|
|
365
372
|
: 'flex justify-start text-left'}
|
|
366
373
|
border-t
|
|
367
374
|
{j !== 0 ? ' border-l ' : ''}
|
|
368
|
-
{outline ? 'border-(--border-color)' : 'border-transparent'} "
|
|
369
|
-
|
|
370
|
-
{#if column.buttons}
|
|
375
|
+
{outline ? 'border-(--border-color)' : 'border-transparent'} ">
|
|
376
|
+
{#if column.action?.type == "buttons" && column.action?.buttons}
|
|
371
377
|
<div class="flex w-full flex-col gap-1">
|
|
372
|
-
{#each column.buttons as button (button)}
|
|
378
|
+
{#each column.action?.buttons as button (button)}
|
|
373
379
|
<button
|
|
374
380
|
class="{twMerge(`cursor-pointer rounded-full
|
|
375
381
|
px-4 py-1 font-semibold shadow-sm transition-shadow duration-200 outline-none select-none hover:shadow-md
|
|
376
382
|
${typeof button.class === 'function' ? button.class(row) : button.class}`)} bg-(--bg-color)"
|
|
377
|
-
onclick={() => buttonClick(row, button)}
|
|
378
|
-
|
|
379
|
-
{typeof button.name === 'function' ? button.name(row) : button.name}
|
|
383
|
+
onclick={() => buttonClick(row, button)}>
|
|
384
|
+
{typeof button.name === "function" ? button.name(row) : button.name}
|
|
380
385
|
</button>
|
|
381
386
|
{/each}
|
|
382
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>
|
|
383
429
|
{:else if column.image?.src || column.image?.defaultIcon}
|
|
384
|
-
<div
|
|
385
|
-
class="flex items-center justify-center"
|
|
386
|
-
style={`width: ${column.image.width || '5rem'}; height: ${column.image.height || '5rem'};`}
|
|
387
|
-
>
|
|
430
|
+
<div class="flex items-center justify-center" style={`width: ${column.image.width || "5rem"}; height: ${column.image.height || "5rem"};`}>
|
|
388
431
|
{#if hasImage(column, row)}
|
|
389
432
|
<img
|
|
390
|
-
src={typeof column.image?.src ===
|
|
391
|
-
alt={column.image.alt ??
|
|
392
|
-
class={twMerge(`h-full w-full object-cover ${column.image.class ||
|
|
393
|
-
loading="lazy"
|
|
394
|
-
/>
|
|
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" />
|
|
395
437
|
{:else if column.image.defaultIcon}
|
|
396
|
-
{#if typeof column.image.defaultIcon ===
|
|
438
|
+
{#if typeof column.image.defaultIcon === "string"}
|
|
397
439
|
{@html column.image.defaultIcon}
|
|
398
440
|
{:else}
|
|
399
441
|
<column.image.defaultIcon />
|
|
@@ -403,25 +445,23 @@
|
|
|
403
445
|
{:else}
|
|
404
446
|
<div
|
|
405
447
|
class=" w-full max-w-full wrap-break-word {column.overflow?.truncated ? 'truncate' : ' whitespace-normal'}"
|
|
406
|
-
onmouseenter={column.overflow?.truncated ?
|
|
448
|
+
onmouseenter={column.overflow?.truncated ? e => showTooltip(e, row[column.key], column.overflow?.formatting) : undefined}
|
|
407
449
|
onmouseleave={column.overflow?.truncated ? hideTooltip : undefined}
|
|
408
450
|
onmousemove={column.overflow?.truncated
|
|
409
|
-
?
|
|
451
|
+
? e => {
|
|
410
452
|
tooltip.x = e.clientX
|
|
411
453
|
tooltip.y = e.clientY
|
|
412
454
|
}
|
|
413
455
|
: undefined}
|
|
414
456
|
role="columnheader"
|
|
415
|
-
tabindex={null}
|
|
416
|
-
>
|
|
457
|
+
tabindex={null}>
|
|
417
458
|
{#if column.overflow?.modal}
|
|
418
459
|
<button
|
|
419
460
|
class="w-full cursor-pointer overflow-hidden text-left text-ellipsis whitespace-nowrap"
|
|
420
|
-
onclick={
|
|
461
|
+
onclick={e => {
|
|
421
462
|
e.stopPropagation()
|
|
422
463
|
showModal(row[column.key], column.overflow?.formatting)
|
|
423
|
-
}}
|
|
424
|
-
>
|
|
464
|
+
}}>
|
|
425
465
|
{@html row[column.key]}
|
|
426
466
|
</button>
|
|
427
467
|
{:else}
|
|
@@ -435,28 +475,25 @@
|
|
|
435
475
|
{#if column.overflow?.copy}
|
|
436
476
|
<button
|
|
437
477
|
class="mx-2 flex cursor-pointer border-none bg-transparent text-2xl"
|
|
438
|
-
onclick={
|
|
478
|
+
onclick={e => {
|
|
439
479
|
e.preventDefault()
|
|
440
480
|
navigator.clipboard.writeText(row[column.key])
|
|
441
481
|
copiedCell = { x: column.key as string, y: index }
|
|
442
|
-
setTimeout(() => (copiedCell = { x:
|
|
482
|
+
setTimeout(() => (copiedCell = { x: "", y: -1 }), 1000)
|
|
443
483
|
}}
|
|
444
|
-
aria-label="Копировать текст"
|
|
445
|
-
>
|
|
484
|
+
aria-label="Копировать текст">
|
|
446
485
|
<div class=" size-5 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
|
|
447
486
|
{#if copiedCell.y === index && copiedCell.x === column.key}
|
|
448
487
|
<div
|
|
449
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"
|
|
450
|
-
transition:fade={{ duration: 200 }}
|
|
451
|
-
>
|
|
489
|
+
transition:fade={{ duration: 200 }}>
|
|
452
490
|
✓
|
|
453
491
|
</div>
|
|
454
492
|
{:else}
|
|
455
493
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
456
494
|
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
457
495
|
<path
|
|
458
|
-
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
|
-
/>
|
|
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" />
|
|
460
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" />
|
|
461
498
|
</g>
|
|
462
499
|
</svg>
|
|
@@ -478,8 +515,7 @@
|
|
|
478
515
|
style="background: color-mix(in srgb, var(--yellow-color) 30%, var(--back-color)); transform: translateX(-50%); left: {tooltip.x +
|
|
479
516
|
10}px; top: {tooltip.y + 10}px;"
|
|
480
517
|
transition:fly={{ y: 10, duration: 200 }}
|
|
481
|
-
role="tooltip"
|
|
482
|
-
>
|
|
518
|
+
role="tooltip">
|
|
483
519
|
{@html tooltip.text}
|
|
484
520
|
</div>
|
|
485
521
|
{/if}
|