poe-svelte-ui-lib 1.48.0 → 1.50.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.
@@ -72,3 +72,7 @@
72
72
  - При единственном элементе в `array` (`array.length == 1`) ручка визуально приглушена (`opacity-50`) и перетаскивание не запускается
73
73
  - Работает только с `MouseEvent` — нет отдельной поддержки touch/pointer-событий и клавиатурной перестановки (ручка фокусируема как `role="button"`, но `onkeydown` не обрабатывается — известное ограничение доступности)
74
74
  - `container`/`elementIndex`/`array` обязательны — компонент не проверяет их наличие и упадёт при обращении к `array.length` в классе, если `array` не передан
75
+
76
+ ## Вид перетаскивания
77
+
78
+ Один на библиотеку и приложения — `dragStyle.ts` (`applyDragGhost`, `clearDragGhost`, `createDropPlaceholder`, экспортируются из пакета): перетаскиваемый элемент — фон с заметным оттенком акцента (30%), рамка акцентом и тень, курсор «захват»; место вставки — заливка мягким акцентом (`--accent-soft`) со штриховой рамкой акцентом. Тот же вид — у перетаскивания компонентов на холсте конструктора DevCloud.
@@ -2,6 +2,7 @@
2
2
  import { tick } from "svelte"
3
3
  import MoveVerticalIcon from "./libIcons/MoveVerticalIcon.svelte"
4
4
  import { twMerge } from "tailwind-merge"
5
+ import { applyDragGhost, clearDragGhost, createDropPlaceholder } from "./dragStyle"
5
6
 
