poe-svelte-ui-lib 1.4.7 → 1.5.0
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/FileAttach/FileAttach.svelte +1 -1
- package/dist/FileAttach/FileAttachProps.svelte +0 -9
- package/dist/Input/Input.svelte +2 -2
- package/dist/Input/InputProps.svelte +6 -2
- package/dist/Select/Select.svelte +3 -3
- package/dist/Slider/Slider.svelte +2 -2
- package/dist/Table/Table.svelte +229 -109
- package/dist/Table/Table.svelte.d.ts +1 -1
- package/dist/Table/TableProps.svelte +31 -16
- package/dist/Tabs/Tabs.svelte +1 -1
- package/dist/locales/translations.js +5 -0
- package/dist/options.d.ts +5 -0
- package/dist/options.js +4 -0
- package/dist/types.d.ts +10 -10
- package/package.json +3 -3
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
onChange = () => {},
|
|
16
16
|
}: IFileAttachProps = $props()
|
|
17
17
|
|
|
18
|
-
let ID = `${id}-${crypto.randomUUID().slice(0, 6)}`
|
|
18
|
+
let ID = $derived(`${id}-${crypto.randomUUID().slice(0, 6)}`)
|
|
19
19
|
let selectedFile = $state<File | null>(null)
|
|
20
20
|
let previewUrl = $derived(currentImage ? (currentImage.startsWith('data:') ? currentImage : `data:image/png;base64,${currentImage}`) : null)
|
|
21
21
|
let fileName = $state('')
|
|
@@ -31,15 +31,6 @@
|
|
|
31
31
|
<div class="relative flex flex-row items-start justify-center">
|
|
32
32
|
<!-- Сообщение для отправки в ws по нажатию кнопки -->
|
|
33
33
|
<div class="flex w-1/3 flex-col items-center px-2">
|
|
34
|
-
<UI.Select
|
|
35
|
-
label={{ name: $t('constructor.props.variable') }}
|
|
36
|
-
options={VARIABLE_OPTIONS}
|
|
37
|
-
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id)}
|
|
38
|
-
onUpdate={(value) => {
|
|
39
|
-
updateProperty('id', value.value as string, component, onPropertyChange)
|
|
40
|
-
onPropertyChange({ name: value.name?.split('—')[1].trim(), eventHandler: { Variables: value.value as string } })
|
|
41
|
-
}}
|
|
42
|
-
/>
|
|
43
34
|
<UI.Select
|
|
44
35
|
label={{ name: $t('constructor.props.access') }}
|
|
45
36
|
type="buttons"
|
package/dist/Input/Input.svelte
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
class={twMerge(
|
|
67
67
|
`w-full rounded-2xl border px-4 py-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition duration-200
|
|
68
68
|
outline-none focus:shadow-[0_0_6px_var(--blue-color)] focus:border-(--blue-color) [&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
|
|
69
|
-
${isValid ? 'border-(--bg-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)] focus:border-red-400'}
|
|
69
|
+
${isValid ? 'border-(--bg-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)] focus:shadow-[0_0_6px_var(--red-color)] focus:border-red-400'}
|
|
70
70
|
${disabled ? 'opacity-50' : 'hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}
|
|
71
71
|
${readonly ? '' : 'hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}
|
|
72
72
|
${help?.info ? 'pl-8' : ''}
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
class={twMerge(
|
|
93
93
|
`h-full w-full resize-y rounded-2xl border border-(--border-color) px-2 py-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition
|
|
94
94
|
duration-200 outline-none focus:border-blue-400
|
|
95
|
-
${isValid ? 'border-(--bg-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)]'}
|
|
95
|
+
${isValid ? 'border-(--bg-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)] focus:shadow-[0_0_6px_var(--red-color)] focus:border-red-400'}
|
|
96
96
|
${disabled ? 'cursor-not-allowed opacity-50' : 'hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}
|
|
97
97
|
${readonly ? '' : 'hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}
|
|
98
98
|
${help?.info ? 'pl-8' : ''}
|
|
@@ -112,7 +112,11 @@
|
|
|
112
112
|
options={$optionsStore.INPUT_TYPE_OPTIONS}
|
|
113
113
|
type="buttons"
|
|
114
114
|
value={$optionsStore.INPUT_TYPE_OPTIONS.find((opt) => opt.value === (component.properties.type || 'text'))}
|
|
115
|
-
onUpdate={(selectedOption) =>
|
|
115
|
+
onUpdate={(selectedOption) => {
|
|
116
|
+
updateProperty('type', selectedOption.value as string)
|
|
117
|
+
if (selectedOption.value === 'text-area') updateProperty('componentClass', twMerge(component.properties.componentClass, 'font-mono'))
|
|
118
|
+
else updateProperty('componentClass', twMerge(component.properties.componentClass, 'font-[Montserrat]'))
|
|
119
|
+
}}
|
|
116
120
|
/>
|
|
117
121
|
{#if component.properties.type === 'text' || component.properties.type === 'password' || component.properties.type === 'text-area'}
|
|
118
122
|
<UI.Input
|
|
@@ -235,7 +239,7 @@
|
|
|
235
239
|
<UI.Input
|
|
236
240
|
label={{ name: $t('constructor.props.componentclass') }}
|
|
237
241
|
value={component.properties.componentClass}
|
|
238
|
-
onUpdate={(value) => updateProperty('componentClass', value as string)}
|
|
242
|
+
onUpdate={(value) => updateProperty('componentClass', twMerge(component.properties.componentClass, value as string))}
|
|
239
243
|
/>
|
|
240
244
|
<UI.Select
|
|
241
245
|
wrapperClass="h-14"
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
100
100
|
value={value?.value ? String(value.value) : ''}
|
|
101
101
|
class={twMerge(
|
|
102
|
-
`w-full rounded-2xl border border-(--bg-color) p-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition
|
|
102
|
+
`w-full rounded-2xl border border-(--bg-color) p-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition duration-200
|
|
103
103
|
${disabled ? 'opacity-50' : 'cursor-pointer hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}`,
|
|
104
104
|
value?.class,
|
|
105
105
|
)}
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
<button
|
|
144
144
|
id={option.id}
|
|
145
145
|
class="{twMerge(
|
|
146
|
-
`m-0 inline-block min-w-0 flex-1 items-center px-2 py-1 font-semibold shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition
|
|
146
|
+
`m-0 inline-block min-w-0 flex-1 items-center px-2 py-1 font-semibold shadow-[0_0_3px_rgb(0_0_0_/0.25)] transition duration-300 select-none border border-(--bg-color)
|
|
147
147
|
${option.disabled || disabled ? 'opacity-50' : 'cursor-pointer hover:shadow-[0_0_6px_rgb(0_0_0_/0.25)]'}
|
|
148
148
|
${option.value === value?.value && value !== null ? 'z-10 py-1 shadow-[0_0_10px_var(--shadow-color)] hover:shadow-[0_0_15px_var(--shadow-color)]' : ''}
|
|
149
149
|
${options.length > 0 && index === 0 ? 'rounded-l-2xl' : ''} ${index === options.length - 1 ? 'rounded-r-2xl' : ''}`,
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
<input
|
|
167
167
|
bind:value={searchValue}
|
|
168
168
|
class="w-full appearance-none rounded-2xl border px-4 py-1 text-center shadow-[0_0_3px_rgb(0_0_0_/0.25)]
|
|
169
|
-
transition
|
|
169
|
+
transition duration-200 outline-none focus:shadow-[0_0_6px_var(--blue-color)]
|
|
170
170
|
[&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
|
|
171
171
|
{disabled
|
|
172
172
|
? 'cursor-not-allowed opacity-50'
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
const isRange = $derived(type === 'range' || (Array.isArray(value) && value.length === 2))
|
|
19
19
|
|
|
20
|
-
const maxDigits = String(number.maxNum ?? 100).length
|
|
21
|
-
const valueWidth = `${maxDigits + 1}ch` /* +1 на запас */
|
|
20
|
+
const maxDigits = $derived(String(number.maxNum ?? 100).length)
|
|
21
|
+
const valueWidth = $derived(`${maxDigits + 1}ch`) /* +1 на запас */
|
|
22
22
|
|
|
23
23
|
/* Инициализация значений с проверкой типа */
|
|
24
24
|
let singleValue = $derived(!isRange && typeof value === 'number' ? value : number.minNum)
|
package/dist/Table/Table.svelte
CHANGED
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
id = crypto.randomUUID(),
|
|
11
11
|
wrapperClass = '',
|
|
12
12
|
label = { name: '', class: '' },
|
|
13
|
-
body =
|
|
13
|
+
body = $bindable(),
|
|
14
14
|
header = [],
|
|
15
15
|
footer = '',
|
|
16
|
+
stashData = false,
|
|
17
|
+
type = 'table',
|
|
18
|
+
rowsAmmount = 10,
|
|
16
19
|
outline = false,
|
|
17
20
|
cursor = null,
|
|
18
21
|
loader,
|
|
@@ -22,6 +25,8 @@
|
|
|
22
25
|
onClick,
|
|
23
26
|
}: ITableProps<any> = $props()
|
|
24
27
|
|
|
28
|
+
let dataBuffer: any[] = $state([])
|
|
29
|
+
|
|
25
30
|
/* Сортировка */
|
|
26
31
|
let sortState: {
|
|
27
32
|
key: string | null
|
|
@@ -33,6 +38,13 @@
|
|
|
33
38
|
|
|
34
39
|
let isAutoscroll = $state(false)
|
|
35
40
|
|
|
41
|
+
const logTypeOptions = [
|
|
42
|
+
{ id: crypto.randomUUID(), name: 'Error', value: 'error', color: 'bg-(--red-color)' },
|
|
43
|
+
{ id: crypto.randomUUID(), name: 'Warning', value: 'warning', color: 'bg-(--yellow-color)' },
|
|
44
|
+
{ id: crypto.randomUUID(), name: 'Info', value: 'info', color: 'bg-(--gray-color)' },
|
|
45
|
+
]
|
|
46
|
+
let logType = $state(['error', 'info'])
|
|
47
|
+
|
|
36
48
|
/* Сортировка столбцов */
|
|
37
49
|
const sortRows = (key: string) => {
|
|
38
50
|
if (sortState.key === key) {
|
|
@@ -87,8 +99,9 @@
|
|
|
87
99
|
container.scrollTop = container.scrollHeight
|
|
88
100
|
}
|
|
89
101
|
}
|
|
102
|
+
|
|
90
103
|
$effect(() => {
|
|
91
|
-
if (
|
|
104
|
+
if (autoscroll && dataBuffer && dataBuffer.length > 0) {
|
|
92
105
|
scrollToBottom()
|
|
93
106
|
}
|
|
94
107
|
})
|
|
@@ -141,12 +154,78 @@
|
|
|
141
154
|
return !!src
|
|
142
155
|
}
|
|
143
156
|
|
|
157
|
+
$effect(() => {
|
|
158
|
+
if (body && type == 'logger') {
|
|
159
|
+
dataBuffer = [
|
|
160
|
+
...dataBuffer,
|
|
161
|
+
{
|
|
162
|
+
type: Object.entries(body)[0][1] as string,
|
|
163
|
+
color: `<div class='size-6 rounded-full ${logTypeOptions.find((o) => o.value == body.logLevel)?.color}'></div>`,
|
|
164
|
+
data: Object.entries(body)[1][1] as string,
|
|
165
|
+
},
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
if (dataBuffer.length > rowsAmmount * 5) {
|
|
169
|
+
dataBuffer = dataBuffer.slice(-(rowsAmmount * 5))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
body = null
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
$effect(() => {
|
|
177
|
+
if (body && stashData && type == 'table') {
|
|
178
|
+
dataBuffer = [...dataBuffer, body]
|
|
179
|
+
if (dataBuffer.length > rowsAmmount) {
|
|
180
|
+
dataBuffer = dataBuffer.slice(-rowsAmmount)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
body = null
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
$effect(() => {
|
|
188
|
+
const currentType = type
|
|
189
|
+
if (type === 'logger') {
|
|
190
|
+
header = [
|
|
191
|
+
{
|
|
192
|
+
key: 'color',
|
|
193
|
+
label: { name: 'Type' },
|
|
194
|
+
width: '3rem',
|
|
195
|
+
} as ITableHeader<any>,
|
|
196
|
+
{
|
|
197
|
+
key: 'data',
|
|
198
|
+
label: { name: 'Data' },
|
|
199
|
+
width: 'calc(100% - 3rem)',
|
|
200
|
+
} as ITableHeader<any>,
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
return () => {
|
|
204
|
+
dataBuffer = []
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
|
|
144
208
|
onMount(() => {
|
|
145
209
|
if (autoscroll) {
|
|
146
210
|
container?.addEventListener('scroll', handleAutoScroll)
|
|
147
211
|
scrollToBottom()
|
|
148
212
|
}
|
|
149
213
|
|
|
214
|
+
if (type === 'logger') {
|
|
215
|
+
header = [
|
|
216
|
+
{
|
|
217
|
+
key: 'color',
|
|
218
|
+
label: { name: 'Type' },
|
|
219
|
+
width: '3rem',
|
|
220
|
+
} as ITableHeader<any>,
|
|
221
|
+
{
|
|
222
|
+
key: 'data',
|
|
223
|
+
label: { name: 'Data' },
|
|
224
|
+
width: 'calc(100% - 3rem)',
|
|
225
|
+
} as ITableHeader<any>,
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
|
|
150
229
|
return () => {
|
|
151
230
|
if (autoscroll) {
|
|
152
231
|
container?.removeEventListener('scroll', handleAutoScroll)
|
|
@@ -155,13 +234,51 @@
|
|
|
155
234
|
})
|
|
156
235
|
</script>
|
|
157
236
|
|
|
158
|
-
<div
|
|
237
|
+
<div
|
|
238
|
+
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
239
|
+
class={twMerge(`bg-blue flex h-full w-full gap-2 items-center flex-col overflow-hidden`, wrapperClass)}
|
|
240
|
+
>
|
|
159
241
|
{#if label.name}
|
|
160
242
|
<h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
161
243
|
{/if}
|
|
162
244
|
|
|
245
|
+
{#if type == 'logger'}
|
|
246
|
+
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="flex w-[50%] justify-center rounded-full">
|
|
247
|
+
{#each logTypeOptions as option, index}
|
|
248
|
+
<button
|
|
249
|
+
id={crypto.randomUUID()}
|
|
250
|
+
class={twMerge(`m-0 inline-block min-w-0 flex-1 cursor-pointer items-center px-2 py-1 font-semibold shadow-sm transition-all duration-300
|
|
251
|
+
select-none hover:shadow-md
|
|
252
|
+
${
|
|
253
|
+
logType.includes(option.value) && logType !== null
|
|
254
|
+
? 'z-10 py-1 shadow-[0_0_10px_var(--shadow-color)] hover:shadow-[0_0_15px_var(--shadow-color)]'
|
|
255
|
+
: ''
|
|
256
|
+
}
|
|
257
|
+
${logTypeOptions.length > 0 && index === 0 ? 'rounded-l-2xl' : ''} ${
|
|
258
|
+
index === logTypeOptions.length - 1 ? 'rounded-r-2xl' : ''
|
|
259
|
+
} ${option.color}`)}
|
|
260
|
+
onclick={() => {
|
|
261
|
+
if (logType.includes(option.value)) {
|
|
262
|
+
logType = logType.filter((type) => type !== option.value)
|
|
263
|
+
} else {
|
|
264
|
+
logType.push(option.value)
|
|
265
|
+
}
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
<span class="flex flex-row items-center justify-center gap-4">
|
|
269
|
+
{#if option}
|
|
270
|
+
<div class="flex-1">
|
|
271
|
+
{option.name}
|
|
272
|
+
</div>
|
|
273
|
+
{/if}
|
|
274
|
+
</span>
|
|
275
|
+
</button>
|
|
276
|
+
{/each}
|
|
277
|
+
</div>
|
|
278
|
+
{/if}
|
|
279
|
+
|
|
163
280
|
<div
|
|
164
|
-
class="flex h-full flex-col overflow-hidden rounded-xl border shadow-sm transition duration-200 hover:shadow-md {outline
|
|
281
|
+
class="flex h-full w-full flex-col overflow-hidden rounded-xl border shadow-sm transition duration-200 hover:shadow-md {outline
|
|
165
282
|
? ' border-(--border-color)'
|
|
166
283
|
: 'border-transparent'} "
|
|
167
284
|
>
|
|
@@ -171,9 +288,9 @@
|
|
|
171
288
|
<div
|
|
172
289
|
class={twMerge(
|
|
173
290
|
`items-center justify-center border-l ${outline && index !== 0 ? ' border-(--border-color)' : 'border-transparent'} ${
|
|
174
|
-
column.align
|
|
291
|
+
column.align === 'center'
|
|
175
292
|
? 'flex justify-center text-center'
|
|
176
|
-
: column.align
|
|
293
|
+
: column.align === 'right'
|
|
177
294
|
? 'flex justify-end text-right'
|
|
178
295
|
: 'flex justify-start text-left'
|
|
179
296
|
} gap-1 bg-(--bg-color) p-2 text-left`,
|
|
@@ -193,126 +310,129 @@
|
|
|
193
310
|
{/each}
|
|
194
311
|
</div>
|
|
195
312
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
313
|
+
{#if body || dataBuffer}
|
|
314
|
+
{@const rows = type == 'logger' ? dataBuffer.filter((str) => logType.includes(str.type)).slice(-rowsAmmount) : stashData ? dataBuffer : body}
|
|
315
|
+
<!-- Table Body с прокруткой -->
|
|
316
|
+
<div class="flex-1 overflow-y-auto bg-(--container-color)/50" bind:this={container} onscroll={handleScroll}>
|
|
317
|
+
<div class="grid min-w-0" style={`grid-template-columns: ${header.map((c) => c.width || 'minmax(0, 1fr)').join(' ')};`}>
|
|
318
|
+
{#each rows as row, index (row)}
|
|
319
|
+
{#each header as column, j (column)}
|
|
320
|
+
<div
|
|
321
|
+
class="relative flex w-full min-w-0 items-center px-2 py-1 wrap-break-word
|
|
203
322
|
{index % 2 ? 'bg-(--back-color)/40' : 'bg-[#edeef3] dark:bg-[#1f2a3a]'}
|
|
204
|
-
{column.align
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
323
|
+
{column.align === 'center'
|
|
324
|
+
? 'flex justify-center text-center'
|
|
325
|
+
: column.align === 'right'
|
|
326
|
+
? 'flex justify-end text-right'
|
|
327
|
+
: 'flex justify-start text-left'}
|
|
209
328
|
border-t
|
|
210
329
|
{j !== 0 ? ' border-l ' : ''}
|
|
211
330
|
{outline ? 'border-(--border-color)' : 'border-transparent'} "
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
331
|
+
>
|
|
332
|
+
{#if column.buttons}
|
|
333
|
+
<div class="flex w-full flex-col gap-1">
|
|
334
|
+
{#each column.buttons as button (button)}
|
|
335
|
+
<button
|
|
336
|
+
class="{twMerge(`cursor-pointer rounded-full
|
|
218
337
|
px-4 py-1 font-semibold shadow-sm transition-shadow duration-200 outline-none select-none hover:shadow-md
|
|
219
338
|
${typeof button.class === 'function' ? button.class(row) : button.class}`)} bg-(--bg-color)"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
339
|
+
onclick={() => buttonClick(row, button)}
|
|
340
|
+
>
|
|
341
|
+
{typeof button.name === 'function' ? button.name(row) : button.name}
|
|
342
|
+
</button>
|
|
343
|
+
{/each}
|
|
344
|
+
</div>
|
|
345
|
+
{:else if column.image?.src || column.image?.defaultIcon}
|
|
346
|
+
<div
|
|
347
|
+
class="flex items-center justify-center"
|
|
348
|
+
style={`width: ${column.image.width || '5rem'}; height: ${column.image.height || '5rem'};`}
|
|
349
|
+
>
|
|
350
|
+
{#if hasImage(column, row)}
|
|
351
|
+
<img
|
|
352
|
+
src={typeof column.image?.src === 'function' ? column.image.src(row) : column.image?.src || ''}
|
|
353
|
+
alt={column.image.alt ?? 'Image'}
|
|
354
|
+
class={twMerge(`h-full w-full object-cover ${column.image.class || ''}`)}
|
|
355
|
+
loading="lazy"
|
|
356
|
+
/>
|
|
357
|
+
{:else if column.image.defaultIcon}
|
|
358
|
+
{#if typeof column.image.defaultIcon === 'string'}
|
|
359
|
+
{@html column.image.defaultIcon}
|
|
360
|
+
{:else}
|
|
361
|
+
<column.image.defaultIcon />
|
|
362
|
+
{/if}
|
|
363
|
+
{/if}
|
|
364
|
+
</div>
|
|
365
|
+
{:else}
|
|
366
|
+
<div
|
|
367
|
+
class=" w-full max-w-full wrap-break-word {column.overflow?.truncated ? 'truncate' : ' whitespace-normal'}"
|
|
368
|
+
onmouseenter={column.overflow?.truncated ? (e) => showTooltip(e, row[column.key], column.overflow?.formatting) : undefined}
|
|
369
|
+
onmouseleave={column.overflow?.truncated ? hideTooltip : undefined}
|
|
370
|
+
onmousemove={column.overflow?.truncated
|
|
371
|
+
? (e) => {
|
|
372
|
+
tooltip.x = e.clientX
|
|
373
|
+
tooltip.y = e.clientY
|
|
374
|
+
}
|
|
375
|
+
: undefined}
|
|
376
|
+
role="columnheader"
|
|
377
|
+
tabindex={null}
|
|
378
|
+
>
|
|
379
|
+
{#if column.overflow?.modal}
|
|
380
|
+
<button
|
|
381
|
+
class="w-full cursor-pointer overflow-hidden text-left text-ellipsis whitespace-nowrap"
|
|
382
|
+
onclick={(e) => {
|
|
383
|
+
e.stopPropagation()
|
|
384
|
+
showModal(row[column.key], column.overflow?.formatting)
|
|
385
|
+
}}
|
|
386
|
+
>
|
|
387
|
+
{@html row[column.key]}
|
|
388
|
+
</button>
|
|
241
389
|
{:else}
|
|
242
|
-
|
|
390
|
+
{@html row[column.key]}
|
|
243
391
|
{/if}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
onmouseleave={column.overflow?.truncated ? hideTooltip : undefined}
|
|
251
|
-
onmousemove={column.overflow?.truncated
|
|
252
|
-
? (e) => {
|
|
253
|
-
tooltip.x = e.clientX
|
|
254
|
-
tooltip.y = e.clientY
|
|
255
|
-
}
|
|
256
|
-
: undefined}
|
|
257
|
-
role="columnheader"
|
|
258
|
-
tabindex={null}
|
|
259
|
-
>
|
|
260
|
-
{#if column.overflow?.modal}
|
|
392
|
+
</div>
|
|
393
|
+
<!-- {#if column.overflow?.truncated}
|
|
394
|
+
<div class="whitespace-nowrap">{row[column.key].slice(-5)}</div>
|
|
395
|
+
{/if} -->
|
|
396
|
+
|
|
397
|
+
{#if column.overflow?.copy}
|
|
261
398
|
<button
|
|
262
|
-
class="
|
|
399
|
+
class="mx-2 flex cursor-pointer border-none bg-transparent text-2xl"
|
|
263
400
|
onclick={(e) => {
|
|
264
|
-
e.
|
|
265
|
-
|
|
401
|
+
e.preventDefault()
|
|
402
|
+
navigator.clipboard.writeText(row[column.key])
|
|
403
|
+
copiedCell = { x: column.key as string, y: index }
|
|
404
|
+
setTimeout(() => (copiedCell = { x: '', y: -1 }), 1000)
|
|
266
405
|
}}
|
|
406
|
+
aria-label="Копировать текст"
|
|
267
407
|
>
|
|
268
|
-
|
|
408
|
+
<div class=" size-5 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
|
|
409
|
+
{#if copiedCell.y === index && copiedCell.x === column.key}
|
|
410
|
+
<div
|
|
411
|
+
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"
|
|
412
|
+
transition:fade={{ duration: 200 }}
|
|
413
|
+
>
|
|
414
|
+
✓
|
|
415
|
+
</div>
|
|
416
|
+
{:else}
|
|
417
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
418
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
419
|
+
<path
|
|
420
|
+
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"
|
|
421
|
+
/>
|
|
422
|
+
<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" />
|
|
423
|
+
</g>
|
|
424
|
+
</svg>
|
|
425
|
+
{/if}
|
|
426
|
+
</div>
|
|
269
427
|
</button>
|
|
270
|
-
{:else}
|
|
271
|
-
{@html row[column.key]}
|
|
272
428
|
{/if}
|
|
273
|
-
</div>
|
|
274
|
-
<!-- {#if column.overflow?.truncated}
|
|
275
|
-
<div class="whitespace-nowrap">{row[column.key].slice(-5)}</div>
|
|
276
|
-
{/if} -->
|
|
277
|
-
|
|
278
|
-
{#if column.overflow?.copy}
|
|
279
|
-
<button
|
|
280
|
-
class="mx-2 flex cursor-pointer border-none bg-transparent text-2xl"
|
|
281
|
-
onclick={(e) => {
|
|
282
|
-
e.preventDefault()
|
|
283
|
-
navigator.clipboard.writeText(row[column.key])
|
|
284
|
-
copiedCell = { x: column.key as string, y: index }
|
|
285
|
-
setTimeout(() => (copiedCell = { x: '', y: -1 }), 1000)
|
|
286
|
-
}}
|
|
287
|
-
aria-label="Копировать текст"
|
|
288
|
-
>
|
|
289
|
-
<div class=" size-5 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
|
|
290
|
-
{#if copiedCell.y === index && copiedCell.x === column.key}
|
|
291
|
-
<div
|
|
292
|
-
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"
|
|
293
|
-
transition:fade={{ duration: 200 }}
|
|
294
|
-
>
|
|
295
|
-
✓
|
|
296
|
-
</div>
|
|
297
|
-
{:else}
|
|
298
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
299
|
-
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
300
|
-
<path
|
|
301
|
-
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"
|
|
302
|
-
/>
|
|
303
|
-
<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" />
|
|
304
|
-
</g>
|
|
305
|
-
</svg>
|
|
306
|
-
{/if}
|
|
307
|
-
</div>
|
|
308
|
-
</button>
|
|
309
429
|
{/if}
|
|
310
|
-
|
|
311
|
-
|
|
430
|
+
</div>
|
|
431
|
+
{/each}
|
|
312
432
|
{/each}
|
|
313
|
-
|
|
433
|
+
</div>
|
|
314
434
|
</div>
|
|
315
|
-
|
|
435
|
+
{/if}
|
|
316
436
|
|
|
317
437
|
{#if tooltip.show}
|
|
318
438
|
<div
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ITableProps } from '../types';
|
|
2
|
-
declare const Table: import("svelte").Component<ITableProps<any>, {}, "modalData">;
|
|
2
|
+
declare const Table: import("svelte").Component<ITableProps<any>, {}, "body" | "modalData">;
|
|
3
3
|
type Table = ReturnType<typeof Table>;
|
|
4
4
|
export default Table;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
const DeviceVariables = getContext<{ id: string; value: string; name: string }[]>('DeviceVariables')
|
|
25
25
|
let VARIABLE_OPTIONS = $derived(DeviceVariables && Array.isArray(DeviceVariables) ? DeviceVariables : [])
|
|
26
26
|
|
|
27
|
-
let defaultIcon = $
|
|
27
|
+
let defaultIcon = $derived({ isModalOpen: false, columnIndex: 0, column: component.properties.header ? component.properties.header[0] : '' })
|
|
28
28
|
|
|
29
29
|
const initialColor = $derived(
|
|
30
30
|
$optionsStore.COLOR_OPTIONS.find((c) =>
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
</script>
|
|
78
78
|
|
|
79
79
|
{#if forConstructor}
|
|
80
|
-
<div class="relative flex flex-row items-start justify-center">
|
|
80
|
+
<div class="relative flex flex-row items-start justify-center pb-4">
|
|
81
81
|
<div class="flex w-1/3 flex-col px-2">
|
|
82
82
|
<UI.Select
|
|
83
83
|
label={{ name: $t('constructor.props.variable') }}
|
|
@@ -95,6 +95,16 @@
|
|
|
95
95
|
value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
|
|
96
96
|
onUpdate={(option) => onPropertyChange({ access: option.value })}
|
|
97
97
|
/>
|
|
98
|
+
<UI.Select
|
|
99
|
+
label={{ name: $t('constructor.props.table.type') }}
|
|
100
|
+
type="buttons"
|
|
101
|
+
options={$optionsStore.TABLE_TYPE_OPTIONS}
|
|
102
|
+
value={$optionsStore.TABLE_TYPE_OPTIONS.find((o) => o.value === component.properties.type)}
|
|
103
|
+
onUpdate={(option) => {
|
|
104
|
+
updateProperty('type', option.value as string, component, onPropertyChange)
|
|
105
|
+
if (option.value === 'logger') updateProperty('stashData', true, component, onPropertyChange)
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
98
108
|
</div>
|
|
99
109
|
<div class="flex w-1/3 flex-col px-2">
|
|
100
110
|
<UI.Select
|
|
@@ -111,6 +121,14 @@
|
|
|
111
121
|
options={[{ id: crypto.randomUUID(), value: 0, class: '' }]}
|
|
112
122
|
onChange={(value) => updateProperty('outline', value, component, onPropertyChange)}
|
|
113
123
|
/>
|
|
124
|
+
<UI.Switch
|
|
125
|
+
label={{ name: $t('constructor.props.table.stashData') }}
|
|
126
|
+
value={component.properties.stashData}
|
|
127
|
+
options={[{ id: crypto.randomUUID(), value: 0, class: '', disabled: component.properties.type === 'logger' }]}
|
|
128
|
+
onChange={(value) => {
|
|
129
|
+
updateProperty('stashData', value, component, onPropertyChange)
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
114
132
|
</div>
|
|
115
133
|
<div class="flex w-1/3 flex-col px-2">
|
|
116
134
|
<UI.Input
|
|
@@ -125,6 +143,12 @@
|
|
|
125
143
|
options={$optionsStore.TEXT_ALIGN_OPTIONS}
|
|
126
144
|
onUpdate={(option) => updateProperty('label.class', twMerge(component.properties.label.class, option.value), component, onPropertyChange)}
|
|
127
145
|
/>
|
|
146
|
+
<UI.Input
|
|
147
|
+
label={{ name: $t('constructor.props.table.buffersize') }}
|
|
148
|
+
type="number"
|
|
149
|
+
value={component.properties.rowsAmmount}
|
|
150
|
+
onUpdate={(value) => updateProperty('rowsAmmount', value as string, component, onPropertyChange)}
|
|
151
|
+
/>
|
|
128
152
|
</div>
|
|
129
153
|
</div>
|
|
130
154
|
|
|
@@ -183,11 +207,9 @@
|
|
|
183
207
|
<UI.Select
|
|
184
208
|
label={{ name: $t('constructor.props.align.content') }}
|
|
185
209
|
type="buttons"
|
|
186
|
-
value={$optionsStore.ALIGN_OPTIONS.find((a) => (a.value as string).includes(column.align
|
|
210
|
+
value={$optionsStore.ALIGN_OPTIONS.find((a) => (a.value as string).includes(column.align))}
|
|
187
211
|
options={$optionsStore.ALIGN_OPTIONS}
|
|
188
|
-
onUpdate={(option) =>
|
|
189
|
-
updateTableHeader(columnIndex, 'align', { header: option.value, content: option.value })
|
|
190
|
-
}}
|
|
212
|
+
onUpdate={(option) => updateTableHeader(columnIndex, 'align', option.value)}
|
|
191
213
|
/>
|
|
192
214
|
<UI.Switch
|
|
193
215
|
wrapperClass="w-30"
|
|
@@ -288,7 +310,7 @@
|
|
|
288
310
|
{/each}
|
|
289
311
|
</div>
|
|
290
312
|
{:else}
|
|
291
|
-
<div class="relative flex flex-row items-start justify-center">
|
|
313
|
+
<div class="relative flex flex-row items-start justify-center pb-4">
|
|
292
314
|
<div class="flex w-1/3 flex-col px-2">
|
|
293
315
|
<UI.Input
|
|
294
316
|
label={{ name: $t('constructor.props.id') }}
|
|
@@ -407,13 +429,6 @@
|
|
|
407
429
|
value={Number(column.width.replace('%', ''))}
|
|
408
430
|
onUpdate={(value) => updateTableHeader(columnIndex, 'width', `${value}%`)}
|
|
409
431
|
/>
|
|
410
|
-
<UI.Select
|
|
411
|
-
label={{ name: $t('constructor.props.align.header') }}
|
|
412
|
-
type="buttons"
|
|
413
|
-
value={$optionsStore.ALIGN_OPTIONS.find((a) => (a.value as string).includes(column.align?.header) || 'left')}
|
|
414
|
-
options={$optionsStore.ALIGN_OPTIONS}
|
|
415
|
-
onUpdate={(option) => updateTableHeader(columnIndex, 'align', { header: option.value, content: column.align?.content })}
|
|
416
|
-
/>
|
|
417
432
|
<UI.Switch
|
|
418
433
|
label={{ name: $t('constructor.props.table.columns.sortable'), class: 'px-0' }}
|
|
419
434
|
wrapperClass="w-30"
|
|
@@ -459,9 +474,9 @@
|
|
|
459
474
|
<UI.Select
|
|
460
475
|
label={{ name: $t('constructor.props.align.content') }}
|
|
461
476
|
type="buttons"
|
|
462
|
-
value={$optionsStore.ALIGN_OPTIONS.find((a) => (a.value as string).includes(column.align
|
|
477
|
+
value={$optionsStore.ALIGN_OPTIONS.find((a) => (a.value as string).includes(column.align))}
|
|
463
478
|
options={$optionsStore.ALIGN_OPTIONS}
|
|
464
|
-
onUpdate={(option) => updateTableHeader(columnIndex, 'align',
|
|
479
|
+
onUpdate={(option) => updateTableHeader(columnIndex, 'align', option.value)}
|
|
465
480
|
/>
|
|
466
481
|
<UI.Switch
|
|
467
482
|
wrapperClass="w-2/10"
|
package/dist/Tabs/Tabs.svelte
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
const isCol = $derived(!!items.find((item) => item.class?.startsWith('flex-col')))
|
|
25
25
|
|
|
26
|
-
let currentTabIndex: number = $
|
|
26
|
+
let currentTabIndex: number = $derived(activeTab)
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="w-full rounded-2xl bg-(--back-color)">
|
|
@@ -132,6 +132,9 @@ const translations = {
|
|
|
132
132
|
'constructor.props.joystick.axes.info': 'Поле для ввода названий осей, разделенных пробелами (2 или 3 названия)',
|
|
133
133
|
'constructor.props.file.select': 'Выберите файл',
|
|
134
134
|
'constructor.props.file.notselected': 'Файл не выбран',
|
|
135
|
+
'constructor.props.table.type': 'Тип таблицы',
|
|
136
|
+
'constructor.props.table.type.table': 'Статическая таблица',
|
|
137
|
+
'constructor.props.table.type.logger': 'Таблица для логов',
|
|
135
138
|
'constructor.props.table.columns': 'Колонки таблицы',
|
|
136
139
|
'constructor.props.table.columns.key': 'Ключ',
|
|
137
140
|
'constructor.props.table.columns.label': 'Название колонки',
|
|
@@ -148,6 +151,8 @@ const translations = {
|
|
|
148
151
|
'constructor.props.table.addaction': 'Добавить кнопку',
|
|
149
152
|
'constructor.props.table.keys': 'Перечень ключей',
|
|
150
153
|
'constructor.props.table.keys.info': 'Ключи таблицы, значения которых будут возвращаться',
|
|
154
|
+
'constructor.props.table.stashData': 'Накопление данных',
|
|
155
|
+
'constructor.props.table.buffersize': 'Размер буфера',
|
|
151
156
|
'constructor.props.icon.access': 'Доступ',
|
|
152
157
|
'constructor.props.icon.common': 'Общее',
|
|
153
158
|
'constructor.props.icon.scenarios': 'Сценарии',
|
package/dist/options.d.ts
CHANGED
|
@@ -115,6 +115,11 @@ export declare const optionsStore: import("svelte/store").Readable<{
|
|
|
115
115
|
value: string;
|
|
116
116
|
name: string;
|
|
117
117
|
}[];
|
|
118
|
+
TABLE_TYPE_OPTIONS: {
|
|
119
|
+
id: string;
|
|
120
|
+
value: string;
|
|
121
|
+
name: string;
|
|
122
|
+
}[];
|
|
118
123
|
AUTOCOMPLETE_CONSTRUCTOR_OPTIONS: {
|
|
119
124
|
id: string;
|
|
120
125
|
value: string;
|
package/dist/options.js
CHANGED
|
@@ -124,6 +124,10 @@ export const optionsStore = derived(t, ($t) => {
|
|
|
124
124
|
{ id: id(), value: 'square', name: $t('constructor.props.type.square') },
|
|
125
125
|
{ id: id(), value: 'circle', name: $t('constructor.props.type.circle') },
|
|
126
126
|
],
|
|
127
|
+
TABLE_TYPE_OPTIONS: [
|
|
128
|
+
{ id: id(), value: 'table', name: $t('constructor.props.table.type.table') },
|
|
129
|
+
{ id: id(), value: 'logger', name: $t('constructor.props.table.type.logger') },
|
|
130
|
+
],
|
|
127
131
|
AUTOCOMPLETE_CONSTRUCTOR_OPTIONS: [
|
|
128
132
|
{ id: id(), value: 'on', name: $t('constructor.props.autocomplete.on') },
|
|
129
133
|
{ id: id(), value: 'off', name: $t('constructor.props.autocomplete.off') },
|
package/dist/types.d.ts
CHANGED
|
@@ -34,10 +34,10 @@ export interface UIComponent {
|
|
|
34
34
|
eventHandler?: IUIComponentHandler;
|
|
35
35
|
}
|
|
36
36
|
export interface Position {
|
|
37
|
-
row
|
|
38
|
-
col
|
|
39
|
-
width
|
|
40
|
-
height
|
|
37
|
+
row?: number;
|
|
38
|
+
col?: number;
|
|
39
|
+
width?: number;
|
|
40
|
+
height?: number;
|
|
41
41
|
}
|
|
42
42
|
export interface IUIComponentHandler {
|
|
43
43
|
Header?: string;
|
|
@@ -233,10 +233,7 @@ export interface ITableHeader<T extends object> {
|
|
|
233
233
|
key: keyof T;
|
|
234
234
|
sortable?: boolean;
|
|
235
235
|
width?: string;
|
|
236
|
-
align?:
|
|
237
|
-
header?: 'left' | 'center' | 'right';
|
|
238
|
-
content?: 'left' | 'center' | 'right';
|
|
239
|
-
};
|
|
236
|
+
align?: 'left' | 'center' | 'right';
|
|
240
237
|
overflow?: {
|
|
241
238
|
truncated?: boolean;
|
|
242
239
|
formatting?: (text: string) => string;
|
|
@@ -265,9 +262,12 @@ export interface ITableProps<T extends object> {
|
|
|
265
262
|
name?: string;
|
|
266
263
|
class?: string;
|
|
267
264
|
};
|
|
268
|
-
header
|
|
269
|
-
body: T[];
|
|
265
|
+
header?: ITableHeader<T>[];
|
|
266
|
+
body: T[] | T | null;
|
|
270
267
|
footer?: string;
|
|
268
|
+
type?: 'table' | 'logger';
|
|
269
|
+
stashData?: boolean;
|
|
270
|
+
rowsAmmount?: number;
|
|
271
271
|
outline?: boolean;
|
|
272
272
|
cursor?: string | null;
|
|
273
273
|
loader?: Writable<boolean>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poe-svelte-ui-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
47
|
-
"@sveltejs/kit": "^2.49.
|
|
47
|
+
"@sveltejs/kit": "^2.49.1",
|
|
48
48
|
"@sveltejs/package": "^2.5.7",
|
|
49
49
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
50
50
|
"@types/node": "^24.10.1",
|
|
51
51
|
"publint": "^0.3.15",
|
|
52
|
-
"svelte": "^5.45.
|
|
52
|
+
"svelte": "^5.45.5",
|
|
53
53
|
"svelte-preprocess": "^6.0.3",
|
|
54
54
|
"vite": "^7.2.6",
|
|
55
55
|
"vite-plugin-compression": "^0.5.1"
|