poe-svelte-ui-lib 1.0.0 → 1.0.2

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.
Files changed (36) hide show
  1. package/LICENSE +3 -3
  2. package/dist/Accordion/Accordion.svelte +53 -53
  3. package/dist/Accordion/AccordionProps.svelte +70 -70
  4. package/dist/Button/Button.svelte +144 -144
  5. package/dist/Button/ButtonProps.svelte +200 -200
  6. package/dist/ColorPicker/ColorPicker.svelte +207 -207
  7. package/dist/ColorPicker/ColorPickerProps.svelte +100 -100
  8. package/dist/FileAttach/FileAttach.svelte +103 -103
  9. package/dist/Graph/Graph.svelte +270 -270
  10. package/dist/Graph/GraphProps.svelte +56 -56
  11. package/dist/Input/Input.svelte +239 -239
  12. package/dist/Input/InputProps.svelte +221 -221
  13. package/dist/Loader.svelte +12 -12
  14. package/dist/MessageModal.svelte +54 -54
  15. package/dist/ProgressBar/ProgressBar.svelte +48 -48
  16. package/dist/ProgressBar/ProgressBarProps.svelte +145 -145
  17. package/dist/Select/Select.svelte +191 -187
  18. package/dist/Select/SelectProps.svelte +260 -260
  19. package/dist/Slider/Slider.svelte +260 -260
  20. package/dist/Slider/SliderProps.svelte +161 -161
  21. package/dist/Switch/Switch.svelte +83 -83
  22. package/dist/Switch/SwitchProps.svelte +144 -144
  23. package/dist/Table/Table.svelte +276 -276
  24. package/dist/Table/TableProps.svelte +286 -286
  25. package/dist/TextField/TextField.svelte +22 -22
  26. package/dist/TextField/TextFieldProps.svelte +92 -92
  27. package/dist/{appIcons → libIcons}/ButtonAdd.svelte +10 -10
  28. package/dist/{appIcons → libIcons}/ButtonDelete.svelte +13 -13
  29. package/dist/{appIcons → libIcons}/LoaderRotate.svelte +9 -9
  30. package/dist/locales/CircleFlagsEn.svelte +14 -14
  31. package/dist/locales/CircleFlagsRu.svelte +8 -8
  32. package/dist/locales/CircleFlagsZh.svelte +8 -8
  33. package/package.json +49 -47
  34. /package/dist/{appIcons → libIcons}/ButtonAdd.svelte.d.ts +0 -0
  35. /package/dist/{appIcons → libIcons}/ButtonDelete.svelte.d.ts +0 -0
  36. /package/dist/{appIcons → libIcons}/LoaderRotate.svelte.d.ts +0 -0
