poe-svelte-ui-lib 1.2.7 → 1.2.9

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.
@@ -174,11 +174,11 @@
174
174
  if (!number.maxNum || !number.step) return
175
175
  if (Number(value) + number.step >= number.maxNum) {
176
176
  value = number.maxNum
177
- onUpdate?.(value as number)
177
+ onUpdate(value as number)
178
178
  return
179
179
  }
180
180
  value = Number(value) + (number.step ?? 1)
181
- onUpdate?.(value as number)
181
+ onUpdate(value as number)
182
182
  }}
183
183
  aria-label="Увеличить">+</button
184
184
  >
@@ -189,11 +189,11 @@
189
189
  if (number.minNum === null || number.minNum === undefined || !number.step) return
190
190
  if (Number(value) - number.step <= number.minNum) {
191
191
  value = number.minNum
192
- onUpdate?.(value as number)
192
+ onUpdate(value as number)
193
193
  return
194
194
  }
195
195
  value = Number(value) - (number.step ?? 1)
196
- onUpdate?.(value as number)
196
+ onUpdate(value as number)
197
197
  }}
198
198
  aria-label="Уменьшить">−</button
199
199
  >
@@ -1,125 +1,325 @@
1
- <!-- $lib/ElementsUI/ProgressBar.svelte -->
2
1
  <script lang="ts">
3
2
  import { twMerge } from 'tailwind-merge'
4
- import type { IProgressBarProps } from '../types'
3
+ import type { IJoystickProps } from '../types'
5
4
 
6
5
  let {
7
6
  id = crypto.randomUUID(),
8
7
  wrapperClass = '',
9
8
  label = { name: '', class: '' },
10
- value = $bindable(0),
11
- number = {
12
- minNum: 0,
13
- maxNum: 100,
14
- units: '%',
15
- },
16
- }: IProgressBarProps = $props()
9
+ value = $bindable([0, 0, 0]),
10
+ limits = [
11
+ { minNum: -100, maxNum: 100 },
12
+ { minNum: -100, maxNum: 100 },
13
+ { minNum: -100, maxNum: 100 },
14
+ ],
15
+ onUpdate = () => {},
16
+ }: IJoystickProps = $props()
17
17
 
