poe-svelte-ui-lib 1.27.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,7 @@
53
53
  />
54
54
  {#if currentImage}
55
55
  <Button
56
- wrapperClass="w-8.5 mt-6"
56
+ wrapperClass="w-8 mt-6"
57
57
  componentClass="p-0.5 bg-red"
58
58
  content={{ icon: CrossIcon }}
59
59
  onClick={() => {
@@ -3,7 +3,7 @@
3
3
  Что изменилось внутри:
4
4
  • роль определяется значением bg-* (намерение), а не фактом его наличия;
5
5
  • убрано автоподчёркивание текста — кнопка больше не выглядит ссылкой;
6
- • единый радиус 10px (иконка без подписи — круг), высота 36px, кегль 14/600;
6
+ • единый радиус 10px (иконка без подписи — круг), высота 32px (стандарт полей h-8), кегль 14/600;
7
7
  • кольцо фокуса вместо размытой тени;
8
8
  • disabled через токены, а не opacity: 0.5;
9
9
  • иконка встала в поток (flex + gap) вместо absolute left-3. -->
@@ -108,7 +108,7 @@
108
108
  `relative inline-flex w-full items-center justify-center gap-2 leading-none font-semibold text-[14px] select-none
109
109
  transition-[background-color,filter,box-shadow] duration-150
110
110
  focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--accent-color)
111
- ${isIconOnly ? "aspect-square rounded-full p-0" : "min-h-9 rounded-[10px] px-4 py-2"}
111
+ ${isIconOnly ? "aspect-square rounded-full p-0" : "h-8 rounded-[10px] px-4 py-2"}
112
112
  ${disabled ? "" : "cursor-pointer active:scale-[0.97]"}`,
113
113
  roleClass,
114
114
  componentClass,
@@ -257,7 +257,7 @@
257
257
  updateProperty: (icon: string | string[]) => {}
258
258
  })}
259
259
  <div class="relative mt-6 flex w-full gap-2">
