poe-svelte-ui-lib 1.0.6 → 1.0.7
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 +51 -40
- package/dist/Accordion/AccordionProps.svelte +76 -0
- package/dist/Accordion/AccordionProps.svelte.d.ts +10 -0
- package/dist/Button/Button.svelte +28 -34
- package/dist/Button/ButtonProps.svelte +113 -0
- package/dist/Button/ButtonProps.svelte.d.ts +10 -0
- package/dist/ColorPicker/ColorPicker.svelte +27 -14
- package/dist/ColorPicker/ColorPickerProps.svelte +71 -0
- package/dist/ColorPicker/ColorPickerProps.svelte.d.ts +10 -0
- package/dist/{FileAttach/FileAttach.svelte → FileAttach.svelte} +3 -11
- package/dist/{FileAttach/FileAttach.svelte.d.ts → FileAttach.svelte.d.ts} +1 -1
- package/dist/Graph/Graph.svelte +3 -3
- package/dist/Graph/GraphProps.svelte +41 -0
- package/dist/Graph/GraphProps.svelte.d.ts +10 -0
- package/dist/Input/Input.svelte +42 -48
- package/dist/Input/InputProps.svelte +205 -0
- package/dist/Input/InputProps.svelte.d.ts +10 -0
- package/dist/Modal.svelte +54 -0
- package/dist/Modal.svelte.d.ts +12 -0
- package/dist/ProgressBar/ProgressBar.svelte +23 -21
- package/dist/ProgressBar/ProgressBarProps.svelte +114 -0
- package/dist/ProgressBar/ProgressBarProps.svelte.d.ts +10 -0
- package/dist/Select/Select.svelte +38 -23
- package/dist/Select/SelectProps.svelte +216 -0
- package/dist/Select/SelectProps.svelte.d.ts +10 -0
- package/dist/Slider/Slider.svelte +17 -10
- package/dist/Slider/SliderProps.svelte +113 -0
- package/dist/Slider/SliderProps.svelte.d.ts +10 -0
- package/dist/Switch/Switch.svelte +15 -10
- package/dist/Switch/SwitchProps.svelte +99 -0
- package/dist/Switch/SwitchProps.svelte.d.ts +10 -0
- package/dist/Table/Table.svelte +62 -38
- package/dist/Table/Table.svelte.d.ts +1 -1
- package/dist/Table/TableProps.svelte +233 -0
- package/dist/Table/TableProps.svelte.d.ts +10 -0
- package/dist/TextField/TextField.svelte +15 -9
- package/dist/TextField/TextFieldProps.svelte +44 -44
- package/dist/TextField/TextFieldProps.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/libIcons/ButtonAdd.svelte +5 -2
- package/dist/libIcons/ButtonDelete.svelte +1 -1
- package/dist/libIcons/CrossIcon.svelte +9 -0
- package/dist/libIcons/CrossIcon.svelte.d.ts +18 -0
- package/dist/locales/translations.js +81 -6
- package/dist/options.d.ts +7 -12
- package/dist/options.js +44 -33
- package/dist/types.d.ts +50 -89
- package/dist/types.js +13 -1
- package/package.json +7 -3
- package/dist/Loader.svelte +0 -12
- package/dist/Loader.svelte.d.ts +0 -5
- package/dist/MessageModal.svelte +0 -54
- package/dist/MessageModal.svelte.d.ts +0 -10
|
@@ -17,6 +17,6 @@ interface FileInputProps {
|
|
|
17
17
|
currentImage?: string | null;
|
|
18
18
|
onChange?: (event: Event, file: File | null) => void;
|
|
19
19
|
}
|
|
20
|
-
declare const FileAttach: import("svelte").Component<FileInputProps, {}, "">;
|
|
20
|
+
declare const FileAttach: import("svelte").Component<FileInputProps, {}, "currentImage">;
|
|
21
21
|
type FileAttach = ReturnType<typeof FileAttach>;
|
|
22
22
|
export default FileAttach;
|
package/dist/Graph/Graph.svelte
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
/* Инициализация пропсов с дефолтными значениями */
|
|
8
8
|
let {
|
|
9
|
-
id =
|
|
9
|
+
id = crypto.randomUUID(),
|
|
10
10
|
wrapperClass = '',
|
|
11
11
|
label = { name: '', class: '' },
|
|
12
12
|
streamingData = { data: [], timestamp: Date.now() },
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
return {
|
|
64
64
|
id: existingData?.id || crypto.randomUUID(),
|
|
65
65
|
points: existingData?.points || [],
|
|
66
|
-
color:
|
|
66
|
+
color: defaultColors[i % defaultColors.length],
|
|
67
67
|
name: d.name || `Value ${i}`,
|
|
68
68
|
}
|
|
69
69
|
})
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
}
|
|
220
220
|
</script>
|
|
221
221
|
|
|
222
|
-
<div
|
|
222
|
+
<div {id} class={`relative flex w-full flex-col items-center justify-center ${wrapperClass}`}>
|
|
223
223
|
{#if label.name}
|
|
224
224
|
<h5 class={`w-full px-4 text-center ${label.class}`}>{label.name}</h5>
|
|
225
225
|
{/if}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!-- $lib/ElementsUI/SwitchProps.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { getContext } from 'svelte'
|
|
4
|
+
import { t } from '../locales/i18n'
|
|
5
|
+
import { type UIComponent, type IGraphProps, updateProperty } from '../types'
|
|
6
|
+
import * as UI from '..'
|
|
7
|
+
|
|
8
|
+
const { component, onPropertyChange } = $props<{
|
|
9
|
+
component: UIComponent & { properties: Partial<IGraphProps> }
|
|
10
|
+
onPropertyChange: (value: string | object) => void
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const DeviceVariables = getContext<{ value: string; name: string }[]>('DeviceVariables')
|
|
14
|
+
let VARIABLE_OPTIONS = $derived(
|
|
15
|
+
DeviceVariables && Array.isArray(DeviceVariables)
|
|
16
|
+
? DeviceVariables.map((variable) => ({
|
|
17
|
+
id: variable.name,
|
|
18
|
+
value: variable.value,
|
|
19
|
+
name: `${variable.value} | ${variable.name}`,
|
|
20
|
+
}))
|
|
21
|
+
: [],
|
|
22
|
+
)
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
{#if component && component.properties}
|
|
26
|
+
<div class="relative flex flex-row items-start justify-center">
|
|
27
|
+
<!-- Сообщение для отправки в ws по нажатию кнопки -->
|
|
28
|
+
<div class="flex w-1/3 flex-col items-center px-2">
|
|
29
|
+
<UI.Select
|
|
30
|
+
label={{ name: $t('constructor.props.variable') }}
|
|
31
|
+
options={VARIABLE_OPTIONS}
|
|
32
|
+
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
|
|
33
|
+
onUpdate={(value) => {
|
|
34
|
+
updateProperty('id', value.value as string, component, onPropertyChange)
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="flex w-1/3 flex-col px-2"></div>
|
|
39
|
+
<div class="flex w-1/3 flex-col px-2"></div>
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type UIComponent, type IGraphProps } from '../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
component: UIComponent & {
|
|
4
|
+
properties: Partial<IGraphProps>;
|
|
5
|
+
};
|
|
6
|
+
onPropertyChange: (value: string | object) => void;
|
|
7
|
+
};
|
|
8
|
+
declare const GraphProps: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type GraphProps = ReturnType<typeof GraphProps>;
|
|
10
|
+
export default GraphProps;
|
package/dist/Input/Input.svelte
CHANGED
|
@@ -3,23 +3,22 @@
|
|
|
3
3
|
import { onMount } from 'svelte'
|
|
4
4
|
import { fly } from 'svelte/transition'
|
|
5
5
|
import type { IInputProps } from '../types'
|
|
6
|
+
import { twMerge } from 'tailwind-merge'
|
|
6
7
|
|
|
7
8
|
let {
|
|
8
|
-
id =
|
|
9
|
+
id = crypto.randomUUID(),
|
|
9
10
|
wrapperClass = '',
|
|
10
11
|
label = { name: '', class: '' },
|
|
11
12
|
disabled = false,
|
|
12
13
|
readonly = false,
|
|
13
14
|
value = $bindable(),
|
|
14
15
|
type = 'text',
|
|
15
|
-
|
|
16
|
+
placeholder = '',
|
|
16
17
|
componentClass = '',
|
|
17
18
|
maxlength = 100,
|
|
18
|
-
number = { minNum: -1000000, maxNum: 1000000, step: 1 },
|
|
19
19
|
textareaRows = 3,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
help = { placeholder: '', info: '' },
|
|
20
|
+
number = { minNum: -1000000, maxNum: Infinity, step: 1 },
|
|
21
|
+
help = { info: '', autocomplete: 'off', copyButton: false, regExp: '^[\\s\\S]*$' },
|
|
23
22
|
onUpdate = () => {},
|
|
24
23
|
}: IInputProps = $props()
|
|
25
24
|
|
|
@@ -27,20 +26,6 @@
|
|
|
27
26
|
let showInfo = $state(false)
|
|
28
27
|
let isCopied = $state(false)
|
|
29
28
|
|
|
30
|
-
/* Закрытие INFO при клике вне компонента */
|
|
31
|
-
onMount(() => {
|
|
32
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
33
|
-
const target = event.target as HTMLElement
|
|
34
|
-
if (!target.closest('.info-container') && !target.closest('.button-info')) {
|
|
35
|
-
showInfo = false
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
window.addEventListener('click', handleClickOutside)
|
|
39
|
-
return () => {
|
|
40
|
-
window.removeEventListener('click', handleClickOutside)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
|
|
44
29
|
$effect(() => {
|
|
45
30
|
if (type === 'number') {
|
|
46
31
|
if (value === undefined || value === null || value === '') value = number.minNum
|
|
@@ -53,7 +38,7 @@
|
|
|
53
38
|
const match = pattern.match(/^\/(.*)\/([gimsuy]*)$/)
|
|
54
39
|
return match ? new RegExp(match[1], match[2]) : new RegExp(pattern)
|
|
55
40
|
}
|
|
56
|
-
let RegExpObj = $derived(() => parseRegExp(regExp))
|
|
41
|
+
let RegExpObj = $derived(() => parseRegExp(help.regExp ?? ''))
|
|
57
42
|
let isValid = $derived(RegExpObj().test(typeof value === 'string' ? value : String(value)))
|
|
58
43
|
|
|
59
44
|
const handleInputChange = (value: string | number) => {
|
|
@@ -67,28 +52,30 @@
|
|
|
67
52
|
}
|
|
68
53
|
</script>
|
|
69
54
|
|
|
70
|
-
<div class=
|
|
55
|
+
<div class={twMerge(`bg-max ${type === 'text-area' ? 'h-full' : ''} relative flex w-full flex-col items-center`, wrapperClass)}>
|
|
71
56
|
{#if label.name}
|
|
72
|
-
<h5 class={`w-full px-4 text-center
|
|
57
|
+
<h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
73
58
|
{/if}
|
|
74
59
|
|
|
75
60
|
<div class="relative flex w-full items-center {type === 'text-area' ? 'h-full' : ''}">
|
|
76
61
|
{#if type === 'text' || type === 'password' || type === 'number'}
|
|
77
62
|
<input
|
|
78
63
|
bind:value
|
|
79
|
-
class=
|
|
64
|
+
class={twMerge(
|
|
65
|
+
`w-full rounded-2xl border px-4 py-1 text-center transition-all duration-300 outline-none focus:border-blue-400
|
|
80
66
|
[&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
|
|
81
|
-
{isValid ? 'border-[var(--border-color)]' : '!border-red-400 shadow-[0_0_6px_var(--red-color)]'}
|
|
82
|
-
{disabled ? 'opacity-50' : 'hover:shadow-md'}
|
|
83
|
-
{readonly ? '' : 'hover:shadow-md'}
|
|
84
|
-
{help?.info ? 'pl-8' : ''}
|
|
85
|
-
{copyButton || type === 'password' || type === 'number' ? 'pr-8' : ''}
|
|
86
|
-
|
|
67
|
+
${isValid ? 'border-[var(--border-color)]' : '!border-red-400 shadow-[0_0_6px_var(--red-color)]'}
|
|
68
|
+
${disabled ? 'opacity-50' : 'hover:shadow-md'}
|
|
69
|
+
${readonly ? '' : 'hover:shadow-md'}
|
|
70
|
+
${help?.info ? 'pl-8' : ''}
|
|
71
|
+
${help.copyButton || type === 'password' || type === 'number' ? 'pr-8' : ''}`,
|
|
72
|
+
componentClass,
|
|
73
|
+
)}
|
|
87
74
|
style="background: color-mix(in srgb, var(--bg-color), var(--back-color) 70%);"
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
{id}
|
|
76
|
+
{placeholder}
|
|
90
77
|
{disabled}
|
|
91
|
-
{autocomplete}
|
|
78
|
+
autocomplete={help?.autocomplete}
|
|
92
79
|
oninput={(e) => handleInputChange((e.currentTarget as HTMLInputElement).value)}
|
|
93
80
|
type={type === 'password' ? (showPassword ? 'text' : 'password') : type === 'number' ? 'number' : 'text'}
|
|
94
81
|
{maxlength}
|
|
@@ -100,19 +87,21 @@
|
|
|
100
87
|
{:else if type === 'text-area'}
|
|
101
88
|
<textarea
|
|
102
89
|
bind:value
|
|
103
|
-
class=
|
|
104
|
-
|
|
105
|
-
{
|
|
106
|
-
{
|
|
107
|
-
{
|
|
108
|
-
{
|
|
109
|
-
{
|
|
90
|
+
class={twMerge(
|
|
91
|
+
`h-full w-full resize-y rounded-2xl border border-[var(--border-color)] px-2 py-1 text-center font-mono transition-all duration-300 outline-none focus:border-blue-400
|
|
92
|
+
${isValid ? 'border-[var(--border-color)]' : '!border-red-400 shadow-[0_0_6px_var(--red-color)]'}
|
|
93
|
+
${disabled ? 'opacity-50' : 'hover:shadow-md'}
|
|
94
|
+
${readonly ? '' : 'hover:shadow-md'}
|
|
95
|
+
${help?.info ? 'pl-8' : ''}
|
|
96
|
+
${help.copyButton ? 'pr-8' : ''}`,
|
|
97
|
+
componentClass,
|
|
98
|
+
)}
|
|
110
99
|
style="background: color-mix(in srgb, var(--bg-color), var(--back-color) 70%);"
|
|
111
|
-
|
|
100
|
+
{id}
|
|
112
101
|
{disabled}
|
|
113
102
|
{maxlength}
|
|
114
103
|
rows={textareaRows}
|
|
115
|
-
|
|
104
|
+
{placeholder}
|
|
116
105
|
{readonly}
|
|
117
106
|
oninput={(e) => handleInputChange((e.currentTarget as HTMLTextAreaElement).value)}
|
|
118
107
|
></textarea>
|
|
@@ -146,7 +135,7 @@
|
|
|
146
135
|
</button>
|
|
147
136
|
{/if}
|
|
148
137
|
|
|
149
|
-
{#if copyButton && (type === 'text' || type === 'text-area')}
|
|
138
|
+
{#if help.copyButton && (type === 'text' || type === 'text-area')}
|
|
150
139
|
<button
|
|
151
140
|
type="button"
|
|
152
141
|
class="absolute right-2 flex cursor-pointer border-none bg-transparent {type === 'text-area' ? 'top-2' : ''}"
|
|
@@ -215,13 +204,19 @@
|
|
|
215
204
|
{#if help.info}
|
|
216
205
|
<button
|
|
217
206
|
type="button"
|
|
218
|
-
class="button-info absolute left-2 flex border-none bg-transparent {type === 'text-area' ? 'top-2' : ''} {disabled
|
|
219
|
-
|
|
207
|
+
class="button-info absolute left-2 flex border-none bg-transparent {type === 'text-area' ? 'top-2' : ''} {disabled
|
|
208
|
+
? 'opacity-50'
|
|
209
|
+
: 'cursor-pointer'}"
|
|
210
|
+
onmouseenter={() => (showInfo = true)}
|
|
211
|
+
onmouseleave={() => (showInfo = false)}
|
|
220
212
|
aria-label={showInfo ? 'Скрыть инфо' : 'Показать инфо'}
|
|
221
213
|
>
|
|
222
214
|
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24"
|
|
223
215
|
><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
|
224
|
-
><circle cx="12" cy="12" r="10" stroke-width="1.3" /><path stroke-width="1.5" d="M12 16v-4.5" /><path
|
|
216
|
+
><circle cx="12" cy="12" r="10" stroke-width="1.3" /><path stroke-width="1.5" d="M12 16v-4.5" /><path
|
|
217
|
+
stroke-width="1.8"
|
|
218
|
+
d="M12 8.012v-.01"
|
|
219
|
+
/></g
|
|
225
220
|
></svg
|
|
226
221
|
>
|
|
227
222
|
</button>
|
|
@@ -229,8 +224,7 @@
|
|
|
229
224
|
{#if showInfo}
|
|
230
225
|
<div
|
|
231
226
|
transition:fly={{ x: -15, duration: 250 }}
|
|
232
|
-
class="
|
|
233
|
-
style="left: 2.5rem; top: 50%; transform: translateY(-50%); background: color-mix(in srgb, var(--yellow-color) 20%, var(--back-color));"
|
|
227
|
+
class="absolute top-1/2 left-10 z-50 w-auto -translate-y-1/2 rounded bg-[var(--container-color)] px-2 py-1 shadow-lg"
|
|
234
228
|
>
|
|
235
229
|
{help?.info}
|
|
236
230
|
</div>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<!-- $lib/ElementsUI/InputProps.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { getContext } from 'svelte'
|
|
4
|
+
import { t } from '../locales/i18n'
|
|
5
|
+
import type { IInputProps, UIComponent, ISelectOption } from '../types'
|
|
6
|
+
import * as UI from '..'
|
|
7
|
+
import { optionsStore } from '../options'
|
|
8
|
+
|
|
9
|
+
const { component, onPropertyChange } = $props<{
|
|
10
|
+
component: UIComponent & { properties: Partial<IInputProps> }
|
|
11
|
+
onPropertyChange: (value: string | object) => void
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
let isValidRegExp = $state(true)
|
|
15
|
+
const DeviceVariables = getContext<{ value: string; name: string }[]>('DeviceVariables')
|
|
16
|
+
let VARIABLE_OPTIONS: ISelectOption<string>[] = $derived(
|
|
17
|
+
DeviceVariables && Array.isArray(DeviceVariables)
|
|
18
|
+
? DeviceVariables.map((variable) => ({
|
|
19
|
+
id: variable.name,
|
|
20
|
+
value: variable.value,
|
|
21
|
+
name: `${variable.value} | ${variable.name}`,
|
|
22
|
+
}))
|
|
23
|
+
: [],
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const initialColor = $derived(
|
|
27
|
+
$optionsStore.COLOR_OPTIONS.find((c) =>
|
|
28
|
+
(c.value as string).includes(component.properties.componentClass?.split(' ').find((cls: string) => cls.startsWith('bg-'))),
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const initialAlign = $derived(
|
|
33
|
+
$optionsStore.ALIGN_OPTIONS.find((a) =>
|
|
34
|
+
(a.value as string).includes(component.properties.label?.class?.split(' ').find((cls: string) => cls.startsWith('text-'))),
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
/* Обновление свойства */
|
|
39
|
+
const updateProperty = (path: string, value: string | object | boolean | number | RegExp) => {
|
|
40
|
+
const newProperties = JSON.parse(JSON.stringify(component.properties))
|
|
41
|
+
if (path === 'regExp') {
|
|
42
|
+
try {
|
|
43
|
+
let regex: RegExp
|
|
44
|
+
if (typeof value === 'string') {
|
|
45
|
+
const pattern = value.match(/^\/(.*)\/([gimsuy]*)$/)
|
|
46
|
+
|
|
47
|
+
regex = pattern ? new RegExp(pattern[1], pattern[2]) : new RegExp(value)
|
|
48
|
+
if (pattern === null) return
|
|
49
|
+
regex.test('')
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error('Invalid RegExp type')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
newProperties.regExp = regex
|
|
55
|
+
isValidRegExp = true
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.warn('Invalid RegExp:', error)
|
|
58
|
+
newProperties.regExp = typeof value === 'string' ? value : String(value)
|
|
59
|
+
isValidRegExp = false
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const parts = path.split('.')
|
|
65
|
+
let obj = newProperties
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
68
|
+
const part = parts[i]
|
|
69
|
+
if (!obj[part]) obj[part] = {}
|
|
70
|
+
obj = obj[part]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
obj[parts[parts.length - 1]] = value
|
|
74
|
+
onPropertyChange(newProperties)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const handleOptionColorChange = (color: string) => {
|
|
78
|
+
let componentClass = component.properties.componentClass || ''
|
|
79
|
+
|
|
80
|
+
componentClass = componentClass
|
|
81
|
+
.split(' ')
|
|
82
|
+
.filter((cls: string) => !cls.startsWith('bg-'))
|
|
83
|
+
.join(' ')
|
|
84
|
+
|
|
85
|
+
if (color) {
|
|
86
|
+
componentClass += ` ${color}`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
updateProperty('componentClass', componentClass)
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
{#if component && component.properties}
|
|
94
|
+
<div class="relative flex flex-row items-start justify-center">
|
|
95
|
+
<!-- Сообщение для отправки в ws по нажатию кнопки -->
|
|
96
|
+
<div class="flex w-1/3 flex-col items-center px-2">
|
|
97
|
+
<UI.Select
|
|
98
|
+
label={{ name: $t('constructor.props.variable') }}
|
|
99
|
+
options={VARIABLE_OPTIONS}
|
|
100
|
+
value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
|
|
101
|
+
onUpdate={(selectedOption) => {
|
|
102
|
+
if (selectedOption && selectedOption.name) {
|
|
103
|
+
updateProperty('id', selectedOption.value as string)
|
|
104
|
+
updateProperty('eventHandler.Variables', selectedOption.value as string)
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
<UI.Select
|
|
109
|
+
label={{ name: $t('constructor.props.type') }}
|
|
110
|
+
options={$optionsStore.INPUT_TYPE_OPTIONS}
|
|
111
|
+
type="buttons"
|
|
112
|
+
value={$optionsStore.INPUT_TYPE_OPTIONS.find((opt) => opt.value === (component.properties.type || 'text'))}
|
|
113
|
+
onUpdate={(selectedOption) => updateProperty('type', selectedOption.value as string)}
|
|
114
|
+
/>
|
|
115
|
+
{#if component.properties.type === 'text' || component.properties.type === 'password' || component.properties.type === 'text-area'}
|
|
116
|
+
<UI.Input
|
|
117
|
+
label={{ name: $t('constructor.props.maxlenght') }}
|
|
118
|
+
value={component.properties.maxlength}
|
|
119
|
+
onUpdate={(value) => updateProperty('maxlength', value as string)}
|
|
120
|
+
/>
|
|
121
|
+
<UI.Input
|
|
122
|
+
label={{ name: $t('constructor.props.regexp') }}
|
|
123
|
+
value={component.properties.help.regExp}
|
|
124
|
+
maxlength={150}
|
|
125
|
+
help={{ info: $t('constructor.props.regexp.info') }}
|
|
126
|
+
componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
|
|
127
|
+
onUpdate={(value) => updateProperty('help.regExp', value)}
|
|
128
|
+
/>
|
|
129
|
+
{:else if component.properties.type === 'number' && !component.properties.readonly && !component.properties.disabled}
|
|
130
|
+
<UI.Input
|
|
131
|
+
label={{ name: $t('constructor.props.minnum') }}
|
|
132
|
+
value={component.properties.number.minNum as number}
|
|
133
|
+
type="number"
|
|
134
|
+
onUpdate={(value) => {
|
|
135
|
+
// if ((value as number) >= component.properties.number.maxNum) {
|
|
136
|
+
// value = component.properties.number.maxNum - component.properties.number.step
|
|
137
|
+
// }
|
|
138
|
+
updateProperty('number.minNum', Number(value))
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
<UI.Input
|
|
142
|
+
label={{ name: $t('constructor.props.maxnum') }}
|
|
143
|
+
value={component.properties.number.maxNum as number}
|
|
144
|
+
type="number"
|
|
145
|
+
onUpdate={(value) => {
|
|
146
|
+
// if ((value as number) <= component.properties.number.minNum) {
|
|
147
|
+
// value = component.properties.number.minNum + component.properties.number.step
|
|
148
|
+
// }
|
|
149
|
+
updateProperty('number.maxNum', Number(value))
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
<UI.Input
|
|
153
|
+
label={{ name: $t('constructor.props.step') }}
|
|
154
|
+
value={component.properties.number.step as number}
|
|
155
|
+
type="number"
|
|
156
|
+
onUpdate={(value) => updateProperty('number.step', Number(value))}
|
|
157
|
+
/>
|
|
158
|
+
{/if}
|
|
159
|
+
</div>
|
|
160
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
161
|
+
<UI.Input
|
|
162
|
+
label={{ name: $t('constructor.props.placeholder') }}
|
|
163
|
+
value={component.properties.placeholder as string}
|
|
164
|
+
onUpdate={(value) => updateProperty('placeholder', value)}
|
|
165
|
+
/>
|
|
166
|
+
<UI.Input
|
|
167
|
+
label={{ name: $t('constructor.props.info') }}
|
|
168
|
+
value={component.properties.help.info as string}
|
|
169
|
+
onUpdate={(value) => updateProperty('help.info', value)}
|
|
170
|
+
/>
|
|
171
|
+
<UI.Switch
|
|
172
|
+
label={{ name: $t('constructor.props.readonly') }}
|
|
173
|
+
value={component.properties.readonly ? 2 : 1}
|
|
174
|
+
onChange={(value) => updateProperty('readonly', value === 2)}
|
|
175
|
+
/>
|
|
176
|
+
<UI.Switch
|
|
177
|
+
label={{ name: $t('constructor.props.copy') }}
|
|
178
|
+
value={component.properties.help.copyButton ? 2 : 1}
|
|
179
|
+
onChange={(value) => updateProperty('help.copyButton', value === 2)}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="flex w-1/3 flex-col px-2">
|
|
183
|
+
<UI.Input
|
|
184
|
+
label={{ name: $t('constructor.props.label') }}
|
|
185
|
+
value={component.properties.label.name}
|
|
186
|
+
onUpdate={(value) => updateProperty('label.name', value as string)}
|
|
187
|
+
/>
|
|
188
|
+
<UI.Select
|
|
189
|
+
label={{ name: $t('constructor.props.align') }}
|
|
190
|
+
type="buttons"
|
|
191
|
+
value={initialAlign}
|
|
192
|
+
options={$optionsStore.ALIGN_OPTIONS}
|
|
193
|
+
onUpdate={(option) => updateProperty('label.class', `${component.properties.label.class} ${option.value}`)}
|
|
194
|
+
/>
|
|
195
|
+
<UI.Select
|
|
196
|
+
wrapperClass="h-14"
|
|
197
|
+
label={{ name: $t('constructor.props.colors') }}
|
|
198
|
+
type="buttons"
|
|
199
|
+
options={$optionsStore.COLOR_OPTIONS}
|
|
200
|
+
value={initialColor}
|
|
201
|
+
onUpdate={(option) => handleOptionColorChange(option.value as string)}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IInputProps, UIComponent } from '../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
component: UIComponent & {
|
|
4
|
+
properties: Partial<IInputProps>;
|
|
5
|
+
};
|
|
6
|
+
onPropertyChange: (value: string | object) => void;
|
|
7
|
+
};
|
|
8
|
+
declare const InputProps: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type InputProps = ReturnType<typeof InputProps>;
|
|
10
|
+
export default InputProps;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, type Snippet } from 'svelte'
|
|
3
|
+
import { fade, scale } from 'svelte/transition'
|
|
4
|
+
import { twMerge } from 'tailwind-merge'
|
|
5
|
+
import CrossIcon from './libIcons/CrossIcon.svelte'
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
isOpen = $bindable(false),
|
|
9
|
+
title,
|
|
10
|
+
wrapperClass = '',
|
|
11
|
+
main,
|
|
12
|
+
footer,
|
|
13
|
+
onCancel = () => (isOpen = false),
|
|
14
|
+
}: {
|
|
15
|
+
isOpen?: boolean
|
|
16
|
+
title?: string
|
|
17
|
+
wrapperClass?: string
|
|
18
|
+
main: Snippet
|
|
19
|
+
footer?: Snippet
|
|
20
|
+
onCancel?: () => void
|
|
21
|
+
} = $props()
|
|
22
|
+
|
|
23
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
24
|
+
if (event.key === 'Escape') isOpen = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
onMount(() => {
|
|
28
|
+
document.addEventListener('keydown', handleKeyDown)
|
|
29
|
+
return () => document.removeEventListener('keydown', handleKeyDown)
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#if isOpen}
|
|
34
|
+
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" transition:fade={{ duration: 200 }}>
|
|
35
|
+
<div
|
|
36
|
+
class={twMerge(`flex w-300 flex-col overflow-hidden rounded-2xl bg-[var(--back-color)] text-center`, wrapperClass)}
|
|
37
|
+
transition:scale={{ duration: 250, start: 0.8 }}
|
|
38
|
+
>
|
|
39
|
+
<div class="flex items-end justify-between bg-[var(--field-color)] px-6 py-3">
|
|
40
|
+
<h4>{title}</h4>
|
|
41
|
+
<button class="h-6 w-6 cursor-pointer" onclick={onCancel}> <CrossIcon /> </button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="flex h-full w-full flex-col overflow-auto p-2">
|
|
45
|
+
{@render main?.()}
|
|
46
|
+
</div>
|
|
47
|
+
{#if footer}
|
|
48
|
+
<div class="flex flex-row-reverse justify-between bg-[var(--field-color)] p-1.5">
|
|
49
|
+
{@render footer?.()}
|
|
50
|
+
</div>
|
|
51
|
+
{/if}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
isOpen?: boolean;
|
|
4
|
+
title?: string;
|
|
5
|
+
wrapperClass?: string;
|
|
6
|
+
main: Snippet;
|
|
7
|
+
footer?: Snippet;
|
|
8
|
+
onCancel?: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare const Modal: import("svelte").Component<$$ComponentProps, {}, "isOpen">;
|
|
11
|
+
type Modal = ReturnType<typeof Modal>;
|
|
12
|
+
export default Modal;
|
|
@@ -1,48 +1,50 @@
|
|
|
1
1
|
<!-- $lib/ElementsUI/ProgressBar.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
4
|
import type { IProgressBarProps } from '../types'
|
|
4
5
|
|
|
5
6
|
let {
|
|
6
|
-
id =
|
|
7
|
+
id = crypto.randomUUID(),
|
|
7
8
|
label = { name: '', class: '' },
|
|
8
9
|
value = $bindable(0),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
number = {
|
|
11
|
+
minNum: 0,
|
|
12
|
+
maxNum: 100,
|
|
12
13
|
units: '%',
|
|
13
14
|
},
|
|
14
15
|
wrapperClass = '',
|
|
15
16
|
}: IProgressBarProps = $props()
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const max = $derived(range.max ?? 100)
|
|
18
|
+
const min = $derived(number.minNum ?? 0)
|
|
19
|
+
const max = $derived(number.maxNum ?? 100)
|
|
20
20
|
|
|
21
|
-
$
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
let numericValue = $derived(
|
|
22
|
+
(() => {
|
|
23
|
+
if (typeof value === 'number' && !isNaN(value)) {
|
|
24
|
+
return Math.max(min, Math.min(max, value))
|
|
25
|
+
} else if (typeof value === 'string') {
|
|
26
|
+
const parsedValue = parseFloat(value)
|
|
27
|
+
if (!isNaN(parsedValue)) {
|
|
28
|
+
return Math.max(min, Math.min(max, parsedValue))
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
return min
|
|
28
32
|
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
+
})(),
|
|
34
|
+
)
|
|
33
35
|
|
|
34
36
|
let progressPercent = $derived((((value as number) - min) / (max - min)) * 100)
|
|
35
37
|
</script>
|
|
36
38
|
|
|
37
|
-
<div
|
|
39
|
+
<div {id} class={twMerge(`relative flex w-full flex-col items-center`, wrapperClass)}>
|
|
38
40
|
{#if label.name}
|
|
39
|
-
<h5 class={`mb-1 w-full px-4 text-center
|
|
41
|
+
<h5 class={twMerge(`mb-1 w-full px-4 text-center`, label.class)}>{label.name}</h5>
|
|
40
42
|
{/if}
|
|
41
43
|
|
|
42
44
|
<div class="flex w-full flex-col items-center">
|
|
43
45
|
<div class="relative h-2 w-full rounded bg-gray-400">
|
|
44
46
|
<div class="absolute top-0 left-0 h-full rounded bg-[var(--bg-color)]" style="width: {progressPercent}%;"></div>
|
|
45
47
|
</div>
|
|
46
|
-
<span class="ml-2 font-semibold">{numericValue
|
|
48
|
+
<span class="ml-2 font-semibold">{numericValue?.toFixed(2)}{number.units}</span>
|
|
47
49
|
</div>
|
|
48
50
|
</div>
|