18
18
  const directions = [
19
- { id: 'right', angle: 30.5, content: true },
20
- { id: 'bottom-right', angle: 58, content: false },
21
- { id: 'bottom', angle: 122, content: true },
22
- { id: 'bottom-left', angle: 149.5, content: false },
23
- { id: 'left', angle: 212, content: true },
24
- { id: 'top-left', angle: 239, content: false },
25
- { id: 'top', angle: 301, content: true },
19
+ {
20
+ id: 'right',
21
+ angle: 30.5,
22
+ content: true,
23
+ onClick: () => {
24
+ if (value[2] + sensitivity >= limits[2].maxNum) {
25
+ value[2] = limits[2].maxNum
26
+ onUpdate(value)
27
+ return
28
+ }
29
+
30
+ value[2] += sensitivity
31
+ onUpdate(value)
32
+ },
33
+ },
34
+ {
35
+ id: 'bottom-right',
36
+ angle: 58,
37
+ content: false,
38
+ onClick: () => {
39
+ if (value[2] + sensitivity >= limits[2].maxNum) {
40
+ value[2] = limits[2].maxNum
41
+ onUpdate(value)
42
+ } else {
43
+ value[2] += sensitivity
44
+ onUpdate(value)
45
+ }
46
+ if (value[1] - sensitivity <= limits[1].minNum) {
47
+ value[1] = limits[1].minNum
48
+ onUpdate(value)
49
+ } else {
50
+ value[1] -= sensitivity
51
+ onUpdate(value)
52
+ }
53
+ },
54
+ },
55
+ {
56
+ id: 'bottom',
57
+ angle: 122,
58
+ content: true,
59
+ onClick: () => {
60
+ if (value[1] - sensitivity <= limits[1].minNum) {
61
+ value[1] = limits[1].minNum
62
+ onUpdate(value)
63
+ return
64
+ }
65
+ value[1] -= sensitivity
66
+ onUpdate(value)
67
+ },
68
+ },
69
+ {
70
+ id: 'bottom-left',
71
+ angle: 149.5,
72
+ content: false,
73
+ onClick: () => {
74
+ if (value[2] - sensitivity <= limits[2].minNum) {
75
+ value[2] = limits[2].minNum
76
+ onUpdate(value)
77
+ } else {
78
+ value[2] -= sensitivity
79
+ onUpdate(value)
80
+ }
81
+ if (value[1] - sensitivity <= limits[1].minNum) {
82
+ value[1] = limits[1].minNum
83
+ onUpdate(value)
84
+ } else {
85
+ value[1] -= sensitivity
86
+ onUpdate(value)
87
+ }
88
+ },
89
+ },
90
+ {
91
+ id: 'left',
92
+ angle: 212,
93
+ content: true,
94
+ onClick: () => {
95
+ if (value[2] - sensitivity <= limits[2].minNum) {
96
+ value[2] = limits[2].minNum
97
+ onUpdate(value)
98
+ return
99
+ }
100
+ value[2] -= sensitivity
101
+ onUpdate(value)
102
+ },
103
+ },
104
+ {
105
+ id: 'top-left',
106
+ angle: 239,
107
+ content: false,
108
+ onClick: () => {
109
+ if (value[1] + sensitivity >= limits[1].maxNum) {
110
+ value[1] = limits[1].maxNum
111
+ onUpdate(value)
112
+ } else {
113
+ value[1] += sensitivity
114
+ onUpdate(value)
115
+ }
116
+ if (value[2] - sensitivity <= limits[2].minNum) {
117
+ value[2] = limits[2].minNum
118
+ onUpdate(value)
119
+ } else {
120
+ value[2] -= sensitivity
121
+ onUpdate(value)
122
+ }
123
+ },
124
+ },
125
+ {
126
+ id: 'top',
127
+ angle: 301,
128
+ content: true,
129
+ onClick: () => {
130
+ if (value[1] + sensitivity >= limits[1].maxNum) {
131
+ value[1] = limits[1].maxNum
132
+ onUpdate(value)
133
+ return
134
+ }
135
+ value[1] += sensitivity
136
+ onUpdate(value)
137
+ },
138
+ },
26
139
  {
27
140
  id: 'top-right',
28
141
  angle: 328,
29
142
  content: false,
143
+ onClick: () => {
144
+ if (value[1] + sensitivity >= limits[1].maxNum) {
145
+ value[1] = limits[1].maxNum
146
+ onUpdate(value)
147
+ } else {
148
+ value[1] += sensitivity
149
+ onUpdate(value)
150
+ }
151
+ if (value[2] + sensitivity >= limits[2].maxNum) {
152
+ value[2] = limits[2].maxNum
153
+ onUpdate(value)
154
+ } else {
155
+ value[2] += sensitivity
156
+ onUpdate(value)
157
+ }
158
+ },
30
159
  },
31
160
  ]
32
161
 
33
- let sectorHeight = 2 * 5 * Math.sin(Math.PI / directions.length)
162
+ const sensitivityOptions = [0.01, 0.1, 1.0, 10, 100]
163
+ let sensitivity = $state(1.0)
164
+
34
165
  let clipPos = Math.cos(Math.PI / directions.length) * 100
35
166
  let angle = 360 / directions.length
36
167
  </script>
37
168
 