260
- <UI.Button content={{ name: initialValue.name }} componentClass="h-8 min-h-8 bg-blue" onClick={() => (showIconLib = true)} />
260
+ <UI.Button content={{ name: initialValue.name }} componentClass="bg-blue" onClick={() => (showIconLib = true)} />
261
261
  {#if showIconLib}
262
262
  <UI.Modal bind:isOpen={showIconLib} wrapperClass="w-130">
263
263
  {#snippet main()}
@@ -288,7 +288,7 @@
288
288
  </UI.Modal>
289
289
  {/if}
290
290
  {#if initialValue.icon}
291
- <UI.Button wrapperClass="w-8.5" componentClass="p-0.5 bg-red" content={{ icon: CrossIcon }} onClick={() => initialValue.updateProperty("")} />
291
+ <UI.Button wrapperClass="w-8" componentClass="p-0.5 bg-red" content={{ icon: CrossIcon }} onClick={() => initialValue.updateProperty("")} />
292
292
  {/if}
293
293
  </div>
294
294
  {/snippet}
@@ -8,6 +8,7 @@
8
8
 
9
9
  let {
10
10
  id = crypto.randomUUID(),
11
+ wrapperClass = "",
11
12
  label = { name: "", class: "" },
12
13
  data = $bindable(),
13
14
  markerIcon,
@@ -15,6 +16,15 @@
15
16
  trackLength = $bindable(1000),
16
17
  }: IMapProps = $props()
17
18
 
19
+ /* Мастер-цвет (тот же bg-* из optionsStore.COLOR_OPTIONS, что и у остальных примитивов) —
20
+ проявляется как акцентная рамка карты, т.к. заливать фон самой карты цветом нет смысла. */
21
+ const roleBorderClass = $derived.by(() => {
22
+ const role = wrapperClass.match(/bg-(\w+)/)?.[1]
23
+ /* border-(--border-color) — реальная utility, красящая рамку; border-{role} лишь задаёт
24
+ переменную --border-color (см. app.css), как и bg-* для --bg-color (см. Button.svelte). */
25
+ return role && role !== "max" ? `border-2 border-(--border-color) border-${role}` : ""
26
+ })
27
+
18
28
  interface MapDevice extends IDeviceGNSS {
19
29
  isFresh: boolean
20
30
  staleTimeoutId: number | null
@@ -131,12 +141,12 @@
131
141
  }
132
142
  </script>
133
143
 
134
- <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="h-full min-h-50">
144
+ <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class={twMerge("h-full min-h-50", wrapperClass)}>
135
145
  {#if label.name}
136
146
  <h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
137
147
  {/if}
138
148
  <MapLibre
139
- class="h-[calc(100%-2rem)] min-h-50 overflow-hidden rounded-xl shadow-sm transition duration-200 hover:shadow-md"
149
+ class={twMerge("h-[calc(100%-2rem)] min-h-50 overflow-hidden rounded-xl shadow-sm transition duration-200 hover:shadow-md", roleBorderClass)}
140
150
  style={isDarkMode ? "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json" : "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"}
141
151
  zoom={1.5}
142
152
  center={{ lat: 30, lng: 0 }}
@@ -28,6 +28,12 @@
28
28
  (a.value as string).includes(component.properties.label?.class?.split(" ").find((cls: string) => cls.startsWith("text-"))),
29
29
  ),
30
30
  )
31
+
32
+ const initialColor = $derived(
33
+ $optionsStore.COLOR_OPTIONS.find((c) =>
34
+ (c.value as string).includes(component.properties.wrapperClass?.split(" ").find((cls: string) => cls.startsWith("bg-"))),
35
+ ),
36
+ )
31
37
  </script>
32
38
 
33
39
  {#if forConstructor}
@@ -67,6 +73,7 @@
67
73
  onUpdate={(option) => updateProperty("trackLength", (option as { value: number }).value, component, onPropertyChange)}
68
74
  />
69
75
  {/if}
76
+ <CommonSnippets snippet="Colors" initialValue={{ color: initialColor }} {component} {onPropertyChange} />
70
77
  </PropsGroup>
71
78
  </div>
72
79
  {:else}
@@ -106,6 +113,7 @@
106
113
  onUpdate={(option) => updateProperty("trackLength", (option as { value: number }).value, component, onPropertyChange)}
107
114
  />
108
115
  {/if}
116
+ <CommonSnippets snippet="Colors" initialValue={{ color: initialColor }} {component} {onPropertyChange} />
109
117
  </PropsGroup>
110
118
  </div>
111
119
  {/if}
@@ -77,28 +77,17 @@
77
77
 
78
78
  <div bind:this={itemsContainer} class="flex flex-col gap-2">
79
79
  {#each component.properties.levels || [] as level, index}
80
- <div id="item-{index}" class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
81
- <div class="flex items-center gap-2">
82
- <UI.Dragging
83
- wrapperClass="shrink-0"
84
- container={itemsContainer}
85
- array={component.properties.levels}
86
- elementIndex={index}
87
- onUpdate={(updatedArray) => updateLevels(updatedArray)}
88
- />
89
- <span class="flex-1"></span>
90
- {#if (component.properties.levels?.length ?? 0) > 1}
91
- <UI.Button
92
- wrapperClass="w-8 shrink-0"
93
- content={{ icon: ButtonDelete }}
94
- onClick={() => {
95
- const levels = [...(component.properties?.levels || [])]
96
- levels.splice(index, 1)
97
- updateLevels(levels)
98
- }}
99
- />
100
- {/if}
101
- </div>
80
+ <div
81
+ id="item-{index}"
82
+ class="grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2"
83
+ >
84
+ <UI.Dragging
85
+ wrapperClass="shrink-0"
86
+ container={itemsContainer}
87
+ array={component.properties.levels}
88
+ elementIndex={index}
89
+ onUpdate={(updatedArray) => updateLevels(updatedArray)}
90
+ />
102
91
  <div class="flex flex-wrap items-end gap-2">
103
92
  <UI.Input
104
93
  label={{ name: $T("constructor.props.optionname") }}
@@ -143,6 +132,17 @@
143
132
  }}
144
133
  />
145
134
  </div>
135
+ {#if (component.properties.levels?.length ?? 0) > 1}
136
+ <UI.Button
137
+ wrapperClass="w-8 shrink-0"
138
+ content={{ icon: ButtonDelete }}
139
+ onClick={() => {
140
+ const levels = [...(component.properties?.levels || [])]
141
+ levels.splice(index, 1)
142
+ updateLevels(levels)
143
+ }}
144
+ />
145
+ {/if}
146
146
  </div>
147
147
  {/each}
148
148
  <div id="item-{component.properties.levels?.length ?? 0}" class="min-h-4"></div>
@@ -74,28 +74,19 @@
74
74
 
75
75
  <div bind:this={itemsContainer} class="flex flex-col gap-2">
76
76
  {#each component.properties.items || [] as progress, index}
77
- <div id="item-{index}" class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
78
- <div class="flex items-center gap-2">
79
- <UI.Dragging
80
- wrapperClass="shrink-0"
81
- container={itemsContainer}
82
- array={component.properties.items}
83
- elementIndex={index}
84
- onUpdate={(updatedArray) => {
85
- updateProperty("items", updatedArray, component, onPropertyChange)
86
- }}
87
- />
88
- <span class="flex-1"></span>
89
- <UI.Button
90
- wrapperClass="w-8 shrink-0"
91
- content={{ icon: ButtonDelete }}
92
- onClick={() => {
93
- const progresses = [...(component.properties?.items || [])]
94
- progresses.splice(index, 1)
95
- updateProperty("items", progresses, component, onPropertyChange)
96
- }}
97
- />
98
- </div>
77
+ <div
78
+ id="item-{index}"
79
+ class="grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2"
80
+ >
81
+ <UI.Dragging
82
+ wrapperClass="shrink-0"
83
+ container={itemsContainer}
84
+ array={component.properties.items}
85
+ elementIndex={index}
86
+ onUpdate={(updatedArray) => {
87
+ updateProperty("items", updatedArray, component, onPropertyChange)
88
+ }}
89
+ />
99
90
  <div class="flex flex-wrap items-end gap-2">
100
91
  <UI.Input
101
92
  label={{ name: $T("constructor.props.optionname") }}
@@ -154,6 +145,15 @@
154
145
  />
155
146
  {/if}
156
147
  </div>
148
+ <UI.Button
149
+ wrapperClass="w-8 shrink-0"
150
+ content={{ icon: ButtonDelete }}
151
+ onClick={() => {
152
+ const progresses = [...(component.properties?.items || [])]
153
+ progresses.splice(index, 1)
154
+ updateProperty("items", progresses, component, onPropertyChange)
155
+ }}
156
+ />
157
157
  </div>
158
158
  {/each}
159
159
  <div id="item-{component.properties.items.length}" class="min-h-4"></div>
@@ -191,28 +191,19 @@
191
191
 
192
192
  <div bind:this={itemsContainer} class="flex flex-col gap-2">
193
193
  {#each component.properties.options || [] as option, index (option.id)}
194
- <div id="item-{index}" class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
195
- <div class="flex items-center gap-2">
196
- <UI.Dragging
197
- wrapperClass="shrink-0"
198
- container={itemsContainer}
199
- array={component.properties.options}
200
- elementIndex={index}
201
- onUpdate={(updatedArray) => {
202
- updateProperty("options", updatedArray, component, onPropertyChange)
203
- }}
204
- />
205
- <span class="flex-1"></span>
206
- <UI.Button
207
- wrapperClass="w-8 shrink-0"
208
- content={{ icon: ButtonDelete }}
209
- onClick={() => {
210
- const options = [...(component.properties?.options || [])]
211
- options.splice(index, 1)
212
- updateProperty("options", options, component, onPropertyChange)
213
- }}
214
- />
215
- </div>
194
+ <div
195
+ id="item-{index}"
196
+ class="grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2"
197
+ >
198
+ <UI.Dragging
199
+ wrapperClass="shrink-0"
200
+ container={itemsContainer}
201
+ array={component.properties.options}
202
+ elementIndex={index}
203
+ onUpdate={(updatedArray) => {
204
+ updateProperty("options", updatedArray, component, onPropertyChange)
205
+ }}
206
+ />
216
207
  <div class="flex flex-wrap items-end gap-2">
217
208
  <UI.Input
218
209
  label={{ name: $T("constructor.props.optionname") }}
@@ -266,6 +257,15 @@
266
257
  />
267
258
  {/if}
268
259
  </div>
260
+ <UI.Button
261
+ wrapperClass="w-8 shrink-0"
262
+ content={{ icon: ButtonDelete }}
263
+ onClick={() => {
264
+ const options = [...(component.properties?.options || [])]
265
+ options.splice(index, 1)
266
+ updateProperty("options", options, component, onPropertyChange)
267
+ }}
268
+ />
269
269
  </div>
270
270
  {/each}
271
271
  <div id="item-{component.properties.options.length}" class="min-h-4"></div>
@@ -109,19 +109,7 @@
109
109
  {/snippet}
110
110
 
111
111
  {#each component.properties.options || [] as option, index (option.id)}
112
- <div class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
113
- <div class="flex items-center gap-2">
114
- <span class="flex-1"></span>
115
- <UI.Button
116
- wrapperClass="w-8 shrink-0"
117
- content={{ icon: ButtonDelete }}
118
- onClick={() => {
119
- const options = [...(component.properties?.options || [])]
120
- options.splice(index, 1)
121
- updateProperty("options", options, component, onPropertyChange)
122
- }}
123
- />
124
- </div>
112
+ <div class="grid grid-cols-[1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
125
113
  <div class="flex flex-wrap items-end gap-2">
126
114
  <UI.Input
127
115
  label={{ name: $T("constructor.props.optionname") }}
@@ -172,6 +160,15 @@
172
160
  }}
173
161
  />
174
162
  </div>
163
+ <UI.Button
164
+ wrapperClass="w-8 shrink-0"
165
+ content={{ icon: ButtonDelete }}
166
+ onClick={() => {
167
+ const options = [...(component.properties?.options || [])]
168
+ options.splice(index, 1)
169
+ updateProperty("options", options, component, onPropertyChange)
170
+ }}
171
+ />
175
172
  </div>
176
173
  {/each}
177
174
  </PropsGroup>
@@ -238,36 +238,25 @@
238
238
  <div class="flex flex-col gap-2" bind:this={settingsContainer[columnIndex]}>
239
239
  {#if typeof column.content !== "function"}
240
240
  {#each column.content as content, index}
241
- <div id={`item-${index}-${columnIndex}`} class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--back-color)/60 p-2">
242
- <!-- Тулбар карточки: перетаскивание слева, удаление справа — поля контента никогда
243
- не переносятся поверх этих кнопок, в отличие от прежней абсолютной раскладки -->
244
- <div class="flex items-center gap-2">
245
- <Dragging
246
- wrapperClass="shrink-0"
247
- container={settingsContainer[columnIndex]}
248
- array={column.content}
249
- elementIndex={index}
250
- containerIndex={columnIndex}
251
- onUpdate={(updatedArray, index) => {
252
- if (index === columnIndex) {
253
- const headers = [...component.properties.header]
254
- headers[columnIndex].content = updatedArray
255
- updateProperty("header", headers, component, onPropertyChange)
256
- }
257
- }}
258
- />
259
- <span class="flex-1"></span>
260
- <UI.Button
261
- wrapperClass="w-8 shrink-0"
262
- content={{ icon: ButtonDelete }}
263
- onClick={() => {
264
- const headers = [...(component.properties.header || [])]
265
- ;((headers as ITableHeader<object>[])[columnIndex].content as ITableContent<object>[]).splice(index, 1)
241
+ <div
242
+ id={`item-${index}-${columnIndex}`}
243
+ class="grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--back-color)/60 p-2"
244
+ >
245
+ <Dragging
246
+ wrapperClass="shrink-0"
247
+ container={settingsContainer[columnIndex]}
248
+ array={column.content}
249
+ elementIndex={index}
250
+ containerIndex={columnIndex}
251
+ onUpdate={(updatedArray, index) => {
252
+ if (index === columnIndex) {
253
+ const headers = [...component.properties.header]
254
+ headers[columnIndex].content = updatedArray
266
255
  updateProperty("header", headers, component, onPropertyChange)
267
- }}
268
- />
269
- </div>
270
-
256
+ }
257
+ }}
258
+ />
259
+ <div class="flex min-w-0 flex-col gap-2">
271
260
  {#if content.type == "text"}
272
261
  {@const text = content.data}
273
262
  <div class="flex flex-wrap items-end gap-2">
@@ -495,6 +484,16 @@
495
484
  />
496
485
  </div>
497
486
  {/if}
487
+ </div>
488
+ <UI.Button
489
+ wrapperClass="w-8 shrink-0"
490
+ content={{ icon: ButtonDelete }}
491
+ onClick={() => {
492
+ const headers = [...(component.properties.header || [])]
493
+ ;((headers as ITableHeader<object>[])[columnIndex].content as ITableContent<object>[]).splice(index, 1)
494
+ updateProperty("header", headers, component, onPropertyChange)
495
+ }}
496
+ />
498
497
  </div>
499
498
  {/each}
500
499
  {/if}
@@ -112,34 +112,19 @@
112
112
 
113
113
  <div bind:this={itemsContainer} class="flex flex-col gap-2">
114
114
  {#each component.properties.items || [] as tab, index}
115
- <div id="item-{index}" class="flex flex-col gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2">
116
- <div class="flex items-center gap-2">
117
- <UI.Dragging
118
- wrapperClass="shrink-0"
119
- container={itemsContainer}
120
- array={component.properties.items}
121
- elementIndex={index}
122
- onUpdate={(updatedArray) => {
123
- updateProperty("items", updatedArray, component, onPropertyChange)
124
- }}
125
- />
126
- <span class="flex-1"></span>
127
- {#if component.properties.items.length > 1}
128
- <UI.Button
129
- wrapperClass="w-8 shrink-0"
130
- content={{ icon: ButtonDelete }}
131
- onClick={() => {
132
- const items = [...(component.properties?.items || [])]
133
- items.splice(index, 1)
134
- items.forEach((_item: any, index: number) => {
135
- items[index]["class"] = twMerge(items[index].class, initialWidth() ? `w-[${(1 / items.length) * 100}%]` : "w-auto")
136
- updateProperty("items", items, component, onPropertyChange)
137
- })
138
- updateProperty("items", items, component, onPropertyChange)
139
- }}
140
- />
141
- {/if}
142
- </div>
115
+ <div
116
+ id="item-{index}"
117
+ class="grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 p-2"
118
+ >
119
+ <UI.Dragging
120
+ wrapperClass="shrink-0"
121
+ container={itemsContainer}
122
+ array={component.properties.items}
123
+ elementIndex={index}
124
+ onUpdate={(updatedArray) => {
125
+ updateProperty("items", updatedArray, component, onPropertyChange)
126
+ }}
127
+ />
143
128
  <div class="flex flex-wrap items-end gap-2">
144
129
  <UI.Input
145
130
  label={{ name: $T("constructor.props.optionname") }}
@@ -181,6 +166,21 @@
181
166
  }}
182
167
  />
183
168
  </div>
169
+ {#if component.properties.items.length > 1}
170
+ <UI.Button
171
+ wrapperClass="w-8 shrink-0"
172
+ content={{ icon: ButtonDelete }}
173
+ onClick={() => {
174
+ const items = [...(component.properties?.items || [])]
175
+ items.splice(index, 1)
176
+ items.forEach((_item: any, index: number) => {
177
+ items[index]["class"] = twMerge(items[index].class, initialWidth() ? `w-[${(1 / items.length) * 100}%]` : "w-auto")
178
+ updateProperty("items", items, component, onPropertyChange)
179
+ })
180
+ updateProperty("items", items, component, onPropertyChange)
181
+ }}
182
+ />
183
+ {/if}
184
184
  </div>
185
185
  {/each}
186
186
  <div id="item-{component.properties.items.length}" class="min-h-4"></div>
@@ -19,6 +19,13 @@
19
19
 
20
20
  const isRemote = $derived(source === "remote")
21
21
 
22
+ /* Мастер-цвет (тот же bg-* из optionsStore.COLOR_OPTIONS, что и у остальных примитивов) —
23
+ проявляется как акцентная рамка кадра, см. Map.svelte для того же приёма. */
24
+ const roleBorderClass = $derived.by(() => {
25
+ const role = wrapperClass?.match(/bg-(\w+)/)?.[1]
26
+ return role && role !== "max" ? `border-2 border-(--border-color) border-${role}` : ""
27
+ })
28
+
22
29
  let videoElement = $state<HTMLVideoElement | null>(null)
23
30
  let stream = $state<MediaStream | null>(null)
24
31
  let error = $state<string | null>(null)
@@ -139,7 +146,7 @@
139
146
  {#if label.name}
140
147
  <h5 class={twMerge(`w-full px-4 text-center`, label.class)}>{label.name}</h5>
141
148
  {/if}
142
- <div class="relative flex flex-1 w-full items-center justify-center">
149
+ <div class={twMerge("relative flex flex-1 w-full items-center justify-center rounded-2xl", roleBorderClass)}>
143
150
  {#if isRemote}
144
151
  {#if status !== "live" || !remoteImgSrc}
145
152
  <div class="absolute h-full w-full rounded-2xl px-40 py-15 flex flex-col items-center justify-center bg-(--border-color)/50 gap-4 z-10">
@@ -4,6 +4,7 @@
4
4
  import * as UI from ".."
5
5
  import CommonSnippets from "../CommonSnippets.svelte"
6
6
  import PropsGroup from "../PropsGroup.svelte"
7
+ import { optionsStore } from "../options"
7
8
 
8
9
  const {
9
10
  component,
@@ -14,6 +15,12 @@
14
15
  onPropertyChange: (updates: Partial<{ properties?: string | object; name?: string; access?: string; eventHandler?: IUIComponentHandler }>) => void
15
16
  forConstructor?: boolean
16
17
  }>()
18
+
19
+ const initialColor = $derived(
20
+ $optionsStore.COLOR_OPTIONS.find((c) =>
21
+ (c.value as string).includes(component.properties.wrapperClass?.split(" ").find((cls: string) => cls.startsWith("bg-"))),
22
+ ),
23
+ )
17
24
  </script>
18
25
 
19
26
  {#snippet VideoViewershowSelect()}
@@ -60,6 +67,7 @@
60
67
  {#if component.properties?.source === "remote"}
61
68
  {@render VideoViewerStreamKey()}
62
69
  {/if}
70
+ <CommonSnippets snippet="Colors" initialValue={{ color: initialColor }} {component} {onPropertyChange} />
63
71
  </PropsGroup>
64
72
  </div>
65
73
  {:else}
@@ -78,6 +86,7 @@
78
86
  {#if component.properties?.source === "remote"}
79
87
  {@render VideoViewerStreamKey()}
80
88
  {/if}
89
+ <CommonSnippets snippet="Colors" initialValue={{ color: initialColor }} {component} {onPropertyChange} />
81
90
  </PropsGroup>
82
91
  </div>
83
92
  {/if}
@@ -27,6 +27,15 @@
27
27
  (c.value as string).includes(component.properties.settings.class?.split(" ").find((cls: string) => cls.startsWith("bg-"))),
28
28
  ),
29
29
  )
30
+
31
+ /* Один мастер-цвет вместо двух независимых пикеров: цвет иконки больше не выбирается отдельно,
32
+ а выводится из того же bg-* (COLOR_OPTIONS и TEXT_COLOR_OPTIONS — одна и та же ролевая палитра
33
+ red/yellow/green/blue/purple/gray, см. options.ts). */
34
+ const deriveIconColorClass = (bgClass: string): string => {
35
+ const role = bgClass.match(/bg-(\w+)/)?.[1]
36
+ const match = role && role !== "max" ? $optionsStore.TEXT_COLOR_OPTIONS.find((c) => c.id === `TEXT_COLOR_${role.toUpperCase()}`) : null
37
+ return (match ?? $optionsStore.TEXT_COLOR_OPTIONS[0]).value as string
38
+ }
30
39
  </script>
31
40
 
32
41
  {#snippet WidgetSettingsLabel()}
@@ -45,22 +54,9 @@
45
54
  options={$optionsStore.COLOR_OPTIONS}
46
55
  value={initialColor}
47
56
  onUpdate={(option) => {
48
- updateProperty("settings.class", twMerge(component.properties.settings.class, (option as UI.IOption<string>).value), component, onPropertyChange)
49
- }}
50
- />
51
- {/snippet}
52
-
53
- {#snippet WidgetIconColor()}
54
- <UI.Select
55
- wrapperClass="!h-14"
56
- label={{ name: $T("constructor.props.iconcolor") }}
57
- type="buttons"
58
- options={$optionsStore.TEXT_COLOR_OPTIONS}
59
- value={$optionsStore.TEXT_COLOR_OPTIONS.find((c) =>
60
- (c.value as string).includes(component.properties.icons.class?.split(" ").find((cls: string) => cls.startsWith("text-"))),
61
- )}
62
- onUpdate={(option) => {
63
- updateProperty("icons.class", twMerge(component.properties.icons.class, (option as UI.IOption<string>).value), component, onPropertyChange)
57
+ const bgClass = (option as UI.IOption<string>).value as string
58
+ updateProperty("settings.class", twMerge(component.properties.settings.class, bgClass), component, onPropertyChange)
59
+ updateProperty("icons.class", twMerge(component.properties.icons.class, deriveIconColorClass(bgClass)), component, onPropertyChange)
64
60
  }}
65
61
  />
66
62
  {/snippet}
@@ -132,7 +128,6 @@
132
128
  <CommonSnippets snippet="Label" {component} {onPropertyChange} />
133
129
  {@render WidgetUnits()}
134
130
  {@render WidgetIcons()}
135
- {@render WidgetIconColor()}
136
131
  {@render WidgetSwitchingMode()}
137
132
  </PropsGroup>
138
133
  <PropsGroup label={$T("constructor.props.group.appearance")}>
@@ -169,7 +164,6 @@
169
164
  <PropsGroup label={$T("constructor.props.group.content")}>
170
165
  {@render WidgetSettingsLabel()}
171
166
  {@render WidgetIcons()}
172
- {@render WidgetIconColor()}
173
167
  {@render WidgetSwitchingMode()}
174
168
  </PropsGroup>
175
169
  <PropsGroup label={$T("constructor.props.group.appearance")}>
@@ -1,7 +1,9 @@
1
1
  <!-- $lib/WidgetHeader.svelte — общая шапка смарт-виджетов конструктора: значок + заголовок
2
2
  сворачивают/разворачивают тело виджета по клику (как аккордеон), справа — снипет с любым
3
3
  содержимым шапки конкретного виджета (переключатель режима, кнопка "Перезагрузить" и т.п.),
4
- клик по нему сворачивание не триггерит. -->
4
+ клик по нему сворачивание не триггерит. Визуально — та же полоса-заголовок, что и у Accordion
5
+ (px-3/py-2, bg-container, мелкая 18px иконка без бейджа) вместо прежнего тяжёлого 32px
6
+ значка-бейджа, который делал шапку заметно выше аккордеона. -->
5
7
  <script lang="ts">
6
8
  import type { Component, Snippet } from "svelte"
7
9
  import ArrowIcon from "./libIcons/ArrowIcon.svelte"
@@ -21,19 +23,19 @@
21
23
  const Icon = $derived(icon)
22
24
  </script>
23
25
 
24
- <div class="flex flex-wrap items-center justify-between gap-3">
26
+ <div class="flex flex-wrap items-center justify-between gap-3 rounded-[10px] bg-(--container-color) px-3 py-2">
25
27
  <button
26
28
  type="button"
27
- class="flex min-w-0 cursor-pointer items-center gap-2 border-none bg-transparent p-0 text-left"
29
+ class="flex min-w-0 flex-1 cursor-pointer items-center gap-2 border-none bg-transparent p-0 text-left"
28
30
  onclick={() => (collapsed = !collapsed)}
29
31
  aria-expanded={!collapsed}
30
32
  >
31
- <span class="flex size-8 shrink-0 items-center justify-center rounded-xl bg-(--accent-soft) text-(--accent-color)">
33
+ <span class="flex h-[18px] w-[18px] shrink-0 items-center justify-center text-(--accent-color) [&_svg]:h-full [&_svg]:w-full">
32
34
  <Icon />
33
35
  </span>
34
36
  <span class="min-w-0 truncate text-[14px] font-semibold">{label}</span>
35
37
  <span
36
- class="flex size-4 shrink-0 items-center justify-center text-(--faint-color) transition-transform duration-150 [&_svg]:h-full [&_svg]:w-full"
38
+ class="flex h-[14px] w-[14px] shrink-0 items-center justify-center text-(--faint-color) transition-transform duration-150 [&_svg]:h-full [&_svg]:w-full"
37
39
  style="transform: rotate({collapsed ? 0 : 90}deg)"
38
40
  >
39
41
  <ArrowIcon />
@@ -153,7 +153,7 @@
153
153
  onUpdate={(option) => (cfg.StaSSID = (option as IOption<string>).name ?? "")}
154
154
  />
155
155
  <UI.Button
156
- wrapperClass="w-9"
156
+ wrapperClass="w-8"
157
157
  componentClass="p-1"
158
158
  content={{ icon: scanning ? LoaderIcon : SearchIcon, info: { text: "Сканировать сети", side: "top" } }}
159
159
  disabled={scanning || !onScan}
package/dist/options.js CHANGED
@@ -57,17 +57,18 @@ export const optionsStore = derived(T, ($T) => {
57
57
  /* Единая семантическая палитра (app.css: --X-color/--X-soft, светлая и тёмная тема уже учтены) —
58
58
  тот же набор ролей, что и в "мастер-цвете" виджетов конструктора (widgetAccent.ts на стороне
59
59
  приложения). bg-max — нейтральный вариант (= --back-color), с ним свотч отмечается рамкой
60
- (Select.svelte: hasOwnColor), а не акцентной синей заливкой, как обычный сегмент-контрол. */
60
+ (Select.svelte: hasOwnColor), а не акцентной синей заливкой, как обычный сегмент-контрол.
61
+ 7 пунктов вместо прежних 9: orange/cyan/pink убраны — на глаз сливались с yellow/green/red
62
+ соответственно; состав теперь 1:1 с TEXT_COLOR_OPTIONS (red/yellow/green/blue/purple/gray) —
63
+ там уже давно только явно различимые цвета. */
61
64
  COLOR_OPTIONS: [
62
65
  { id: id(), name: "-", value: "bg-max", class: "bg-max text-transparent" },
63
66
  { id: id(), name: "", value: "bg-red", class: "bg-red" },
64
- { id: id(), name: "", value: "bg-orange", class: "bg-orange" },
65
67
  { id: id(), name: "", value: "bg-yellow", class: "bg-yellow" },
66
68
  { id: id(), name: "", value: "bg-green", class: "bg-green" },
67
- { id: id(), name: "", value: "bg-cyan", class: "bg-cyan" },
68
69
  { id: id(), name: "", value: "bg-blue", class: "bg-blue" },
69
70
  { id: id(), name: "", value: "bg-purple", class: "bg-purple" },
70
- { id: id(), name: "", value: "bg-pink", class: "bg-pink" },
71
+ { id: id(), name: "", value: "bg-gray", class: "bg-gray" },
71
72
  ],
72
73
  TEXT_COLOR_OPTIONS: [
73
74
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {