poe-svelte-ui-lib 1.3.2 → 1.3.4

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.
@@ -20,8 +20,8 @@
20
20
  <div
21
21
  id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
22
22
  class={twMerge(
23
- `${outline ? 'border-none' : 'rounded-xl hover:shadow-md'} w-full
24
- border border-(--border-color) bg-(--container-color) p-0 transition-shadow duration-250`,
23
+ `${outline ? 'border-none' : 'rounded-xl shadow-sm hover:shadow-md'} w-full
24
+ bg-(--container-color) p-0 transition-shadow duration-250`,
25
25
  wrapperClass,
26
26
  )}
27
27
  transition:slide={{ duration: 250 }}
@@ -36,11 +36,20 @@
36
36
  class={`flex h-7 w-7 shrink-0 items-center justify-center overflow-visible [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full`}
37
37
  >
38
38
  {#if label?.icon}
39
- {@html label.icon}
39
+ {#if typeof label?.icon === 'string'}
40
+ {@html label.icon}
41
+ {:else}
42
+ {@const IconComponent = label?.icon}
43
+ <IconComponent />
44
+ {/if}
40
45
  {/if}
41
46
  </span>
42
47
 
43
- <span class="{twMerge('m-0 w-full cursor-pointer px-3 text-left font-semibold', label.class)} text-lg">
48
+ <span
49
+ class="{twMerge('m-0 w-full cursor-pointer px-3 text-left font-semibold', label.class)} text-lg {isOpen
50
+ ? 'text-blue-500 dark:text-blue-400'
51
+ : ''}"
52
+ >
44
53
  {label?.name}
45
54
  </span>
46
55
  </div>
@@ -69,10 +69,10 @@
69
69
  <button
70
70
  id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
71
71
  class="{twMerge(
72
- `relative m-0 inline-block w-full items-center rounded-2xl
73
- px-2 py-1 font-semibold transition duration-200 select-none
72
+ `relative m-0 inline-block w-full items-center rounded-2xl px-2
73
+ py-1 font-semibold shadow-sm transition duration-200 select-none
74
74
  ${content.icon && !content.name ? 'bg-transparent p-0' : 'bg-blue border border-(--bg-color) '}
75
- ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer active:scale-97'} `,
75
+ ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:shadow-md active:scale-97'} `,
76
76
  componentClass,
77
77
  )} bg-(--bg-color)"
78
78
  onclick={handleClick}
@@ -88,7 +88,7 @@
88
88
  <div class=" flex flex-row items-center justify-center gap-2">
89
89
  {#if content?.icon}
90
90
  <span
91
- class={` ${content.name ? 'absolute left-3' : ''} flex items-center justify-center overflow-visible
91
+ class={` ${content.name ? 'absolute left-3' : ''} ${typeof content?.icon == 'string' ? 'p-0.5' : ''} flex items-center justify-center overflow-visible
92
92
  ${content.name ? 'h-8 w-8' : `${svgSize()}`} [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full`}
93
93
  >
94
94
  {#if typeof content?.icon === 'string'}
@@ -5,6 +5,10 @@
5
5
  import * as UI from '..'
6
6
  import { optionsStore } from '../options'
7
7
  import { twMerge } from 'tailwind-merge'
8
+ import CrossIcon from '../libIcons/CrossIcon.svelte'
9
+ import Modal from '../Modal.svelte'
10
+ import { ICONS } from '../icons'
11
+ import Button from './Button.svelte'
8
12
 
9
13
  const {
10
14
  component,
@@ -16,6 +20,8 @@
16
20
  forConstructor?: boolean
17
21
  }>()
18
22
 
23
+ let showIconLib = $state(false)
24
+
19
25
  let hasValue: boolean = $derived(component.eventHandler.Value)
20
26
 
21
27
  let Header: ISelectOption = $derived(
@@ -104,6 +110,43 @@
104
110
  value={$optionsStore.ACCESS_OPTION.find((o) => o.value === component.access)}
105
111
  onUpdate={(option) => onPropertyChange({ access: option.value })}
106
112
  />
113
+ <div class="relative mt-6 flex w-full gap-2">
114
+ <UI.Button content={{ name: $t('constructor.props.buttonIcon') }} onClick={() => (showIconLib = true)} />
115
+ {#if showIconLib}
116
+ <Modal bind:isOpen={showIconLib} wrapperClass="w-130">
117
+ {#snippet main()}
118
+ <div class="grid grid-cols-3">
119
+ {#each ICONS as category}
120
+ <div class="relative m-1.5 rounded-xl border-2 border-(--border-color) p-3">
121
+ <div class="absolute -top-3.5 bg-(--back-color) px-1">{$t(`constructor.props.icon.${category[0]}`)}</div>
122
+ <div class="grid grid-cols-3 place-items-center gap-2">
123
+ {#each category[1] as icon}
124
+ <button
125
+ class="h-8 w-8 cursor-pointer [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full"
126
+ onclick={() => {
127
+ updateProperty('content.icon', icon as string, component, onPropertyChange)
128
+ }}
129
+ >
130
+ {@html icon}
131
+ </button>{/each}
132
+ </div>
133
+ </div>
134
+ {/each}
135
+ </div>
136
+ {/snippet}
137
+ </Modal>
138
+ {/if}
139
+ {#if component.properties.content.icon}
140
+ <Button
141
+ wrapperClass="w-8.5 "
142
+ componentClass="p-0.5 bg-red"
143
+ content={{ icon: CrossIcon }}
144
+ onClick={() => {
145
+ updateProperty('content.icon', '', component, onPropertyChange)
146
+ }}
147
+ />
148
+ {/if}
149
+ </div>
107
150
  </div>
108
151
  <div class="flex w-1/3 flex-col items-center px-2">
109
152
  <UI.Input
@@ -194,14 +237,43 @@
194
237
  onUpdate={(option) =>
195
238
  updateProperty('componentClass', twMerge(component.properties.componentClass, option.value), component, onPropertyChange)}
196
239
  />
197
-
198
- <UI.Input
199
- label={{ name: $t('constructor.props.svgicon') }}
200
- type="text-area"
201
- maxlength={100000}
202
- value={component.properties.content.icon}
203
- onUpdate={(value) => updateProperty('content.icon', value as string, component, onPropertyChange)}
204
- />
240
+ <div class="relative mt-6 flex w-full gap-2">
241
+ <UI.Button content={{ name: $t('constructor.props.buttonIcon') }} onClick={() => (showIconLib = true)} />
242
+ {#if showIconLib}
243
+ <Modal bind:isOpen={showIconLib} wrapperClass="w-130">
244
+ {#snippet main()}
245
+ <div class="grid grid-cols-3">
246
+ {#each ICONS as category}
247
+ <div class="relative m-1.5 rounded-xl border-2 border-(--border-color) p-3">
248
+ <div class="absolute -top-3.5 bg-(--back-color) px-1">{$t(`constructor.props.icon.${category[0]}`)}</div>
249
+ <div class="grid grid-cols-3 place-items-center gap-2">
250
+ {#each category[1] as icon}
251
+ <button
252
+ class="h-8 w-8 cursor-pointer [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full"
253
+ onclick={() => {
254
+ updateProperty('content.icon', icon as string, component, onPropertyChange)
255
+ }}
256
+ >
257
+ {@html icon}
258
+ </button>{/each}
259
+ </div>
260
+ </div>
261
+ {/each}
262
+ </div>
263
+ {/snippet}
264
+ </Modal>
265
+ {/if}
266
+ {#if component.properties.content.icon}
267
+ <Button
268
+ wrapperClass="w-8.5 "
269
+ componentClass="p-0.5 bg-red"
270
+ content={{ icon: CrossIcon }}
271
+ onClick={() => {
272
+ updateProperty('content.icon', '', component, onPropertyChange)
273
+ }}
274
+ />
275
+ {/if}
276
+ </div>
205
277
  </div>
206
278
  </div>
207
279
  {/if}
@@ -140,7 +140,7 @@
140
140
  <div class="flex w-full flex-col gap-2">
141
141
  <!-- Выбор цвета -->
142
142
  <div
143
- class="hue-slider relative h-7 w-full cursor-pointer overflow-hidden rounded-full shadow-md"
143
+ class="hue-slider relative h-7 w-full cursor-pointer overflow-hidden rounded-full shadow-sm transition duration-200 hover:shadow-md"
144
144
  role="slider"
145
145
  aria-valuenow={null}
146
146
  tabindex={null}
@@ -163,7 +163,9 @@
163
163
 
164
164
  <!-- Яркость цвета -->
165
165
  <div
166
- class="brightness-slider relative h-4 w-full cursor-pointer overflow-hidden rounded-full {mode === 'hsv' ? 'shadow-md' : ''}"
166
+ class="brightness-slider relative h-4 w-full cursor-pointer overflow-hidden rounded-full {mode === 'hsv'
167
+ ? 'shadow-sm transition duration-200 hover:shadow-md'
168
+ : ''}"
167
169
  role="slider"
168
170
  aria-valuenow={null}
169
171
  tabindex={null}
@@ -184,7 +186,7 @@
184
186
 
185
187
  <!-- Яркость белого цвета -->
186
188
  <div
187
- class="white-slider relative mt-4 h-4 w-full cursor-pointer overflow-hidden rounded-full shadow-sm"
189
+ class="white-slider relative mt-4 h-4 w-full cursor-pointer overflow-hidden rounded-full shadow-sm transition duration-200 hover:shadow-md"
188
190
  role="slider"
189
191
  aria-valuenow={null}
190
192
  tabindex={null}
@@ -204,7 +206,7 @@
204
206
 
205
207
  <div class="flex w-25 flex-col items-center">
206
208
  <div
207
- class={`flex size-15 flex-col justify-center gap-1 rounded-full px-2 font-mono text-sm shadow-md select-none ${textColor()}`}
209
+ class={`flex size-15 flex-col justify-center gap-1 rounded-full px-2 font-mono text-sm shadow-sm transition duration-200 select-none ${textColor()}`}
208
210
  style={`background: rgb(${previewBaseColor().join(',')})`}
209
211
  ></div>
210
212
  <div class="w-full text-center font-semibold">{hex()}</div>
@@ -17,15 +17,36 @@
17
17
  <div class="max-h-[70%]" transition:fade={{ duration: 200 }}>
18
18
  {@render componentProps()}
19
19
  <div class="relative mt-3">
20
- <UI.Button
21
- wrapperClass="absolute top-3 right-5 w-6"
22
- content={{ icon: isCopied ? '<div class="rounded-md bg-(--green-color) shadow-lg px-1">✓</div>' : CopyButton }}
23
- onClick={() => {
20
+ <button
21
+ class="absolute top-2 right-3 flex cursor-pointer border-none bg-transparent"
22
+ onclick={(e) => {
23
+ e.preventDefault()
24
+ navigator.clipboard.writeText(codeText)
24
25
  isCopied = true
25
26
  setTimeout(() => (isCopied = false), 1000)
26
- navigator.clipboard.writeText(codeText)
27
27
  }}
28
- />
28
+ aria-label="Копировать текст"
29
+ >
30
+ <div class=" size-6 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
31
+ {#if isCopied}
32
+ <div
33
+ class="right-1..5 absolute top-1/2 -translate-y-1/2 transform rounded-md bg-(--green-color) px-1.5 py-1 shadow-lg"
34
+ transition:fade={{ duration: 200 }}
35
+ >
36
+
37
+ </div>
38
+ {:else}
39
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
40
+ <g fill="none" stroke="currentColor" stroke-width="1.5">
41
+ <path
42
+ 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"
43
+ />
44
+ <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" />
45
+ </g>
46
+ </svg>
47
+ {/if}
48
+ </div>
49
+ </button>
29
50
  <pre class="overflow-x-auto">{codeText}
30
51
  </pre>
31
52
  </div>
@@ -51,8 +51,8 @@
51
51
  <div class="relative">
52
52
  <button
53
53
  class="flex items-center justify-center overflow-hidden {imageSize.form === 'circle' ? 'rounded-full' : 'rounded-2xl'}
54
- bg-(--back-color) shadow-sm transition duration-250 hover:shadow-md
55
- {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}"
54
+ bg-(--back-color) shadow-sm transition duration-250
55
+ {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:shadow-md'}"
56
56
  style={`height: ${imageSize.height}; width: ${imageSize.width}`}
57
57
  onclick={triggerFileInput}
58
58
  {disabled}
@@ -83,14 +83,14 @@
83
83
  onchange={handleFileChange}
84
84
  />
85
85
  <div
86
- class="flex h-8.5 w-full overflow-hidden rounded-2xl font-semibold shadow-sm transition duration-250 hover:shadow-md
86
+ class="flex w-full overflow-hidden rounded-2xl font-semibold shadow-sm transition duration-250 {disabled ? '' : 'hover:shadow-md'}
87
87
  "
88
88
  >
89
- <div class="flex w-1/3 items-center justify-center bg-(--blue-color) {disabled ? 'opacity-50' : ''}">
89
+ <div class="flex w-1/3 items-center justify-center bg-(--blue-color) p-2 py-1 {disabled ? 'opacity-50' : ''}">
90
90
  {$t('constructor.props.file.select')}
91
91
  </div>
92
- <div class="flex w-2/3 items-center justify-start bg-(--back-color) px-2 {disabled ? 'opacity-50' : ''}">
93
- {fileName || $t('constructor.props.file.notselected')}
92
+ <div class="flex flex-1 items-center justify-start truncate bg-(--back-color) px-2 {disabled ? 'opacity-50' : ''}">
93
+ <p class="truncate">{fileName || $t('constructor.props.file.notselected')}</p>
94
94
  </div>
95
95
  </div>
96
96
  </label>
@@ -225,7 +225,7 @@
225
225
 
226
226
  <div class="flex w-full flex-row gap-4">
227
227
  <!-- График -->
228
- <div bind:this={container} class="h-64 grow overflow-hidden rounded-md border border-gray-200">
228
+ <div bind:this={container} class="h-64 grow overflow-hidden rounded-2xl border border-gray-200 shadow-sm">
229
229
  <canvas class="h-full w-full bg-(--back-color)" bind:this={canvas}></canvas>
230
230
  </div>
231
231
 
@@ -64,8 +64,8 @@
64
64
  <input
65
65
  bind:value
66
66
  class={twMerge(
67
- `w-full rounded-2xl border px-4 py-1 text-center transition-all duration-300 outline-none focus:border-blue-400
68
- [&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
67
+ `w-full rounded-2xl border px-4 py-1 text-center shadow-sm transition duration-200
68
+ outline-none focus:border-blue-400 [&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
69
69
  ${isValid ? 'border-(--border-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)] focus:border-red-400'}
70
70
  ${disabled ? 'opacity-50' : 'hover:shadow-md'}
71
71
  ${readonly ? '' : 'hover:shadow-md'}
@@ -90,7 +90,8 @@
90
90
  <textarea
91
91
  bind:value
92
92
  class={twMerge(
93
- `h-full w-full resize-y rounded-2xl border border-(--border-color) px-2 py-1 text-center font-mono outline-none focus:border-blue-400
93
+ `h-full w-full resize-y rounded-2xl border border-(--border-color) px-2 py-1 text-center font-mono shadow-sm transition
94
+ duration-200 outline-none focus:border-blue-400
94
95
  ${isValid ? 'border-(--border-color)' : 'border-red-400 shadow-[0_0_6px_var(--red-color)]'}
95
96
  ${disabled ? 'cursor-not-allowed opacity-50' : 'hover:shadow-md'}
96
97
  ${readonly ? '' : 'hover:shadow-md'}
@@ -177,15 +177,27 @@
177
177
  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"
178
178
  >
179
179
  <button
180
- class="flex size-18 cursor-pointer items-center justify-center rounded-full"
180
+ class="flex size-18 cursor-pointer items-center justify-center rounded-full p-3.5 [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full"
181
181
  style="background: {value[3] == 1 ? 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)' : 'var(--bg-color)'}"
182
182
  onclick={() => {
183
183
  value[3] = value[3] == 0 ? 1 : 0
184
184
  }}
185
185
  >
186
- {@html buttonIcon
187
- ? buttonIcon
188
- : '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor"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"/></svg>'}
186
+ {#if buttonIcon}
187
+ {#if typeof buttonIcon === 'string'}
188
+ {@html buttonIcon}
189
+ {:else}
190
+ {@const IconComponent = buttonIcon}
191
+ <IconComponent />
192
+ {/if}
193
+ {:else}
194
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
195
+ ><path
196
+ fill="currentColor"
197
+ 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"
198
+ /></svg
199
+ >
200
+ {/if}
189
201
  </button>
190
202
  </div>
191
203
  </div>
@@ -196,7 +208,7 @@
196
208
  style="background: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)"
197
209
  >
198
210
  <button
199
- class="h-full rotate-270 cursor-pointer rounded-l-full px-3.5"
211
+ class="h-full cursor-pointer rounded-l-full px-3.5"
200
212
  title=""
201
213
  onclick={() => {
202
214
  if (value[0] - sensitivity <= (axes[0].minNum ?? -360)) {
@@ -209,17 +221,20 @@
209
221
  }}
210
222
  onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 30%)')}
211
223
  onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'background: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)')}
212
- ><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
213
- ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
214
- ><path
215
- stroke-miterlimit="10"
216
- 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"
217
- /><path stroke-linejoin="round" d="m7.12 2.75l-.95 3.858a1.33 1.33 0 0 0 .97 1.609l3.869.948" /></g
218
- ></svg
219
- ></button
224
+ >
225
+ <div class="rotate-270">
226
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
227
+ ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
228
+ ><path
229
+ stroke-miterlimit="10"
230
+ 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"
231
+ /><path stroke-linejoin="round" d="m7.12 2.75l-.95 3.858a1.33 1.33 0 0 0 .97 1.609l3.869.948" /></g
232
+ ></svg
233
+ >
234
+ </div></button
220
235
  >
221
236
  <button
222
- class="h-full rotate-90 cursor-pointer rounded-r-full px-3.5"
237
+ class="h-full cursor-pointer rounded-r-full px-3.5"
223
238
  title=""
224
239
  onclick={() => {
225
240
  if (value[0] + sensitivity >= (axes[0].maxNum ?? 360)) {
@@ -232,14 +247,17 @@
232
247
  }}
233
248
  onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 30%)')}
234
249
  onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'vabackground: color-mix(in srgb, var(--bg-color), var(--shadow-color) 10%)')}
235
- ><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
236
- ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
237
- ><path
238
- stroke-miterlimit="10"
239
- 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"
240
- /><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
241
- ></svg
242
- ></button
250
+ >
251
+ <div class="rotate-90">
252
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
253
+ ><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5"
254
+ ><path
255
+ stroke-miterlimit="10"
256
+ 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"
257
+ /><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
258
+ ></svg
259
+ >
260
+ </div></button
243
261
  >
244
262
  </div>
245
263
  {/if}
@@ -101,7 +101,7 @@
101
101
  <h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
102
102
  {/if}
103
103
  <MapLibre
104
- class="h-[calc(100%-2rem)] min-h-[200px]"
104
+ class="h-[calc(100%-2rem)] min-h-[200px] overflow-hidden rounded-2xl shadow-sm transition duration-200 hover:shadow-md"
105
105
  style={isDarkMode
106
106
  ? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'
107
107
  : 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json'}
@@ -136,10 +136,29 @@
136
136
  <div
137
137
  class="flex size-8 shrink-0 items-center justify-center [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full
138
138
  {device.isFresh ? 'text-green-500' : 'text-red-500'}"
139
- style="rotate: {device.NavHeading - 90}deg;"
139
+ style="rotate: {device.NavHeading}deg;"
140
140
  >
141
- {@html markerIcon ||
142
- '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M14.76 12H6.832m0 0c0-.275-.057-.55-.17-.808L4.285 5.814c-.76-1.72 1.058-3.442 2.734-2.591L20.8 10.217c1.46.74 1.46 2.826 0 3.566L7.02 20.777c-1.677.851-3.495-.872-2.735-2.591l2.375-5.378A2 2 0 0 0 6.83 12"/></svg>'}
141
+ {#if markerIcon}
142
+ {#if typeof markerIcon === 'string'}
143
+ {@html markerIcon}
144
+ {:else}
145
+ {@const IconComponent = markerIcon}
146
+ <IconComponent />
147
+ {/if}
148
+ {:else}
149
+ <div class="rotate-270">
150
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
151
+ <path
152
+ fill="none"
153
+ stroke="currentColor"
154
+ stroke-linecap="round"
155
+ stroke-linejoin="round"
156
+ stroke-width="1.5"
157
+ d="M14.76 12H6.832m0 0c0-.275-.057-.55-.17-.808L4.285 5.814c-.76-1.72 1.058-3.442 2.734-2.591L20.8 10.217c1.46.74 1.46 2.826 0 3.566L7.02 20.777c-1.677.851-3.495-.872-2.735-2.591l2.375-5.378A2 2 0 0 0 6.83 12"
158
+ /></svg
159
+ >
160
+ </div>
161
+ {/if}
143
162
  </div>
144
163
  <p class="font-bold">{device.DevName}</p>
145
164
  </div>
@@ -7,7 +7,7 @@
7
7
  id = crypto.randomUUID(),
8
8
  wrapperClass = '',
9
9
  label = { name: '', class: '' },
10
- value = $bindable(0),
10
+ value = $bindable([0]),
11
11
  type = 'horizontal',
12
12
  number = {
13
13
  minNum: 0,
@@ -16,29 +16,35 @@
16
16
  },
17
17
  }: IProgressBarProps = $props()
18
18
 
19
+ let innerValue: number[] | null = $derived(
20
+ (() => {
21
+ if (typeof value == 'number') {
22
+ return [value]
23
+ } else return value
24
+ })(),
25
+ )
26
+
19
27
  const min = $derived(number.minNum ?? 0)
20
28
  const max = $derived(number.maxNum ?? 100)
21
29
 
22
- let numericValue = $derived(
23
- (() => {
24
- if (typeof value === 'number' && !isNaN(value)) {
25
- return Math.max(min, Math.min(max, value))
26
- } else if (typeof value === 'string') {
27
- const parsedValue = parseFloat(value)
28
- if (!isNaN(parsedValue)) {
29
- return Math.max(min, Math.min(max, parsedValue))
30
- }
31
- } else {
32
- return min
30
+ const numericValue = (value: number) => {
31
+ if (typeof value === 'number' && !isNaN(value)) {
32
+ return Math.max(min, Math.min(max, value))
33
+ } else if (typeof value === 'string') {
34
+ const parsedValue = parseFloat(value)
35
+ if (!isNaN(parsedValue)) {
36
+ return Math.max(min, Math.min(max, parsedValue))
33
37
  }
34
- })(),
35
- )
38
+ } else {
39
+ return min
40
+ }
41
+ }
36
42
 
37
- const progressPercent = $derived(() => {
43
+ const progressPercent = (value: number) => {
38
44
  if (value) {
39
45
  return (((Math.min(Math.max(value, min), max) - min) / (max - min)) * 100) as number
40
46
  }
41
- })
47
+ }
42
48
 
43
49
  const roundToClean = (num: number): number => {
44
50
  if (Number.isInteger(num)) return num
@@ -60,20 +66,27 @@
60
66
  {#if label.name}
61
67
  <h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
62
68
  {/if}
63
-
64
69
  {#if type == 'vertical'}
65
- <div class="flex h-full w-fit min-w-16 flex-col items-center gap-2 rounded-full bg-(--bg-color) p-2">
66
- <div class="relative my-auto h-[80%] w-[70%] rounded-full bg-(--back-color)/40">
67
- <div class="absolute bottom-0 left-0 flex w-full rounded-full bg-(--field-color)" style="height: {progressPercent()}%;"></div>
68
- </div>
69
- <span class="m-auto font-semibold">{roundToClean(Number(numericValue))}{number.units}</span>
70
+ <div class="flex h-full flex-wrap gap-3">
71
+ {#each innerValue as val}
72
+ <div class="flex h-full w-fit min-w-16 flex-col items-center gap-2 rounded-full bg-(--bg-color) p-2 shadow-sm">
73
+ <div class="relative my-auto h-[80%] w-[70%] rounded-full bg-(--back-color)/40">
74
+ <div class="absolute bottom-0 left-0 flex w-full rounded-full bg-(--field-color)" style="height: {progressPercent(val)}%;"></div>
75
+ </div>
76
+ <span class="m-auto font-semibold">{roundToClean(Number(numericValue(val)))}{number.units}</span>
77
+ </div>
78
+ {/each}
70
79
  </div>
71
80
  {:else}
72
- <div class="flex h-7 w-full items-center gap-2 rounded-full bg-(--bg-color) px-2">
73
- <span class="m-auto font-semibold">{roundToClean(Number(numericValue))}{number.units}</span>
74
- <div class="relative my-auto h-3.5 w-[85%] rounded-full bg-(--back-color)/40">
75
- <div class="absolute top-0 left-0 flex h-full rounded-full bg-(--field-color)" style="width: {progressPercent()}%;"></div>
76
- </div>
81
+ <div class="flex w-full flex-col gap-2">
82
+ {#each innerValue as val}
83
+ <div class="flex h-7 w-full items-center gap-2 rounded-full bg-(--bg-color) px-2 shadow-sm">
84
+ <span class="m-auto font-semibold">{roundToClean(Number(numericValue(val)))}{number.units}</span>
85
+ <div class="relative my-auto h-3.5 w-[85%] rounded-full bg-(--back-color)/40">
86
+ <div class="absolute top-0 left-0 flex h-full rounded-full bg-(--field-color)" style="width: {progressPercent(val)}%;"></div>
87
+ </div>
88
+ </div>
89
+ {/each}
77
90
  </div>
78
91
  {/if}
79
92
  </div>
@@ -99,8 +99,8 @@
99
99
  id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
100
100
  value={value?.value ? String(value.value) : ''}
101
101
  class={twMerge(
102
- `w-full rounded-2xl border border-(--border-color) p-1 text-center duration-250
103
- ${disabled ? 'opacity-50' : 'cursor-pointer hover:shadow-lg'}`,
102
+ `w-full rounded-2xl border border-(--border-color) p-1 text-center shadow-sm transition-shadow duration-200
103
+ ${disabled ? 'opacity-50' : 'cursor-pointer hover:shadow-md'}`,
104
104
  value?.class,
105
105
  )}
106
106
  style="background: color-mix(in srgb, var(--bg-color), var(--back-color) 70%);"
@@ -164,10 +164,10 @@
164
164
  {:else if type === 'input'}
165
165
  <input
166
166
  bind:value={searchValue}
167
- class="w-full appearance-none rounded-2xl border px-4 py-1 text-center transition-shadow
168
- outline-none hover:shadow-md focus:border-blue-400
167
+ class="w-full appearance-none rounded-2xl border px-4 py-1 text-center shadow-sm
168
+ transition-shadow duration-200 outline-none focus:border-blue-400
169
169
  [&::-webkit-inner-spin-button]:hidden [&::-webkit-outer-spin-button]:hidden
170
- {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-text'} border-(--border-color)"
170
+ {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-text'} border-(--border-color) hover:shadow-md"
171
171
  style="background: color-mix(in srgb, var(--bg-color), var(--back-color) 70%);"
172
172
  id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
173
173
  {disabled}
@@ -110,6 +110,7 @@
110
110
  [&::-webkit-slider-runnable-track]:rounded-l-full
111
111
  [&::-webkit-slider-runnable-track]:bg-(--gray-color)
112
112
  [&::-webkit-slider-runnable-track]:px-2
113
+ [&::-webkit-slider-runnable-track]:shadow-sm
113
114
  [&::-webkit-slider-thumb]:relative
114
115
  [&::-webkit-slider-thumb]:size-4
115
116
  [&::-webkit-slider-thumb]:cursor-pointer
@@ -156,6 +157,7 @@
156
157
  [&::-webkit-slider-runnable-track]:rounded-r-full
157
158
  [&::-webkit-slider-runnable-track]:bg-(--gray-color)
158
159
  [&::-webkit-slider-runnable-track]:px-2
160
+ [&::-webkit-slider-runnable-track]:shadow-sm
159
161
  [&::-webkit-slider-thumb]:relative
160
162
  [&::-webkit-slider-thumb]:size-4
161
163
  [&::-webkit-slider-thumb]:cursor-pointer
@@ -197,6 +199,7 @@
197
199
  `h-8 w-full appearance-none overflow-hidden rounded-full accent-(--back-color)
198
200
  [&::-webkit-slider-runnable-track]:rounded-full
199
201
  [&::-webkit-slider-runnable-track]:bg-(--gray-color)
202
+ [&::-webkit-slider-runnable-track]:shadow-sm
200
203
  [&::-webkit-slider-thumb]:relative
201
204
 
202
205
  [&::-webkit-slider-thumb]:ml-[-0.4rem]
@@ -74,9 +74,9 @@
74
74
  {/if}
75
75
 
76
76
  <label
77
- class="relative flex items-center justify-between rounded-full shadow-md
77
+ class="relative flex items-center justify-between rounded-full shadow-sm transition duration-200
78
78
  {checkedOptions[index] ? 'border-(--bg-color)' : 'border-(--bg-color)'}
79
- {option.disabled ? 'opacity-60' : ''}"
79
+ {option.disabled ? 'opacity-60' : 'hover:shadow-md'}"
80
80
  >
81
81
  <input
82
82
  id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
@@ -126,7 +126,7 @@
126
126
  disabled={localOptions[0].disabled}
127
127
  class="
128
128
  relative size-8 cursor-pointer appearance-none rounded-2xl border border-(--bg-color)
129
- bg-white transition duration-300 after:origin-bottom-left after:opacity-0
129
+ bg-white shadow-sm transition duration-200 after:origin-bottom-left after:opacity-0
130
130
  checked:border-(--bg-color)
131
131
  checked:bg-(--bg-color) checked:after:absolute checked:after:-top-px checked:after:left-[5px]
132
132
  checked:after:h-[13.5px] checked:after:w-[7.5px] checked:after:rotate-43
@@ -160,7 +160,11 @@
160
160
  <h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
161
161
  {/if}
162
162
 
163
- <div class="flex h-full flex-col overflow-hidden rounded-xl border {outline ? ' border-(--border-color)' : 'border-transparent'} ">
163
+ <div
164
+ class="flex h-full flex-col overflow-hidden rounded-xl border shadow-sm transition duration-200 hover:shadow-md {outline
165
+ ? ' border-(--border-color)'
166
+ : 'border-transparent'} "
167
+ >
164
168
  <!-- Table Header -->
165
169
  <div class="grid font-semibold" style={`grid-template-columns: ${header.map((c) => c.width || 'minmax(0, 1fr)').join(' ')};`}>
166
170
  {#each header as column, index (column)}
@@ -211,7 +215,7 @@
211
215
  {#each column.buttons as button (button)}
212
216
  <button
213
217
  class="{twMerge(`cursor-pointer rounded-full
214
- px-4 py-1 font-medium transition-shadow outline-none select-none hover:shadow-md
218
+ px-4 py-1 font-medium shadow-sm transition-shadow duration-200 outline-none select-none hover:shadow-md
215
219
  ${typeof button.class === 'function' ? button.class(row) : button.class}`)} bg-(--bg-color)"
216
220
  onclick={() => buttonClick(row, button)}
217
221
  >
@@ -48,7 +48,12 @@
48
48
  >
49
49
  {#if item?.icon}
50
50
  <span class="flex h-7 w-7 items-center justify-center overflow-visible [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
51
- {@html item.icon}
51
+ {#if typeof item.icon === 'string'}
52
+ {@html item.icon}
53
+ {:else}
54
+ {@const IconComponent = item.icon}
55
+ <IconComponent />
56
+ {/if}
52
57
  </span>
53
58
  {/if}
54
59
  {#if item?.name}
package/dist/types.d.ts CHANGED
@@ -69,7 +69,7 @@ export interface IButtonProps {
69
69
  }
70
70
  export interface IAccordionProps {
71
71
  id?: string;
72
- isOpen: boolean;
72
+ isOpen?: boolean;
73
73
  outline?: boolean;
74
74
  wrapperClass?: string;
75
75
  size?: {
@@ -79,7 +79,7 @@ export interface IAccordionProps {
79
79
  label?: {
80
80
  name?: string;
81
81
  class?: string;
82
- icon?: string | null;
82
+ icon?: ConstructorOfATypedSvelteComponent | string | null;
83
83
  };
84
84
  children?: Snippet;
85
85
  image?: string;
@@ -199,7 +199,7 @@ export interface IProgressBarProps {
199
199
  name?: string;
200
200
  class?: string;
201
201
  };
202
- value?: number | null;
202
+ value?: number | number[] | null;
203
203
  number?: {
204
204
  minNum?: number;
205
205
  maxNum?: number;
@@ -291,7 +291,7 @@ export interface ITabsProps {
291
291
  activeTab?: number;
292
292
  items: {
293
293
  name?: string;
294
- icon?: string;
294
+ icon?: ConstructorOfATypedSvelteComponent | string;
295
295
  class?: string;
296
296
  children?: Snippet;
297
297
  }[];
@@ -312,7 +312,7 @@ export interface IJoystickProps {
312
312
  minNum?: number;
313
313
  maxNum?: number;
314
314
  }[];
315
- buttonIcon?: string;
315
+ buttonIcon?: ConstructorOfATypedSvelteComponent | string;
316
316
  onUpdate?: (value: number[]) => void;
317
317
  }
318
318
  export interface IDeviceGNSS {
@@ -331,7 +331,7 @@ export interface IMapProps {
331
331
  class?: string;
332
332
  };
333
333
  data: IDeviceGNSS | null;
334
- markerIcon?: string;
334
+ markerIcon?: ConstructorOfATypedSvelteComponent | string;
335
335
  }
336
336
  export interface IFileAttachProps {
337
337
  id?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -45,11 +45,11 @@
45
45
  "devDependencies": {
46
46
  "@sveltejs/adapter-static": "^3.0.10",
47
47
  "@sveltejs/kit": "^2.49.0",
48
- "@sveltejs/package": "^2.5.6",
48
+ "@sveltejs/package": "^2.5.7",
49
49
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
50
50
  "@types/node": "^24.10.1",
51
51
  "publint": "^0.3.15",
52
- "svelte": "^5.44.1",
52
+ "svelte": "^5.45.2",
53
53
  "svelte-preprocess": "^6.0.3",
54
54
  "vite": "^7.2.4",
55
55
  "vite-plugin-compression": "^0.5.1"