poe-svelte-ui-lib 1.1.22 → 1.2.1

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.
Files changed (58) hide show
  1. package/dist/{IconsLib → IconsCatalog}/iconsLib.js +1 -1
  2. package/dist/Joystick/Joystick.svelte +97 -0
  3. package/dist/Joystick/Joystick.svelte.d.ts +4 -0
  4. package/dist/Joystick/JoystickProps.svelte +0 -0
  5. package/dist/Joystick/JoystickProps.svelte.d.ts +26 -0
  6. package/dist/Tabs/Tabs.svelte +86 -0
  7. package/dist/Tabs/Tabs.svelte.d.ts +4 -0
  8. package/dist/Tabs/TabsProps.svelte +420 -0
  9. package/dist/Tabs/TabsProps.svelte.d.ts +11 -0
  10. package/dist/index.d.ts +3 -1
  11. package/dist/index.js +4 -0
  12. package/dist/locales/translations.js +2 -0
  13. package/dist/options.d.ts +10 -0
  14. package/dist/options.js +11 -0
  15. package/dist/types.d.ts +20 -4
  16. package/package.json +7 -7
  17. /package/dist/{IconsLib → IconsCatalog}/access.card.svg +0 -0
  18. /package/dist/{IconsLib → IconsCatalog}/access.key.svg +0 -0
  19. /package/dist/{IconsLib → IconsCatalog}/access.lock.svg +0 -0
  20. /package/dist/{IconsLib → IconsCatalog}/common.battery.svg +0 -0
  21. /package/dist/{IconsLib → IconsCatalog}/common.block.svg +0 -0
  22. /package/dist/{IconsLib → IconsCatalog}/common.edit.svg +0 -0
  23. /package/dist/{IconsLib → IconsCatalog}/common.list.svg +0 -0
  24. /package/dist/{IconsLib → IconsCatalog}/common.search.svg +0 -0
  25. /package/dist/{IconsLib → IconsCatalog}/common.trash.svg +0 -0
  26. /package/dist/{IconsLib → IconsCatalog}/common.warning.svg +0 -0
  27. /package/dist/{IconsLib → IconsCatalog}/iconsLib.d.ts +0 -0
  28. /package/dist/{IconsLib → IconsCatalog}/info.bell.svg +0 -0
  29. /package/dist/{IconsLib → IconsCatalog}/info.clock.svg +0 -0
  30. /package/dist/{IconsLib → IconsCatalog}/info.graph.svg +0 -0
  31. /package/dist/{IconsLib → IconsCatalog}/info.info.svg +0 -0
  32. /package/dist/{IconsLib → IconsCatalog}/media.camera.svg +0 -0
  33. /package/dist/{IconsLib → IconsCatalog}/media.movie.svg +0 -0
  34. /package/dist/{IconsLib → IconsCatalog}/media.sound.svg +0 -0
  35. /package/dist/{IconsLib → IconsCatalog}/media.webcam.svg +0 -0
  36. /package/dist/{IconsLib → IconsCatalog}/network.cellular.svg +0 -0
  37. /package/dist/{IconsLib → IconsCatalog}/network.cloud.svg +0 -0
  38. /package/dist/{IconsLib → IconsCatalog}/network.globe.svg +0 -0
  39. /package/dist/{IconsLib → IconsCatalog}/network.link.svg +0 -0
  40. /package/dist/{IconsLib → IconsCatalog}/network.radio.svg +0 -0
  41. /package/dist/{IconsLib → IconsCatalog}/network.share.svg +0 -0
  42. /package/dist/{IconsLib → IconsCatalog}/network.wifi.svg +0 -0
  43. /package/dist/{IconsLib → IconsCatalog}/periphery.board.svg +0 -0
  44. /package/dist/{IconsLib → IconsCatalog}/periphery.chip.svg +0 -0
  45. /package/dist/{IconsLib → IconsCatalog}/periphery.magnit.svg +0 -0
  46. /package/dist/{IconsLib → IconsCatalog}/periphery.memory.svg +0 -0
  47. /package/dist/{IconsLib → IconsCatalog}/periphery.volume.svg +0 -0
  48. /package/dist/{IconsLib → IconsCatalog}/power.jeck.svg +0 -0
  49. /package/dist/{IconsLib → IconsCatalog}/power.power.svg +0 -0
  50. /package/dist/{IconsLib → IconsCatalog}/scenarios.point.svg +0 -0
  51. /package/dist/{IconsLib → IconsCatalog}/scenarios.speed.svg +0 -0
  52. /package/dist/{IconsLib → IconsCatalog}/settings.debug.svg +0 -0
  53. /package/dist/{IconsLib → IconsCatalog}/settings.hammer.svg +0 -0
  54. /package/dist/{IconsLib → IconsCatalog}/settings.list.svg +0 -0
  55. /package/dist/{IconsLib → IconsCatalog}/settings.save.svg +0 -0
  56. /package/dist/{IconsLib → IconsCatalog}/settings.settings.svg +0 -0
  57. /package/dist/{IconsLib → IconsCatalog}/settings.spanner.svg +0 -0
  58. /package/dist/{IconsLib → IconsCatalog}/settings.terminal.svg +0 -0
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import { join } from 'path';
3
3
  const scanAllIcons = async () => {
4
- const dirPath = 'src/lib/IconsLib';
4
+ const dirPath = 'src/lib/IconsCatalog';
5
5
  const files = fs.readdirSync(dirPath);
6
6
  const categories = {};
7
7
  files
@@ -0,0 +1,97 @@
1
+ <!-- $lib/ElementsUI/ProgressBar.svelte -->
2
+ <script lang="ts">
3
+ import { twMerge } from 'tailwind-merge'
4
+ import type { IProgressBarProps } from '../types'
5
+
6
+ let {
7
+ id = crypto.randomUUID(),
8
+ wrapperClass = '',
9
+ label = { name: '', class: '' },
10
+ value = $bindable(0),
11
+ number = {
12
+ minNum: 0,
13
+ maxNum: 100,
14
+ units: '%',
15
+ },
16
+ }: IProgressBarProps = $props()
17
+
18
+ const directions = [
19
+ { id: 'right', angle: 30.5, content: true },
20
+ { id: 'bottom-right', angle: 58, content: false },
21
+ { id: 'bottom', angle: 122, content: true },
22
+ { id: 'bottom-left', angle: 149.5, content: false },
23
+ { id: 'left', angle: 212, content: true },
24
+ { id: 'top-left', angle: 239, content: false },
25
+ { id: 'top', angle: 301, content: true },
26
+ {
27
+ id: 'top-right',
28
+ angle: 328,
29
+ content: false,
30
+ },
31
+ ]
32
+
33
+ let sectorHeight = 2 * 5 * Math.sin(Math.PI / directions.length)
34
+ let clipPos = Math.cos(Math.PI / directions.length) * 100
35
+ let angle = 360 / directions.length
36
+ </script>
37
+
38
+ <div {id} class={twMerge(`bg-red relative flex w-full flex-col items-center`, wrapperClass)}>
39
+ {#if label.name}
40
+ <h5 class={twMerge(` w-full px-4 text-center`, label.class)}>{label.name}</h5>
41
+ {/if}
42
+
43
+ <div class="relative flex size-40 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_20px_rgb(0_0_0_/0.25)]">
44
+ <div class="absolute h-full w-full overflow-hidden rounded-full">
45
+ {#each directions as direction, index}
46
+ <button class="pointer-events-none absolute top-1/2 left-1/2 block w-1/2 -translate-y-1/2 cursor-pointer" title="">
47
+ <span
48
+ class="relative flex w-full origin-left items-center justify-center pl-[60%] h-[{direction.content
49
+ ? 2 * 5 * Math.sin((Math.PI * 65) / 360)
50
+ : 2 * 5 * Math.sin((Math.PI * 25) / 360)}rem] pointer-events-auto
51
+ {direction.content ? 'bg-(--bg-color)' : ''}
52
+ "
53
+ style="transform: rotate({angle * index}deg); clip-path: polygon(100% 0, {clipPos}% 0, 0 50%, {clipPos}% 100%, 100% 100%)"
54
+ role="application"
55
+ onmouseenter={(e) => (e.currentTarget.style.backgroundColor = 'color-mix(in srgb, var(--bg-color), var(--shadow-color) 20%)')}
56
+ onmouseleave={(e) => (e.currentTarget.style.backgroundColor = 'var(--bg-color)')}
57
+ >
58
+ {#if direction.content}
59
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
60
+ ><path
61
+ fill="currentColor"
62
+ d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"
63
+ /></svg
64
+ >
65
+ {/if}
66
+ </span>
67
+ </button>
68
+ {/each}
69
+ </div>
70
+ <div class=" pointer-events-none absolute h-full w-full overflow-hidden rounded-full">
71
+ {#each directions as direction, index}
72
+ <span
73
+ class=" absolute top-1/2 left-1/2 h-0 w-[52%] origin-left border-b border-(--bg-color) {index % 2 == 0
74
+ ? 'shadow-[0_3px_5px_rgb(0_0_0_/0.5)] '
75
+ : 'shadow-[0_-3px_5px_rgb(0_0_0_/0.5)]'}"
76
+ style="transform: rotate({direction.angle}deg);"
77
+ >
78
+ </span>
79
+ {/each}
80
+ </div>
81
+ <div class="z-10 flex size-20 items-center justify-center rounded-full bg-(--bg-color) shadow-[0_0_15px_rgb(0_0_0_/0.25)]">
82
+ <button class="flex size-18 cursor-pointer items-center justify-center rounded-full bg-(--bg-color)" title=""
83
+ ><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"
84
+ ><path
85
+ fill="currentColor"
86
+ d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"
87
+ /></svg
88
+ ></button
89
+ >
90
+ </div>
91
+ </div>
92
+
93
+ <!-- {direction.content ? 2 * 6.25 * Math.sin((Math.PI * 65) / 360) : 2 * 6.25 * Math.sin((Math.PI * 25) / 360)} -->
94
+ <!-- angle / 2 + angle * index -->
95
+ <!-- ? 'shadow-[0_-2px_5px_rgb(254_202_202)] '
96
+ : 'shadow-[0_2px_5px_rgb(254_202_202)]'}" -->
97
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { IProgressBarProps } from '../types';
2
+ declare const Joystick: import("svelte").Component<IProgressBarProps, {}, "value">;
3
+ type Joystick = ReturnType<typeof Joystick>;
4
+ export default Joystick;
File without changes
@@ -0,0 +1,26 @@
1
+ export default JoystickProps;
2
+ type JoystickProps = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
8
+ };
9
+ declare const JoystickProps: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import type { ITabsProps } from '../types'
3
+ import { onMount } from 'svelte'
4
+ import { twMerge } from 'tailwind-merge'
5
+
6
+ let {
7
+ id = crypto.randomUUID(),
8
+ wrapperClass = '',
9
+ size = { width: 12, height: 6 },
10
+ activeTab = 0,
11
+ items = [
12
+ {
13
+ name: 'tab 1',
14
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"\r\n ><path\r\n fill="none"\r\n stroke="currentColor"\r\n stroke-linecap="round"\r\n stroke-linejoin="round"\r\n stroke-width="1.5"\r\n d="M14.44 5.78L4.198 16.02a2 2 0 0 0-.565 1.125l-.553 3.774l3.775-.553A2 2 0 0 0 7.98 19.8L18.22 9.56m-3.78-3.78l2.229-2.23a1.6 1.6 0 0 1 2.263 0l1.518 1.518a1.6 1.6 0 0 1 0 2.263l-2.23 2.23M14.44 5.78l3.78 3.78"\r\n /></svg\r\n>\r\n',
15
+ class: '',
16
+ },
17
+ { name: 'tab 2', icon: '', class: 'bg-red' },
18
+ ],
19
+ apiArray = [],
20
+ Components,
21
+ }: ITabsProps = $props()
22
+
23
+ const isCol = $derived(items.find((item) => item.class?.startsWith('flex-col')))
24
+
25
+ let currentTabIndex: number = $state(activeTab)
26
+ </script>
27
+
28
+ <div {id} class="w-full rounded-2xl bg-(--back-color)">
29
+ <!-- Вкладки -->
30
+ <div
31
+ class="{twMerge('bg-blue sticky top-0 z-50 flex h-fit items-center overflow-x-auto rounded-t-2xl px-1 pt-1', wrapperClass)}
32
+ bg-(--bg-color)"
33
+ >
34
+ {#each items as item, index}
35
+ <button
36
+ class={twMerge(
37
+ `tab mt-1 flex min-w-fit cursor-pointer items-center justify-center gap-0 self-end rounded-t-2xl px-5 py-2.5 ${!isCol && 'gap-2'}`,
38
+ item.class,
39
+ index === currentTabIndex ? twMerge('bg-(--back-color) text-blue-500', item.class) : 'bg-(--bg-color) text-gray-500',
40
+ )}
41
+ onclick={() => (currentTabIndex = index)}
42
+ >
43
+ {#if item?.icon}
44
+ <span class="flex h-7 w-7 items-center justify-center overflow-visible [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full">
45
+ {@html item.icon}
46
+ </span>
47
+ {/if}
48
+ {#if item?.name}
49
+ <span class="text-lg font-semibold">{item.name}</span>
50
+ {/if}
51
+ </button>
52
+ <span
53
+ class="h-[{isCol && items.find((item) => item.icon) ? 35 : 16}px] w-0 border border-l {index !== items.length - 1 &&
54
+ index !== currentTabIndex &&
55
+ index !== currentTabIndex - 1
56
+ ? 'border-gray-500'
57
+ : 'border-(--bg-color)'}"
58
+ ></span>
59
+ {/each}
60
+ </div>
61
+
62
+ <!-- Контент вкладки -->
63
+ <div
64
+ class="grid w-full gap-2 rounded-2xl bg-(--back-color) p-4"
65
+ style="grid-template-columns: repeat({size.width || 1}, minmax(0, 1fr)); grid-template-rows: repeat({size.height || 1}, auto);"
66
+ >
67
+ {#if Components}
68
+ {#each (apiArray ?? []).filter((c) => c.id.endsWith(`${currentTabIndex}`)) as comp}
69
+ {@render Components(comp, false)}
70
+ {/each}
71
+ {:else}
72
+ {@render items[currentTabIndex]?.children?.()}
73
+ {/if}
74
+ </div>
75
+ </div>
76
+
77
+ <style>
78
+ ::-webkit-scrollbar-track {
79
+ background: var(--back-color);
80
+ }
81
+ ::-webkit-scrollbar-thumb {
82
+ background-color: color-mix(in srgb, var(--bg-color), var(--back-color) 20%);
83
+ border-radius: 8px;
84
+ cursor: pointer;
85
+ }
86
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ITabsProps } from '../types';
2
+ declare const Tabs: import("svelte").Component<ITabsProps, {}, "">;
3
+ type Tabs = ReturnType<typeof Tabs>;
4
+ export default Tabs;
@@ -0,0 +1,420 @@
1
+ <script lang="ts">
2
+ import { t } from '../locales/i18n'
3
+ import { updateProperty, type ISelectOption, type ITabsProps, type UIComponent } from '../types'
4
+ import * as UI from '..'
5
+ import { optionsStore } from '../options'
6
+ import { ICONS } from '../icons'
7
+ import Modal from '../Modal.svelte'
8
+ import Button from '../Button/Button.svelte'
9
+ import CrossIcon from '../libIcons/CrossIcon.svelte'
10
+ import ButtonAdd from '../libIcons/ButtonAdd.svelte'
11
+ import ButtonDelete from '../libIcons/ButtonDelete.svelte'
12
+ import { twMerge } from 'tailwind-merge'
13
+
14
+ const {
15
+ component,
16
+ onPropertyChange,
17
+ forConstructor = true,
18
+ } = $props<{
19
+ component: UIComponent & { properties: Partial<ITabsProps> }
20
+ onPropertyChange: (value: string | object, name?: string) => void
21
+ forConstructor?: boolean
22
+ }>()
23
+
24
+ let showIconLib = $state(false)
25
+ let tabIcon = $state({ index: 0, isModalOpen: false })
26
+
27
+ const initialType = $derived($optionsStore.ACCORDION_TYPE_OPTIONS.find((t) => t.value === component.properties.outline))
28
+
29
+ const initialAlign = $derived(
30
+ $optionsStore.JUSTIFY_ALIGN_OPTIONS.find((a) =>
31
+ (a.value as string).includes(component.properties.wrapperClass?.split(' ').find((cls: string) => cls.startsWith('justify-'))),
32
+ ),
33
+ )
34
+
35
+ const initialColor = $derived(
36
+ $optionsStore.COLOR_OPTIONS.find((c) =>
37
+ (c.value as string).includes(component.properties.wrapperClass?.split(' ').find((cls: string) => cls.startsWith('bg-'))),
38
+ ),
39
+ )
40
+
41
+ const initialPosition = $derived(
42
+ $optionsStore.ICON_TEXT_POSITION_OPTIONS.find((c) =>
43
+ (c.value as string).includes(
44
+ component.properties.items
45
+ .find((item: ISelectOption) => item.class?.includes('flex-'))
46
+ .class.split(' ')
47
+ .find((cls: string) => cls.startsWith('flex-')),
48
+ ),
49
+ ),
50
+ )
51
+
52
+ const initialWidth = $derived(() => {
53
+ let width = component.properties.items.find((item: ISelectOption) => item.class?.split(' ').find((cls: string) => cls.startsWith('w-')))
54
+ if (!width) {
55
+ return 1
56
+ } else if (width.class.includes('w-auto')) return 1
57
+ else return 2
58
+ })
59
+
60
+ const handleImageUpload = (event: Event) => {
61
+ const input = event.target as HTMLInputElement
62
+ if (!input.files || input.files.length === 0) return
63
+
64
+ const file = input.files[0]
65
+ const reader = new FileReader()
66
+ reader.onload = (e) => {
67
+ const base64String = e.target?.result as string
68
+ updateProperty('image', base64String, component, onPropertyChange)
69
+ }
70
+ reader.readAsDataURL(file)
71
+ }
72
+
73
+ let currentImage = $derived(component.properties.image ?? '')
74
+ </script>
75
+
76
+ {#if forConstructor}
77
+ <div class="flex items-center justify-center gap-8">
78
+ <div class="flex w-1/3 flex-col items-center px-2">
79
+ <UI.Select
80
+ label={{ name: $t('constructor.props.align') }}
81
+ type="buttons"
82
+ value={initialAlign}
83
+ options={$optionsStore.JUSTIFY_ALIGN_OPTIONS}
84
+ onUpdate={(option) => updateProperty('wrapperClass', twMerge(component.properties.wrapperClass, option.value), component, onPropertyChange)}
85
+ />
86
+
87
+ <UI.Select
88
+ wrapperClass="h-14"
89
+ label={{ name: $t('constructor.props.colors') }}
90
+ type="buttons"
91
+ options={$optionsStore.COLOR_OPTIONS.slice(1)}
92
+ value={initialColor}
93
+ onUpdate={(option) => {
94
+ updateProperty('wrapperClass', twMerge(component.properties.wrapperClass, option.value), component, onPropertyChange)
95
+ component.properties.items.forEach((_item: any, index: number) => {
96
+ const items = [...(component.properties?.items || [])]
97
+ items[index]['class'] = twMerge(items[index].class, `text-${option.value?.slice(3)}-500`)
98
+ updateProperty('items', items, component, onPropertyChange)
99
+ })
100
+ }}
101
+ />
102
+ </div>
103
+ <div class="flex w-1/3 flex-col items-center px-2">
104
+ <UI.Select
105
+ wrapperClass="h-14"
106
+ label={{ name: $t('constructor.props.icon.text.position') }}
107
+ type="buttons"
108
+ options={$optionsStore.ICON_TEXT_POSITION_OPTIONS}
109
+ value={initialPosition}
110
+ onUpdate={(option) => {
111
+ component.properties.items.forEach((_item: any, index: number) => {
112
+ const items = [...(component.properties?.items || [])]
113
+ items[index]['class'] = twMerge(items[index].class, option.value)
114
+ updateProperty('items', items, component, onPropertyChange)
115
+ })
116
+ }}
117
+ />
118
+ <UI.Switch
119
+ label={{
120
+ name: $t('constructor.props.widthMode'),
121
+ captionLeft: $t('constructor.props.height.small'),
122
+ captionRight: $t('constructor.props.equal'),
123
+ }}
124
+ value={initialWidth()}
125
+ onChange={(value) => {
126
+ if (value === 2) {
127
+ component.properties.items.forEach((_item: any, index: number) => {
128
+ let tabWidth = Math.max(...Array.from(document.body.querySelectorAll('.tab')).map((item) => (item as HTMLElement).offsetWidth))
129
+ const items = [...(component.properties?.items || [])]
130
+ items[index]['class'] = twMerge(items[index].class, `w-[${tabWidth}px]`)
131
+ updateProperty('items', items, component, onPropertyChange)
132
+ })
133
+ } else {
134
+ component.properties.items.forEach((_item: any, index: number) => {
135
+ const items = [...(component.properties?.items || [])]
136
+ items[index]['class'] = twMerge(items[index].class, 'w-auto')
137
+ updateProperty('items', items, component, onPropertyChange)
138
+ })
139
+ }
140
+ console.log(component.properties?.items)
141
+ }}
142
+ />
143
+ </div>
144
+ </div>
145
+
146
+ <div class="space-y-4">
147
+ <div class="m-0 flex items-center justify-center gap-2">
148
+ <h4>{$t('constructor.props.options.title')}</h4>
149
+ <UI.Button
150
+ wrapperClass="w-8"
151
+ content={{ icon: ButtonAdd }}
152
+ onClick={() => {
153
+ let tabWidth = Math.max(...Array.from(document.body.querySelectorAll('.tab')).map((item) => (item as HTMLElement).offsetWidth))
154
+ const newItem: { name: string; icon: string; class: string } = {
155
+ name: `Tab ${component.properties?.items.length + 1}`,
156
+ class: `w-${initialWidth() === 2 ? `[${tabWidth}px]` : 'auto'} text-${initialColor?.value.slice(3)}-500 ${initialPosition?.value}`,
157
+ icon: '',
158
+ }
159
+ const items = [...(component.properties?.items || []), newItem]
160
+ updateProperty('items', items, component, onPropertyChange)
161
+ }}
162
+ />
163
+ </div>
164
+
165
+ {#each component.properties.items || [] as tab, index}
166
+ <div class="m-0 flex items-end justify-around gap-2 border-gray-400">
167
+ <UI.Input
168
+ label={{ name: $t('constructor.props.optionname') }}
169
+ wrapperClass="w-5/10"
170
+ value={tab.name}
171
+ onUpdate={(value) => {
172
+ const items = [...(component.properties?.items || [])]
173
+ items[index]['name'] = value
174
+ updateProperty('items', items, component, onPropertyChange)
175
+ }}
176
+ />
177
+ <div class="relative mt-5 flex w-3/10 gap-2">
178
+ <UI.Button content={{ name: $t('constructor.props.labelicon') }} onClick={() => (tabIcon = { index: index, isModalOpen: true })} />
179
+
180
+ {#if tab.icon}
181
+ <Button
182
+ wrapperClass="w-8.5 "
183
+ componentClass="p-0.5 bg-red"
184
+ content={{ icon: CrossIcon }}
185
+ onClick={() => {
186
+ const items = [...(component.properties?.items || [])]
187
+ items[index]['icon'] = ''
188
+ updateProperty('items', items, component, onPropertyChange)
189
+ }}
190
+ />
191
+ {/if}
192
+ </div>
193
+
194
+ {#if component.properties.items.length > 1}
195
+ <UI.Button
196
+ wrapperClass="w-8"
197
+ content={{ icon: ButtonDelete }}
198
+ onClick={() => {
199
+ const items = [...(component.properties?.items || [])]
200
+ items.splice(index, 1)
201
+ updateProperty('items', items, component, onPropertyChange)
202
+ }}
203
+ />
204
+ {/if}
205
+ </div>
206
+ {/each}
207
+ {#if tabIcon.isModalOpen}
208
+ <Modal bind:isOpen={tabIcon.isModalOpen} wrapperClass="w-130">
209
+ {#snippet main()}
210
+ <div class="grid grid-cols-3">
211
+ {#each ICONS as category}
212
+ <div class="relative m-1.5 rounded-xl border-2 border-(--border-color) p-3">
213
+ <div class="absolute -top-3.5 bg-(--back-color) px-1">{$t(`constructor.props.icon.${category[0]}`)}</div>
214
+ <div class="grid grid-cols-3 place-items-center gap-2">
215
+ {#each category[1] as icon}
216
+ <button
217
+ class="h-8 w-8 cursor-pointer [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full"
218
+ onclick={() => {
219
+ const items = [...(component.properties?.items || [])]
220
+ items[tabIcon.index]['icon'] = icon as string
221
+ updateProperty('items', items, component, onPropertyChange)
222
+ }}
223
+ >
224
+ {@html icon}
225
+ </button>{/each}
226
+ </div>
227
+ </div>
228
+ {/each}
229
+ </div>
230
+ {/snippet}
231
+ </Modal>
232
+ {/if}
233
+ </div>
234
+ {:else}
235
+ <div class="flex items-center justify-center gap-8">
236
+ <div class="flex w-1/3 flex-col items-center px-2">
237
+ <UI.Input
238
+ label={{ name: $t('constructor.props.id') }}
239
+ value={component.properties.id}
240
+ onUpdate={(value) => updateProperty('id', value as string, component, onPropertyChange)}
241
+ />
242
+ <UI.Input
243
+ label={{ name: $t('constructor.props.wrapperclass') }}
244
+ value={component.properties.wrapperClass}
245
+ onUpdate={(value) => updateProperty('wrapperClass', value as string, component, onPropertyChange)}
246
+ />
247
+ <div class="flex w-full gap-4">
248
+ <UI.Input
249
+ label={{ name: $t('constructor.props.size.height') }}
250
+ value={component.properties.size.height}
251
+ onUpdate={(value) => updateProperty('size.height', value as number, component, onPropertyChange)}
252
+ number={{ minNum: 1, maxNum: 1000, step: 1 }}
253
+ type="number"
254
+ />
255
+ <UI.Input
256
+ label={{ name: $t('constructor.props.size.width') }}
257
+ value={component.properties.size.width}
258
+ onUpdate={(value) => updateProperty('size.width', value as number, component, onPropertyChange)}
259
+ number={{ minNum: 1, maxNum: 1000, step: 1 }}
260
+ type="number"
261
+ />
262
+ </div>
263
+ </div>
264
+ <div class="flex w-1/3 flex-col items-center px-2">
265
+ <UI.Select
266
+ label={{ name: $t('constructor.props.align') }}
267
+ type="buttons"
268
+ value={initialAlign}
269
+ options={$optionsStore.JUSTIFY_ALIGN_OPTIONS}
270
+ onUpdate={(option) => updateProperty('wrapperClass', twMerge(component.properties.wrapperClass, option.value), component, onPropertyChange)}
271
+ />
272
+
273
+ <UI.Select
274
+ wrapperClass="h-14"
275
+ label={{ name: $t('constructor.props.colors') }}
276
+ type="buttons"
277
+ options={$optionsStore.COLOR_OPTIONS.slice(1)}
278
+ value={initialColor}
279
+ onUpdate={(option) => {
280
+ updateProperty('wrapperClass', twMerge(component.properties.wrapperClass, option.value), component, onPropertyChange)
281
+ component.properties.items.forEach((_item: any, index: number) => {
282
+ const items = [...(component.properties?.items || [])]
283
+ items[index]['class'] = twMerge(items[index].class, `text-${option.value?.slice(3)}-500`)
284
+ updateProperty('items', items, component, onPropertyChange)
285
+ })
286
+ }}
287
+ />
288
+ </div>
289
+ <div class="flex w-1/3 flex-col items-center px-2">
290
+ <UI.Select
291
+ wrapperClass="h-14"
292
+ label={{ name: $t('constructor.props.icon.text.position') }}
293
+ type="buttons"
294
+ options={$optionsStore.ICON_TEXT_POSITION_OPTIONS}
295
+ value={initialPosition}
296
+ onUpdate={(option) => {
297
+ component.properties.items.forEach((_item: any, index: number) => {
298
+ const items = [...(component.properties?.items || [])]
299
+ items[index]['class'] = twMerge(items[index].class, option.value)
300
+ updateProperty('items', items, component, onPropertyChange)
301
+ })
302
+ }}
303
+ />
304
+ <UI.Switch
305
+ label={{
306
+ name: $t('constructor.props.widthMode'),
307
+ captionLeft: $t('constructor.props.height.small'),
308
+ captionRight: $t('constructor.props.equal'),
309
+ }}
310
+ value={initialWidth()}
311
+ onChange={(value) => {
312
+ if (value === 2) {
313
+ component.properties.items.forEach((_item: any, index: number) => {
314
+ let tabWidth = Math.max(...Array.from(document.body.querySelectorAll('.tab')).map((item) => (item as HTMLElement).offsetWidth))
315
+ const items = [...(component.properties?.items || [])]
316
+ items[index]['class'] = twMerge(items[index].class, `w-[${tabWidth}px]`)
317
+ updateProperty('items', items, component, onPropertyChange)
318
+ })
319
+ } else {
320
+ component.properties.items.forEach((_item: any, index: number) => {
321
+ const items = [...(component.properties?.items || [])]
322
+ items[index]['class'] = twMerge(items[index].class, 'w-auto')
323
+ updateProperty('items', items, component, onPropertyChange)
324
+ })
325
+ }
326
+ console.log(component.properties?.items)
327
+ }}
328
+ />
329
+ </div>
330
+ </div>
331
+
332
+ <div class="space-y-4">
333
+ <div class="m-0 flex items-center justify-center gap-2">
334
+ <h4>{$t('constructor.props.options.title')}</h4>
335
+ <UI.Button
336
+ wrapperClass="w-8"
337
+ content={{ icon: ButtonAdd }}
338
+ onClick={() => {
339
+ let tabWidth = Math.max(...Array.from(document.body.querySelectorAll('.tab')).map((item) => (item as HTMLElement).offsetWidth))
340
+ const newItem: { name: string; icon: string; class: string } = {
341
+ name: `Tab ${component.properties?.items.length + 1}`,
342
+ class: `w-${initialWidth() === 2 ? `[${tabWidth}px]` : 'auto'} text-${initialColor?.value.slice(3)}-500 ${initialPosition?.value}`,
343
+ icon: '',
344
+ }
345
+ const items = [...(component.properties?.items || []), newItem]
346
+ updateProperty('items', items, component, onPropertyChange)
347
+ }}
348
+ />
349
+ </div>
350
+
351
+ {#each component.properties.items || [] as tab, index}
352
+ <div class="m-0 flex items-end justify-around gap-2 border-gray-400">
353
+ <UI.Input
354
+ label={{ name: $t('constructor.props.optionname') }}
355
+ wrapperClass="w-5/10"
356
+ value={tab.name}
357
+ onUpdate={(value) => {
358
+ const items = [...(component.properties?.items || [])]
359
+ items[index]['name'] = value
360
+ updateProperty('items', items, component, onPropertyChange)
361
+ }}
362
+ />
363
+ <div class="relative mt-5 flex w-3/10 gap-2">
364
+ <UI.Button content={{ name: $t('constructor.props.labelicon') }} onClick={() => (tabIcon = { index: index, isModalOpen: true })} />
365
+
366
+ {#if tab.icon}
367
+ <Button
368
+ wrapperClass="w-8.5 "
369
+ componentClass="p-0.5 bg-red"
370
+ content={{ icon: CrossIcon }}
371
+ onClick={() => {
372
+ const items = [...(component.properties?.items || [])]
373
+ items[index]['icon'] = ''
374
+ updateProperty('items', items, component, onPropertyChange)
375
+ }}
376
+ />
377
+ {/if}
378
+ </div>
379
+
380
+ {#if component.properties.items.length > 1}
381
+ <UI.Button
382
+ wrapperClass="w-8"
383
+ content={{ icon: ButtonDelete }}
384
+ onClick={() => {
385
+ const items = [...(component.properties?.items || [])]
386
+ items.splice(index, 1)
387
+ updateProperty('items', items, component, onPropertyChange)
388
+ }}
389
+ />
390
+ {/if}
391
+ </div>
392
+ {/each}
393
+ {#if tabIcon.isModalOpen}
394
+ <Modal bind:isOpen={tabIcon.isModalOpen} wrapperClass="w-130">
395
+ {#snippet main()}
396
+ <div class="grid grid-cols-3">
397
+ {#each ICONS as category}
398
+ <div class="relative m-1.5 rounded-xl border-2 border-(--border-color) p-3">
399
+ <div class="absolute -top-3.5 bg-(--back-color) px-1">{$t(`constructor.props.icon.${category[0]}`)}</div>
400
+ <div class="grid grid-cols-3 place-items-center gap-2">
401
+ {#each category[1] as icon}
402
+ <button
403
+ class="h-8 w-8 cursor-pointer [&_svg]:h-full [&_svg]:max-h-full [&_svg]:w-full [&_svg]:max-w-full"
404
+ onclick={() => {
405
+ const items = [...(component.properties?.items || [])]
406
+ items[tabIcon.index]['icon'] = icon as string
407
+ updateProperty('items', items, component, onPropertyChange)
408
+ }}
409
+ >
410
+ {@html icon}
411
+ </button>{/each}
412
+ </div>
413
+ </div>
414
+ {/each}
415
+ </div>
416
+ {/snippet}
417
+ </Modal>
418
+ {/if}
419
+ </div>
420
+ {/if}
@@ -0,0 +1,11 @@
1
+ import { type ITabsProps, type UIComponent } from '../types';
2
+ type $$ComponentProps = {
3
+ component: UIComponent & {
4
+ properties: Partial<ITabsProps>;
5
+ };
6
+ onPropertyChange: (value: string | object, name?: string) => void;
7
+ forConstructor?: boolean;
8
+ };
9
+ declare const TabsProps: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type TabsProps = ReturnType<typeof TabsProps>;
11
+ export default TabsProps;
package/dist/index.d.ts CHANGED
@@ -20,8 +20,10 @@ export { default as Switch } from './Switch/Switch.svelte';
20
20
  export { default as SwitchProps } from './Switch/SwitchProps.svelte';
21
21
  export { default as Table } from './Table/Table.svelte';
22
22
  export { default as TableProps } from './Table/TableProps.svelte';
23
+ export { default as Tabs } from './Tabs/Tabs.svelte';
24
+ export { default as TabsProps } from './Tabs/TabsProps.svelte';
23
25
  export { default as TextField } from './TextField/TextField.svelte';
24
26
  export { default as TextFieldProps } from './TextField/TextFieldProps.svelte';
25
27
  export * from './locales/i18n';
26
28
  export * from './locales/translations';
27
- export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type ISelectOption, type ISwitchProps, type IColorPickerProps, type ISliderProps, type ITextFieldProps, type IProgressBarProps, type IGraphProps, type IGraphDataObject, type ITableHeader, type ITableProps, } from './types';
29
+ export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type ISelectOption, type ISwitchProps, type IColorPickerProps, type ISliderProps, type ITextFieldProps, type IProgressBarProps, type IGraphProps, type IGraphDataObject, type ITableHeader, type ITableProps, type ITabsProps, } from './types';
package/dist/index.js CHANGED
@@ -10,6 +10,8 @@ export { default as Graph } from './Graph/Graph.svelte';
10
10
  export { default as GraphProps } from './Graph/GraphProps.svelte';
11
11
  export { default as Input } from './Input/Input.svelte';
12
12
  export { default as InputProps } from './Input/InputProps.svelte';
13
+ // export { default as Joystick } from './Joystick/Joystick.svelte'
14
+ // export { default as JoystickProps } from './Joystick/JoystickProps.svelte'
13
15
  export { default as Modal } from './Modal.svelte';
14
16
  export { default as ProgressBar } from './ProgressBar/ProgressBar.svelte';
15
17
  export { default as ProgressBarProps } from './ProgressBar/ProgressBarProps.svelte';
@@ -21,6 +23,8 @@ export { default as Switch } from './Switch/Switch.svelte';
21
23
  export { default as SwitchProps } from './Switch/SwitchProps.svelte';
22
24
  export { default as Table } from './Table/Table.svelte';
23
25
  export { default as TableProps } from './Table/TableProps.svelte';
26
+ export { default as Tabs } from './Tabs/Tabs.svelte';
27
+ export { default as TabsProps } from './Tabs/TabsProps.svelte';
24
28
  export { default as TextField } from './TextField/TextField.svelte';
25
29
  export { default as TextFieldProps } from './TextField/TextFieldProps.svelte';
26
30
  export * from './locales/i18n';
@@ -111,6 +111,8 @@ const translations = {
111
111
  'constructor.props.copy': 'Копирование',
112
112
  'constructor.props.size.height': 'Высота сетки',
113
113
  'constructor.props.size.width': 'Ширина сетки',
114
+ 'constructor.props.icon.text.position': 'Положение иконки',
115
+ 'constructor.props.equal': 'Равные',
114
116
  'constructor.props.table.columns': 'Колонки таблицы',
115
117
  'constructor.props.table.columns.key': 'Ключ',
116
118
  'constructor.props.table.columns.label': 'Название колонки',
package/dist/options.d.ts CHANGED
@@ -65,6 +65,16 @@ export declare const optionsStore: import("svelte/store").Readable<{
65
65
  value: string;
66
66
  name: string;
67
67
  }[];
68
+ JUSTIFY_ALIGN_OPTIONS: {
69
+ id: string;
70
+ value: string;
71
+ name: string;
72
+ }[];
73
+ ICON_TEXT_POSITION_OPTIONS: {
74
+ id: string;
75
+ value: string;
76
+ name: string;
77
+ }[];
68
78
  HEIGHT_OPTIONS: {
69
79
  id: string;
70
80
  value: string;
package/dist/options.js CHANGED
@@ -76,6 +76,17 @@ export const optionsStore = derived(t, ($t) => {
76
76
  { id: id(), value: 'center', name: $t('constructor.props.align.center') },
77
77
  { id: id(), value: 'right', name: $t('constructor.props.align.right') },
78
78
  ],
79
+ JUSTIFY_ALIGN_OPTIONS: [
80
+ { id: id(), value: 'justify-start', name: $t('constructor.props.align.left') },
81
+ { id: id(), value: 'justify-center', name: $t('constructor.props.align.center') },
82
+ { id: id(), value: 'justify-end', name: $t('constructor.props.align.right') },
83
+ ],
84
+ ICON_TEXT_POSITION_OPTIONS: [
85
+ { id: id(), value: 'flex-row', name: '←' },
86
+ { id: id(), value: 'flex-col', name: '↑' },
87
+ { id: id(), value: 'flex-row-reverse', name: '→' },
88
+ { id: id(), value: 'flex-col-reverse', name: '↓' },
89
+ ],
79
90
  HEIGHT_OPTIONS: [
80
91
  { id: id(), value: 'py-1', name: $t('constructor.props.height.small') },
81
92
  { id: id(), value: 'py-2', name: $t('constructor.props.height.medium') },
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Component, Snippet } from 'svelte';
1
+ import type { Snippet } from 'svelte';
2
2
  import type { Writable } from 'svelte/store';
3
3
  import type { IFileInputProps } from './FileAttach/FileAttach.svelte';
4
4
  export declare const updateProperty: (path: string, value: string | number | boolean | object | string[], component: UIComponent & {
@@ -7,9 +7,8 @@ export declare const updateProperty: (path: string, value: string | number | boo
7
7
  export interface UIComponent {
8
8
  id: string;
9
9
  name?: string;
10
- type: 'Button' | 'Accordion' | 'Input' | 'Select' | 'Switch' | 'ColorPicker' | 'Slider' | 'TextField' | 'ProgressBar' | 'Graph' | 'Table' | 'FileAttach';
11
- component: Component<IButtonProps> | Component<IInputProps> | Component<ISelectProps> | Component<ISwitchProps> | Component<IColorPickerProps> | Component<ISliderProps> | Component<ITextFieldProps> | Component<IProgressBarProps> | Component<IGraphProps> | Component<ITableProps<object>> | Component<IFileInputProps> | null;
12
- properties: IAccordionProps | IButtonProps | IInputProps | ISelectProps | ISwitchProps | IColorPickerProps | ISliderProps | ITextFieldProps | IProgressBarProps | IGraphProps | ITableProps<object> | IFileInputProps;
10
+ type: 'Button' | 'Accordion' | 'Input' | 'Select' | 'Switch' | 'ColorPicker' | 'Slider' | 'TextField' | 'Joystick' | 'ProgressBar' | 'Graph' | 'Table' | 'Tabs' | 'FileAttach';
11
+ properties: IAccordionProps | IButtonProps | IInputProps | ISelectProps | ISwitchProps | IColorPickerProps | ISliderProps | ITextFieldProps | IProgressBarProps | IGraphProps | ITableProps<object> | ITabsProps | IFileInputProps;
13
12
  position: Position;
14
13
  parentId: string;
15
14
  }
@@ -256,3 +255,20 @@ export interface ITableProps<T extends object> {
256
255
  };
257
256
  onClick?: (eventHandler: IUIComponentHandler) => void;
258
257
  }
258
+ export interface ITabsProps {
259
+ id?: string;
260
+ wrapperClass?: string;
261
+ size?: {
262
+ height?: number;
263
+ width?: number;
264
+ };
265
+ activeTab?: number;
266
+ items: {
267
+ name?: string;
268
+ icon?: string;
269
+ class?: string;
270
+ children?: Snippet;
271
+ }[];
272
+ apiArray?: UIComponent[];
273
+ Components?: Snippet<[component: UIComponent, fixedHeight: boolean]>;
274
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.1.22",
3
+ "version": "1.2.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "preview": "vite preview",
10
10
  "prepack": "svelte-kit sync && svelte-package && publint",
11
11
  "CheckUpdate": "npx npm-check-updates -u && npm install",
12
- "UpdateIconsLib": "tsx src/lib/IconsLib/iconsLib.ts"
12
+ "UpdateIconsLib": "tsx src/lib/IconsCatalog/iconsLib.ts"
13
13
  },
14
14
  "svelte": "./dist/index.js",
15
15
  "types": "./dist/index.d.ts",
@@ -33,12 +33,12 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@sveltejs/adapter-static": "^3.0.10",
36
- "@tailwindcss/vite": "^4.1.16",
36
+ "@tailwindcss/vite": "^4.1.17",
37
37
  "prettier": "^3.6.2",
38
38
  "prettier-plugin-svelte": "^3.4.0",
39
39
  "prettier-plugin-tailwindcss": "^0.7.1",
40
- "tailwind-merge": "^3.3.1",
41
- "tailwindcss": "^4.1.16",
40
+ "tailwind-merge": "^3.4.0",
41
+ "tailwindcss": "^4.1.17",
42
42
  "tsx": "^4.20.6",
43
43
  "typescript": "^5.9.3"
44
44
  },
@@ -48,9 +48,9 @@
48
48
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
49
49
  "@types/node": "^24.10.0",
50
50
  "publint": "^0.3.15",
51
- "svelte": "^5.43.3",
51
+ "svelte": "^5.43.5",
52
52
  "svelte-preprocess": "^6.0.3",
53
- "vite": "^7.1.12",
53
+ "vite": "^7.2.2",
54
54
  "vite-plugin-compression": "^0.5.1"
55
55
  }
56
56
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes