poe-svelte-ui-lib 1.34.1 → 1.35.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.
- package/dist/Select/Select.svelte +29 -18
- package/dist/libIcons/propsIcons.d.ts +36 -0
- package/dist/libIcons/propsIcons.js +47 -0
- package/dist/locales/translations.js +109 -40
- package/dist/options.d.ts +18 -0
- package/dist/options.js +59 -58
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/dist/ComponentExample.svelte +0 -89
- package/dist/ComponentExample.svelte.d.ts +0 -12
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
import type { IOption, ISelectProps } from "../types"
|
|
8
8
|
import { twMerge } from "tailwind-merge"
|
|
9
9
|
import { T } from "../locales/i18n"
|
|
10
|
+
import Tooltip from "../Tooltip.svelte"
|
|
10
11
|
|
|
11
12
|
let isDropdownOpen = $state(false)
|
|
13
|
+
/* Вариант-иконка под курсором в type="buttons": его name показывается подсказкой */
|
|
14
|
+
let hoveredOptionId: string | null = $state(null)
|
|
12
15
|
let dropdownElement: HTMLDivElement
|
|
13
16
|
let portalElement: HTMLDivElement | undefined = $state()
|
|
14
17
|
let anchorRect: { top: number; bottom: number; left: number; width: number } | null = $state(null)
|
|
@@ -121,10 +124,7 @@
|
|
|
121
124
|
const selectedOption = options.find((option) => option.name?.toString() === searchValue) || {
|
|
122
125
|
id: `input-${Date.now()}`,
|
|
123
126
|
name: searchValue,
|
|
124
|
-
value:
|
|
125
|
-
typeof options[0]?.value == "number" && searchValue !== "" && !isNaN(Number(searchValue))
|
|
126
|
-
? (Number(searchValue) as T)
|
|
127
|
-
: (searchValue as T),
|
|
127
|
+
value: typeof options[0]?.value == "number" && searchValue !== "" && !isNaN(Number(searchValue)) ? (Number(searchValue) as T) : (searchValue as T),
|
|
128
128
|
}
|
|
129
129
|
value = selectedOption
|
|
130
130
|
onUpdate?.(selectedOption)
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
<!-- Сплошная группа: общий контур, разделители внутри, без зазоров и теней -->
|
|
207
207
|
<div
|
|
208
208
|
id={`${id}-${crypto.randomUUID().slice(0, 6)}`}
|
|
209
|
-
class="flex w-full
|
|
209
|
+
class="flex w-full rounded-[10px] border border-(--border-color) {disabled ? 'opacity-45' : ''}"
|
|
210
210
|
role="group"
|
|
211
211
|
>
|
|
212
212
|
{#each options as option, index (option.id)}
|
|
@@ -220,10 +220,12 @@
|
|
|
220
220
|
в ряд одинаковых белых кнопок. Выбор у таких опций отмечается рамкой, а не заливкой;
|
|
221
221
|
для обычного сегмент-контрола без своего цвета (Access, Header и т.п.) поведение
|
|
222
222
|
прежнее — акцентная заливка только у выбранного пункта. -->
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
<!-- Обёртка — позиционирующий предок подсказки; overflow-hidden у группы убран, иначе подсказка обрезается -->
|
|
224
|
+
<div class="relative flex min-w-0 flex-1">
|
|
225
|
+
<button
|
|
226
|
+
id={option.id}
|
|
227
|
+
class="{twMerge(
|
|
228
|
+
`flex h-8 w-full min-w-0 items-center justify-center px-3 py-2 text-[13px] font-semibold transition-colors duration-150
|
|
227
229
|
${index === 0 ? 'rounded-l-[9px]' : ''} ${index === options.length - 1 ? 'rounded-r-[9px]' : ''}
|
|
228
230
|
${index > 0 ? 'border-l border-(--border-color)' : ''}
|
|
229
231
|
${
|
|
@@ -236,16 +238,25 @@
|
|
|
236
238
|
: 'text-(--font-color) hover:bg-(--container-color)'
|
|
237
239
|
}
|
|
238
240
|
${option.disabled || disabled ? 'cursor-not-allowed text-(--faint-color)' : 'cursor-pointer'}`,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
241
|
+
option.class,
|
|
242
|
+
)} {hasOwnColor ? 'bg-(--bg-color)' : selected ? 'bg-(--bg-color,var(--accent-color))' : 'bg-(--back-color)'}"
|
|
243
|
+
onclick={(e) => selectOption(option, e)}
|
|
244
|
+
disabled={option.disabled || disabled}
|
|
245
|
+
aria-pressed={!!isSelected(option)}
|
|
246
|
+
aria-label={option.icon ? option.name : undefined}
|
|
247
|
+
onmouseenter={() => (hoveredOptionId = option.icon && option.name ? option.id : null)}
|
|
248
|
+
onmouseleave={() => (hoveredOptionId = null)}
|
|
249
|
+
>
|
|
250
|
+
{#if option.icon}
|
|
251
|
+
<span class="flex h-[18px] min-w-[18px] items-center justify-center leading-none [&_svg]:h-full [&_svg]:w-full">{@html option.icon}</span>
|
|
252
|
+
{:else if option.name}
|
|
253
|
+
<span class="block truncate">{option.name}</span>
|
|
254
|
+
{/if}
|
|
255
|
+
</button>
|
|
256
|
+
{#if option.icon && option.name}
|
|
257
|
+
<Tooltip show={hoveredOptionId === option.id} text={option.name} side="top" />
|
|
247
258
|
{/if}
|
|
248
|
-
</
|
|
259
|
+
</div>
|
|
249
260
|
{/each}
|
|
250
261
|
</div>
|
|
251
262
|
{:else if type === "input"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const PROPS_ICONS: {
|
|
2
|
+
alignLeft: string;
|
|
3
|
+
alignCenter: string;
|
|
4
|
+
alignRight: string;
|
|
5
|
+
alignJustify: string;
|
|
6
|
+
bold: string;
|
|
7
|
+
italic: string;
|
|
8
|
+
underline: string;
|
|
9
|
+
highlight: string;
|
|
10
|
+
arrowUp: string;
|
|
11
|
+
arrowDown: string;
|
|
12
|
+
arrowLeft: string;
|
|
13
|
+
arrowRight: string;
|
|
14
|
+
anchorTop: string;
|
|
15
|
+
anchorCenter: string;
|
|
16
|
+
anchorBottom: string;
|
|
17
|
+
square: string;
|
|
18
|
+
circle: string;
|
|
19
|
+
toggleHorizontal: string;
|
|
20
|
+
toggleVertical: string;
|
|
21
|
+
checkbox: string;
|
|
22
|
+
sliderSingle: string;
|
|
23
|
+
sliderRange: string;
|
|
24
|
+
dividerLine: string;
|
|
25
|
+
dividerSpace: string;
|
|
26
|
+
fitCover: string;
|
|
27
|
+
fitContain: string;
|
|
28
|
+
file: string;
|
|
29
|
+
image: string;
|
|
30
|
+
sort: string;
|
|
31
|
+
copy: string;
|
|
32
|
+
ellipsis: string;
|
|
33
|
+
tooltip: string;
|
|
34
|
+
modal: string;
|
|
35
|
+
lock: string;
|
|
36
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* $lib/libIcons/propsIcons.ts — иконки вариантов панелей свойств (IOption.icon): контурные 24×24 в стиле остальных иконок библиотеки */
|
|
2
|
+
const svg = (d) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${d}"/></svg>`;
|
|
3
|
+
export const PROPS_ICONS = {
|
|
4
|
+
/* Выравнивание текста и блока */
|
|
5
|
+
alignLeft: svg("M15 12H3M17 18H3M21 6H3"),
|
|
6
|
+
alignCenter: svg("M17 12H7M19 18H5M21 6H3"),
|
|
7
|
+
alignRight: svg("M21 12H9M21 18H7M21 6H3"),
|
|
8
|
+
alignJustify: svg("M3 12h18M3 18h18M3 6h18"),
|
|
9
|
+
/* Начертание текста */
|
|
10
|
+
bold: svg("M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8"),
|
|
11
|
+
italic: svg("M19 4h-9M14 20H5M15 4 9 20"),
|
|
12
|
+
underline: svg("M6 4v6a6 6 0 0 0 12 0V4M4 20h16"),
|
|
13
|
+
highlight: svg("m9 11-6 6v3h9l3-3m7-5-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"),
|
|
14
|
+
/* Стороны и направления */
|
|
15
|
+
arrowUp: svg("m5 12 7-7 7 7M12 19V5"),
|
|
16
|
+
arrowDown: svg("M12 5v14m7-7-7 7-7-7"),
|
|
17
|
+
arrowLeft: svg("m12 19-7-7 7-7m7 7H5"),
|
|
18
|
+
arrowRight: svg("M5 12h14m-7-7 7 7-7 7"),
|
|
19
|
+
/* Привязка по вертикали */
|
|
20
|
+
anchorTop: svg("M5 3h14M18 13l-6-6-6 6M12 7v14"),
|
|
21
|
+
anchorCenter: svg("M3 12h18M8 4l4 4 4-4M8 20l4-4 4 4"),
|
|
22
|
+
anchorBottom: svg("M12 17V3M6 11l6 6 6-6M19 21H5"),
|
|
23
|
+
/* Формы и ориентация */
|
|
24
|
+
square: svg("M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z"),
|
|
25
|
+
circle: svg("M12 2a10 10 0 1 0 0 20 10 10 0 1 0 0-20z"),
|
|
26
|
+
toggleHorizontal: svg("M8 5h8a7 7 0 0 1 0 14H8A7 7 0 0 1 8 5zm8 4a3 3 0 1 0 0 6 3 3 0 1 0 0-6z"),
|
|
27
|
+
toggleVertical: svg("M5 8a7 7 0 0 1 14 0v8a7 7 0 0 1-14 0zm7-3a3 3 0 1 0 0 6 3 3 0 1 0 0-6z"),
|
|
28
|
+
checkbox: svg("M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm4 9 2 2 4-4"),
|
|
29
|
+
/* Слайдер и разделитель */
|
|
30
|
+
sliderSingle: svg("M3 12h2m6 0h10M8 9a3 3 0 1 0 0 6 3 3 0 1 0 0-6z"),
|
|
31
|
+
sliderRange: svg("M3 12h1m6 0h4m6 0h1M7 9a3 3 0 1 0 0 6 3 3 0 1 0 0-6zm10 0a3 3 0 1 0 0 6 3 3 0 1 0 0-6z"),
|
|
32
|
+
dividerLine: svg("M3 12h18"),
|
|
33
|
+
dividerSpace: svg("M4 6v12M20 6v12M8 12h8"),
|
|
34
|
+
/* Вписывание изображения */
|
|
35
|
+
fitCover: svg("M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3m8 0h3a2 2 0 0 0 2-2v-3"),
|
|
36
|
+
fitContain: svg("M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3m8 0v-3a2 2 0 0 1 2-2h3"),
|
|
37
|
+
/* Файлы */
|
|
38
|
+
file: svg("M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zm0 0v6h6"),
|
|
39
|
+
image: svg("M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm4 6h.01M21 15l-5-5L5 21"),
|
|
40
|
+
/* Настройки ячеек и полей */
|
|
41
|
+
sort: svg("m3 16 4 4 4-4M7 20V4m14 4-4-4-4 4m4-4v16"),
|
|
42
|
+
copy: svg("M10 8h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2zM4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2"),
|
|
43
|
+
ellipsis: svg("M5 12h.01M12 12h.01M19 12h.01"),
|
|
44
|
+
tooltip: svg("M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"),
|
|
45
|
+
modal: svg("M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2zM2 9h20"),
|
|
46
|
+
lock: svg("M7 11V7a5 5 0 0 1 10 0v4M5 11h14a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2z"),
|
|
47
|
+
};
|
|
@@ -13,55 +13,120 @@ const translations = {
|
|
|
13
13
|
/* Документация компонентов для ⓘ в конструкторе (приложение читает через UI.T) — механизм общий
|
|
14
14
|
для всех типов компонентов, заполнено пока только там, где есть что объяснить (фиксированная
|
|
15
15
|
прошивочная конвенция у смарт-виджетов) */
|
|
16
|
-
"component.info.
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
16
|
+
"component.info.Accordion": [
|
|
17
|
+
"🎯 **Аккордеон** — сворачиваемый блок для группировки компонентов: настройки, диагностика, редко используемые разделы.\n\n**🧩 Как пользоваться**\n- Перетащите компоненты внутрь аккордеона — у него своя сетка, размер задаётся при создании и ресайзе\n- Заголовок с иконкой открывает и закрывает содержимое\n- «Открыт по умолчанию» — состояние при загрузке интерфейса\n\n**🎨 Оформление**\n- Картинка фона — изображение под содержимым (например схема устройства), поверх неё располагаются компоненты\n\n💡 Сам аккордеон ничего не отправляет и не читает с устройства. По высоте занимает одну строку сетки и растягивается по содержимому.",
|
|
18
|
+
'🎯 **Accordion** — a collapsible block for grouping components: settings, diagnostics, rarely used sections.\n\n**🧩 How to use**\n- Drag components inside — the accordion has its own grid, sized on creation and resize\n- The header with an icon opens and closes the content\n- "Open by default" — state when the interface loads\n\n**🎨 Appearance**\n- Background image — shown under the content (e.g. a device diagram), components are placed on top\n\n💡 The accordion itself neither sends nor reads anything from the device. It takes one grid row and grows with its content.',
|
|
19
|
+
"🎯 **折叠面板** —— 用于分组组件的可折叠区块:设置、诊断、不常用的部分。\n\n**🧩 使用方法**\n- 将组件拖入其中 —— 折叠面板有自己的网格,大小在创建和调整时设定\n- 带图标的标题用于展开和收起内容\n- 「默认展开」—— 界面加载时的状态\n\n**🎨 外观**\n- 背景图片 —— 显示在内容下方(例如设备示意图),组件叠放在其上\n\n💡 折叠面板本身不向设备发送也不读取任何数据。它占网格的一行,并随内容伸展。",
|
|
20
20
|
],
|
|
21
|
-
"component.info.
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
|
|
21
|
+
"component.info.Button": [
|
|
22
|
+
'🎯 **Кнопка** — отправляет команду на устройство по нажатию.\n\n**📤 Что уходит в пакете**\n- **Заголовок** и **Аргумент** — команда, например `SET` + `Restart`\n- **Переменные** — текущие значения других компонентов. Так делается кнопка «Сохранить»: поля с аргументом `NoSend` редактируются без отправки, а кнопка отправляет их все одним пакетом\n- **Фиксированное значение** (JSON) — отправляется как есть вместо переменных, например `{"Mode": 2}`\n\n**🎨 Оформление**\n- Текст, иконка, цвет, подсказка при наведении\n\n💡 Кнопка не показывает значение с устройства — только отправляет.',
|
|
23
|
+
'🎯 **Button** — sends a command to the device when pressed.\n\n**📤 What goes into the packet**\n- **Header** and **Argument** — the command, e.g. `SET` + `Restart`\n- **Variables** — current values of other components. This is how a "Save" button works: fields with the `NoSend` argument are edited without sending, and the button sends them all in one packet\n- **Fixed value** (JSON) — sent as is instead of variables, e.g. `{"Mode": 2}`\n\n**🎨 Appearance**\n- Text, icon, color, hover tooltip\n\n💡 The button doesn\'t show a device value — it only sends.',
|
|
24
|
+
'🎯 **按钮** —— 按下时向设备发送命令。\n\n**📤 数据包内容**\n- **标头** 和 **参数** —— 命令,例如 `SET` + `Restart`\n- **变量** —— 其他组件的当前值。「保存」按钮就是这样实现的:参数为 `NoSend` 的字段编辑时不发送,由按钮一次性全部发出\n- **固定值**(JSON)—— 原样发送以代替变量,例如 `{"Mode": 2}`\n\n**🎨 外观**\n- 文本、图标、颜色、悬停提示\n\n💡 按钮不显示设备值 —— 只负责发送。',
|
|
25
25
|
],
|
|
26
|
-
"component.info.
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
26
|
+
"component.info.ColorPicker": [
|
|
27
|
+
"🎯 **Выбор цвета** — палитра для задания цвета, например подсветки или RGB-ленты.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — массив `[R, G, B]` (0–255) на устройстве, текущий цвет показывается из него\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n- Значение отправляется при выборе цвета на палитре",
|
|
28
|
+
"🎯 **Color picker** — a palette for setting a color, e.g. backlight or an RGB strip.\n\n**🔗 Device link**\n- **Variable name** — an `[R, G, B]` array (0–255) on the device, the current color is shown from it\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don't send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n- The value is sent when a color is picked on the palette",
|
|
29
|
+
"🎯 **颜色选择器** —— 用于设置颜色的调色板,例如背光或 RGB 灯带。\n\n**🔗 与设备的关联**\n- **变量名** —— 设备上的 `[R, G, B]` 数组(0–255),当前颜色从中显示\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n- 在调色板上选色时发送值",
|
|
30
30
|
],
|
|
31
|
-
"component.info.
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"
|
|
31
|
+
"component.info.Divider": [
|
|
32
|
+
"🎯 **Разделитель** — визуально разделяет группы компонентов.\n\n**⚙️ Настройки**\n- Тип: **линия** или **пустое место** (отступ без линии)\n- Привязка: линия сверху, по центру или снизу ячейки\n- Цвет и толщина линии\n\n💡 Чисто визуальный элемент — с устройством не связан.",
|
|
33
|
+
"🎯 **Divider** — visually separates groups of components.\n\n**⚙️ Settings**\n- Type: **line** or **empty space** (gap without a line)\n- Anchor: line at the top, center or bottom of the cell\n- Line color and thickness\n\n💡 Purely visual — not linked to the device.",
|
|
34
|
+
"🎯 **分隔线** —— 在视觉上分隔组件组。\n\n**⚙️ 设置**\n- 类型:**线条** 或 **空白**(无线条的间距)\n- 位置:线条位于单元格顶部、中间或底部\n- 线条颜色和粗细\n\n💡 纯视觉元素 —— 与设备无关。",
|
|
35
35
|
],
|
|
36
|
-
"component.info.
|
|
37
|
-
"
|
|
38
|
-
'
|
|
39
|
-
"
|
|
36
|
+
"component.info.FileAttach": [
|
|
37
|
+
"🎯 **Выбор файла** — закачивает выбранный файл в файловую систему устройства.\n\n**📤 Закачка**\n- После выбора файл передаётся частями командой `SET FS` (`UploadStart` → `UploadChunk` → `UploadDone`)\n- Имя файла — не длиннее 31 символа (ограничение прошивки)\n- «Допустимые файлы» (`accept`) — фильтр в окне выбора, например `.bin, .pem`\n\n**🎨 Вид**\n- **Файл** — кнопка с именем выбранного файла\n- **Изображение** — квадратное превью; выбранная картинка сразу показывается локально\n\n💡 Для полноценной работы с файлами (список, удаление, прогресс) используйте виджет «Файловая система».",
|
|
38
|
+
'🎯 **File picker** — uploads the chosen file to the device file system.\n\n**📤 Upload**\n- After selection the file is sent in chunks with `SET FS` (`UploadStart` → `UploadChunk` → `UploadDone`)\n- File name — up to 31 characters (firmware limit)\n- "Allowed files" (`accept`) — filter in the file dialog, e.g. `.bin, .pem`\n\n**🎨 View**\n- **File** — a button with the selected file name\n- **Image** — a square preview; the chosen picture is shown locally right away\n\n💡 For full file management (list, delete, progress) use the "File system" widget.',
|
|
39
|
+
"🎯 **文件选择** —— 将所选文件上传到设备文件系统。\n\n**📤 上传**\n- 选择后文件通过 `SET FS` 分块发送(`UploadStart` → `UploadChunk` → `UploadDone`)\n- 文件名 —— 不超过 31 个字符(固件限制)\n- 「允许的文件」(`accept`)—— 文件对话框中的过滤器,例如 `.bin, .pem`\n\n**🎨 视图**\n- **文件** —— 显示所选文件名的按钮\n- **图片** —— 方形预览;所选图片立即在本地显示\n\n💡 如需完整的文件管理(列表、删除、进度),请使用「文件系统」组件。",
|
|
40
|
+
],
|
|
41
|
+
"component.info.Graph": [
|
|
42
|
+
'🎯 **График** — линии значений, обновляющиеся в реальном времени.\n\n**🔗 Данные**\n- **Имя переменной** — массив `[{ "name": "Temp", "value": 23.4 }, ...]`: каждое `name` — отдельная линия\n- Каждое обновление добавляет точку справа, старые точки уходят влево\n\n**⚙️ Настройки**\n- Тестовые данные — показывают график без устройства, удобно при вёрстке\n\n💡 Для накопленных по периодам значений (энергия за час/сутки) лучше «Диаграмма по периодам».',
|
|
43
|
+
'🎯 **Graph** — value lines updated in real time.\n\n**🔗 Data**\n- **Variable name** — an array `[{ "name": "Temp", "value": 23.4 }, ...]`: each `name` is a separate line\n- Every update adds a point on the right, old points move left\n\n**⚙️ Settings**\n- Test data — shows the graph without a device, handy while laying out\n\n💡 For values accumulated per period (energy per hour/day) the "Period chart" fits better.',
|
|
44
|
+
'🎯 **图表** —— 实时更新的数值曲线。\n\n**🔗 数据**\n- **变量名** —— 数组 `[{ "name": "Temp", "value": 23.4 }, ...]`:每个 `name` 是一条独立曲线\n- 每次更新在右侧添加一个点,旧点向左移动\n\n**⚙️ 设置**\n- 测试数据 —— 无需设备即可显示图表,便于排版\n\n💡 按周期累计的值(每小时/每天的能耗)更适合用「周期图表」。',
|
|
45
|
+
],
|
|
46
|
+
"component.info.Input": [
|
|
47
|
+
"🎯 **Поле ввода** — показывает и изменяет значение переменной устройства.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — например `CFG.DevName`; поле показывает текущее значение с устройства\n- Значение отправляется при каждом изменении — для текста удобнее аргумент `NoSend` и отдельная кнопка «Сохранить»\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**🔤 Тип**\n- `Aa` текст, `•••` пароль, `123` число, `¶` многострочный текст\n- `01` битовое поле — редактирует биты с N по M целого числа, остальные биты не трогает\n\n**🛡️ Проверки**\n- Максимальная длина, диапазон чисел, выражение валидации — неверный ввод подсвечивается и не отправляется\n- Только чтение — поле для отображения, с кнопкой копирования",
|
|
48
|
+
'🎯 **Input field** — shows and changes a device variable.\n\n**🔗 Device link**\n- **Variable name** — e.g. `CFG.DevName`; the field shows the current device value\n- The value is sent on every change — for text, the `NoSend` argument plus a separate "Save" button is more convenient\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don\'t send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**🔤 Type**\n- `Aa` text, `•••` password, `123` number, `¶` multiline text\n- `01` bit field — edits bits N to M of an integer, other bits stay intact\n\n**🛡️ Validation**\n- Max length, number range, validation expression — invalid input is highlighted and not sent\n- Read only — a display field with a copy button',
|
|
49
|
+
"🎯 **输入框** —— 显示并修改设备变量。\n\n**🔗 与设备的关联**\n- **变量名** —— 例如 `CFG.DevName`;输入框显示设备上的当前值\n- 每次修改都会发送值 —— 对于文本,使用 `NoSend` 参数加单独的「保存」按钮更方便\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**🔤 类型**\n- `Aa` 文本,`•••` 密码,`123` 数字,`¶` 多行文本\n- `01` 位字段 —— 编辑整数的第 N 到第 M 位,其余位保持不变\n\n**🛡️ 校验**\n- 最大长度、数值范围、校验表达式 —— 无效输入会高亮且不发送\n- 只读 —— 带复制按钮的显示字段",
|
|
50
|
+
],
|
|
51
|
+
"component.info.Joystick": [
|
|
52
|
+
"🎯 **Джойстик** — управление по нескольким осям, например движением или поворотом камеры.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — массив значений по осям, например `[x, y, z]`\n- Кнопки направлений меняют значения осей на шаг чувствительности, центральная — своя команда; значение отправляется при каждом нажатии\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**⚙️ Оси**\n- Для каждой оси — имя и диапазон (минимум / максимум)\n- Иконка центральной кнопки\n\n💡 Только чтение — джойстик показывает положение с устройства, но не управляет.",
|
|
53
|
+
"🎯 **Joystick** — multi-axis control, e.g. motion or camera pan.\n\n**🔗 Device link**\n- **Variable name** — an array of axis values, e.g. `[x, y, z]`\n- Direction buttons change axis values by the sensitivity step, the center one has its own command; the value is sent on every press\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don't send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**⚙️ Axes**\n- For each axis — name and range (minimum / maximum)\n- Center button icon\n\n💡 Read only — the joystick shows the device position but doesn't control it.",
|
|
54
|
+
"🎯 **摇杆** —— 多轴控制,例如运动或摄像头转动。\n\n**🔗 与设备的关联**\n- **变量名** —— 各轴数值的数组,例如 `[x, y, z]`\n- 方向按钮按灵敏度步长改变轴值,中心按钮有自己的命令;每次按下都会发送值\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**⚙️ 轴**\n- 每个轴 —— 名称和范围(最小值 / 最大值)\n- 中心按钮图标\n\n💡 只读 —— 摇杆显示设备位置,但不进行控制。",
|
|
55
|
+
],
|
|
56
|
+
"component.info.Map": [
|
|
57
|
+
"🎯 **Карта** — показывает местоположение устройства.\n\n**🔗 Данные**\n- **Имя переменной** — объект навигации:\n - `NavLat`, `NavLon` — широта и долгота\n - `NavAlt` — высота, `NavHeading` — курс\n - `NavSatUse` — число спутников в решении\n - `DevName`, `DevSN` — подпись маркера\n\n💡 Только отображение, ничего не отправляет.",
|
|
58
|
+
"🎯 **Map** — shows the device location.\n\n**🔗 Data**\n- **Variable name** — a navigation object:\n - `NavLat`, `NavLon` — latitude and longitude\n - `NavAlt` — altitude, `NavHeading` — heading\n - `NavSatUse` — satellites used in the fix\n - `DevName`, `DevSN` — marker label\n\n💡 Display only, sends nothing.",
|
|
59
|
+
"🎯 **地图** —— 显示设备位置。\n\n**🔗 数据**\n- **变量名** —— 导航对象:\n - `NavLat`、`NavLon` —— 纬度和经度\n - `NavAlt` —— 高度,`NavHeading` —— 航向\n - `NavSatUse` —— 参与定位的卫星数\n - `DevName`、`DevSN` —— 标记标签\n\n💡 仅显示,不发送任何数据。",
|
|
60
|
+
],
|
|
61
|
+
"component.info.PeriodChart": [
|
|
62
|
+
"🎯 **Диаграмма по периодам** — столбики накопленных значений с переключателем периода (час / сутки / месяц / год).\n\n**🔗 Данные**\n- Каждый **уровень** — своя вкладка переключателя со своей **переменной**: массив чисел, один столбик на элемент\n- Подписи столбиков задаются списком, иначе — номера 1, 2, 3…\n- **Единицы** — показываются в подсказке, например `Вт·ч`\n\n💡 Открывается всегда на первом уровне. Сама диаграмма ничего не запрашивает — данные приходят с устройства.",
|
|
63
|
+
"🎯 **Period chart** — bars of accumulated values with a period switch (hour / day / month / year).\n\n**🔗 Data**\n- Each **level** is a switch tab with its own **variable**: an array of numbers, one bar per element\n- Bar labels are set as a list, otherwise numbers 1, 2, 3…\n- **Units** — shown in the tooltip, e.g. `Wh`\n\n💡 Always opens on the first level. The chart requests nothing itself — data comes from the device.",
|
|
64
|
+
"🎯 **周期图表** —— 累计值柱状图,带周期切换(小时 / 天 / 月 / 年)。\n\n**🔗 数据**\n- 每个 **级别** 是一个切换标签,有自己的 **变量**:数字数组,每个元素一根柱\n- 柱的标签以列表设置,否则为编号 1、2、3…\n- **单位** —— 显示在提示中,例如 `Wh`\n\n💡 总是在第一个级别打开。图表本身不请求数据 —— 数据来自设备。",
|
|
65
|
+
],
|
|
66
|
+
"component.info.ProgressBar": [
|
|
67
|
+
'🎯 **Шкала прогресса** — одна или несколько полос заполнения: уровень, заряд, загрузка.\n\n**🔗 Данные**\n- **Имя переменной** — массив `[{ "Value": 75 }, ...]`, по одному элементу на полосу\n- Полосы настраиваются в том же порядке: подпись и цвет каждой\n- Диапазон — минимум / максимум (по умолчанию 0–100)\n\n⚠️ Порядок полос в настройках и элементов в данных должен совпадать — связь по номеру, а не по имени.',
|
|
68
|
+
'🎯 **Progress bar** — one or more fill bars: level, charge, load.\n\n**🔗 Data**\n- **Variable name** — an array `[{ "Value": 75 }, ...]`, one element per bar\n- Bars are configured in the same order: label and color of each\n- Range — minimum / maximum (0–100 by default)\n\n⚠️ The order of bars in settings and elements in data must match — they are linked by position, not by name.',
|
|
69
|
+
'🎯 **进度条** —— 一个或多个填充条:液位、电量、负载。\n\n**🔗 数据**\n- **变量名** —— 数组 `[{ "Value": 75 }, ...]`,每根条一个元素\n- 条按相同顺序配置:每根的标签和颜色\n- 范围 —— 最小值 / 最大值(默认 0–100)\n\n⚠️ 设置中条的顺序必须与数据中元素的顺序一致 —— 按位置而非名称关联。',
|
|
70
|
+
],
|
|
71
|
+
"component.info.Select": [
|
|
72
|
+
"🎯 **Список выбора** — выбор одного значения из вариантов.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — выбранное значение\n- Варианты задаются в настройках или приходят с устройства переменной `<имя>-S` (массив `{ id, name, value }`) — так строится, например, список найденных Wi-Fi сетей\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**🔤 Вид**\n- **Список** — выпадающий, **Кнопки** — все варианты в ряд, **С вводом** — список с ручным вводом\n- Битовое поле — значение записывается в биты с N по M целого числа\n\n💡 Для 2–4 коротких вариантов удобнее «Кнопки».",
|
|
73
|
+
'🎯 **Select** — picks one value from options.\n\n**🔗 Device link**\n- **Variable name** — the selected value\n- Options are set in settings or come from the device in the `<name>-S` variable (an array of `{ id, name, value }`) — this is how e.g. the found Wi-Fi networks list is built\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don\'t send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**🔤 View**\n- **List** — dropdown, **Buttons** — all options in a row, **With input** — list with manual entry\n- Bit field — the value is written into bits N to M of an integer\n\n💡 For 2–4 short options "Buttons" is handier.',
|
|
74
|
+
"🎯 **下拉选择** —— 从选项中选择一个值。\n\n**🔗 与设备的关联**\n- **变量名** —— 选中的值\n- 选项在设置中指定,或由设备通过 `<名称>-S` 变量提供(`{ id, name, value }` 数组)—— 例如找到的 Wi-Fi 网络列表就是这样生成的\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**🔤 视图**\n- **列表** —— 下拉,**按钮** —— 所有选项排成一行,**可输入** —— 可手动输入的列表\n- 位字段 —— 值写入整数的第 N 到第 M 位\n\n💡 2–4 个短选项用「按钮」更方便。",
|
|
75
|
+
],
|
|
76
|
+
"component.info.Slider": [
|
|
77
|
+
"🎯 **Слайдер** — плавный выбор числа или диапазона.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — число, а для диапазона — массив `[от, до]`\n- Значение отправляется при перемещении ручки и нажатии − / +\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**⚙️ Настройки**\n- Тип: одно значение или диапазон\n- Минимум, максимум, шаг",
|
|
78
|
+
"🎯 **Slider** — smooth choice of a number or range.\n\n**🔗 Device link**\n- **Variable name** — a number, or an `[from, to]` array for a range\n- The value is sent while moving the handle and on − / + presses\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don't send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**⚙️ Settings**\n- Type: single value or range\n- Minimum, maximum, step",
|
|
79
|
+
"🎯 **滑块** —— 平滑选择数值或范围。\n\n**🔗 与设备的关联**\n- **变量名** —— 数字,范围模式下为 `[起, 止]` 数组\n- 拖动滑块以及按 − / + 时发送值\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**⚙️ 设置**\n- 类型:单值或范围\n- 最小值、最大值、步长",
|
|
40
80
|
],
|
|
41
81
|
"component.info.Switch": [
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
82
|
+
"🎯 **Переключатель** — включение и выключение: реле, режимы, флаги.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — `0` или `1`\n- Значение отправляется сразу при переключении\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**🔤 Вид**\n- Горизонтальный или вертикальный тумблер с подписями слева и справа\n- Флажок — компактный вариант\n\n**🔢 Битовый режим**\n- Переменная — битовая маска; каждый переключатель управляет своим битом (номер 0–31)",
|
|
83
|
+
"🎯 **Switch** — on/off: relays, modes, flags.\n\n**🔗 Device link**\n- **Variable name** — `0` or `1`\n- The value is sent immediately on toggle\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don't send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**🔤 View**\n- Horizontal or vertical toggle with captions on both sides\n- Checkbox — a compact option\n\n**🔢 Bit mode**\n- The variable is a bit mask; each switch controls its own bit (number 0–31)",
|
|
84
|
+
"🎯 **开关** —— 开/关:继电器、模式、标志。\n\n**🔗 与设备的关联**\n- **变量名** —— `0` 或 `1`\n- 切换时立即发送值\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**🔤 视图**\n- 水平或垂直拨动开关,两侧带标签\n- 复选框 —— 紧凑形式\n\n**🔢 位模式**\n- 变量为位掩码;每个开关控制自己的位(编号 0–31)",
|
|
45
85
|
],
|
|
46
86
|
"component.info.Table": [
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
"
|
|
87
|
+
"🎯 **Таблица** — список строк с данными устройства: файлы, журнал, параметры.\n\n**🔗 Данные**\n- **Имя переменной** — массив объектов, одна строка на объект\n- **Колонки** («Настроить») — для каждой ключ поля, ширина, выравнивание и тип ячейки\n\n**🧱 Типы ячеек**\n- Текст — с сортировкой, копированием, обрезкой «…», подсказкой и окном с полным текстом\n- Список, кнопка (отправляет своё событие), полоса прогресса, иконка\n\n**📜 Накопление данных**\n- Включено — новые строки дописываются в буфер (размер буфера), таблица работает как журнал\n- Добавление строк вверху или внизу, кнопка очистки, видимое число строк",
|
|
88
|
+
'🎯 **Table** — a list of device data rows: files, log, parameters.\n\n**🔗 Data**\n- **Variable name** — an array of objects, one row per object\n- **Columns** ("Configure") — for each: field key, width, alignment and cell type\n\n**🧱 Cell types**\n- Text — with sorting, copy, "…" truncation, tooltip and a full-text window\n- Select, button (sends its own event), progress bar, icon\n\n**📜 Data accumulation**\n- On — new rows are appended to a buffer (buffer size), the table works as a log\n- Adding rows at the top or bottom, clear button, visible rows count',
|
|
89
|
+
"🎯 **表格** —— 设备数据行列表:文件、日志、参数。\n\n**🔗 数据**\n- **变量名** —— 对象数组,每个对象一行\n- **列**(「配置」)—— 每列:字段键、宽度、对齐方式和单元格类型\n\n**🧱 单元格类型**\n- 文本 —— 支持排序、复制、「…」截断、提示和全文窗口\n- 下拉、按钮(发送自己的事件)、进度条、图标\n\n**📜 数据累积**\n- 开启时 —— 新行追加到缓冲区(缓冲区大小),表格作为日志使用\n- 在顶部或底部添加行、清除按钮、可见行数",
|
|
50
90
|
],
|
|
51
|
-
"component.info.
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
91
|
+
"component.info.Tabs": [
|
|
92
|
+
"🎯 **Вкладки** — несколько страниц компонентов в одном месте.\n\n**🧩 Как пользоваться**\n- Добавьте вкладки, у каждой — название и иконка\n- У каждой вкладки своя сетка: компоненты размещаются на активной вкладке конструктора\n\n**🎨 Оформление**\n- Ширина кнопок: по содержимому или поровну\n- Положение иконки относительно названия — слева, сверху, справа, снизу\n\n⚠️ Не больше 10 вкладок. С устройством не связаны.",
|
|
93
|
+
"🎯 **Tabs** — several pages of components in one place.\n\n**🧩 How to use**\n- Add tabs, each with a name and an icon\n- Every tab has its own grid: components are placed on the tab active in the constructor\n\n**🎨 Appearance**\n- Button width: by content or equal\n- Icon position relative to the name — left, top, right, bottom\n\n⚠️ Up to 10 tabs. Not linked to the device.",
|
|
94
|
+
"🎯 **标签页** —— 在同一位置放置多页组件。\n\n**🧩 使用方法**\n- 添加标签页,每页有名称和图标\n- 每个标签页有自己的网格:组件放在构造器中当前激活的标签页上\n\n**🎨 外观**\n- 按钮宽度:按内容或等宽\n- 图标相对名称的位置 —— 左、上、右、下\n\n⚠️ 最多 10 个标签页。与设备无关。",
|
|
55
95
|
],
|
|
56
|
-
"component.info.
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
96
|
+
"component.info.TextField": [
|
|
97
|
+
"🎯 **Текст** — надпись, заголовок или пояснение.\n\n**⚙️ Настройки**\n- Размер `S`–`XXL`\n- Начертание: жирный, курсив, подчёркнутый, фон\n- Выравнивание и цвет текста\n\n💡 Статичный текст — значения устройства не показывает. Для значения используйте поле ввода «Только чтение» или виджет.",
|
|
98
|
+
"🎯 **Text** — a caption, heading or note.\n\n**⚙️ Settings**\n- Size `S`–`XXL`\n- Style: bold, italic, underline, background\n- Alignment and text color\n\n💡 Static text — doesn't show device values. For a value use a read-only input or a widget.",
|
|
99
|
+
"🎯 **文本** —— 标签、标题或说明。\n\n**⚙️ 设置**\n- 大小 `S`–`XXL`\n- 样式:粗体、斜体、下划线、背景\n- 对齐方式和文本颜色\n\n💡 静态文本 —— 不显示设备值。如需显示值,请使用只读输入框或组件。",
|
|
100
|
+
],
|
|
101
|
+
"component.info.VideoViewer": [
|
|
102
|
+
"🎯 **Видео** — показ видеопотока.\n\n**🎥 Источник**\n- **Камера** — камера этого компьютера или телефона, с выбором камеры\n- **С устройства** — кадры, которые устройство передаёт через облако по ключу потока\n\n💡 Видео не сохраняется — это просмотр в реальном времени.",
|
|
103
|
+
"🎯 **Video** — shows a video stream.\n\n**🎥 Source**\n- **Camera** — the camera of this computer or phone, with camera selection\n- **From device** — frames the device sends through the cloud by stream key\n\n💡 Video isn't recorded — it's real-time viewing.",
|
|
104
|
+
"🎯 **视频** —— 显示视频流。\n\n**🎥 来源**\n- **摄像头** —— 本电脑或手机的摄像头,可选择摄像头\n- **来自设备** —— 设备通过云端按流密钥发送的画面\n\n💡 视频不会录制 —— 仅实时观看。",
|
|
60
105
|
],
|
|
61
106
|
"component.info.Widget": [
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
107
|
+
"🎯 **Индикатор** — карточка одного значения с крупной цифрой, иконкой и управлением.\n\n**🔗 Связь с устройством**\n- **Имя переменной** — число; карточка показывает его крупно, с единицами измерения\n- Изменение отправляется сразу\n- **📤 Отправка** — по событию из настроек: **Заголовок** (обычно `SET`), **Аргумент** (обычно `ModCfg`; `NoSend` — не отправлять сразу, значение уйдёт вместе с другим компонентом) и **Переменные** — какие ещё значения положить в тот же пакет.\n\n**🎛️ Управление**\n- **Поле** с кнопками − / +, **слайдер** или **переключатель** с подписями\n- Минимум, максимум и шаг\n\n**🖼️ Иконки**\n- Набор иконок распределяется по диапазону значения\n- Режим анимации — иконки сменяются тем быстрее, чем больше значение (например вентилятор)",
|
|
108
|
+
"🎯 **Indicator** — a single-value card with a big number, icon and control.\n\n**🔗 Device link**\n- **Variable name** — a number; the card shows it large, with units\n- Changes are sent immediately\n- **📤 Sending** — by the event in settings: **Header** (usually `SET`), **Argument** (usually `ModCfg`; `NoSend` — don't send right away, the value goes out with another component) and **Variables** — which other values to put in the same packet.\n\n**🎛️ Control**\n- **Field** with − / + buttons, **slider** or **switch** with captions\n- Minimum, maximum and step\n\n**🖼️ Icons**\n- The icon set is spread across the value range\n- Animation mode — icons cycle faster as the value grows (e.g. a fan)",
|
|
109
|
+
"🎯 **指示器** —— 单值卡片,带大号数字、图标和控件。\n\n**🔗 与设备的关联**\n- **变量名** —— 数字;卡片以大字体及单位显示\n- 修改立即发送\n- **📤 发送** —— 按设置中的事件:**标头**(通常为 `SET`)、**参数**(通常为 `ModCfg`;`NoSend` —— 不立即发送,值随其他组件一起发出)和 **变量** —— 同一数据包中还要包含哪些值。\n\n**🎛️ 控件**\n- 带 − / + 按钮的**输入框**、**滑块**或带标签的**开关**\n- 最小值、最大值和步长\n\n**🖼️ 图标**\n- 图标集按数值范围分布\n- 动画模式 —— 数值越大图标切换越快(例如风扇)",
|
|
110
|
+
],
|
|
111
|
+
"component.info.WidgetWiFi": [
|
|
112
|
+
"🎯 **Виджет Wi-Fi** — полная настройка сети устройства: режим, подключение к роутеру, собственная точка доступа.\n\n**🔗 Связь с устройством** — фиксированные поля группы `CFG`, привязывать переменные не нужно\n- Режим `WiFiMode`: выключен, клиент, точка доступа, оба\n- **Клиент (STA)**: `StaSSID`, `StaPSK`, статический IP (`StaIP`, `StaMS`, `StaGW`)\n- **Точка доступа (AP)**: `ApSSID`, `ApPSK`, `ApIP`, `ApMS`, `ApGW`\n\n**📤 Отправка**\n- Поля редактируются локально, «Сохранить» отправляет всё одним пакетом\n- 🔍 Поиск сетей — `GET APsList`, найденные сети появляются в списке SSID\n- При включении только точки доступа виджет предупреждает: устройство может пропасть из локальной сети\n\n⚙️ Другое имя группы вместо `CFG` — поменяйте префикс (ID) и имена полей в настройках виджета.",
|
|
113
|
+
'🎯 **Wi-Fi widget** — full device network setup: mode, router connection, own access point.\n\n**🔗 Device link** — fixed fields of the `CFG` group, no variable binding needed\n- Mode `WiFiMode`: off, client, access point, both\n- **Client (STA)**: `StaSSID`, `StaPSK`, static IP (`StaIP`, `StaMS`, `StaGW`)\n- **Access point (AP)**: `ApSSID`, `ApPSK`, `ApIP`, `ApMS`, `ApGW`\n\n**📤 Sending**\n- Fields are edited locally, "Save" sends everything in one packet\n- 🔍 Network scan — `GET APsList`, found networks appear in the SSID list\n- When switching to access point only, the widget warns that the device may disappear from the local network\n\n⚙️ A group name other than `CFG` — change the prefix (ID) and field names in widget settings.',
|
|
114
|
+
"🎯 **Wi-Fi 组件** —— 完整的设备网络设置:模式、连接路由器、自建热点。\n\n**🔗 与设备的关联** —— `CFG` 分组的固定字段,无需绑定变量\n- 模式 `WiFiMode`:关闭、客户端、热点、两者\n- **客户端(STA)**:`StaSSID`、`StaPSK`、静态 IP(`StaIP`、`StaMS`、`StaGW`)\n- **热点(AP)**:`ApSSID`、`ApPSK`、`ApIP`、`ApMS`、`ApGW`\n\n**📤 发送**\n- 字段在本地编辑,「保存」一次性发送全部\n- 🔍 搜索网络 —— `GET APsList`,找到的网络出现在 SSID 列表中\n- 仅切换为热点时,组件会提示设备可能从局域网中消失\n\n⚙️ 若分组名不是 `CFG` —— 在组件设置中修改前缀(ID)和字段名。",
|
|
115
|
+
],
|
|
116
|
+
"component.info.WidgetDeviceInfo": [
|
|
117
|
+
"🎯 **Виджет «Об устройстве»** — паспорт устройства и его основные настройки.\n\n**🔗 Связь с устройством** — фиксированные поля группы `CFG`\n- 🔒 Только чтение: серийный номер `DevSN`, `DevID`, версия ПО `DevFW`, счётчик запусков `RunCnt`, адреса `StaIP` и `ApIP`\n- ✏️ Редактируемые: имя `DevName`, имя хоста `HostName`, логин и пароль веб-интерфейса `WebUser` / `WebPsw`\n\n**📤 Отправка**\n- «Сохранить» отправляет изменённые поля одним пакетом\n- 🔌 «Перезагрузить» — команда `SET Restart`\n\n⚙️ Другое имя группы — поменяйте префикс (ID) в настройках виджета.",
|
|
118
|
+
'🎯 **Device info widget** — device passport and its main settings.\n\n**🔗 Device link** — fixed fields of the `CFG` group\n- 🔒 Read only: serial number `DevSN`, `DevID`, firmware version `DevFW`, run counter `RunCnt`, addresses `StaIP` and `ApIP`\n- ✏️ Editable: name `DevName`, host name `HostName`, web interface login and password `WebUser` / `WebPsw`\n\n**📤 Sending**\n- "Save" sends the changed fields in one packet\n- 🔌 "Restart" — the `SET Restart` command\n\n⚙️ A different group name — change the prefix (ID) in widget settings.',
|
|
119
|
+
"🎯 **设备信息组件** —— 设备档案及其主要设置。\n\n**🔗 与设备的关联** —— `CFG` 分组的固定字段\n- 🔒 只读:序列号 `DevSN`、`DevID`、固件版本 `DevFW`、启动次数 `RunCnt`、地址 `StaIP` 和 `ApIP`\n- ✏️ 可编辑:名称 `DevName`、主机名 `HostName`、网页界面登录名和密码 `WebUser` / `WebPsw`\n\n**📤 发送**\n- 「保存」一次性发送已修改的字段\n- 🔌 「重启」—— `SET Restart` 命令\n\n⚙️ 分组名不同 —— 在组件设置中修改前缀(ID)。",
|
|
120
|
+
],
|
|
121
|
+
"component.info.WidgetFileSystem": [
|
|
122
|
+
"🎯 **Виджет «Файловая система»** — память устройства, список файлов, удаление и закачка.\n\n**🔗 Связь с устройством** — один объект `CFG.FS`, привязывать переменные не нужно\n- 📊 Занятость: `Total`, `Used`, `Free` — полоса и процент (оранжевый от 75%, красный от 90%)\n- 📄 Файлы: `List` — путь и размер; «Удалить» — `SET FS` с `Op: Delete` (после подтверждения)\n- ⬆️ Закачка: `SET FS` с `Op` `UploadStart` → `UploadChunk` → `UploadDone`, прогресс — `Upload.Progress`. Закачка с другого клиента тоже видна\n\n**⚠️ Ограничения**\n- Имя файла — не длиннее 31 символа\n- «Допустимые файлы» — фильтр в окне выбора\n\n🔄 «Обновить» — `GET FS`.",
|
|
123
|
+
'🎯 **File system widget** — device storage, file list, deletion and upload.\n\n**🔗 Device link** — a single `CFG.FS` object, no variable binding needed\n- 📊 Usage: `Total`, `Used`, `Free` — bar and percent (orange from 75%, red from 90%)\n- 📄 Files: `List` — path and size; "Delete" — `SET FS` with `Op: Delete` (after confirmation)\n- ⬆️ Upload: `SET FS` with `Op` `UploadStart` → `UploadChunk` → `UploadDone`, progress — `Upload.Progress`. An upload from another client is visible too\n\n**⚠️ Limits**\n- File name — up to 31 characters\n- "Allowed files" — filter in the file dialog\n\n🔄 "Refresh" — `GET FS`.',
|
|
124
|
+
"🎯 **文件系统组件** —— 设备存储、文件列表、删除和上传。\n\n**🔗 与设备的关联** —— 单个 `CFG.FS` 对象,无需绑定变量\n- 📊 占用:`Total`、`Used`、`Free` —— 进度条和百分比(75% 起橙色,90% 起红色)\n- 📄 文件:`List` —— 路径和大小;「删除」—— 带 `Op: Delete` 的 `SET FS`(确认后)\n- ⬆️ 上传:带 `Op` `UploadStart` → `UploadChunk` → `UploadDone` 的 `SET FS`,进度 —— `Upload.Progress`。其他客户端的上传也可见\n\n**⚠️ 限制**\n- 文件名 —— 不超过 31 个字符\n- 「允许的文件」—— 文件对话框中的过滤器\n\n🔄 「刷新」—— `GET FS`。",
|
|
125
|
+
],
|
|
126
|
+
"component.info.WidgetStackInfo": [
|
|
127
|
+
"🎯 **Виджет «Память и задачи»** — отладка: свободная память и запас стека задач прошивки.\n\n**🔗 Связь с устройством** — один объект `StackInfo` из ответа на `GET StackInfo`\n- 📊 **Кучи** `Heap[]`: занято / всего, свободно и минимум свободного с момента старта; светлая часть полосы — пик занятости\n- 🧵 **Задачи** `Tasks[]`: имя, состояние, приоритет, ядро (ESP32), минимальный запас стека в байтах\n\n**⚙️ Настройки**\n- Порог запаса стека — меньше порога красным, меньше удвоенного — оранжевым\n- Автоопрос: выкл / 1 / 5 / 10 с; свёрнутый виджет не опрашивает\n\n💡 Задачи всегда отсортированы по запасу стека — самые рискованные сверху.",
|
|
128
|
+
"🎯 **Memory and tasks widget** — debugging: free memory and firmware task stack headroom.\n\n**🔗 Device link** — a single `StackInfo` object from the `GET StackInfo` response\n- 📊 **Heaps** `Heap[]`: used / total, free and minimum free since boot; the light part of the bar is the usage peak\n- 🧵 **Tasks** `Tasks[]`: name, state, priority, core (ESP32), minimum stack headroom in bytes\n\n**⚙️ Settings**\n- Stack headroom threshold — below it red, below double — orange\n- Auto-polling: off / 1 / 5 / 10 s; a collapsed widget doesn't poll\n\n💡 Tasks are always sorted by stack headroom — the riskiest on top.",
|
|
129
|
+
"🎯 **内存与任务组件** —— 调试用:空闲内存和固件任务的栈余量。\n\n**🔗 与设备的关联** —— 来自 `GET StackInfo` 响应的单个 `StackInfo` 对象\n- 📊 **堆** `Heap[]`:已用 / 总量、空闲以及启动以来的最小空闲;进度条浅色部分为占用峰值\n- 🧵 **任务** `Tasks[]`:名称、状态、优先级、核心(ESP32)、最小栈余量(字节)\n\n**⚙️ 设置**\n- 栈余量阈值 —— 低于阈值显示红色,低于两倍显示橙色\n- 自动轮询:关闭 / 1 / 5 / 10 秒;折叠的组件不轮询\n\n💡 任务始终按栈余量排序 —— 风险最高的在最上面。",
|
|
65
130
|
],
|
|
66
131
|
/* Опции редактора свойств */
|
|
67
132
|
"constructor.props.action.update": ["Обновить", "Update", "更新"],
|
|
@@ -78,8 +143,8 @@ const translations = {
|
|
|
78
143
|
"constructor.props.type.text": ["Текст", "Text", "文本"],
|
|
79
144
|
"constructor.props.type.password": ["Пароль", "Password", "密码"],
|
|
80
145
|
"constructor.props.type.number": ["Число", "Number", "数字"],
|
|
81
|
-
"constructor.props.type.textarea": ["
|
|
82
|
-
"constructor.props.type.bitMode": ["
|
|
146
|
+
"constructor.props.type.textarea": ["Многострочный текст", "Multiline text", "多行文本"],
|
|
147
|
+
"constructor.props.type.bitMode": ["Битовое поле", "Bit field", "位字段"],
|
|
83
148
|
"constructor.props.type.single": ["Простой", "Simple", "简单"],
|
|
84
149
|
"constructor.props.type.range": ["Диапазон", "Range", "范围"],
|
|
85
150
|
"constructor.props.type.small": ["XS", "XS", "XS"],
|
|
@@ -89,7 +154,7 @@ const translations = {
|
|
|
89
154
|
"constructor.props.type.massive": ["XL", "XL", "XL"],
|
|
90
155
|
"constructor.props.type.select": ["Список", "List", "列表"],
|
|
91
156
|
"constructor.props.type.buttons": ["Кнопки", "Buttons", "按钮组"],
|
|
92
|
-
"constructor.props.type.selectput": ["
|
|
157
|
+
"constructor.props.type.selectput": ["С вводом", "With input", "可输入"],
|
|
93
158
|
"constructor.props.valuetype.text": ["Строка", "String", "字符串"],
|
|
94
159
|
"constructor.props.valuetype.number": ["Число", "Number", "数字"],
|
|
95
160
|
"constructor.props.type.file": ["Файл", "File", "文件"],
|
|
@@ -224,6 +289,10 @@ const translations = {
|
|
|
224
289
|
"constructor.props.placeholder": ["Заполнитель", "Placeholder", "占位符"],
|
|
225
290
|
"constructor.props.info": ["Подсказка", "Hint", "提示"],
|
|
226
291
|
"constructor.props.info.side": ["Положение подсказки", "Hint position", "提示位置"],
|
|
292
|
+
"constructor.props.iconpos.left": ["Иконка слева", "Icon on the left", "图标在左"],
|
|
293
|
+
"constructor.props.iconpos.top": ["Иконка сверху", "Icon on top", "图标在上"],
|
|
294
|
+
"constructor.props.iconpos.right": ["Иконка справа", "Icon on the right", "图标在右"],
|
|
295
|
+
"constructor.props.iconpos.bottom": ["Иконка снизу", "Icon at the bottom", "图标在下"],
|
|
227
296
|
"constructor.props.info.top": ["Вверху", "Top", "上方"],
|
|
228
297
|
"constructor.props.info.bottom": ["Внизу", "Bottom", "下方"],
|
|
229
298
|
"constructor.props.info.left": ["Слева", "Left", "左侧"],
|
package/dist/options.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare const optionsStore: import("svelte/store").Readable<{
|
|
|
21
21
|
id: string;
|
|
22
22
|
value: string;
|
|
23
23
|
name: string;
|
|
24
|
+
icon: string;
|
|
24
25
|
}[];
|
|
25
26
|
SELECT_TYPE_OPTIONS: {
|
|
26
27
|
id: string;
|
|
@@ -40,16 +41,19 @@ export declare const optionsStore: import("svelte/store").Readable<{
|
|
|
40
41
|
id: string;
|
|
41
42
|
value: string;
|
|
42
43
|
name: string;
|
|
44
|
+
icon: string;
|
|
43
45
|
}[];
|
|
44
46
|
TEXTFIELD_SIZE_OPTIONS: {
|
|
45
47
|
id: string;
|
|
46
48
|
value: string;
|
|
47
49
|
name: string;
|
|
50
|
+
icon: string;
|
|
48
51
|
}[];
|
|
49
52
|
TEXTFIELD_SETTINGS_OPTIONS: {
|
|
50
53
|
id: string;
|
|
51
54
|
value: string;
|
|
52
55
|
name: string;
|
|
56
|
+
icon: string;
|
|
53
57
|
}[];
|
|
54
58
|
COLOR_OPTIONS: {
|
|
55
59
|
id: string;
|
|
@@ -67,66 +71,79 @@ export declare const optionsStore: import("svelte/store").Readable<{
|
|
|
67
71
|
id: string;
|
|
68
72
|
value: string;
|
|
69
73
|
name: string;
|
|
74
|
+
icon: string;
|
|
70
75
|
}[];
|
|
71
76
|
ALIGN_OPTIONS: {
|
|
72
77
|
id: string;
|
|
73
78
|
value: string;
|
|
74
79
|
name: string;
|
|
80
|
+
icon: string;
|
|
75
81
|
}[];
|
|
76
82
|
JUSTIFY_ALIGN_OPTIONS: {
|
|
77
83
|
id: string;
|
|
78
84
|
value: string;
|
|
79
85
|
name: string;
|
|
86
|
+
icon: string;
|
|
80
87
|
}[];
|
|
81
88
|
ICON_TEXT_POSITION_OPTIONS: {
|
|
82
89
|
id: string;
|
|
83
90
|
value: string;
|
|
84
91
|
name: string;
|
|
92
|
+
icon: string;
|
|
85
93
|
}[];
|
|
86
94
|
TABLE_TEXT_SETTING_OPTIONS: {
|
|
87
95
|
id: string;
|
|
88
96
|
value: string;
|
|
89
97
|
name: string;
|
|
98
|
+
icon: string;
|
|
90
99
|
}[];
|
|
91
100
|
HEIGHT_OPTIONS: {
|
|
92
101
|
id: string;
|
|
93
102
|
value: string;
|
|
94
103
|
name: string;
|
|
104
|
+
icon: string;
|
|
95
105
|
}[];
|
|
96
106
|
FILE_ATTACH_TYPE_OPTIONS: {
|
|
97
107
|
id: string;
|
|
98
108
|
value: string;
|
|
99
109
|
name: string;
|
|
110
|
+
icon: string;
|
|
100
111
|
}[];
|
|
101
112
|
DIVIDER_TYPE_OPTIONS: {
|
|
102
113
|
id: string;
|
|
103
114
|
value: string;
|
|
104
115
|
name: string;
|
|
116
|
+
icon: string;
|
|
105
117
|
}[];
|
|
106
118
|
DIVIDER_ANCHOR_OPTIONS: {
|
|
107
119
|
id: string;
|
|
108
120
|
value: string;
|
|
109
121
|
name: string;
|
|
122
|
+
icon: string;
|
|
110
123
|
}[];
|
|
111
124
|
FITMODE_OPTIONS: {
|
|
112
125
|
id: string;
|
|
113
126
|
value: string;
|
|
114
127
|
name: string;
|
|
128
|
+
icon: string;
|
|
115
129
|
}[];
|
|
116
130
|
SWITCH_OPTIONS: {
|
|
117
131
|
id: string;
|
|
118
132
|
value: string;
|
|
119
133
|
name: string;
|
|
134
|
+
icon: string;
|
|
120
135
|
}[];
|
|
121
136
|
INFO_SIDE_OPTIONS: {
|
|
122
137
|
id: string;
|
|
123
138
|
value: string;
|
|
124
139
|
name: string;
|
|
140
|
+
icon: string;
|
|
125
141
|
}[];
|
|
126
142
|
FORM_OPTIONS: {
|
|
127
143
|
id: string;
|
|
128
144
|
value: string;
|
|
129
145
|
name: string;
|
|
146
|
+
icon: string;
|
|
130
147
|
}[];
|
|
131
148
|
TABLE_CONTENT_TYPE_OPTIONS: {
|
|
132
149
|
id: string;
|
|
@@ -137,6 +154,7 @@ export declare const optionsStore: import("svelte/store").Readable<{
|
|
|
137
154
|
id: string;
|
|
138
155
|
value: string;
|
|
139
156
|
name: string;
|
|
157
|
+
icon: string;
|
|
140
158
|
}[];
|
|
141
159
|
BUFFER_SIZE_OPTIONS: {
|
|
142
160
|
id: string;
|
package/dist/options.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// $lib/ElementsUI/options.ts
|
|
2
2
|
import { derived } from "svelte/store";
|
|
3
3
|
import { T } from "./locales/i18n";
|
|
4
|
+
import { PROPS_ICONS } from "./libIcons/propsIcons";
|
|
4
5
|
function id() {
|
|
5
6
|
return crypto.randomUUID();
|
|
6
7
|
}
|
|
@@ -28,11 +29,11 @@ export const optionsStore = derived(T, ($T) => {
|
|
|
28
29
|
{ id: id(), value: "hidden", name: $T("constructor.props.type.hidden") },
|
|
29
30
|
],
|
|
30
31
|
INPUT_TYPE_OPTIONS: [
|
|
31
|
-
{ id: id(), value: "text", name: $T("constructor.props.type.text") },
|
|
32
|
-
{ id: id(), value: "password", name: $T("constructor.props.type.password") },
|
|
33
|
-
{ id: id(), value: "number", name: $T("constructor.props.type.number") },
|
|
34
|
-
{ id: id(), value: "text-area", name: $T("constructor.props.type.textarea") },
|
|
35
|
-
{ id: id(), value: "bitMode", name: $T("constructor.props.type.bitMode") },
|
|
32
|
+
{ id: id(), value: "text", name: $T("constructor.props.type.text"), icon: "Aa" },
|
|
33
|
+
{ id: id(), value: "password", name: $T("constructor.props.type.password"), icon: "•••" },
|
|
34
|
+
{ id: id(), value: "number", name: $T("constructor.props.type.number"), icon: "123" },
|
|
35
|
+
{ id: id(), value: "text-area", name: $T("constructor.props.type.textarea"), icon: "¶" },
|
|
36
|
+
{ id: id(), value: "bitMode", name: $T("constructor.props.type.bitMode"), icon: "01" },
|
|
36
37
|
],
|
|
37
38
|
SELECT_TYPE_OPTIONS: [
|
|
38
39
|
{ id: id(), value: "select", name: $T("constructor.props.type.select") },
|
|
@@ -44,21 +45,21 @@ export const optionsStore = derived(T, ($T) => {
|
|
|
44
45
|
{ id: id(), value: "number", name: $T("constructor.props.valuetype.number") },
|
|
45
46
|
],
|
|
46
47
|
SLIDER_TYPE_OPTIONS: [
|
|
47
|
-
{ id: id(), value: "single", name: $T("constructor.props.type.single") },
|
|
48
|
-
{ id: id(), value: "range", name: $T("constructor.props.type.range") },
|
|
48
|
+
{ id: id(), value: "single", name: $T("constructor.props.type.single"), icon: PROPS_ICONS.sliderSingle },
|
|
49
|
+
{ id: id(), value: "range", name: $T("constructor.props.type.range"), icon: PROPS_ICONS.sliderRange },
|
|
49
50
|
],
|
|
50
51
|
TEXTFIELD_SIZE_OPTIONS: [
|
|
51
|
-
{ id: id(), value: "small", name: $T("constructor.props.type.small") },
|
|
52
|
-
{ id: id(), value: "base", name: $T("constructor.props.type.base") },
|
|
53
|
-
{ id: id(), value: "large", name: $T("constructor.props.type.large") },
|
|
54
|
-
{ id: id(), value: "huge", name: $T("constructor.props.type.huge") },
|
|
55
|
-
{ id: id(), value: "massive", name: $T("constructor.props.type.massive") },
|
|
52
|
+
{ id: id(), value: "small", name: $T("constructor.props.type.small"), icon: "S" },
|
|
53
|
+
{ id: id(), value: "base", name: $T("constructor.props.type.base"), icon: "M" },
|
|
54
|
+
{ id: id(), value: "large", name: $T("constructor.props.type.large"), icon: "L" },
|
|
55
|
+
{ id: id(), value: "huge", name: $T("constructor.props.type.huge"), icon: "XL" },
|
|
56
|
+
{ id: id(), value: "massive", name: $T("constructor.props.type.massive"), icon: "XXL" },
|
|
56
57
|
],
|
|
57
58
|
TEXTFIELD_SETTINGS_OPTIONS: [
|
|
58
|
-
{ id: id(), value: "bold", name: $T("constructor.props.bold") },
|
|
59
|
-
{ id: id(), value: "italic", name: $T("constructor.props.italic") },
|
|
60
|
-
{ id: id(), value: "underline", name: $T("constructor.props.underline") },
|
|
61
|
-
{ id: id(), value: "background", name: $T("constructor.props.background") },
|
|
59
|
+
{ id: id(), value: "bold", name: $T("constructor.props.bold"), icon: PROPS_ICONS.bold },
|
|
60
|
+
{ id: id(), value: "italic", name: $T("constructor.props.italic"), icon: PROPS_ICONS.italic },
|
|
61
|
+
{ id: id(), value: "underline", name: $T("constructor.props.underline"), icon: PROPS_ICONS.underline },
|
|
62
|
+
{ id: id(), value: "background", name: $T("constructor.props.background"), icon: PROPS_ICONS.highlight },
|
|
62
63
|
],
|
|
63
64
|
/* Единая семантическая палитра (app.css: --X-color/--X-soft, светлая и тёмная тема уже учтены) —
|
|
64
65
|
тот же набор ролей, что и в "мастер-цвете" виджетов конструктора (widgetAccent.ts на стороне
|
|
@@ -116,70 +117,70 @@ export const optionsStore = derived(T, ($T) => {
|
|
|
116
117
|
},
|
|
117
118
|
],
|
|
118
119
|
TEXT_ALIGN_OPTIONS: [
|
|
119
|
-
{ id: id(), value: "text-left", name: $T("constructor.props.align.left") },
|
|
120
|
-
{ id: id(), value: "text-center", name: $T("constructor.props.align.center") },
|
|
121
|
-
{ id: id(), value: "text-right", name: $T("constructor.props.align.right") },
|
|
122
|
-
{ id: id(), value: "text-justify", name: $T("constructor.props.align.justify") },
|
|
120
|
+
{ id: id(), value: "text-left", name: $T("constructor.props.align.left"), icon: PROPS_ICONS.alignLeft },
|
|
121
|
+
{ id: id(), value: "text-center", name: $T("constructor.props.align.center"), icon: PROPS_ICONS.alignCenter },
|
|
122
|
+
{ id: id(), value: "text-right", name: $T("constructor.props.align.right"), icon: PROPS_ICONS.alignRight },
|
|
123
|
+
{ id: id(), value: "text-justify", name: $T("constructor.props.align.justify"), icon: PROPS_ICONS.alignJustify },
|
|
123
124
|
],
|
|
124
125
|
ALIGN_OPTIONS: [
|
|
125
|
-
{ id: id(), value: "left", name: $T("constructor.props.align.left") },
|
|
126
|
-
{ id: id(), value: "center", name: $T("constructor.props.align.center") },
|
|
127
|
-
{ id: id(), value: "right", name: $T("constructor.props.align.right") },
|
|
126
|
+
{ id: id(), value: "left", name: $T("constructor.props.align.left"), icon: PROPS_ICONS.alignLeft },
|
|
127
|
+
{ id: id(), value: "center", name: $T("constructor.props.align.center"), icon: PROPS_ICONS.alignCenter },
|
|
128
|
+
{ id: id(), value: "right", name: $T("constructor.props.align.right"), icon: PROPS_ICONS.alignRight },
|
|
128
129
|
],
|
|
129
130
|
JUSTIFY_ALIGN_OPTIONS: [
|
|
130
|
-
{ id: id(), value: "justify-start", name: $T("constructor.props.align.left") },
|
|
131
|
-
{ id: id(), value: "justify-center", name: $T("constructor.props.align.center") },
|
|
132
|
-
{ id: id(), value: "justify-end", name: $T("constructor.props.align.right") },
|
|
131
|
+
{ id: id(), value: "justify-start", name: $T("constructor.props.align.left"), icon: PROPS_ICONS.alignLeft },
|
|
132
|
+
{ id: id(), value: "justify-center", name: $T("constructor.props.align.center"), icon: PROPS_ICONS.alignCenter },
|
|
133
|
+
{ id: id(), value: "justify-end", name: $T("constructor.props.align.right"), icon: PROPS_ICONS.alignRight },
|
|
133
134
|
],
|
|
134
135
|
ICON_TEXT_POSITION_OPTIONS: [
|
|
135
|
-
{ id: id(), value: "flex-row", name: "
|
|
136
|
-
{ id: id(), value: "flex-col", name: "
|
|
137
|
-
{ id: id(), value: "flex-row-reverse", name: "
|
|
138
|
-
{ id: id(), value: "flex-col-reverse", name: "
|
|
136
|
+
{ id: id(), value: "flex-row", name: $T("constructor.props.iconpos.left"), icon: PROPS_ICONS.arrowLeft },
|
|
137
|
+
{ id: id(), value: "flex-col", name: $T("constructor.props.iconpos.top"), icon: PROPS_ICONS.arrowUp },
|
|
138
|
+
{ id: id(), value: "flex-row-reverse", name: $T("constructor.props.iconpos.right"), icon: PROPS_ICONS.arrowRight },
|
|
139
|
+
{ id: id(), value: "flex-col-reverse", name: $T("constructor.props.iconpos.bottom"), icon: PROPS_ICONS.arrowDown },
|
|
139
140
|
],
|
|
140
141
|
TABLE_TEXT_SETTING_OPTIONS: [
|
|
141
|
-
{ id: id(), value: "sortable", name: $T("constructor.props.table.columns.sortable") },
|
|
142
|
-
{ id: id(), value: "copy", name: $T("constructor.props.copy") },
|
|
143
|
-
{ id: id(), value: "truncated", name: $T("constructor.props.table.columns.truncated") },
|
|
144
|
-
{ id: id(), value: "tooltip", name: $T("constructor.props.table.columns.tooltip") },
|
|
145
|
-
{ id: id(), value: "modal", name: $T("constructor.props.table.columns.modal") },
|
|
142
|
+
{ id: id(), value: "sortable", name: $T("constructor.props.table.columns.sortable"), icon: PROPS_ICONS.sort },
|
|
143
|
+
{ id: id(), value: "copy", name: $T("constructor.props.copy"), icon: PROPS_ICONS.copy },
|
|
144
|
+
{ id: id(), value: "truncated", name: $T("constructor.props.table.columns.truncated"), icon: PROPS_ICONS.ellipsis },
|
|
145
|
+
{ id: id(), value: "tooltip", name: $T("constructor.props.table.columns.tooltip"), icon: PROPS_ICONS.tooltip },
|
|
146
|
+
{ id: id(), value: "modal", name: $T("constructor.props.table.columns.modal"), icon: PROPS_ICONS.modal },
|
|
146
147
|
],
|
|
147
148
|
HEIGHT_OPTIONS: [
|
|
148
|
-
{ id: id(), value: "py-1", name: $T("constructor.props.height.small") },
|
|
149
|
-
{ id: id(), value: "py-2", name: $T("constructor.props.height.medium") },
|
|
150
|
-
{ id: id(), value: "py-3", name: $T("constructor.props.height.large") },
|
|
149
|
+
{ id: id(), value: "py-1", name: $T("constructor.props.height.small"), icon: "S" },
|
|
150
|
+
{ id: id(), value: "py-2", name: $T("constructor.props.height.medium"), icon: "M" },
|
|
151
|
+
{ id: id(), value: "py-3", name: $T("constructor.props.height.large"), icon: "L" },
|
|
151
152
|
],
|
|
152
153
|
FILE_ATTACH_TYPE_OPTIONS: [
|
|
153
|
-
{ id: id(), value: "file", name: $T("constructor.props.type.file") },
|
|
154
|
-
{ id: id(), value: "image", name: $T("constructor.props.type.image") },
|
|
154
|
+
{ id: id(), value: "file", name: $T("constructor.props.type.file"), icon: PROPS_ICONS.file },
|
|
155
|
+
{ id: id(), value: "image", name: $T("constructor.props.type.image"), icon: PROPS_ICONS.image },
|
|
155
156
|
],
|
|
156
157
|
DIVIDER_TYPE_OPTIONS: [
|
|
157
|
-
{ id: id(), value: "line", name: $T("constructor.props.divider.type.line") },
|
|
158
|
-
{ id: id(), value: "empty", name: $T("constructor.props.divider.type.empty") },
|
|
158
|
+
{ id: id(), value: "line", name: $T("constructor.props.divider.type.line"), icon: PROPS_ICONS.dividerLine },
|
|
159
|
+
{ id: id(), value: "empty", name: $T("constructor.props.divider.type.empty"), icon: PROPS_ICONS.dividerSpace },
|
|
159
160
|
],
|
|
160
161
|
DIVIDER_ANCHOR_OPTIONS: [
|
|
161
|
-
{ id: id(), value: "top", name: $T("constructor.props.divider.anchor.top") },
|
|
162
|
-
{ id: id(), value: "center", name: $T("constructor.props.divider.anchor.center") },
|
|
163
|
-
{ id: id(), value: "bottom", name: $T("constructor.props.divider.anchor.bottom") },
|
|
162
|
+
{ id: id(), value: "top", name: $T("constructor.props.divider.anchor.top"), icon: PROPS_ICONS.anchorTop },
|
|
163
|
+
{ id: id(), value: "center", name: $T("constructor.props.divider.anchor.center"), icon: PROPS_ICONS.anchorCenter },
|
|
164
|
+
{ id: id(), value: "bottom", name: $T("constructor.props.divider.anchor.bottom"), icon: PROPS_ICONS.anchorBottom },
|
|
164
165
|
],
|
|
165
166
|
FITMODE_OPTIONS: [
|
|
166
|
-
{ id: id(), value: "cover", name: $T("constructor.props.type.cover") },
|
|
167
|
-
{ id: id(), value: "contain", name: $T("constructor.props.type.contain") },
|
|
167
|
+
{ id: id(), value: "cover", name: $T("constructor.props.type.cover"), icon: PROPS_ICONS.fitCover },
|
|
168
|
+
{ id: id(), value: "contain", name: $T("constructor.props.type.contain"), icon: PROPS_ICONS.fitContain },
|
|
168
169
|
],
|
|
169
170
|
SWITCH_OPTIONS: [
|
|
170
|
-
{ id: id(), value: "horizontal", name: $T("constructor.props.type.horizontal") },
|
|
171
|
-
{ id: id(), value: "vertical", name: $T("constructor.props.type.vertical") },
|
|
172
|
-
{ id: id(), value: "checkbox", name: $T("constructor.props.type.checkbox") },
|
|
171
|
+
{ id: id(), value: "horizontal", name: $T("constructor.props.type.horizontal"), icon: PROPS_ICONS.toggleHorizontal },
|
|
172
|
+
{ id: id(), value: "vertical", name: $T("constructor.props.type.vertical"), icon: PROPS_ICONS.toggleVertical },
|
|
173
|
+
{ id: id(), value: "checkbox", name: $T("constructor.props.type.checkbox"), icon: PROPS_ICONS.checkbox },
|
|
173
174
|
],
|
|
174
175
|
INFO_SIDE_OPTIONS: [
|
|
175
|
-
{ id: id(), value: "top", name: $T("constructor.props.info.top") },
|
|
176
|
-
{ id: id(), value: "bottom", name: $T("constructor.props.info.bottom") },
|
|
177
|
-
{ id: id(), value: "left", name: $T("constructor.props.info.left") },
|
|
178
|
-
{ id: id(), value: "right", name: $T("constructor.props.info.right") },
|
|
176
|
+
{ id: id(), value: "top", name: $T("constructor.props.info.top"), icon: PROPS_ICONS.arrowUp },
|
|
177
|
+
{ id: id(), value: "bottom", name: $T("constructor.props.info.bottom"), icon: PROPS_ICONS.arrowDown },
|
|
178
|
+
{ id: id(), value: "left", name: $T("constructor.props.info.left"), icon: PROPS_ICONS.arrowLeft },
|
|
179
|
+
{ id: id(), value: "right", name: $T("constructor.props.info.right"), icon: PROPS_ICONS.arrowRight },
|
|
179
180
|
],
|
|
180
181
|
FORM_OPTIONS: [
|
|
181
|
-
{ id: id(), value: "square", name: $T("constructor.props.type.square") },
|
|
182
|
-
{ id: id(), value: "circle", name: $T("constructor.props.type.circle") },
|
|
182
|
+
{ id: id(), value: "square", name: $T("constructor.props.type.square"), icon: PROPS_ICONS.square },
|
|
183
|
+
{ id: id(), value: "circle", name: $T("constructor.props.type.circle"), icon: PROPS_ICONS.circle },
|
|
183
184
|
],
|
|
184
185
|
TABLE_CONTENT_TYPE_OPTIONS: [
|
|
185
186
|
{ id: id(), value: "text", name: $T("constructor.props.table.type.text") },
|
|
@@ -189,8 +190,8 @@ export const optionsStore = derived(T, ($T) => {
|
|
|
189
190
|
{ id: id(), value: "image", name: $T("constructor.props.table.type.icon") },
|
|
190
191
|
],
|
|
191
192
|
INPUT_SETTING_OPTIONS: [
|
|
192
|
-
{ id: id(), value: "readonly", name: $T("constructor.props.readonly") },
|
|
193
|
-
{ id: id(), value: "help.copyButton", name: $T("constructor.props.copy") },
|
|
193
|
+
{ id: id(), value: "readonly", name: $T("constructor.props.readonly"), icon: PROPS_ICONS.lock },
|
|
194
|
+
{ id: id(), value: "help.copyButton", name: $T("constructor.props.copy"), icon: PROPS_ICONS.copy },
|
|
194
195
|
],
|
|
195
196
|
BUFFER_SIZE_OPTIONS: [
|
|
196
197
|
{ id: id(), value: 10, name: "10" },
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Select, Switch, T, type IOption } from "./"
|
|
3
|
-
import type { Snippet } from "svelte"
|
|
4
|
-
import { fade } from "svelte/transition"
|
|
5
|
-
import CopiedChip from "./CopiedChip.svelte"
|
|
6
|
-
|
|
7
|
-
let {
|
|
8
|
-
component,
|
|
9
|
-
componentProps,
|
|
10
|
-
examples,
|
|
11
|
-
readmeHtml,
|
|
12
|
-
codeText,
|
|
13
|
-
forConstructor = $bindable(),
|
|
14
|
-
}: { component: Snippet; componentProps: Snippet; examples?: Snippet; readmeHtml: string; codeText: string; forConstructor: boolean } = $props()
|
|
15
|
-
|
|
16
|
-
let isCopied = $state(false)
|
|
17
|
-
let snippetOptions = [
|
|
18
|
-
{ id: crypto.randomUUID(), value: "overview", name: $T("library.overview") },
|
|
19
|
-
{ id: crypto.randomUUID(), value: "examples", name: $T("library.examples") },
|
|
20
|
-
{ id: crypto.randomUUID(), value: "info", name: $T("library.info") },
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
let mainSnippet: string | undefined = $state("overview")
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<div class="flex h-full flex-col">
|
|
27
|
-
<Select
|
|
28
|
-
wrapperClass="w-1/2 mx-auto mb-3 shrink-0"
|
|
29
|
-
type="buttons"
|
|
30
|
-
options={snippetOptions}
|
|
31
|
-
value={snippetOptions.find((o) => o.value == mainSnippet)}
|
|
32
|
-
onUpdate={(value) => (mainSnippet = (value as IOption<string>).value)}
|
|
33
|
-
/>
|
|
34
|
-
<div class="min-h-0 flex-1">
|
|
35
|
-
{#key mainSnippet}
|
|
36
|
-
{#if mainSnippet === "examples" && examples}
|
|
37
|
-
{@render examples()}
|
|
38
|
-
{:else if mainSnippet === "info"}
|
|
39
|
-
<div class="h-full overflow-y-auto text-left">
|
|
40
|
-
{@html readmeHtml}
|
|
41
|
-
</div>
|
|
42
|
-
{:else}
|
|
43
|
-
<div class="flex h-full w-full flex-col">
|
|
44
|
-
<div class="min-h-40 flex-[1]">
|
|
45
|
-
{@render component()}
|
|
46
|
-
</div>
|
|
47
|
-
<Switch
|
|
48
|
-
wrapperClass="w-60 self-end p-2 shrink-0"
|
|
49
|
-
label={{ name: $T("library.for_constructor") }}
|
|
50
|
-
value={forConstructor ? 1 : 0}
|
|
51
|
-
options={[{ id: crypto.randomUUID(), value: 0 }]}
|
|
52
|
-
onChange={(value) => (forConstructor = value == 0 ? false : true)}
|
|
53
|
-
/>
|
|
54
|
-
<div class="border-t border-gray-500 shrink-0"></div>
|
|
55
|
-
<div class="min-h-0 flex-[2] overflow-y-auto" transition:fade={{ duration: 200 }}>
|
|
56
|
-
{@render componentProps()}
|
|
57
|
-
<div class="relative mt-3">
|
|
58
|
-
<button
|
|
59
|
-
class="absolute top-2 right-3 flex cursor-pointer border-none bg-transparent"
|
|
60
|
-
onclick={(e) => {
|
|
61
|
-
e.preventDefault()
|
|
62
|
-
navigator.clipboard.writeText(codeText)
|
|
63
|
-
isCopied = true
|
|
64
|
-
setTimeout(() => (isCopied = false), 1000)
|
|
65
|
-
}}
|
|
66
|
-
aria-label="Копировать текст"
|
|
67
|
-
>
|
|
68
|
-
<div class="size-6 text-sm [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
|
|
69
|
-
{#if !isCopied}
|
|
70
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
|
71
|
-
<g fill="none" stroke="currentColor" stroke-width="1.5">
|
|
72
|
-
<path
|
|
73
|
-
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"
|
|
74
|
-
/>
|
|
75
|
-
<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" />
|
|
76
|
-
</g>
|
|
77
|
-
</svg>
|
|
78
|
-
{/if}
|
|
79
|
-
<CopiedChip show={isCopied} class="right-1.5 top-1/2 -translate-y-1/2" />
|
|
80
|
-
</div>
|
|
81
|
-
</button>
|
|
82
|
-
<pre class="overflow-x-auto">{codeText}</pre>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
{/if}
|
|
87
|
-
{/key}
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from "svelte";
|
|
2
|
-
type $$ComponentProps = {
|
|
3
|
-
component: Snippet;
|
|
4
|
-
componentProps: Snippet;
|
|
5
|
-
examples?: Snippet;
|
|
6
|
-
readmeHtml: string;
|
|
7
|
-
codeText: string;
|
|
8
|
-
forConstructor: boolean;
|
|
9
|
-
};
|
|
10
|
-
declare const ComponentExample: import("svelte").Component<$$ComponentProps, {}, "forConstructor">;
|
|
11
|
-
type ComponentExample = ReturnType<typeof ComponentExample>;
|
|
12
|
-
export default ComponentExample;
|