38
- <div {id} class={twMerge(`bg-red relative flex w-full flex-col items-center justify-center`, wrapperClass)}>
169
+ <div {id} class={twMerge(`bg-blue relative flex w-full flex-col items-center`, wrapperClass)}>
39
170
  {#if label.name}
40
171
  <h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
41
172
  {/if}
42
173
 
43
- <div class="relative flex size-40 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_20px_rgb(0_0_0_/0.25)]">
44
- <div class="absolute h-full w-full overflow-hidden rounded-full">
45
- {#each directions as direction, index}
46
- <button class="pointer-events-none absolute top-1/2 left-1/2 block w-1/2 -translate-y-1/2 cursor-pointer" title="">
47
- <span
48
- class="relative flex w-full origin-left items-center justify-center pl-[60%] h-[{direction.content
49
- ? 2 * 5 * Math.sin((Math.PI * 65) / 360)
50
- : 2 * 5 * Math.sin((Math.PI * 25) / 360)}rem] pointer-events-auto
174
+ <div class=" flex items-center justify-center">
175
+ <div class="relative z-10 flex size-40 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_20px_rgb(0_0_0_/0.25)]">
176
+ <div class="absolute h-full w-full overflow-hidden rounded-full">
177
+ {#each directions as direction, index}
178
+ <button
179
+ class="pointer-events-none absolute top-1/2 left-1/2 block w-1/2 -translate-y-1/2 cursor-pointer"
180
+ onclick={direction.onClick}
181
+ title=""
182
+ >
183
+ <span
184
+ class="relative flex w-full origin-left items-center justify-center pl-[60%] h-[{direction.content
185
+ ? 2 * 5 * Math.sin((Math.PI * 65) / 360)
186
+ : 2 * 5 * Math.sin((Math.PI * 25) / 360)}rem] pointer-events-auto
51
187
  {direction.content ? 'bg-(--bg-color)' : ''}
52
188
  "
53
- style="transform: rotate({angle * index}deg); clip-path: polygon(100% 0, {clipPos}% 0, 0 50%, {clipPos}% 100%, 100% 100%)"
54
- role="application"
55
- onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 20%)')}
56
- onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'var(--bg-color)')}
189
+ style="transform: rotate({angle * index}deg); clip-path: polygon(100% 0, {clipPos}% 0, 0 50%, {clipPos}% 100%, 100% 100%)"
190
+ role="application"
191
+ onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 20%)')}
192
+ onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'var(--bg-color)')}
193
+ >
194
+ {#if direction.content}
195
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
196
+ ><path
197
+ fill="currentColor"
198
+ d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"
199
+ /></svg
200
+ >
201
+ {:else}
202
+
203
+ {/if}
204
+ </span>
205
+ </button>
206
+ {/each}
207
+ </div>
208
+ <div class="pointer-events-none absolute h-full w-full overflow-hidden rounded-full">
209
+ {#each directions as direction, index}
210
+ <span
211
+ class=" absolute top-1/2 left-1/2 h-0 w-[52%] origin-left border-b border-(--bg-color) {index % 2 == 0
212
+ ? 'shadow-[0_3px_5px_rgb(0_0_0_/0.5)] '
213
+ : 'shadow-[0_-3px_5px_rgb(0_0_0_/0.5)]'}"
214
+ style="transform: rotate({direction.angle}deg);"
57
215
  >
58
- {#if direction.content}
59
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
60
- ><path
61
- fill="currentColor"
62
- d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"
63
- /></svg
64
- >
65
- {/if}
66
216
  </span>
67
- </button>
68
- {/each}
69
- </div>
70
- <div class="pointer-events-none absolute h-full w-full overflow-hidden rounded-full">
71
- {#each directions as direction, index}
72
- <span
73
- class=" absolute top-1/2 left-1/2 h-0 w-[52%] origin-left border-b border-(--bg-color) {index % 2 == 0
74
- ? 'shadow-[0_3px_5px_rgb(0_0_0_/0.5)] '
75
- : 'shadow-[0_-3px_5px_rgb(0_0_0_/0.5)]'}"
76
- style="transform: rotate({direction.angle}deg);"
77
- >
78
- </span>
79
- {/each}
80
- </div>
81
- <div class="z-10 flex size-20 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_15px_rgb(0_0_0_/0.25)]">
82
- <button class="flex size-18 cursor-pointer items-center justify-center rounded-full bg-(--bg-color)" title=""
83
- ><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
84
- ><path
85
- fill="currentColor"
86
- d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"
87
- /></svg
88
- ></button
217
+ {/each}
218
+ </div>
219
+ <div
220
+ class="z-20 flex size-20 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_15px_rgb(0_0_0_/0.25)] transition hover:scale-103"
89
221
  >
222
+ <button
223
+ class="flex size-18 cursor-pointer items-center justify-center rounded-full bg-(--bg-color)"
224
+ title=""
225
+ onclick={() => {
226
+ value = [0, 0, 0]
227
+ onUpdate(value)
228
+ }}
229
+ ><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
230
+ ><path
231
+ fill="currentColor"
232
+ d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"
233
+ /></svg
234
+ ></button
235
+ >
236
+ </div>
90
237
  </div>
91
-
92
238
  <div
93
- class="absolute -z-20 flex h-20 w-80 items-center justify-between rounded-full px-3"
94
- style="background: color-mix(in srgb, var(--bg-color), var(--shadow-color) 50%)"
239
+ class="absolute flex h-15 w-65 items-center justify-between rounded-full shadow-[0_0_15px_rgb(0_0_0_/0.25)]"
240
+ style="background: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)"
95
241
  >
96
- <button class="cursor-pointer" title=""
97
- ><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
98
- ><path
99
- fill="none"
100
- stroke="currentColor"
101
- stroke-linecap="round"
102
- stroke-linejoin="round"
103
- stroke-width="2"
104
- d="M11 18h3.75a5.25 5.25 0 1 0 0-10.5H5M7.5 4L4 7.5L7.5 11"
105
- /></svg
242
+ <button
243
+ class="h-full cursor-pointer rounded-l-full px-3.5"
244
+ title=""
245
+ onclick={() => {
246
+ if (value[0] - sensitivity <= limits[0].minNum) {
247
+ value[0] = limits[0].minNum
248
+ onUpdate(value)
249
+ return
250
+ }
251
+ value[0] -= sensitivity
252
+ onUpdate(value)
253
+ }}
254
+ onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 30%)')}
255
+ onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'background: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)')}
256
+ ><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
257
+ ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
258
+ ><path
259
+ stroke-miterlimit="10"
260
+ d="M6.395 7.705A7.9 7.9 0 0 1 12 5.382a7.93 7.93 0 0 1 7.929 7.929A7.94 7.94 0 0 1 12 21.25a7.94 7.94 0 0 1-7.929-7.94"
261
+ /><path stroke-linejoin="round" d="m7.12 2.75l-.95 3.858a1.33 1.33 0 0 0 .97 1.609l3.869.948" /></g
262
+ ></svg
106
263
  ></button