6
7
  let dragState = $state({
7
8
  isDragging: false,
@@ -38,7 +39,8 @@
38
39
 
39
40
  /* Перетаскивание с MouseEvent */
40
41
  const startDrag = async (e: MouseEvent) => {
41
- const movedElement = document.querySelector(
42
+ /* Строка ищется только внутри своего списка — id вида item-N есть и у других списков на странице */
43
+ const movedElement = container.querySelector(
42
44
  containerIndex == null ? `#${elementsId}-${elementIndex}` : `#${elementsId}-${elementIndex}-${containerIndex}`,
43
45
  ) as HTMLElement
44
46
 
@@ -47,24 +49,19 @@
47
49
  const element = movedElement.cloneNode(true) as HTMLElement
48
50
  const rect = movedElement.getBoundingClientRect()
49
51
 
50
- /* Создаем визуальный эффект перетаскивания */
52
+ /* Визуальный эффект перетаскивания — общий для библиотеки и приложений (dragStyle.ts) */
51
53
  element.id = "drag-element"
52
54
  element.style.position = "fixed"
53
55
  element.style.zIndex = "9999"
54
56
  element.style.minHeight = `${rect.height}px`
55
57
  element.style.pointerEvents = "none"
56
58
  element.style.width = `${rect.width}px`
57
- element.style.background = "var(--field-color)"
58
- element.style.padding = "0.25rem"
59
- element.style.borderRadius = "1rem"
59
+ element.style.left = `${rect.left}px`
60
60
  element.style.top = `${rect.top}px`
61
+ applyDragGhost(element)
61
62
  movedElement.parentNode?.appendChild(element)
62
63
 
63
- const placeholder = document.createElement("div")
64
- placeholder.style.minHeight = `${rect.height}px`
65
- placeholder.style.width = "100%"
66
- placeholder.style.border = "2px dashed var(--blue-color)"
67
- placeholder.style.borderRadius = "1rem"
64
+ const placeholder = createDropPlaceholder(rect.height)
68
65
  element.parentNode?.insertBefore(placeholder, movedElement)
69
66
 
70
67
  const [movedItem] = array.splice(elementIndex, 1)
@@ -126,6 +123,7 @@
126
123
 
127
124
  if (dragState.placeholderElement) dragState.placeholderElement.remove()
128
125
  if (dragState.ghostElement) dragState.ghostElement.remove()
126
+ clearDragGhost()
129
127
 
130
128
  dragState = {
131
129
  isDragging: false,
@@ -38,6 +38,7 @@
38
38
  | `size.height` | `number` | `6` | Количество строк в сетке содержимого |
39
39
  | `activeTab` | `number` | `0` | Индекс активной вкладки; `$bindable` — `bind:activeTab` получает выбор по клику, изменение снаружи переключает вкладку |
40
40
  | `items` | `{ name?: string; icon?: ConstructorOfATypedSvelteComponent \| string; class?: string; children?: Snippet; onClick?: () => {}; disabled?: boolean }[]` | `[{ name: "tab 1", icon: "", class: "" }, { name: "tab 2", icon: "", class: "" }]` | Массив элементов вкладок |
41
+ | `items[].id` | `string` | `undefined` | Стабильный id вкладки: окно свойств выдаёт его каждой вкладке, конструктор по нему переносит содержимое при перестановке/удалении вкладок (в GUI содержимое привязано к номеру вкладки — последний символ id дочернего компонента) |
41
42
  | `items[].name` | `string` | `undefined` | Название (заголовок) вкладки |
42
43
  | `items[].icon` | `ConstructorOfATypedSvelteComponent \| string` | `undefined` | Иконка вкладки (SVG строка или Svelte-компонент) |
43
44
  | `items[].class` | `string` | `undefined` | CSS-классы для стилизации вкладки; `flex-row`/`flex-col`/`flex-row-reverse`/`flex-col-reverse` — позиция иконки относительно текста; `flex-1` — вкладка растягивается на равную долю ширины (учитывает отступы между кнопками), `w-auto` — ширина по контенту |
@@ -127,6 +128,8 @@
127
128
 
128
129
  ## Конструктор свойств (TabsProps.svelte)
129
130
 
131
+ Окно «Вкладки» — список вкладок: у каждой строки заголовок «Вкладка N · название на текущем языке» (или «без названия»), иконка, «Отключить», перетаскивание за ручку слева и удаление; названия правятся в «Тексты и переводы». Новая вкладка создаётся без названия.
132
+
130
133
  ### Описание
131
134
 
132
135
  Компонент `TabsProps.svelte` предоставляет визуальный интерфейс для редактирования свойств компонента вкладок. Поддерживает два режима отображения: для конструктора и для редактирования.
@@ -11,6 +11,8 @@
11
11
  import CommonSnippets from "../CommonSnippets.svelte"
12
12
  import PropsGroup from "../PropsGroup.svelte"
13
13
  import PropsListModal from "../PropsListModal.svelte"
14
+ import { Language } from "../locales/i18n"
15
+ import { localizeText } from "../locales/localize"
14
16
 
15
17
  const {
16
18
  component,
@@ -24,6 +26,18 @@
24
26
 
25
27
  let itemsContainer: HTMLDivElement | null = $state(null)
26
28
 
29
+ /* У каждой вкладки — стабильный id: по нему конструктор переносит её содержимое при перестановке и удалении вкладок */
30
+ $effect(() => {
31
+ const items = component.properties.items ?? []
32
+ if (items.every((item: { id?: string }) => item.id)) return
33
+ updateProperty(
34
+ "items",
35
+ items.map((item: { id?: string }) => (item.id ? item : { ...item, id: crypto.randomUUID() })),
36
+ component,
37
+ onPropertyChange,
38
+ )
39
+ })
40
+
27
41
  const initialColor = $derived(findColorOption($optionsStore.COLOR_OPTIONS, component.properties.wrapperClass))
28
42
 
29
43
  const initialPosition = $derived.by(() => {
@@ -91,8 +105,9 @@
91
105
  wrapperClass="w-8"
92
106
  content={{ icon: ButtonAdd }}
93
107
  onClick={() => {
94
- const newItem: { name: string; icon: string; class: string } = {
95
- name: `Tab ${component.properties?.items.length + 1}`,
108
+ const newItem: { id: string; name: string; icon: string; class: string } = {
109
+ id: crypto.randomUUID(),
110
+ name: "",
96
111
  class: `text-${initialColor?.value.slice(3)}-500 ${initialPosition?.value}`,
97
112
  icon: "",
98
113
  }
@@ -122,6 +137,11 @@
122
137
  }}
123
138
  />
124
139
  <div class="flex flex-wrap items-end gap-2">
140
+ <!-- Какая это вкладка: номер и название на текущем языке (название правится в «Тексты и переводы») -->
141
+ <div class="flex w-full items-baseline gap-2 text-[13px]">
142
+ <span class="font-semibold">{$T("constructor.props.tabs.item", { n: index + 1 })}</span>
143
+ <span class="truncate text-(--muted-color)">{localizeText(tab.name, $Language) || $T("constructor.props.tabs.untitled")}</span>
144
+ </div>
125
145
  <div class="relative min-w-32">
126
146
  <CommonSnippets
127
147
  snippet="IconsLib"
@@ -0,0 +1,3 @@
1
+ export declare const applyDragGhost: (element: HTMLElement) => void;
2
+ export declare const clearDragGhost: (element?: HTMLElement | null) => void;
3
+ export declare const createDropPlaceholder: (height: number) => HTMLElement;
@@ -0,0 +1,35 @@
1
+ /* $lib/dragStyle.ts — единый вид перетаскивания для библиотеки и приложений (списки Dragging, холст конструктора DevCloud):
2
+ перетаскиваемый элемент — фон с заметным оттенком акцента, рамка акцентом и тень; место вставки — заливка мягким акцентом со штриховой рамкой */
3
+ const GHOST_STYLE = {
4
+ background: "color-mix(in srgb, var(--accent-color) 30%, var(--back-color))",
5
+ outline: "2px solid var(--accent-color)",
6
+ outlineOffset: "-2px",
7
+ boxShadow: "var(--elevation-3)",
8
+ borderRadius: "12px",
9
+ opacity: "1",
10
+ };
11
+ /* Оформить перетаскиваемый элемент; курсор «захват» — на всё время перетаскивания */
12
+ export const applyDragGhost = (element) => {
13
+ Object.assign(element.style, GHOST_STYLE);
14
+ document.body.style.cursor = "grabbing";
15
+ };
16
+ /* Снять оформление перетаскивания с элемента (если он остаётся на странице) и вернуть курсор */
17
+ export const clearDragGhost = (element) => {
18
+ if (element)
19
+ for (const key of Object.keys(GHOST_STYLE))
20
+ element.style[key] = "";
21
+ document.body.style.cursor = "";
22
+ };
23
+ /* Место вставки: заливка мягким акцентом и штриховая рамка акцентом */
24
+ export const createDropPlaceholder = (height) => {
25
+ const placeholder = document.createElement("div");
26
+ Object.assign(placeholder.style, {
27
+ minHeight: `${height}px`,
28
+ width: "100%",
29
+ background: "var(--accent-soft)",
30
+ border: "2px dashed var(--accent-color)",
31
+ borderRadius: "12px",
32
+ boxSizing: "border-box",
33
+ });
34
+ return placeholder;
35
+ };
package/dist/index.d.ts CHANGED
@@ -54,5 +54,6 @@ export * from "./locales/i18n";
54
54
  export * from "./locales/languages";
55
55
  export * from "./locales/localize";
56
56
  export { widgetAccentStyle } from "./widgetAccent";
57
+ export { applyDragGhost, clearDragGhost, createDropPlaceholder } from "./dragStyle";
57
58
  export * from "./ModalStackStore";
58
59
  export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type IOption, type ISwitchProps, type IDividerProps, type ISliderProps, type ITextFieldProps, type IProgressBarProps, type IWidgetProps, type IWidgetWiFiProps, type IWidgetWiFiConfig, type IWidgetDeviceInfoProps, type IWidgetDeviceInfoConfig, type IWidgetFileSystemProps, type IWidgetFileSystemInfo, type IWidgetFileSystemFile, type IWidgetFileSystemUpload, type IWidgetStackInfoProps, type IWidgetStackInfo, type IWidgetStackInfoHeap, type IWidgetStackInfoTask, type IWidgetRGBStripProps, type IWidgetRGBStripConfig, type IWidgetPFDProps, type IWidgetPFDData, type IWidgetPFDLimits, type IWidgetMapProps, type IWidgetMapData, type IGraphProps, type IGraphDataObject, type IGraphHistoryPoint, type IGraphHistorySeries, type ITableHeader, type ITableText, type ITableSelect, type ITableButton, type ITableProgressBar, type ITableImage, type ITableContent, type ITableProps, type ITabsProps, type IJoystickProps, type IFileAttachProps, type ICarouselProps, type IReceivingDataObject, type IVideoViewerProps, type IPeriodChartProps, type IPeriodChartLevel, } from "./types";
package/dist/index.js CHANGED
@@ -55,5 +55,6 @@ export * from "./locales/i18n";
55
55
  export * from "./locales/languages";
56
56
  export * from "./locales/localize";
57
57
  export { widgetAccentStyle } from "./widgetAccent";
58
+ export { applyDragGhost, clearDragGhost, createDropPlaceholder } from "./dragStyle";
58
59
  export * from "./ModalStackStore";
59
60
  export {} from "./types";
@@ -304,7 +304,9 @@ const translations = {
304
304
  "constructor.props.options.title": { ru: "Опции компонента", en: "Component options", zh: "组件选项" },
305
305
  "constructor.props.progressbar.title": { ru: "Полосы прогресса", en: "Progress bars", zh: "进度条" },
306
306
  "constructor.props.bits.title": { ru: "Биты", en: "Bits", zh: "位" },
307
- "constructor.props.tabs.title": { ru: "Вкладки меню", en: "Menu tabs", zh: "菜单标签" },
307
+ "constructor.props.tabs.title": { ru: "Вкладки", en: "Tabs", zh: "标签页" },
308
+ "constructor.props.tabs.item": { ru: "Вкладка {n}", en: "Tab {n}", zh: "标签页 {n}" },
309
+ "constructor.props.tabs.untitled": { ru: "без названия", en: "untitled", zh: "未命名" },
308
310
  "constructor.props.periodchart.title": { ru: "Уровни детализации", en: "Detail levels", zh: "详细级别" },
309
311
  "constructor.props.periodchart.data": { ru: "Данные (через пробел)", en: "Data (comma-separated)", zh: "数据(逗号分隔)" },
310
312
  "constructor.props.periodchart.labels": { ru: "Подписи столбиков (через пробел)", en: "Bar labels (comma-separated)", zh: "柱标签(逗号分隔)" },
package/dist/types.d.ts CHANGED
@@ -396,6 +396,7 @@ export interface ITabsProps {
396
396
  };
397
397
  activeTab?: number;
398
398
  items: {
399
+ id?: string;
399
400
  name?: string;
400
401
  icon?: ConstructorOfATypedSvelteComponent | string;
401
402
  class?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.48.0",
3
+ "version": "1.50.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"