@@ -1,221 +1,221 @@
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 '../index'
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.map((variable: { value: string; name: string }) => ({
18
- id: variable.name,
19
- value: variable.value,
20
- name: `${variable.value} | ${variable.name}`,
21
- })),
22
- )
23
-
24
- const initialColor = $derived(
25
- $optionsStore.COLOR_OPTIONS.find((c) =>
26
- (c.value as string).includes(component.properties.componentClass?.split(' ').find((cls: string) => cls.startsWith('bg-'))),
27
- ),
28
- )
29
-
30
- const initialAlign = $derived(
31
- $optionsStore.ALIGN_OPTIONS.find((a) =>
32
- (a.value as string).includes(component.properties.label?.class?.split(' ').find((cls: string) => cls.startsWith('text-'))),
33
- ),
34
- )
35
-
36
- /* Обновление свойства */
37
- const updateProperty = (path: string, value: string | object | boolean | number | RegExp) => {
38
- const newProperties = JSON.parse(JSON.stringify(component.properties))
39
- if (path === 'regExp') {
40
- try {
41
- let regex: RegExp
42
- if (typeof value === 'string') {
43
- const pattern = value.match(/^\/(.*)\/([gimsuy]*)$/)
44
-
45
- regex = pattern ? new RegExp(pattern[1], pattern[2]) : new RegExp(value)
46
- if (pattern === null) return
47
- regex.test('')
48
- } else {
49
- throw new Error('Invalid RegExp type')
50
- }
51
-
52
- newProperties.regExp = regex
53
- isValidRegExp = true
54
- } catch (error) {
55
- console.warn('Invalid RegExp:', error)
56
- newProperties.regExp = typeof value === 'string' ? value : String(value)
57
- isValidRegExp = false
58
- return
59
- }
60
- }
61
-
62
- const parts = path.split('.')
63
- let obj = newProperties
64
-
65
- for (let i = 0; i < parts.length - 1; i++) {
66
- const part = parts[i]
67
- if (!obj[part]) obj[part] = {}
68
- obj = obj[part]
69
- }
70
-
71
- obj[parts[parts.length - 1]] = value
72
- onPropertyChange(newProperties)
73
- }
74
-
75
- const handleOptionColorChange = (color: string) => {
76
- let componentClass = component.properties.componentClass || ''
77
-
78
- componentClass = componentClass
79
- .split(' ')
80
- .filter((cls: string) => !cls.startsWith('bg-'))
81
- .join(' ')
82
-
83
- if (color) {
84
- componentClass += ` ${color}`
85
- }
86
-
87
- updateProperty('componentClass', componentClass)
88
- }
89
-
90
- const handleLabelAlign = (align: string) => {
91
- let labelClass = component.properties.label.class || ''
92
-
93
- labelClass = labelClass
94
- .split(' ')
95
- .filter((cls: string) => !cls.startsWith('text-'))
96
- .join(' ')
97
- if (align) {
98
- labelClass += ` ${align}`
99
- }
100
- updateProperty('label.class', labelClass)
101
- }
102
- </script>
103
-
104
- {#if component && component.properties}
105
- <div class="relative flex flex-row items-start justify-center">
106
- <!-- Сообщение для отправки в ws по нажатию кнопки -->
107
- <div class="flex w-1/3 flex-col items-center px-2">
108
- <UI.Select
109
- label={{ name: $t('service.constructor.props.variable') }}
110
- options={VARIABLE_OPTIONS}
111
- value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
112
- onUpdate={(selectedOption) => {
113
- if (selectedOption && selectedOption.name) {
114
- updateProperty('id.name', selectedOption.name.split('|')[1].trim())
115
- updateProperty('id.value', selectedOption.value as string)
116
- updateProperty('eventHandler.Variables', selectedOption.value as string)
117
- }
118
- }}
119
- />
120
- <UI.Select
121
- label={{ name: $t('service.constructor.props.type') }}
122
- options={$optionsStore.INPUT_TYPE_OPTIONS}
123
- value={$optionsStore.INPUT_TYPE_OPTIONS.find((opt) => opt.value === (component.properties.type || 'text'))}
124
- onUpdate={(selectedOption) => updateProperty('type', selectedOption.value as string)}
125
- />
126
- {#if component.properties.type === 'text' || component.properties.type === 'password'}
127
- <UI.Input
128
- label={{ name: $t('service.constructor.props.maxlenght') }}
129
- value={component.properties.maxlength}
130
- type="text"
131
- onUpdate={(value) => updateProperty('maxlength', value as string)}
132
- />
133
- <UI.Input
134
- label={{ name: $t('service.constructor.props.regexp') }}
135
- value={component.properties.regExp}
136
- type="text"
137
- maxlength={150}
138
- help={{ info: $t('service.constructor.props.regexp_info') }}
139
- componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
140
- onUpdate={(value) => updateProperty('regExp', value)}
141
- />
142
- {:else if component.properties.type === 'number' && !component.properties.readonly && !component.properties.disabled}
143
- <UI.Input
144
- label={{ name: $t('service.constructor.props.minnum') }}
145
- value={component.properties.number.minNum as number}
146
- type="number"
147
- onUpdate={(value) => updateProperty('number.minNum', Number(value))}
148
- />
149
- <UI.Input
150
- label={{ name: $t('service.constructor.props.maxnum') }}
151
- value={component.properties.number.maxNum as number}
152
- type="number"
153
- onUpdate={(value) => updateProperty('number.maxNum', Number(value))}
154
- />
155
- <UI.Input
156
- label={{ name: $t('service.constructor.props.step') }}
157
- value={component.properties.number.step as number}
158
- type="number"
159
- onUpdate={(value) => updateProperty('number.step', Number(value))}
160
- />
161
- {:else if component.properties.type === 'text-area'}
162
- <UI.Input
163
- label={{ name: $t('service.constructor.props.regexp') }}
164
- value={component.properties.regExp}
165
- type="text"
166
- maxlength={150}
167
- help={{ info: $t('service.constructor.props.regexp_info') }}
168
- componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
169
- onUpdate={(value) => updateProperty('regExp', value)}
170
- />
171
- {/if}
172
- </div>
173
- <div class="flex w-1/3 flex-col px-2">
174
- <UI.Input
175
- label={{ name: $t('service.constructor.props.placeholder') }}
176
- value={component.properties.help.placeholder as string}
177
- type="text"
178
- onUpdate={(value) => updateProperty('help.placeholder', value)}
179
- />
180
- <UI.Input
181
- label={{ name: $t('service.constructor.props.info') }}
182
- value={component.properties.help.info as string}
183
- type="text"
184
- onUpdate={(value) => updateProperty('help.info', value)}
185
- />
186
- <UI.Switch
187
- label={{ name: $t('service.constructor.props.readonly') }}
188
- value={component.properties.readonly ? 2 : 1}
189
- onChange={(value) => updateProperty('readonly', value === 2)}
190
- />
191
- <UI.Switch
192
- label={{ name: $t('service.constructor.props.copy') }}
193
- value={component.properties.copyButton ? 2 : 1}
194
- onChange={(value) => updateProperty('copyButton', value === 2)}
195
- />
196
- </div>
197
- <div class="flex w-1/3 flex-col px-2">
198
- <UI.Input
199
- label={{ name: $t('service.constructor.props.label') }}
200
- value={component.properties.label.name}
201
- type="text"
202
- onUpdate={(value) => updateProperty('label.name', value as string)}
203
- />
204
- <UI.Select
205
- label={{ name: $t('service.constructor.props.align') }}
206
- type="buttons"
207
- value={initialAlign}
208
- options={$optionsStore.ALIGN_OPTIONS}
209
- onUpdate={(option) => handleLabelAlign(option.value as string)}
210
- />
211
- <UI.Select
212
- wrapperClass="h-14"
213
- label={{ name: $t('service.constructor.props.colors') }}
214
- type="buttons"
215
- options={$optionsStore.COLOR_OPTIONS}
216
- value={initialColor}
217
- onUpdate={(option) => handleOptionColorChange(option.value as string)}
218
- />
219
- </div>
220
- </div>
221
- {/if}
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 '../index'
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.map((variable: { value: string; name: string }) => ({
18
+ id: variable.name,
19
+ value: variable.value,
20
+ name: `${variable.value} | ${variable.name}`,
21
+ })),
22
+ )
23
+
24
+ const initialColor = $derived(
25
+ $optionsStore.COLOR_OPTIONS.find((c) =>
26
+ (c.value as string).includes(component.properties.componentClass?.split(' ').find((cls: string) => cls.startsWith('bg-'))),
27
+ ),
28
+ )
29
+
30
+ const initialAlign = $derived(
31
+ $optionsStore.ALIGN_OPTIONS.find((a) =>
32
+ (a.value as string).includes(component.properties.label?.class?.split(' ').find((cls: string) => cls.startsWith('text-'))),
33
+ ),
34
+ )
35
+
36
+ /* Обновление свойства */
37
+ const updateProperty = (path: string, value: string | object | boolean | number | RegExp) => {
38
+ const newProperties = JSON.parse(JSON.stringify(component.properties))
39
+ if (path === 'regExp') {
40
+ try {
41
+ let regex: RegExp
42
+ if (typeof value === 'string') {
43
+ const pattern = value.match(/^\/(.*)\/([gimsuy]*)$/)
44
+
45
+ regex = pattern ? new RegExp(pattern[1], pattern[2]) : new RegExp(value)
46
+ if (pattern === null) return
47
+ regex.test('')
48
+ } else {
49
+ throw new Error('Invalid RegExp type')
50
+ }
51
+
52
+ newProperties.regExp = regex
53
+ isValidRegExp = true
54
+ } catch (error) {
55
+ console.warn('Invalid RegExp:', error)
56
+ newProperties.regExp = typeof value === 'string' ? value : String(value)
57
+ isValidRegExp = false
58
+ return
59
+ }
60
+ }
61
+
62
+ const parts = path.split('.')
63
+ let obj = newProperties
64
+
65
+ for (let i = 0; i < parts.length - 1; i++) {
66
+ const part = parts[i]
67
+ if (!obj[part]) obj[part] = {}
68
+ obj = obj[part]
69
+ }
70
+
71
+ obj[parts[parts.length - 1]] = value
72
+ onPropertyChange(newProperties)
73
+ }
74
+
75
+ const handleOptionColorChange = (color: string) => {
76
+ let componentClass = component.properties.componentClass || ''
77
+
78
+ componentClass = componentClass
79
+ .split(' ')
80
+ .filter((cls: string) => !cls.startsWith('bg-'))
81
+ .join(' ')
82
+
83
+ if (color) {
84
+ componentClass += ` ${color}`
85
+ }
86
+
87
+ updateProperty('componentClass', componentClass)
88
+ }
89
+
90
+ const handleLabelAlign = (align: string) => {
91
+ let labelClass = component.properties.label.class || ''
92
+
93
+ labelClass = labelClass
94
+ .split(' ')
95
+ .filter((cls: string) => !cls.startsWith('text-'))
96
+ .join(' ')
97
+ if (align) {
98
+ labelClass += ` ${align}`
99
+ }
100
+ updateProperty('label.class', labelClass)
101
+ }
102
+ </script>
103
+
104
+ {#if component && component.properties}
105
+ <div class="relative flex flex-row items-start justify-center">
106
+ <!-- Сообщение для отправки в ws по нажатию кнопки -->
107
+ <div class="flex w-1/3 flex-col items-center px-2">
108
+ <UI.Select
109
+ label={{ name: $t('service.constructor.props.variable') }}
110
+ options={VARIABLE_OPTIONS}
111
+ value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id.value)}
112
+ onUpdate={(selectedOption) => {
113
+ if (selectedOption && selectedOption.name) {
114
+ updateProperty('id.name', selectedOption.name.split('|')[1].trim())
115
+ updateProperty('id.value', selectedOption.value as string)
116
+ updateProperty('eventHandler.Variables', selectedOption.value as string)
117
+ }
118
+ }}
119
+ />
120
+ <UI.Select
121
+ label={{ name: $t('service.constructor.props.type') }}
122
+ options={$optionsStore.INPUT_TYPE_OPTIONS}
123
+ value={$optionsStore.INPUT_TYPE_OPTIONS.find((opt) => opt.value === (component.properties.type || 'text'))}
124
+ onUpdate={(selectedOption) => updateProperty('type', selectedOption.value as string)}
125
+ />
126
+ {#if component.properties.type === 'text' || component.properties.type === 'password'}
127
+ <UI.Input
128
+ label={{ name: $t('service.constructor.props.maxlenght') }}
129
+ value={component.properties.maxlength}
130
+ type="text"
131
+ onUpdate={(value) => updateProperty('maxlength', value as string)}
132
+ />
133
+ <UI.Input
134
+ label={{ name: $t('service.constructor.props.regexp') }}
135
+ value={component.properties.regExp}
136
+ type="text"
137
+ maxlength={150}
138
+ help={{ info: $t('service.constructor.props.regexp_info') }}
139
+ componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
140
+ onUpdate={(value) => updateProperty('regExp', value)}
141
+ />
142
+ {:else if component.properties.type === 'number' && !component.properties.readonly && !component.properties.disabled}
143
+ <UI.Input
144
+ label={{ name: $t('service.constructor.props.minnum') }}
145
+ value={component.properties.number.minNum as number}
146
+ type="number"
147
+ onUpdate={(value) => updateProperty('number.minNum', Number(value))}
148
+ />
149
+ <UI.Input
150
+ label={{ name: $t('service.constructor.props.maxnum') }}
151
+ value={component.properties.number.maxNum as number}
152
+ type="number"
153
+ onUpdate={(value) => updateProperty('number.maxNum', Number(value))}
154
+ />
155
+ <UI.Input
156
+ label={{ name: $t('service.constructor.props.step') }}
157
+ value={component.properties.number.step as number}
158
+ type="number"
159
+ onUpdate={(value) => updateProperty('number.step', Number(value))}
160
+ />
161
+ {:else if component.properties.type === 'text-area'}
162
+ <UI.Input
163
+ label={{ name: $t('service.constructor.props.regexp') }}
164
+ value={component.properties.regExp}
165
+ type="text"
166
+ maxlength={150}
167
+ help={{ info: $t('service.constructor.props.regexp_info') }}
168
+ componentClass={isValidRegExp === false ? '!border-2 !border-red-400' : ''}
169
+ onUpdate={(value) => updateProperty('regExp', value)}
170
+ />
171
+ {/if}
172
+ </div>
173
+ <div class="flex w-1/3 flex-col px-2">
174
+ <UI.Input
175
+ label={{ name: $t('service.constructor.props.placeholder') }}
176
+ value={component.properties.help.placeholder as string}
177
+ type="text"
178
+ onUpdate={(value) => updateProperty('help.placeholder', value)}
179
+ />
180
+ <UI.Input
181
+ label={{ name: $t('service.constructor.props.info') }}
182
+ value={component.properties.help.info as string}
183
+ type="text"
184
+ onUpdate={(value) => updateProperty('help.info', value)}
185
+ />
186
+ <UI.Switch
187
+ label={{ name: $t('service.constructor.props.readonly') }}
188
+ value={component.properties.readonly ? 2 : 1}
189
+ onChange={(value) => updateProperty('readonly', value === 2)}
190
+ />
191
+ <UI.Switch
192
+ label={{ name: $t('service.constructor.props.copy') }}
193
+ value={component.properties.copyButton ? 2 : 1}
194
+ onChange={(value) => updateProperty('copyButton', value === 2)}
195
+ />
196
+ </div>
197
+ <div class="flex w-1/3 flex-col px-2">
198
+ <UI.Input
199
+ label={{ name: $t('service.constructor.props.label') }}
200
+ value={component.properties.label.name}
201
+ type="text"
202
+ onUpdate={(value) => updateProperty('label.name', value as string)}
203
+ />
204
+ <UI.Select
205
+ label={{ name: $t('service.constructor.props.align') }}
206
+ type="buttons"
207
+ value={initialAlign}
208
+ options={$optionsStore.ALIGN_OPTIONS}
209
+ onUpdate={(option) => handleLabelAlign(option.value as string)}
210
+ />
211
+ <UI.Select
212
+ wrapperClass="h-14"
213
+ label={{ name: $t('service.constructor.props.colors') }}
214
+ type="buttons"
215
+ options={$optionsStore.COLOR_OPTIONS}
216
+ value={initialColor}
217
+ onUpdate={(option) => handleOptionColorChange(option.value as string)}
218
+ />
219
+ </div>
220
+ </div>
221
+ {/if}
@@ -1,12 +1,12 @@
1
- <!-- $lib/ElementsUI/Loader.svelte -->
2
- <script lang="ts">
3
- import LoaderRotate from './appIcons/LoaderRotate.svelte'
4
-
5
- let { show = false } = $props()
6
- </script>
7
-
8
- {#if show}
9
- <div class="absolute inset-0 z-50 flex items-center justify-center">
10
- <LoaderRotate />
11
- </div>
12
- {/if}
1
+ <!-- $lib/ElementsUI/Loader.svelte -->
2
+ <script lang="ts">
3
+ import LoaderRotate from './libIcons/LoaderRotate.svelte'
4
+
5
+ let { show = false } = $props()
6
+ </script>
7
+
8
+ {#if show}
9
+ <div class="absolute inset-0 z-50 flex items-center justify-center">
10
+ <LoaderRotate />
11
+ </div>
12
+ {/if}
@@ -1,54 +1,54 @@
1
- <!-- $lib/ElementsUI/MessageModal.svelte -->
2
- <script lang="ts">
3
- import { onMount } from 'svelte'
4
- import { fly } from 'svelte/transition'
5
-
6
- interface Props {
7
- message: { id: number; text: string }
8
- onCLick: (messageId: number) => {}
9
- }
10
-
11
- let { message, onCLick }: Props = $props()
12
-
13
- const getMessageStyle = (text: string) => {
14
- if (text.startsWith('ERR: ')) return 'text-red-500'
15
- if (text.startsWith('OK: ')) return 'text-lime-500'
16
- if (text.startsWith('WR: ')) return 'text-yellow-500'
17
- return 'text-gray-400'
18
- }
19
-
20
- const getMessageText = (text: string) => {
21
- if (text.startsWith('ERR: ')) return text.replace('ERR: ', '')
22
- if (text.startsWith('OK: ')) return text.replace('OK: ', '')
23
- if (text.startsWith('WR: ')) return text.replace('WR: ', '')
24
- return text
25
- }
26
-
27
- let progress = $state(100)
28
- onMount(() => {
29
- const duration = 5000
30
- const interval = 50
31
- const step = (interval / duration) * 100
32
- const timer = setInterval(() => {
33
- progress -= step
34
- if (progress <= 0) {
35
- clearInterval(timer)
36
- onCLick(message.id)
37
- }
38
- }, interval)
39
- })
40
- </script>
41
-
42
- <div
43
- transition:fly={{ y: 5, duration: 250 }}
44
- class="my-1 flex flex-col rounded-2xl border border-[var(--border-color)] bg-[var(--back-color)] px-4 py-2 shadow-lg"
45
- >
46
- <div class="flex items-center justify-between">
47
- <p class={`font-semibold ${getMessageStyle(message.text)}`}>{getMessageText(message.text)}</p>
48
- <button class="ml-2 cursor-pointer text-2xl" onclick={() => onCLick(message.id)}>&times;</button>
49
- </div>
50
-
51
- <div class="mt-2 h-2 w-full overflow-hidden rounded bg-gray-200">
52
- <div class="h-full bg-[var(--green-color)]" style={`width: ${progress}%`}></div>
53
- </div>
54
- </div>
1
+ <!-- $lib/ElementsUI/MessageModal.svelte -->
2
+ <script lang="ts">
3
+ import { onMount } from 'svelte'
4
+ import { fly } from 'svelte/transition'
5
+
6
+ interface Props {
7
+ message: { id: number; text: string }
8
+ onCLick: (messageId: number) => {}
9
+ }
10
+
11
+ let { message, onCLick }: Props = $props()
12
+
13
+ const getMessageStyle = (text: string) => {
14
+ if (text.startsWith('ERR: ')) return 'text-red-500'
15
+ if (text.startsWith('OK: ')) return 'text-lime-500'
16
+ if (text.startsWith('WR: ')) return 'text-yellow-500'
17
+ return 'text-gray-400'
18
+ }
19
+
20
+ const getMessageText = (text: string) => {
21
+ if (text.startsWith('ERR: ')) return text.replace('ERR: ', '')
22
+ if (text.startsWith('OK: ')) return text.replace('OK: ', '')
23
+ if (text.startsWith('WR: ')) return text.replace('WR: ', '')
24
+ return text
25
+ }
26
+
27
+ let progress = $state(100)
28
+ onMount(() => {
29
+ const duration = 5000
30
+ const interval = 50
31
+ const step = (interval / duration) * 100
32
+ const timer = setInterval(() => {
33
+ progress -= step
34
+ if (progress <= 0) {
35
+ clearInterval(timer)
36
+ onCLick(message.id)
37
+ }
38
+ }, interval)
39
+ })
40
+ </script>
41
+
42
+ <div
43
+ transition:fly={{ y: 5, duration: 250 }}
44
+ class="my-1 flex flex-col rounded-2xl border border-[var(--border-color)] bg-[var(--back-color)] px-4 py-2 shadow-lg"
45
+ >
46
+ <div class="flex items-center justify-between">
47
+ <p class={`font-semibold ${getMessageStyle(message.text)}`}>{getMessageText(message.text)}</p>
48
+ <button class="ml-2 cursor-pointer text-2xl" onclick={() => onCLick(message.id)}>&times;</button>
49
+ </div>
50
+
51
+ <div class="mt-2 h-2 w-full overflow-hidden rounded bg-gray-200">
52
+ <div class="h-full bg-[var(--green-color)]" style={`width: ${progress}%`}></div>
53
+ </div>
54
+ </div>