107
264
  >
108
- <button class="cursor-pointer" title=""
109
- ><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
110
- ><path
111
- fill="none"
112
- stroke="currentColor"
113
- stroke-linecap="round"
114
- stroke-linejoin="round"
115
- stroke-width="2"
116
- d="M13 18H9.25a5.25 5.25 0 1 1 0-10.5H19M16.5 4L20 7.5L16.5 11"
117
- /></svg
265
+ <button
266
+ class="h-full cursor-pointer rounded-r-full px-3.5"
267
+ title=""
268
+ onclick={() => {
269
+ if (value[0] + sensitivity >= limits[0].maxNum) {
270
+ value[0] = limits[0].maxNum
271
+ onUpdate(value)
272
+ return
273
+ }
274
+ value[0] += sensitivity
275
+ onUpdate(value)
276
+ }}
277
+ onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 30%)')}
278
+ onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'vabackground: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)')}
279
+ ><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
280
+ ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
281
+ ><path
282
+ stroke-miterlimit="10"
283
+ d="M17.605 7.705A7.9 7.9 0 0 0 12 5.382a7.93 7.93 0 0 0-7.929 7.929A7.94 7.94 0 0 0 12 21.25a7.94 7.94 0 0 0 7.929-7.94"
284
+ /><path stroke-linejoin="round" d="m16.88 2.75l.95 3.858a1.33 1.33 0 0 1-.97 1.609l-3.869.948" /></g
285
+ ></svg
118
286
  ></button
119
287
  >
120
288
  </div>
121
289
  </div>
122
290
 
