poe-svelte-ui-lib 0.2.0 → 1.0.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/Accordion/Accordion.svelte +53 -0
- package/dist/Accordion/Accordion.svelte.d.ts +4 -0
- package/dist/Accordion/AccordionProps.svelte +70 -0
- package/dist/Accordion/AccordionProps.svelte.d.ts +10 -0
- package/dist/{Button.svelte → Button/Button.svelte} +43 -24
- package/dist/{Button.svelte.d.ts → Button/Button.svelte.d.ts} +5 -5
- package/dist/Button/ButtonProps.svelte +200 -0
- package/dist/Button/ButtonProps.svelte.d.ts +10 -0
- package/dist/ColorPicker/ColorPicker.svelte +207 -0
- package/dist/ColorPicker/ColorPicker.svelte.d.ts +4 -0
- package/dist/ColorPicker/ColorPickerProps.svelte +100 -0
- package/dist/ColorPicker/ColorPickerProps.svelte.d.ts +10 -0
- package/dist/FileAttach/FileAttach.svelte +103 -0
- package/dist/FileAttach/FileAttach.svelte.d.ts +22 -0
- package/dist/Graph/Graph.svelte +270 -0
- package/dist/Graph/Graph.svelte.d.ts +4 -0
- package/dist/Graph/GraphProps.svelte +56 -0
- package/dist/Graph/GraphProps.svelte.d.ts +10 -0
- package/dist/Input/Input.svelte +239 -0
- package/dist/Input/Input.svelte.d.ts +4 -0
- package/dist/Input/InputProps.svelte +221 -0
- package/dist/Input/InputProps.svelte.d.ts +10 -0
- package/dist/Loader.svelte +12 -0
- package/dist/Loader.svelte.d.ts +5 -0
- package/dist/MessageModal.svelte +54 -0
- package/dist/MessageModal.svelte.d.ts +10 -0
- package/dist/ProgressBar/ProgressBar.svelte +48 -0
- package/dist/ProgressBar/ProgressBar.svelte.d.ts +4 -0
- package/dist/ProgressBar/ProgressBarProps.svelte +145 -0
- package/dist/ProgressBar/ProgressBarProps.svelte.d.ts +10 -0
- package/dist/Select/Select.svelte +187 -0
- package/dist/Select/Select.svelte.d.ts +18 -0
- package/dist/Select/SelectProps.svelte +260 -0
- package/dist/Select/SelectProps.svelte.d.ts +10 -0
- package/dist/Slider/Slider.svelte +260 -0
- package/dist/Slider/Slider.svelte.d.ts +4 -0
- package/dist/Slider/SliderProps.svelte +161 -0
- package/dist/Slider/SliderProps.svelte.d.ts +10 -0
- package/dist/Switch/Switch.svelte +83 -0
- package/dist/Switch/Switch.svelte.d.ts +4 -0
- package/dist/Switch/SwitchProps.svelte +144 -0
- package/dist/Switch/SwitchProps.svelte.d.ts +10 -0
- package/dist/Table/Table.svelte +276 -0
- package/dist/Table/Table.svelte.d.ts +4 -0
- package/dist/Table/TableProps.svelte +286 -0
- package/dist/Table/TableProps.svelte.d.ts +10 -0
- package/dist/TextField/TextField.svelte +22 -0
- package/dist/TextField/TextField.svelte.d.ts +4 -0
- package/dist/TextField/TextFieldProps.svelte +92 -0
- package/dist/TextField/TextFieldProps.svelte.d.ts +10 -0
- package/dist/appIcons/ButtonAdd.svelte +10 -0
- package/dist/appIcons/ButtonAdd.svelte.d.ts +18 -0
- package/dist/appIcons/ButtonDelete.svelte +13 -0
- package/dist/appIcons/ButtonDelete.svelte.d.ts +18 -0
- package/dist/appIcons/LoaderRotate.svelte +9 -0
- package/dist/appIcons/LoaderRotate.svelte.d.ts +18 -0
- package/dist/index.d.ts +26 -1
- package/dist/index.js +27 -2
- package/dist/locales/CircleFlagsEn.svelte +14 -0
- package/dist/locales/CircleFlagsEn.svelte.d.ts +26 -0
- package/dist/locales/CircleFlagsRu.svelte +8 -0
- package/dist/locales/CircleFlagsRu.svelte.d.ts +26 -0
- package/dist/locales/CircleFlagsZh.svelte +8 -0
- package/dist/locales/CircleFlagsZh.svelte.d.ts +26 -0
- package/dist/locales/i18n.d.ts +10 -0
- package/dist/locales/i18n.js +36 -0
- package/dist/locales/translations.d.ts +7 -0
- package/dist/locales/translations.js +450 -0
- package/dist/options.d.ts +78 -0
- package/dist/options.js +71 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.js +1 -0
- package/package.json +28 -21
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<!-- $lib/ElementsUI/Table.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { t } from '../locales/i18n'
|
|
4
|
+
import { get } from 'svelte/store'
|
|
5
|
+
import type { ITableHeader, ITableProps } from '../types'
|
|
6
|
+
import { fly } from 'svelte/transition'
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
id = { value: crypto.randomUUID(), name: '' },
|
|
10
|
+
wrapperClass = 'bg-blue',
|
|
11
|
+
label = { name: '', class: '' },
|
|
12
|
+
tableBody = [],
|
|
13
|
+
tableHeader = [],
|
|
14
|
+
cursor = null,
|
|
15
|
+
loader,
|
|
16
|
+
getData = () => {},
|
|
17
|
+
showInfo = true,
|
|
18
|
+
modalData = $bindable({ isOpen: false, rawData: '', formattedData: '' }),
|
|
19
|
+
onClick,
|
|
20
|
+
}: ITableProps<any> = $props()
|
|
21
|
+
|
|
22
|
+
/* Сортировка */
|
|
23
|
+
let sortState: {
|
|
24
|
+
key: string | null
|
|
25
|
+
direction: 'asc' | 'desc' | null
|
|
26
|
+
} = {
|
|
27
|
+
key: null,
|
|
28
|
+
direction: null,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Сортировка столбцов */
|
|
32
|
+
const sortRows = (key: string) => {
|
|
33
|
+
if (sortState.key === key) {
|
|
34
|
+
sortState.direction = sortState.direction === 'asc' ? 'desc' : 'asc'
|
|
35
|
+
} else {
|
|
36
|
+
sortState.key = key
|
|
37
|
+
sortState.direction = 'asc'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
tableBody = [...tableBody].sort((a, b) => {
|
|
41
|
+
const aValue = a[key]
|
|
42
|
+
const bValue = b[key]
|
|
43
|
+
if (typeof aValue === 'number' && typeof bValue === 'number') {
|
|
44
|
+
return sortState.direction === 'asc' ? aValue - bValue : bValue - aValue
|
|
45
|
+
}
|
|
46
|
+
if (aValue instanceof Date && bValue instanceof Date) {
|
|
47
|
+
return sortState.direction === 'asc' ? aValue.getTime() - bValue.getTime() : bValue.getTime() - aValue.getTime()
|
|
48
|
+
}
|
|
49
|
+
const strA = String(aValue).toLowerCase()
|
|
50
|
+
const strB = String(bValue).toLowerCase()
|
|
51
|
+
const numA = strA.match(/\d+/g)?.[0] || ''
|
|
52
|
+
const numB = strB.match(/\d+/g)?.[0] || ''
|
|
53
|
+
if (numA && numB) {
|
|
54
|
+
const numCompare = parseInt(numA, 10) - parseInt(numB, 10)
|
|
55
|
+
if (numCompare !== 0) {
|
|
56
|
+
return sortState.direction === 'asc' ? numCompare : -numCompare
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const stringCompare = strA.localeCompare(strB)
|
|
60
|
+
return sortState.direction === 'asc' ? stringCompare : -stringCompare
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Запрос данных, если в конце списка */
|
|
65
|
+
let container: HTMLElement | null = $state(null)
|
|
66
|
+
function handleScroll() {
|
|
67
|
+
if (!container) return
|
|
68
|
+
const { scrollTop, clientHeight, scrollHeight } = container
|
|
69
|
+
if (scrollTop + clientHeight >= scrollHeight - 50 && cursor !== null && loader && !get(loader)) {
|
|
70
|
+
getData()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buttonClick(row: any, button: any) {
|
|
75
|
+
if (button.onClick) button.onClick(row)
|
|
76
|
+
else if (button.eventHandler && onClick) {
|
|
77
|
+
let value: Record<string, boolean | string | number | number[] | object | null> = {}
|
|
78
|
+
button.eventHandler.Variables.forEach((v: string) => {
|
|
79
|
+
value[v] = row[v]
|
|
80
|
+
})
|
|
81
|
+
button.eventHandler.Value = JSON.stringify(value)
|
|
82
|
+
onClick(button.eventHandler)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let copiedCell = $state({ x: '', y: -1 })
|
|
87
|
+
let tooltip = $state({
|
|
88
|
+
show: false,
|
|
89
|
+
text: '',
|
|
90
|
+
x: 0,
|
|
91
|
+
y: 0,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const showModal = async (text: string, formatting?: (text: string) => string) => {
|
|
95
|
+
modalData = {
|
|
96
|
+
isOpen: !modalData.isOpen,
|
|
97
|
+
rawData: text,
|
|
98
|
+
formattedData: formatting ? formatting(text) : (text ?? ''),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const showTooltip = (event: MouseEvent, text: string, formatting?: (text: string) => string) => {
|
|
103
|
+
tooltip = {
|
|
104
|
+
show: true,
|
|
105
|
+
text: formatting ? formatting(text) : (text ?? ''),
|
|
106
|
+
x: event.clientX,
|
|
107
|
+
y: event.clientY,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const hideTooltip = () => {
|
|
112
|
+
tooltip.show = false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Для работы этой проверки в описании столбцов таблицы нужно явно указать что строка будет пустая при отсутствии иконки в БД -
|
|
116
|
+
src: (row) => (row.icon ? `data:image/png;base64,${row.icon}` : '') */
|
|
117
|
+
const hasImage = (column: ITableHeader<any>, row: any): boolean => {
|
|
118
|
+
const src = typeof column.image?.src === 'function' ? column.image.src(row) : column.image?.src
|
|
119
|
+
return !!src
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<div id={id.value} class={`flex h-full w-full flex-col overflow-hidden ${wrapperClass}`}>
|
|
124
|
+
{#if label.name}
|
|
125
|
+
<h5 class={`w-full px-4 text-center ${label.class}`}>{label.name}</h5>
|
|
126
|
+
{/if}
|
|
127
|
+
|
|
128
|
+
<div class="flex h-full flex-col overflow-hidden rounded-xl border-[var(--border-color)]">
|
|
129
|
+
<!-- Table Header -->
|
|
130
|
+
<div class="grid font-semibold" style={`grid-template-columns: ${tableHeader.map((c) => c.width || 'minmax(0, 1fr)').join(' ')};`}>
|
|
131
|
+
{#each tableHeader as column (column)}
|
|
132
|
+
<div class="justify-center bg-[var(--bg-color)] p-2 text-center">
|
|
133
|
+
<div class="flex items-center justify-start gap-2">
|
|
134
|
+
<span>{column.label}</span>
|
|
135
|
+
{#if column.sortable}
|
|
136
|
+
<button
|
|
137
|
+
class="cursor-pointer font-bold transition-transform duration-75 hover:scale-110 active:scale-95"
|
|
138
|
+
onclick={() => sortRows(column.key as string)}
|
|
139
|
+
>
|
|
140
|
+
↑↓
|
|
141
|
+
</button>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
{/each}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- Table Body с прокруткой -->
|
|
149
|
+
<div class="flex-1 overflow-y-auto bg-[var(--conteiner-color)]/50" bind:this={container} onscroll={handleScroll}>
|
|
150
|
+
<div class="grid min-w-0" style={`grid-template-columns: ${tableHeader.map((c) => c.width || 'minmax(0, 1fr)').join(' ')};`}>
|
|
151
|
+
{#each tableBody as row, index (row)}
|
|
152
|
+
{#each tableHeader as column (column)}
|
|
153
|
+
<div
|
|
154
|
+
class="relative flex w-full min-w-0 items-center px-2 py-1 break-words
|
|
155
|
+
{index % 2 ? '!bg-[var(--field-color)]' : ''}
|
|
156
|
+
{column.cellClass}
|
|
157
|
+
{column.align === 'center'
|
|
158
|
+
? 'flex justify-center text-center'
|
|
159
|
+
: column.align === 'right'
|
|
160
|
+
? 'flex justify-end text-right'
|
|
161
|
+
: 'flex justify-start text-left'}"
|
|
162
|
+
>
|
|
163
|
+
{#if column.buttons}
|
|
164
|
+
<div class="flex w-full flex-col gap-1">
|
|
165
|
+
{#each column.buttons as button (button)}
|
|
166
|
+
<button
|
|
167
|
+
class="
|
|
168
|
+
cursor-pointer rounded-full !bg-[var(--bg-color)] px-4 py-1 font-medium
|
|
169
|
+
transition-shadow outline-none select-none hover:shadow-md
|
|
170
|
+
{typeof button.class === 'function' ? button.class(row) : button.class}
|
|
171
|
+
"
|
|
172
|
+
onclick={() => buttonClick(row, button)}
|
|
173
|
+
>
|
|
174
|
+
{typeof button.name === 'function' ? button.name(row) : button.name}
|
|
175
|
+
</button>
|
|
176
|
+
{/each}
|
|
177
|
+
</div>
|
|
178
|
+
{:else if column.image}
|
|
179
|
+
<div class="flex items-center justify-center" style={`width: ${column.image.width || '5rem'}; height: ${column.image.height || '5rem'};`}>
|
|
180
|
+
{#if hasImage(column, row)}
|
|
181
|
+
<img
|
|
182
|
+
src={typeof column.image?.src === 'function' ? column.image.src(row) : column.image?.src || ''}
|
|
183
|
+
alt={column.image.alt ?? 'Image'}
|
|
184
|
+
class="h-full w-full object-cover {column.image.class || ''}"
|
|
185
|
+
loading="lazy"
|
|
186
|
+
/>
|
|
187
|
+
{:else if column.image.defaultIcon}
|
|
188
|
+
<column.image.defaultIcon />
|
|
189
|
+
{/if}
|
|
190
|
+
</div>
|
|
191
|
+
{:else}
|
|
192
|
+
<div
|
|
193
|
+
class="w-full max-w-full break-words {column.overflow?.truncated ? 'overflow-hidden text-ellipsis whitespace-nowrap' : 'whitespace-normal'}"
|
|
194
|
+
onmouseenter={column.overflow?.truncated ? (e) => showTooltip(e, row[column.key], column.overflow?.formatting) : undefined}
|
|
195
|
+
onmouseleave={column.overflow?.truncated ? hideTooltip : undefined}
|
|
196
|
+
onmousemove={column.overflow?.truncated
|
|
197
|
+
? (e) => {
|
|
198
|
+
tooltip.x = e.clientX
|
|
199
|
+
tooltip.y = e.clientY
|
|
200
|
+
}
|
|
201
|
+
: undefined}
|
|
202
|
+
role="columnheader"
|
|
203
|
+
tabindex={null}
|
|
204
|
+
>
|
|
205
|
+
{#if column.overflow?.modal}
|
|
206
|
+
<button
|
|
207
|
+
class="w-full cursor-pointer overflow-hidden text-left text-ellipsis whitespace-nowrap"
|
|
208
|
+
onclick={(e) => {
|
|
209
|
+
e.stopPropagation()
|
|
210
|
+
showModal(row[column.key], column.overflow?.formatting)
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{@html row[column.key]}
|
|
214
|
+
</button>
|
|
215
|
+
{:else}
|
|
216
|
+
{@html row[column.key]}
|
|
217
|
+
{/if}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{#if column.overflow?.copy}
|
|
221
|
+
<button
|
|
222
|
+
class="right-2 flex cursor-pointer border-none bg-transparent"
|
|
223
|
+
onclick={(e) => {
|
|
224
|
+
e.preventDefault()
|
|
225
|
+
navigator.clipboard.writeText(row[column.key])
|
|
226
|
+
copiedCell = { x: column.key as string, y: index }
|
|
227
|
+
setTimeout(() => (copiedCell = { x: '', y: -1 }), 1000)
|
|
228
|
+
}}
|
|
229
|
+
aria-label="Копировать текст"
|
|
230
|
+
>
|
|
231
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.3em" height="1.3em" viewBox="0 0 24 24">
|
|
232
|
+
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
233
|
+
<path
|
|
234
|
+
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"
|
|
235
|
+
/>
|
|
236
|
+
<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" />
|
|
237
|
+
</g>
|
|
238
|
+
</svg>
|
|
239
|
+
</button>
|
|
240
|
+
|
|
241
|
+
{#if copiedCell.y === index && copiedCell.x === column.key}
|
|
242
|
+
<div
|
|
243
|
+
class="absolute top-1/2 right-10 -translate-y-1/2 transform rounded-md bg-[var(--green-color)] px-2 py-1 text-sm shadow-lg"
|
|
244
|
+
transition:fly={{ x: 10, duration: 200 }}
|
|
245
|
+
>
|
|
246
|
+
{$t('component.input.copy')}
|
|
247
|
+
</div>
|
|
248
|
+
{/if}
|
|
249
|
+
{/if}
|
|
250
|
+
{/if}
|
|
251
|
+
</div>
|
|
252
|
+
{/each}
|
|
253
|
+
{/each}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
{#if tooltip.show}
|
|
258
|
+
<div
|
|
259
|
+
class="fixed z-50 max-w-xs rounded-md px-2 py-1 text-sm whitespace-pre-wrap shadow-lg"
|
|
260
|
+
style="background: color-mix(in srgb, var(--yellow-color) 30%, var(--back-color)); transform: translateX(-50%); left: {tooltip.x +
|
|
261
|
+
10}px; top: {tooltip.y + 10}px;"
|
|
262
|
+
transition:fly={{ y: 10, duration: 200 }}
|
|
263
|
+
role="tooltip"
|
|
264
|
+
>
|
|
265
|
+
{@html tooltip.text}
|
|
266
|
+
</div>
|
|
267
|
+
{/if}
|
|
268
|
+
|
|
269
|
+
<!-- Нижнее поле для сводной информации -->
|
|
270
|
+
{#if showInfo}
|
|
271
|
+
<div class="flex h-8 items-center justify-center bg-[var(--bg-color)]">
|
|
272
|
+
<h5>{$t('catalog.nums')} {tableBody.length}</h5>
|
|
273
|
+
</div>
|
|
274
|
+
{/if}
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
<!-- $lib/ElementsUI/TableProps.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { getContext } from 'svelte'
|
|
4
|
+
import { t } from '../locales/i18n'
|
|
5
|
+
import type { UIComponent, ITableProps, ITableHeader } from '../types'
|
|
6
|
+
import * as UI from '../index'
|
|
7
|
+
import ButtonDelete from '../appIcons/ButtonDelete.svelte'
|
|
8
|
+
import ButtonAdd from '../appIcons/ButtonAdd.svelte'
|
|
9
|
+
import { optionsStore } from '../options'
|
|
10
|
+
|
|
11
|
+
const { component, onPropertyChange } = $props<{
|
|
12
|
+
component: UIComponent & { properties: Partial<ITableProps<object>> }
|
|
13
|
+
onPropertyChange: (value: string | object) => void
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const DeviceVariables = getContext<{ value: string; name: string }[]>('DeviceVariables')
|
|
17
|
+
let VARIABLE_OPTIONS = $derived(
|
|
18
|
+
DeviceVariables.map((variable: { value: string; name: string }) => ({
|
|
19
|
+
id: variable.name,
|
|
20
|
+
value: variable.value,
|
|
21
|
+
name: `${variable.value} | ${variable.name}`,
|
|
22
|
+
})),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const initialColor = $derived(
|
|
26
|
+
$optionsStore.COLOR_OPTIONS.find((c) =>
|
|
27
|
+
(c.value as string).includes(component.properties.wrapperClass?.split(' ').find((cls: string) => cls.startsWith('bg-'))),
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const initialAlign = $derived(
|
|
32
|
+
$optionsStore.ALIGN_OPTIONS.find((a) =>
|
|
33
|
+
(a.value as string).includes(component.properties.label?.class?.split(' ').find((cls: string) => cls.startsWith('text-'))),
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
/* Добавление цветов через селект */
|
|
38
|
+
const handleOptionColorChange = (color: string) => {
|
|
39
|
+
let componentClass = component.properties.componentClass || ''
|
|
40
|
+
componentClass = componentClass
|
|
41
|
+
.split(' ')
|
|
42
|
+
.filter((cls: string) => !cls.startsWith('bg-'))
|
|
43
|
+
.join(' ')
|
|
44
|
+
if (color) {
|
|
45
|
+
componentClass += ` ${color}`
|
|
46
|
+
}
|
|
47
|
+
updateProperty('wrapperClass', componentClass)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleLabelAlign = (align: string) => {
|
|
51
|
+
let labelClass = component.properties.label.class || ''
|
|
52
|
+
labelClass = labelClass
|
|
53
|
+
.split(' ')
|
|
54
|
+
.filter((cls: string) => !cls.startsWith('text-'))
|
|
55
|
+
.join(' ')
|
|
56
|
+
if (align) {
|
|
57
|
+
labelClass += ` ${align}`
|
|
58
|
+
}
|
|
59
|
+
updateProperty('label.class', labelClass)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Обновление свойства */
|
|
63
|
+
const updateProperty = (path: string, value: string | object) => {
|
|
64
|
+
const newProperties = JSON.parse(JSON.stringify(component.properties))
|
|
65
|
+
const parts = path.split('.')
|
|
66
|
+
let obj = newProperties
|
|
67
|
+
|
|
68
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
69
|
+
const part = parts[i]
|
|
70
|
+
if (!obj[part]) obj[part] = {}
|
|
71
|
+
obj = obj[part]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
obj[parts[parts.length - 1]] = value
|
|
75
|
+
onPropertyChange(newProperties)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const updateTableHeader = (index: number, field: keyof ITableHeader<any>, value: any) => {
|
|
79
|
+
const headers = [...component.properties.tableHeader]
|
|
80
|
+
headers[index] = { ...headers[index], [field]: value }
|
|
81
|
+
updateProperty('tableHeader', headers)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const updateButtonProperty = (columnIndex: number, buttonIndex: number, field: string, value: any) => {
|
|
85
|
+
const headers = [...component.properties.tableHeader]
|
|
86
|
+
const buttons = [...headers[columnIndex].buttons]
|
|
87
|
+
buttons[buttonIndex] = { ...buttons[buttonIndex], [field]: value }
|
|
88
|
+
headers[columnIndex].buttons = buttons
|
|
89
|
+
updateProperty('tableHeader', headers)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const removeButtonFromColumn = (columnIndex: number, buttonIndex: number) => {
|
|
93
|
+
const headers = [...component.properties.tableHeader]
|
|
94
|
+
const buttons = [...headers[columnIndex].buttons]
|
|
95
|
+
buttons.splice(buttonIndex, 1)
|
|
96
|
+
headers[columnIndex].buttons = buttons.length ? buttons : undefined
|
|
97
|
+
updateProperty('tableHeader', headers)
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
{#if component && component.properties}
|
|
102
|
+
<div class="relative flex flex-row items-start justify-center">
|
|
103
|
+
<div class="flex w-1/3 flex-col items-center px-2">
|
|
104
|
+
<UI.Select
|
|
105
|
+
label={{ name: $t('service.constructor.props.variable') }}
|
|
106
|
+
options={VARIABLE_OPTIONS}
|
|
107
|
+
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
|
|
108
|
+
onUpdate={(value) => {
|
|
109
|
+
updateProperty('id.name', (value.name as string).split('|')[1].trim())
|
|
110
|
+
updateProperty('id.value', value.value as string)
|
|
111
|
+
updateProperty('eventHandler.Variables', value.value as string)
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
116
|
+
<UI.Select
|
|
117
|
+
wrapperClass="!h-14"
|
|
118
|
+
label={{ name: $t('service.constructor.props.colors') }}
|
|
119
|
+
type="buttons"
|
|
120
|
+
options={$optionsStore.COLOR_OPTIONS}
|
|
121
|
+
value={initialColor}
|
|
122
|
+
onUpdate={(option) => handleOptionColorChange(option.value as string)}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
126
|
+
<UI.Input
|
|
127
|
+
wrapperClass="w-full"
|
|
128
|
+
label={{ name: $t('service.constructor.props.label') }}
|
|
129
|
+
value={component.properties.label.name}
|
|
130
|
+
type="text"
|
|
131
|
+
componentClass="w-full"
|
|
132
|
+
onUpdate={(value) => updateProperty('label.name', value as string)}
|
|
133
|
+
/>
|
|
134
|
+
<UI.Select
|
|
135
|
+
wrapperClass="w-full"
|
|
136
|
+
label={{ name: $t('service.constructor.props.align') }}
|
|
137
|
+
type="buttons"
|
|
138
|
+
value={initialAlign}
|
|
139
|
+
options={$optionsStore.ALIGN_OPTIONS}
|
|
140
|
+
onUpdate={(option) => handleLabelAlign(option.value as string)}
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<hr class="border-gray-400" />
|
|
146
|
+
|
|
147
|
+
<!-- Настройки столбцов таблицы -->
|
|
148
|
+
<div class="space-y-4">
|
|
149
|
+
<div class="m-0 flex items-center justify-center gap-2">
|
|
150
|
+
<h4>{$t('service.constructor.props.table.columns')}</h4>
|
|
151
|
+
<UI.Button
|
|
152
|
+
wrapperClass="!w-10"
|
|
153
|
+
icon={{ component: ButtonAdd, properties: { height: '1.5rem', width: '1.5rem' } }}
|
|
154
|
+
componentClass="h-10 border-none hover:shadow-none"
|
|
155
|
+
onClick={() => {
|
|
156
|
+
const newColumn: ITableHeader<any> = {
|
|
157
|
+
key: `column${(component.properties.tableHeader?.length || 0) + 1}`,
|
|
158
|
+
label: `Column ${(component.properties.tableHeader?.length || 0) + 1}`,
|
|
159
|
+
width: '100px',
|
|
160
|
+
sortable: false,
|
|
161
|
+
}
|
|
162
|
+
const headers = [...(component.properties.tableHeader || []), newColumn]
|
|
163
|
+
updateProperty('tableHeader', headers)
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{#each component.properties.tableHeader as column, columnIndex}
|
|
169
|
+
<div class="m-0 flex items-end justify-around gap-2">
|
|
170
|
+
<UI.Input
|
|
171
|
+
label={{ name: $t('service.constructor.props.table.columns.key') }}
|
|
172
|
+
wrapperClass="!w-2/10"
|
|
173
|
+
value={column.key}
|
|
174
|
+
type="text"
|
|
175
|
+
regExp={/^[0-9a-zA-Z_-]{0,16}$/}
|
|
176
|
+
onUpdate={(value) => updateTableHeader(columnIndex, 'key', value)}
|
|
177
|
+
/>
|
|
178
|
+
<UI.Input
|
|
179
|
+
label={{ name: $t('service.constructor.props.table.columns.label') }}
|
|
180
|
+
wrapperClass="!w-2/10"
|
|
181
|
+
value={column.label}
|
|
182
|
+
type="text"
|
|
183
|
+
onUpdate={(value) => updateTableHeader(columnIndex, 'label', value)}
|
|
184
|
+
/>
|
|
185
|
+
<UI.Input
|
|
186
|
+
label={{ name: $t('service.constructor.props.table.columns.width') }}
|
|
187
|
+
wrapperClass="!w-2/10"
|
|
188
|
+
value={column.width}
|
|
189
|
+
type="text"
|
|
190
|
+
onUpdate={(value) => updateTableHeader(columnIndex, 'width', value)}
|
|
191
|
+
/>
|
|
192
|
+
<UI.Switch
|
|
193
|
+
wrapperClass="!w-1/10"
|
|
194
|
+
label={{ name: $t('service.constructor.props.table.columns.sortable') }}
|
|
195
|
+
value={column.sortable ? 2 : 1}
|
|
196
|
+
onChange={(value) => updateTableHeader(columnIndex, 'sortable', value === 2)}
|
|
197
|
+
/>
|
|
198
|
+
<UI.Button
|
|
199
|
+
wrapperClass="!w-1/20"
|
|
200
|
+
icon={{ component: ButtonAdd, properties: { height: '1.5rem', width: '1.5rem' } }}
|
|
201
|
+
componentClass="h-10 w-10 border-none hover:shadow-none"
|
|
202
|
+
info={$t('service.constructor.props.table.addaction')}
|
|
203
|
+
onClick={() => {
|
|
204
|
+
const newButton = {
|
|
205
|
+
name: `button${(component.properties.tableHeader[columnIndex].buttons || 0) + 1}`,
|
|
206
|
+
class: 'bg-blue',
|
|
207
|
+
eventHandler: { Header: 'SET', Argument: 'Save', Variables: [] },
|
|
208
|
+
onClick: () => {},
|
|
209
|
+
}
|
|
210
|
+
const buttons = [...(component.properties.tableHeader[columnIndex].buttons || []), newButton]
|
|
211
|
+
updateTableHeader(columnIndex, 'buttons', buttons)
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
<UI.Button
|
|
215
|
+
wrapperClass="!w-1/20"
|
|
216
|
+
icon={{ component: ButtonDelete, properties: { height: '1.5rem', width: '1.5rem' } }}
|
|
217
|
+
componentClass="h-10 w-10 border-none hover:shadow-none"
|
|
218
|
+
info={$t('service.constructor.props.table.deletecolumn')}
|
|
219
|
+
onClick={() => {
|
|
220
|
+
const headers = [...(component.properties.tableHeader || [])]
|
|
221
|
+
headers.splice(columnIndex, 1)
|
|
222
|
+
updateProperty('tableHeader', headers)
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
{#if column.buttons && column.buttons.length > 0}
|
|
227
|
+
<div class="m-2 rounded-lg bg-[var(--field-color)] p-2">
|
|
228
|
+
{#each column.buttons as button, buttonIndex}
|
|
229
|
+
<div class="flex items-end justify-around gap-2">
|
|
230
|
+
<UI.Input
|
|
231
|
+
label={{ name: $t('service.constructor.props.name') }}
|
|
232
|
+
wrapperClass="!w-2/10"
|
|
233
|
+
value={button.name}
|
|
234
|
+
type="text"
|
|
235
|
+
onUpdate={(value) => updateButtonProperty(columnIndex, buttonIndex, 'name', value)}
|
|
236
|
+
/>
|
|
237
|
+
<UI.Select
|
|
238
|
+
wrapperClass="!w-3/10"
|
|
239
|
+
label={{ name: $t('service.constructor.props.header') }}
|
|
240
|
+
type="buttons"
|
|
241
|
+
value={$optionsStore.HEADER_OPTIONS.find((h) => h.value === button.eventHandler?.Header)}
|
|
242
|
+
options={$optionsStore.HEADER_OPTIONS}
|
|
243
|
+
onUpdate={(option) => {
|
|
244
|
+
const handler = button.eventHandler
|
|
245
|
+
handler.Header = option.value as string
|
|
246
|
+
updateButtonProperty(columnIndex, buttonIndex, 'eventHandler', handler)
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
<UI.Input
|
|
250
|
+
wrapperClass="!w-2/10"
|
|
251
|
+
label={{ name: $t('service.constructor.props.argument') }}
|
|
252
|
+
value={button.eventHandler?.Argument}
|
|
253
|
+
type="text"
|
|
254
|
+
onUpdate={(value) => {
|
|
255
|
+
const handler = button.eventHandler
|
|
256
|
+
handler.Argument = value as string
|
|
257
|
+
updateButtonProperty(columnIndex, buttonIndex, 'eventHandler', handler)
|
|
258
|
+
}}
|
|
259
|
+
/>
|
|
260
|
+
<UI.Input
|
|
261
|
+
wrapperClass="!w-2/10"
|
|
262
|
+
label={{ name: $t('service.constructor.props.table.keys') }}
|
|
263
|
+
value={button.eventHandler?.Variables.join(' ')}
|
|
264
|
+
type="text"
|
|
265
|
+
maxlength={500}
|
|
266
|
+
regExp={/^[a-zA-Z0-9\-_ ]{0,500}$/}
|
|
267
|
+
help={{ info: $t('service.constructor.props.table.keys.info') }}
|
|
268
|
+
onUpdate={(value) => {
|
|
269
|
+
const handler = { ...button.eventHandler }
|
|
270
|
+
handler.Variables = (value as string).trim().split(/\s+/)
|
|
271
|
+
updateButtonProperty(columnIndex, buttonIndex, 'eventHandler', handler)
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
<UI.Button
|
|
275
|
+
wrapperClass="!w-1/20"
|
|
276
|
+
icon={{ component: ButtonDelete, properties: { height: '1.5rem', width: '1.5rem' } }}
|
|
277
|
+
componentClass="h-10 w-10 border-none hover:shadow-none"
|
|
278
|
+
onClick={() => removeButtonFromColumn(columnIndex, buttonIndex)}
|
|
279
|
+
/>
|
|
280
|
+
</div>
|
|
281
|
+
{/each}
|
|
282
|
+
</div>
|
|
283
|
+
{/if}
|
|
284
|
+
{/each}
|
|
285
|
+
</div>
|
|
286
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UIComponent, ITableProps } from '../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
component: UIComponent & {
|
|
4
|
+
properties: Partial<ITableProps<object>>;
|
|
5
|
+
};
|
|
6
|
+
onPropertyChange: (value: string | object) => void;
|
|
7
|
+
};
|
|
8
|
+
declare const TableProps: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type TableProps = ReturnType<typeof TableProps>;
|
|
10
|
+
export default TableProps;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ITextFieldProps } from '../types'
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
id = { value: crypto.randomUUID(), name: '' },
|
|
6
|
+
wrapperClass = '',
|
|
7
|
+
label = { name: '', class: '' },
|
|
8
|
+
type = 'small',
|
|
9
|
+
bold = false,
|
|
10
|
+
italic = false,
|
|
11
|
+
}: ITextFieldProps = $props()
|
|
12
|
+
|
|
13
|
+
const textSize = {
|
|
14
|
+
small: 'text-base',
|
|
15
|
+
medium: 'text-xl',
|
|
16
|
+
xlarge: 'text-2xl',
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div id={id.value} class="relative flex w-full flex-col items-center {wrapperClass}">
|
|
21
|
+
<p class="w-full text-center {label.class} {textSize[type]} {bold ? 'font-bold' : ''} {italic ? 'italic' : ''}">{label.name}</p>
|
|
22
|
+
</div>
|