291
+ <p>{value[0]}</p>
292
+ <p>{value[1]}</p>
293
+ <p>{value[2]}</p>
294
+
295
+ <div {id} class="flex h-full w-full flex-row justify-center rounded-full p-10">
296
+ {#each sensitivityOptions as option, index}
297
+ <button
298
+ id={crypto.randomUUID()}
299
+ 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
300
+ select-none hover:shadow-md
301
+ ${
302
+ option === sensitivity && sensitivity !== null
303
+ ? 'z-10 py-1 shadow-[0_0_10px_var(--shadow-color)] hover:shadow-[0_0_15px_var(--shadow-color)]'
304
+ : ''
305
+ }
306
+ ${sensitivityOptions.length > 0 && index === 0 ? 'rounded-l-2xl' : ''} ${
307
+ index === sensitivityOptions.length - 1 ? 'rounded-r-2xl' : ''
308
+ } bg-(--back-color)`)}
309
+ onclick={() => {
310
+ sensitivity = option
311
+ }}
312
+ >
313
+ <span class="flex flex-row items-center justify-center gap-4">
314
+ {#if option}
315
+ <div class="flex-1">
316
+ {option}
317
+ </div>
318
+ {/if}
319
+ </span>
320
+ </button>
321
+ {/each}
322
+ </div>
123
323
  <!-- {direction.content ? 2 * 6.25 * Math.sin((Math.PI * 65) / 360) : 2 * 6.25 * Math.sin((Math.PI * 25) / 360)} -->
124
324
  <!-- angle / 2 + angle * index -->
125
325
  <!-- ? 'shadow-[0_-2px_5px_rgb(254_202_202)] '
@@ -1,4 +1,4 @@
1
- import type { IProgressBarProps } from '../types';
2
- declare const Joystick: import("svelte").Component<IProgressBarProps, {}, "value">;
1
+ import type { IJoystickProps } from '../types';
2
+ declare const Joystick: import("svelte").Component<IJoystickProps, {}, "value">;
3
3
  type Joystick = ReturnType<typeof Joystick>;
4
4
  export default Joystick;
@@ -9,6 +9,7 @@
9
9
  wrapperClass = '',
10
10
  label = { name: '', class: '', captionLeft: '', captionRight: '' },
11
11
  height = '2rem',
12
+ type = 'horizontal',
12
13
  value = $bindable(),
13
14
  onChange = () => {},
14
15
  }: ISwitchProps = $props()
@@ -38,6 +38,11 @@
38
38
  item.class,
39
39
  index === currentTabIndex ? twMerge('bg-(--back-color) text-blue-500', item.class) : 'bg-(--bg-color) text-gray-500',
40
40
  )}
41
+ style="width: {item.class
42
+ ?.split(' ')
43
+ .find((cls: string) => cls.startsWith('w-'))
44
+ ?.replace('w-[', '')
45
+ .slice(0, -1)};"
41
46
  onclick={() => (currentTabIndex = index)}
42
47
  >
43
48
  {#if item?.icon}
@@ -56,21 +56,6 @@
56
56
  } else if (width.class.includes('w-auto')) return 1
57
57
  else return 2
58
58
  })
59
-
60
- const handleImageUpload = (event: Event) => {
61
- const input = event.target as HTMLInputElement
62
- if (!input.files || input.files.length === 0) return
63
-
64
- const file = input.files[0]
65
- const reader = new FileReader()
66
- reader.onload = (e) => {
67
- const base64String = e.target?.result as string
68
- updateProperty('image', base64String, component, onPropertyChange)
69
- }
70
- reader.readAsDataURL(file)
71
- }
72
-
73
- let currentImage = $derived(component.properties.image ?? '')
74
59
  </script>
75
60
 
76
61
  {#if forConstructor}
@@ -123,19 +108,11 @@
123
108
  }}
124
109
  value={initialWidth()}
125
110
  onChange={(value) => {
126
- if (value === 2) {
127
- component.properties.items.forEach((_item: any, index: number) => {
128
- const items = [...(component.properties?.items || [])]
129
- items[index]['class'] = twMerge(items[index].class, `w-1/${items.length}`)
130
- updateProperty('items', items, component, onPropertyChange)
131
- })
132
- } else {
133
- component.properties.items.forEach((_item: any, index: number) => {
134
- const items = [...(component.properties?.items || [])]
135
- items[index]['class'] = twMerge(items[index].class, 'w-auto')
136
- updateProperty('items', items, component, onPropertyChange)
137
- })
138
- }
111
+ component.properties.items.forEach((_item: any, index: number) => {
112
+ const items = [...(component.properties?.items || [])]
113
+ items[index]['class'] = twMerge(items[index].class, value === 2 ? `w-[${(1 / items.length) * 100}%]` : 'w-auto')
114
+ updateProperty('items', items, component, onPropertyChange)
115
+ })
139
116
  }}
140
117
  />
141
118
  </div>
@@ -143,19 +120,21 @@
143
120
 
144
121
  <div class="space-y-4">
145
122
  <div class="m-0 flex items-center justify-center gap-2">
146
- <h4>{$t('constructor.props.options.title')}</h4>
123
+ <h4>{$t('constructor.props.tabs.title')}</h4>
147
124
  <UI.Button
148
125
  wrapperClass="w-8"
149
126
  content={{ icon: ButtonAdd }}
150
127
  onClick={() => {
151
- let tabWidth = Math.max(...Array.from(document.body.querySelectorAll('.tab')).map((item) => (item as HTMLElement).offsetWidth))
152
128
  const newItem: { name: string; icon: string; class: string } = {
153
129
  name: `Tab ${component.properties?.items.length + 1}`,
154
- class: `w-${initialWidth() === 2 ? `[${tabWidth}px]` : 'auto'} text-${initialColor?.value.slice(3)}-500 ${initialPosition?.value}`,
130
+ class: `text-${initialColor?.value.slice(3)}-500 ${initialPosition?.value}`,
155
131
  icon: '',
156
132
  }
157
133
  const items = [...(component.properties?.items || []), newItem]
158
- updateProperty('items', items, component, onPropertyChange)
134
+ items.forEach((_item: any, index: number) => {
135
+ items[index]['class'] = twMerge(items[index].class, initialWidth() === 2 ? `w-[${(1 / items.length) * 100}%]` : 'w-auto')
136
+ updateProperty('items', items, component, onPropertyChange)
137
+ })
159
138
  }}
160
139
  />
161
140
  </div>
@@ -196,6 +175,10 @@
196
175
  onClick={() => {
197
176
  const items = [...(component.properties?.items || [])]
198
177
  items.splice(index, 1)
178
+ items.forEach((_item: any, index: number) => {
179
+ items[index]['class'] = twMerge(items[index].class, initialWidth() === 2 ? `w-[${(1 / items.length) * 100}%]` : 'w-auto')
180
+ updateProperty('items', items, component, onPropertyChange)
181
+ })
199
182
  updateProperty('items', items, component, onPropertyChange)
200
183
  }}
201
184
  />
@@ -328,7 +311,7 @@
328
311
 
329
312
  <div class="space-y-4">
330
313
  <div class="m-0 flex items-center justify-center gap-2">
331
- <h4>{$t('constructor.props.options.title')}</h4>
314
+ <h4>{$t('constructor.props.tabs.title')}</h4>
332
315
  <UI.Button
333
316
  wrapperClass="w-8"
334
317
  content={{ icon: ButtonAdd }}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ export { default as Graph } from './Graph/Graph.svelte';
9
9
  export { default as GraphProps } from './Graph/GraphProps.svelte';
10
10
  export { default as Input } from './Input/Input.svelte';
11
11
  export { default as InputProps } from './Input/InputProps.svelte';
12
+ export { default as Joystick } from './Joystick/Joystick.svelte';
13
+ export { default as JoystickProps } from './Joystick/JoystickProps.svelte';
12
14
  export { default as Modal } from './Modal.svelte';
13
15
  export { default as ProgressBar } from './ProgressBar/ProgressBar.svelte';
14
16
  export { default as ProgressBarProps } from './ProgressBar/ProgressBarProps.svelte';
@@ -26,4 +28,4 @@ export { default as TextField } from './TextField/TextField.svelte';
26
28
  export { default as TextFieldProps } from './TextField/TextFieldProps.svelte';
27
29
  export * from './locales/i18n';
28
30
  export * from './locales/translations';
29
- export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type ISelectOption, type ISwitchProps, type IColorPickerProps, type ISliderProps, type ITextFieldProps, type IProgressBarProps, type IGraphProps, type IGraphDataObject, type ITableHeader, type ITableProps, type ITabsProps, } from './types';
31
+ export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type ISelectOption, type ISwitchProps, type IColorPickerProps, type ISliderProps, type ITextFieldProps, type IProgressBarProps, type IGraphProps, type IGraphDataObject, type ITableHeader, type ITableProps, type ITabsProps, type IJoystickProps, } from './types';
package/dist/index.js CHANGED
@@ -10,8 +10,8 @@ export { default as Graph } from './Graph/Graph.svelte';
10
10
  export { default as GraphProps } from './Graph/GraphProps.svelte';
11
11
  export { default as Input } from './Input/Input.svelte';
12
12
  export { default as InputProps } from './Input/InputProps.svelte';
13
- // export { default as Joystick } from './Joystick/Joystick.svelte'
14
- // export { default as JoystickProps } from './Joystick/JoystickProps.svelte'
13
+ export { default as Joystick } from './Joystick/Joystick.svelte';
14
+ export { default as JoystickProps } from './Joystick/JoystickProps.svelte';
15
15
  export { default as Modal } from './Modal.svelte';
16
16
  export { default as ProgressBar } from './ProgressBar/ProgressBar.svelte';
17
17
  export { default as ProgressBarProps } from './ProgressBar/ProgressBarProps.svelte';
@@ -95,6 +95,7 @@ const translations = {
95
95
  'constructor.props.widthMode': 'Ширина кнопок',
96
96
  'constructor.props.graphdata.title': 'Начальные данные ',
97
97
  'constructor.props.options.title': 'Опции компонента',
98
+ 'constructor.props.tabs.title': 'Вкладки меню',
98
99
  'constructor.props.valuetype': 'Тип значения',
99
100
  'constructor.props.action': 'Действие при переключении',
100
101
  'constructor.props.caption.left': 'Надпись слева',
package/dist/types.d.ts CHANGED
@@ -8,7 +8,7 @@ export interface UIComponent {
8
8
  id: string;
9
9
  name?: string;
10
10
  type: 'Button' | 'Accordion' | 'Input' | 'Select' | 'Switch' | 'ColorPicker' | 'Slider' | 'TextField' | 'Joystick' | 'ProgressBar' | 'Graph' | 'Table' | 'Tabs' | 'FileAttach';
11
- properties: IAccordionProps | IButtonProps | IInputProps | ISelectProps | ISwitchProps | IColorPickerProps | ISliderProps | ITextFieldProps | IProgressBarProps | IGraphProps | ITableProps<object> | ITabsProps | IFileInputProps;
11
+ properties: IAccordionProps | IButtonProps | IInputProps | ISelectProps | ISwitchProps | IColorPickerProps | ISliderProps | ITextFieldProps | IProgressBarProps | IGraphProps | ITableProps<object> | ITabsProps | IFileInputProps | IJoystickProps;
12
12
  position: Position;
13
13
  parentId: string;
14
14
  }
@@ -126,6 +126,7 @@ export interface ISwitchProps {
126
126
  captionLeft?: string;
127
127
  captionRight?: string;
128
128
  };
129
+ type?: 'horizontal' | 'vertical' | 'checkbox';
129
130
  value?: number;
130
131
  eventHandler?: IUIComponentHandler;
131
132
  onChange?: (value: number) => void;
@@ -272,3 +273,17 @@ export interface ITabsProps {
272
273
  apiArray?: UIComponent[];
273
274
  Components?: Snippet<[component: UIComponent, fixedHeight: boolean]>;
274
275
  }
276
+ export interface IJoystickProps {
277
+ id?: string;
278
+ wrapperClass?: string;
279
+ label?: {
280
+ name?: string;
281
+ class?: string;
282
+ };
283
+ value?: number[];
284
+ limits?: {
285
+ minNum: number;
286
+ maxNum: number;
287
+ }[];
288
+ onUpdate?: (value: number[]) => void;
289
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -46,9 +46,9 @@
46
46
  "@sveltejs/kit": "^2.48.4",
47
47
  "@sveltejs/package": "^2.5.4",
48
48
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
49
- "@types/node": "^24.10.0",
49
+ "@types/node": "^24.10.1",
50
50
  "publint": "^0.3.15",
51
- "svelte": "^5.43.5",
51
+ "svelte": "^5.43.6",
52
52
  "svelte-preprocess": "^6.0.3",
53
53
  "vite": "^7.2.2",
54
54
  "vite-plugin-compression": "^0.5.1"