@rettangoli/ui 0.1.2-rc2 → 0.1.2-rc20
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/rettangoli-iife-layout.min.js +102 -32
- package/dist/rettangoli-iife-ui.min.js +182 -69
- package/package.json +5 -3
- package/src/cli/buildSvg.js +86 -0
- package/src/cli/index.js +1 -0
- package/src/components/breadcrumb/breadcrumb.handlers.js +9 -0
- package/src/components/breadcrumb/breadcrumb.store.js +29 -0
- package/src/components/breadcrumb/breadcrumb.view.yaml +64 -0
- package/src/components/dropdownMenu/dropdownMenu.handlers.js +4 -4
- package/src/components/dropdownMenu/dropdownMenu.store.js +5 -17
- package/src/components/dropdownMenu/dropdownMenu.view.yaml +15 -13
- package/src/components/form/form.handlers.js +133 -24
- package/src/components/form/form.store.js +123 -23
- package/src/components/form/form.view.yaml +137 -29
- package/src/components/pageOutline/pageOutline.handlers.js +1 -1
- package/src/components/popoverInput/popoverInput.handlers.js +99 -0
- package/src/components/popoverInput/popoverInput.store.js +48 -0
- package/src/components/popoverInput/popoverInput.view.yaml +55 -0
- package/src/components/select/select.handlers.js +2 -5
- package/src/components/select/select.view.yaml +3 -3
- package/src/components/sidebar/sidebar.view.yaml +1 -1
- package/src/components/sliderInput/sliderInput.handlers.js +24 -0
- package/src/components/sliderInput/sliderInput.store.js +17 -0
- package/src/components/sliderInput/sliderInput.view.yaml +42 -0
- package/src/components/table/table.handlers.js +1 -1
- package/src/components/tabs/tabs.handlers.js +10 -0
- package/src/components/tabs/tabs.store.js +29 -0
- package/src/components/tabs/tabs.view.yaml +64 -0
- package/src/components/waveform/waveform.handlers.js +92 -0
- package/src/components/waveform/waveform.store.js +17 -0
- package/src/components/waveform/waveform.view.yaml +38 -0
- package/src/entry-iife-layout.js +3 -0
- package/src/entry-iife-ui.js +4 -0
- package/src/index.js +5 -1
- package/src/primitives/dialog.js +208 -0
- package/src/primitives/input.js +32 -11
- package/src/primitives/popover.js +275 -0
- package/src/primitives/slider.js +8 -9
- package/src/styles/viewStyles.js +1 -0
- package/src/components/dialog/dialog.handlers.js +0 -5
- package/src/components/dialog/dialog.store.js +0 -25
- package/src/components/dialog/dialog.view.yaml +0 -44
- package/src/components/popover/popover.handlers.js +0 -5
- package/src/components/popover/popover.store.js +0 -12
- package/src/components/popover/popover.view.yaml +0 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rettangoli/ui",
|
|
3
|
-
"version": "0.1.2-
|
|
3
|
+
"version": "0.1.2-rc20",
|
|
4
4
|
"description": "A UI component library for building web interfaces.",
|
|
5
5
|
"main": "dist/rettangoli-esm.min.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"src"
|
|
10
10
|
],
|
|
11
11
|
"exports": {
|
|
12
|
-
".": "./src/index.js"
|
|
12
|
+
".": "./src/index.js",
|
|
13
|
+
"./cli": "./src/cli/index.js"
|
|
13
14
|
},
|
|
14
15
|
"module": "./src/index.js",
|
|
15
16
|
"repository": {
|
|
@@ -48,10 +49,11 @@
|
|
|
48
49
|
"homepage": "https://github.com/yuusoft-org/rettangoli#readme",
|
|
49
50
|
"dependencies": {
|
|
50
51
|
"@floating-ui/dom": "^1.6.13",
|
|
52
|
+
"@rettangoli/fe": "0.0.7-rc11",
|
|
51
53
|
"commander": "^13.1.0",
|
|
54
|
+
"jempl": "^0.1.2",
|
|
52
55
|
"js-yaml": "^4.1.0",
|
|
53
56
|
"liquidjs": "^10.21.0",
|
|
54
|
-
"@rettangoli/fe": "0.0.7-rc4",
|
|
55
57
|
"snabbdom": "^3.6.2"
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, basename } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function getAllSvgFiles(dir) {
|
|
5
|
+
const svgFiles = [];
|
|
6
|
+
|
|
7
|
+
function traverse(currentDir) {
|
|
8
|
+
const files = readdirSync(currentDir);
|
|
9
|
+
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
const fullPath = join(currentDir, file);
|
|
12
|
+
const stats = statSync(fullPath);
|
|
13
|
+
|
|
14
|
+
if (stats.isDirectory()) {
|
|
15
|
+
traverse(fullPath);
|
|
16
|
+
} else if (file.endsWith('.svg')) {
|
|
17
|
+
svgFiles.push(fullPath);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
traverse(dir);
|
|
23
|
+
return svgFiles;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function removeFrontmatter(content) {
|
|
27
|
+
// Look for a line containing only '---'
|
|
28
|
+
const lines = content.split('\n');
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
if (lines[i].trim() === '---') {
|
|
32
|
+
// Found the separator, return everything after it
|
|
33
|
+
return lines.slice(i + 1).join('\n').trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// No frontmatter separator found, return original content
|
|
38
|
+
return content;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const buildSvgIcons = (options) => {
|
|
42
|
+
const { dir, outfile } = options;
|
|
43
|
+
|
|
44
|
+
if (!dir || !outfile) {
|
|
45
|
+
console.error('Error: Both dir and outfile options are required');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`Scanning for SVG files in: ${dir}`);
|
|
50
|
+
|
|
51
|
+
const svgFiles = getAllSvgFiles(dir);
|
|
52
|
+
|
|
53
|
+
if (svgFiles.length === 0) {
|
|
54
|
+
console.log('No SVG files found');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(`Found ${svgFiles.length} SVG files`);
|
|
59
|
+
|
|
60
|
+
const icons = {};
|
|
61
|
+
|
|
62
|
+
for (const filePath of svgFiles) {
|
|
63
|
+
const fileName = basename(filePath, '.svg');
|
|
64
|
+
let content = readFileSync(filePath, 'utf8');
|
|
65
|
+
|
|
66
|
+
content = removeFrontmatter(content);
|
|
67
|
+
|
|
68
|
+
content = content.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
69
|
+
|
|
70
|
+
icons[fileName] = content;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Convert to custom format with single quotes for keys and backticks for values
|
|
74
|
+
const iconEntries = Object.entries(icons).map(([key, value]) => {
|
|
75
|
+
return ` '${key}': \`${value}\``;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const output = `window.rtglIcons = {\n${iconEntries.join(',\n')}\n};`;
|
|
79
|
+
|
|
80
|
+
writeFileSync(outfile, output);
|
|
81
|
+
|
|
82
|
+
console.log(`SVG icons written to: ${outfile}`);
|
|
83
|
+
console.log(`Total icons: ${Object.keys(icons).length}`);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default buildSvgIcons;
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as buildSvg } from './buildSvg.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const INITIAL_STATE = Object.freeze({});
|
|
2
|
+
|
|
3
|
+
const blacklistedAttrs = ['id', 'class', 'style', 'slot'];
|
|
4
|
+
|
|
5
|
+
const stringifyAttrs = (attrs) => {
|
|
6
|
+
return Object.entries(attrs).filter(([key]) => !blacklistedAttrs.includes(key)).map(([key, value]) => `${key}=${value}`).join(' ');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const toViewData = ({ props, attrs }) => {
|
|
10
|
+
const containerAttrString = stringifyAttrs(attrs);
|
|
11
|
+
|
|
12
|
+
const items = props.items || [];
|
|
13
|
+
const separator = props.separator || 'breadcrumb-arrow';
|
|
14
|
+
|
|
15
|
+
// Add separators between items, but not after the last one
|
|
16
|
+
const itemsWithSeparators = [];
|
|
17
|
+
items.forEach((item, index) => {
|
|
18
|
+
itemsWithSeparators.push(item);
|
|
19
|
+
if (index < items.length - 1) {
|
|
20
|
+
itemsWithSeparators.push({ isSeparator: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
containerAttrString,
|
|
26
|
+
items: itemsWithSeparators,
|
|
27
|
+
separator
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
elementName: rtgl-breadcrumb
|
|
2
|
+
|
|
3
|
+
viewDataSchema:
|
|
4
|
+
type: object
|
|
5
|
+
properties:
|
|
6
|
+
containerAttrString:
|
|
7
|
+
type: string
|
|
8
|
+
items:
|
|
9
|
+
type: array
|
|
10
|
+
items:
|
|
11
|
+
type: object
|
|
12
|
+
properties:
|
|
13
|
+
label:
|
|
14
|
+
type: string
|
|
15
|
+
id:
|
|
16
|
+
type: string
|
|
17
|
+
separator:
|
|
18
|
+
type: string
|
|
19
|
+
|
|
20
|
+
propsSchema:
|
|
21
|
+
type: object
|
|
22
|
+
properties:
|
|
23
|
+
items:
|
|
24
|
+
type: array
|
|
25
|
+
items:
|
|
26
|
+
type: object
|
|
27
|
+
properties:
|
|
28
|
+
label:
|
|
29
|
+
type: string
|
|
30
|
+
id:
|
|
31
|
+
type: string
|
|
32
|
+
separator:
|
|
33
|
+
type: string
|
|
34
|
+
default: "breadcrumb-arrow"
|
|
35
|
+
|
|
36
|
+
refs:
|
|
37
|
+
item-*:
|
|
38
|
+
eventListeners:
|
|
39
|
+
click:
|
|
40
|
+
handler: handleClickItem
|
|
41
|
+
events:
|
|
42
|
+
item-click:
|
|
43
|
+
type: object
|
|
44
|
+
properties:
|
|
45
|
+
item:
|
|
46
|
+
type: object
|
|
47
|
+
properties:
|
|
48
|
+
label:
|
|
49
|
+
type: string
|
|
50
|
+
id:
|
|
51
|
+
type: string
|
|
52
|
+
index:
|
|
53
|
+
type: number
|
|
54
|
+
|
|
55
|
+
template:
|
|
56
|
+
- rtgl-view d=h av=c g=md p=sm ${containerAttrString}:
|
|
57
|
+
- $for item in items:
|
|
58
|
+
- $if item.isSeparator:
|
|
59
|
+
- rtgl-svg wh=16 svg=${separator} c=mu-fg:
|
|
60
|
+
$elif item.id:
|
|
61
|
+
- rtgl-view#item-${item.id} data-id=${item.id} cur=p:
|
|
62
|
+
- rtgl-text s=sm c=mu-fg: "${item.label}"
|
|
63
|
+
$else:
|
|
64
|
+
- rtgl-text s=sm c=mu-fg: "${item.label}"
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
export const
|
|
2
|
+
export const handleClosePopover = (e, deps) => {
|
|
3
3
|
const { dispatchEvent } = deps;
|
|
4
|
-
dispatchEvent(new CustomEvent('
|
|
4
|
+
dispatchEvent(new CustomEvent('close'));
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export const handleClickMenuItem = (e, deps) => {
|
|
8
8
|
const { dispatchEvent } = deps;
|
|
9
9
|
const index = parseInt(e.currentTarget.id.replace('option-', ''));
|
|
10
10
|
const item = deps.props.items[index];
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
dispatchEvent(new CustomEvent('click-item', {
|
|
13
13
|
detail: {
|
|
14
14
|
index,
|
|
15
15
|
item
|
|
16
16
|
}
|
|
17
17
|
}));
|
|
18
|
-
}
|
|
18
|
+
}
|
|
@@ -2,24 +2,12 @@
|
|
|
2
2
|
export const INITIAL_STATE = Object.freeze({
|
|
3
3
|
});
|
|
4
4
|
|
|
5
|
-
export const toViewData = ({
|
|
5
|
+
export const toViewData = ({ props, attrs }) => {
|
|
6
6
|
return {
|
|
7
7
|
items: props.items || [],
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
},
|
|
8
|
+
open: !!attrs.open,
|
|
9
|
+
x: attrs.x,
|
|
10
|
+
y: attrs.y,
|
|
11
|
+
placement: attrs.placement
|
|
13
12
|
};
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
export const selectState = ({ state }) => {
|
|
17
|
-
return state;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const setState = (state) => {
|
|
21
|
-
// do doSomething
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
@@ -19,28 +19,31 @@ propsSchema:
|
|
|
19
19
|
- label
|
|
20
20
|
- item
|
|
21
21
|
- separator
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
|
|
23
|
+
attrsSchema:
|
|
24
|
+
type: object
|
|
25
|
+
properties:
|
|
26
|
+
open:
|
|
27
|
+
type: string
|
|
28
|
+
x:
|
|
29
|
+
type: string
|
|
30
|
+
y:
|
|
31
|
+
type: string
|
|
32
|
+
placement:
|
|
33
|
+
type: string
|
|
31
34
|
|
|
32
35
|
refs:
|
|
33
36
|
popover:
|
|
34
37
|
eventListeners:
|
|
35
|
-
|
|
36
|
-
handler:
|
|
38
|
+
close:
|
|
39
|
+
handler: handleClosePopover
|
|
37
40
|
option-*:
|
|
38
41
|
eventListeners:
|
|
39
42
|
click:
|
|
40
43
|
handler: handleClickMenuItem
|
|
41
44
|
|
|
42
45
|
template:
|
|
43
|
-
- rtgl-popover#popover
|
|
46
|
+
- rtgl-popover#popover ?open=${open} x=${x} y=${y} placement=${placement}:
|
|
44
47
|
- rtgl-view wh=300 g=xs slot=content bgc=background br=md:
|
|
45
48
|
- $for item, i in items:
|
|
46
49
|
- $if item.type == 'label':
|
|
@@ -51,4 +54,3 @@ template:
|
|
|
51
54
|
- rtgl-text: ${item.label}
|
|
52
55
|
$elif item.type == 'separator':
|
|
53
56
|
- rtgl-view w=f h=1 ph=lg mv=md bgc=bo:
|
|
54
|
-
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const handleBeforeMount = (deps) => {
|
|
2
2
|
const { store, props } = deps;
|
|
3
|
-
store.
|
|
3
|
+
store.setFormValues(props.defaultValues);
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
export const handleOnUpdate = (changes, deps) => {
|
|
7
|
+
const { oldAttrs, newAttrs } = changes;
|
|
8
|
+
const { store, props, render } = deps;
|
|
9
|
+
|
|
10
|
+
if (oldAttrs?.key === newAttrs?.key) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
store.setFormValues(props.defaultValues);
|
|
15
|
+
render();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const dispatchFormChange = (name, fieldValue, formValues, dispatchEvent) => {
|
|
7
19
|
dispatchEvent(
|
|
8
20
|
new CustomEvent("form-change", {
|
|
9
21
|
detail: {
|
|
10
|
-
|
|
22
|
+
name,
|
|
11
23
|
fieldValue,
|
|
12
24
|
formValues,
|
|
13
25
|
},
|
|
@@ -29,51 +41,148 @@ export const handleActionClick = (e, deps) => {
|
|
|
29
41
|
};
|
|
30
42
|
|
|
31
43
|
export const handleInputChange = (e, deps) => {
|
|
44
|
+
const { store, dispatchEvent, props } = deps;
|
|
45
|
+
const name = e.currentTarget.id.replace("input-", "");
|
|
46
|
+
// TODO fix double event
|
|
47
|
+
if (name && e.detail.value !== undefined) {
|
|
48
|
+
store.setFormFieldValue({
|
|
49
|
+
name: name,
|
|
50
|
+
value: e.detail.value,
|
|
51
|
+
props,
|
|
52
|
+
});
|
|
53
|
+
dispatchFormChange(
|
|
54
|
+
name,
|
|
55
|
+
e.detail.value,
|
|
56
|
+
store.selectFormValues(),
|
|
57
|
+
dispatchEvent,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const handlePopoverInputChange = (e, deps) => {
|
|
32
63
|
const { store, dispatchEvent } = deps;
|
|
33
|
-
const
|
|
64
|
+
const name = e.currentTarget.id.replace("popover-input-", "");
|
|
34
65
|
// TODO fix double event
|
|
35
|
-
if (
|
|
66
|
+
if (name && e.detail.value !== undefined) {
|
|
36
67
|
store.setFormFieldValue({
|
|
37
|
-
|
|
38
|
-
fieldName: id,
|
|
68
|
+
name: name,
|
|
39
69
|
value: e.detail.value,
|
|
70
|
+
props,
|
|
40
71
|
});
|
|
41
|
-
dispatchFormChange(
|
|
72
|
+
dispatchFormChange(
|
|
73
|
+
name,
|
|
74
|
+
e.detail.value,
|
|
75
|
+
store.selectFormValues(),
|
|
76
|
+
dispatchEvent,
|
|
77
|
+
);
|
|
42
78
|
}
|
|
43
79
|
};
|
|
44
80
|
|
|
45
81
|
export const handleSelectChange = (e, deps) => {
|
|
46
|
-
const { store, dispatchEvent } = deps;
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
82
|
+
const { store, dispatchEvent, render, props } = deps;
|
|
83
|
+
const name = e.currentTarget.id.replace("select-", "");
|
|
84
|
+
if (name && e.detail.selectedValue !== undefined) {
|
|
49
85
|
store.setFormFieldValue({
|
|
50
|
-
|
|
86
|
+
name: name,
|
|
51
87
|
value: e.detail.selectedValue,
|
|
88
|
+
props,
|
|
52
89
|
});
|
|
53
|
-
dispatchFormChange(
|
|
90
|
+
dispatchFormChange(
|
|
91
|
+
name,
|
|
92
|
+
e.detail.selectedValue,
|
|
93
|
+
store.selectFormValues(),
|
|
94
|
+
dispatchEvent,
|
|
95
|
+
);
|
|
96
|
+
render();
|
|
54
97
|
}
|
|
55
98
|
};
|
|
56
99
|
|
|
57
100
|
export const handleColorPickerChange = (e, deps) => {
|
|
58
|
-
const { store, dispatchEvent } = deps;
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
101
|
+
const { store, dispatchEvent, props } = deps;
|
|
102
|
+
const name = e.currentTarget.id.replace("colorpicker-", "");
|
|
103
|
+
if (name && e.detail.value !== undefined) {
|
|
61
104
|
store.setFormFieldValue({
|
|
62
|
-
|
|
105
|
+
name: name,
|
|
63
106
|
value: e.detail.value,
|
|
107
|
+
props,
|
|
64
108
|
});
|
|
65
|
-
dispatchFormChange(
|
|
109
|
+
dispatchFormChange(
|
|
110
|
+
name,
|
|
111
|
+
e.detail.value,
|
|
112
|
+
store.selectFormValues(),
|
|
113
|
+
dispatchEvent,
|
|
114
|
+
);
|
|
66
115
|
}
|
|
67
116
|
};
|
|
68
117
|
|
|
69
118
|
export const handleSliderChange = (e, deps) => {
|
|
70
|
-
const { store, dispatchEvent } = deps;
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
119
|
+
const { store, dispatchEvent, props } = deps;
|
|
120
|
+
const name = e.currentTarget.id.replace("slider-", "");
|
|
121
|
+
if (name && e.detail.value !== undefined) {
|
|
73
122
|
store.setFormFieldValue({
|
|
74
|
-
|
|
123
|
+
name: name,
|
|
75
124
|
value: e.detail.value,
|
|
125
|
+
props,
|
|
76
126
|
});
|
|
77
|
-
dispatchFormChange(
|
|
127
|
+
dispatchFormChange(
|
|
128
|
+
name,
|
|
129
|
+
e.detail.value,
|
|
130
|
+
store.selectFormValues(),
|
|
131
|
+
dispatchEvent,
|
|
132
|
+
);
|
|
78
133
|
}
|
|
79
134
|
};
|
|
135
|
+
|
|
136
|
+
export const handleSliderInputChange = (e, deps) => {
|
|
137
|
+
const { store, dispatchEvent, props } = deps;
|
|
138
|
+
const name = e.currentTarget.id.replace("slider-input-", "");
|
|
139
|
+
if (name && e.detail.value !== undefined) {
|
|
140
|
+
store.setFormFieldValue({
|
|
141
|
+
name: name,
|
|
142
|
+
value: e.detail.value,
|
|
143
|
+
props,
|
|
144
|
+
});
|
|
145
|
+
dispatchFormChange(
|
|
146
|
+
name,
|
|
147
|
+
e.detail.value,
|
|
148
|
+
store.selectFormValues(),
|
|
149
|
+
dispatchEvent,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const handleImageClick = (e, deps) => {
|
|
155
|
+
if (e.type === "contextmenu") {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
}
|
|
158
|
+
const { dispatchEvent } = deps;
|
|
159
|
+
const name = e.currentTarget.id.replace("image-", "");
|
|
160
|
+
dispatchEvent(
|
|
161
|
+
new CustomEvent("extra-event", {
|
|
162
|
+
detail: {
|
|
163
|
+
name: name,
|
|
164
|
+
x: e.clientX,
|
|
165
|
+
y: e.clientY,
|
|
166
|
+
trigger: e.type,
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const handleWaveformClick = (e, deps) => {
|
|
173
|
+
if (e.type === "contextmenu") {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
}
|
|
176
|
+
const { dispatchEvent } = deps;
|
|
177
|
+
const name = e.currentTarget.id.replace("waveform-", "");
|
|
178
|
+
dispatchEvent(
|
|
179
|
+
new CustomEvent("extra-event", {
|
|
180
|
+
detail: {
|
|
181
|
+
name: name,
|
|
182
|
+
x: e.clientX,
|
|
183
|
+
y: e.clientY,
|
|
184
|
+
trigger: e.type,
|
|
185
|
+
},
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
};
|
|
@@ -1,45 +1,145 @@
|
|
|
1
|
+
import { parseAndRender } from "jempl";
|
|
2
|
+
|
|
3
|
+
function pick(obj, keys) {
|
|
4
|
+
return keys.reduce((acc, key) => {
|
|
5
|
+
if (key in obj) acc[key] = obj[key];
|
|
6
|
+
return acc;
|
|
7
|
+
}, {});
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
export const INITIAL_STATE = Object.freeze({
|
|
2
|
-
formValues: {}
|
|
11
|
+
formValues: {},
|
|
3
12
|
});
|
|
4
13
|
|
|
5
|
-
|
|
14
|
+
// Lodash-like utility functions for nested property access
|
|
15
|
+
const get = (obj, path, defaultValue = undefined) => {
|
|
16
|
+
const keys = path.split(/[\[\].]/).filter((key) => key !== "");
|
|
17
|
+
let current = obj;
|
|
18
|
+
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
if (current === null || current === undefined || !(key in current)) {
|
|
21
|
+
return defaultValue;
|
|
22
|
+
}
|
|
23
|
+
current = current[key];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return current;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const set = (obj, path, value) => {
|
|
30
|
+
const keys = path.split(/[\[\].]/).filter((key) => key !== "");
|
|
31
|
+
|
|
32
|
+
// If path contains array notation, delete the original flat key
|
|
33
|
+
if (path.includes("[") && path in obj) {
|
|
34
|
+
delete obj[path];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let current = obj;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
40
|
+
const key = keys[i];
|
|
41
|
+
if (
|
|
42
|
+
!(key in current) ||
|
|
43
|
+
typeof current[key] !== "object" ||
|
|
44
|
+
current[key] === null
|
|
45
|
+
) {
|
|
46
|
+
// Check if next key is a number to create array
|
|
47
|
+
const nextKey = keys[i + 1];
|
|
48
|
+
const isArrayIndex = /^\d+$/.test(nextKey);
|
|
49
|
+
current[key] = isArrayIndex ? [] : {};
|
|
50
|
+
}
|
|
51
|
+
current = current[key];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
current[keys[keys.length - 1]] = value;
|
|
55
|
+
return obj;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const blacklistedAttrs = ["id", "class", "style", "slot"];
|
|
6
59
|
|
|
7
60
|
const stringifyAttrs = (attrs) => {
|
|
8
|
-
return Object.entries(attrs)
|
|
9
|
-
|
|
61
|
+
return Object.entries(attrs)
|
|
62
|
+
.filter(([key]) => !blacklistedAttrs.includes(key))
|
|
63
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
64
|
+
.join(" ");
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const selectForm = ({ state, props }) => {
|
|
68
|
+
const { form } = props;
|
|
69
|
+
const { context } = props;
|
|
70
|
+
|
|
71
|
+
if (context) {
|
|
72
|
+
return parseAndRender(form, context);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return form;
|
|
76
|
+
};
|
|
10
77
|
|
|
11
78
|
export const toViewData = ({ state, props, attrs }) => {
|
|
12
79
|
const containerAttrString = stringifyAttrs(attrs);
|
|
80
|
+
const defaultValues = props.defaultValues || {};
|
|
81
|
+
|
|
82
|
+
const form = selectForm({ state, props });
|
|
83
|
+
const fields = structuredClone(form.fields || []);
|
|
84
|
+
fields.forEach((field) => {
|
|
85
|
+
field.defaultValue = get(defaultValues, field.name);
|
|
86
|
+
|
|
87
|
+
if (field.inputType === "image") {
|
|
88
|
+
const src = field.src;
|
|
89
|
+
// Only set imageSrc if src exists and is not empty
|
|
90
|
+
field.imageSrc = src && src.trim() ? src : null;
|
|
91
|
+
// Set placeholder text
|
|
92
|
+
field.placeholderText = field.placeholder || "No Image";
|
|
93
|
+
}
|
|
94
|
+
if (field.inputType === "waveform") {
|
|
95
|
+
const waveformData = field.waveformData;
|
|
96
|
+
// Only set waveformData if it exists
|
|
97
|
+
field.waveformData = waveformData || null;
|
|
98
|
+
// Set placeholder text
|
|
99
|
+
field.placeholderText = field.placeholder || "No Waveform";
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
13
103
|
return {
|
|
14
104
|
containerAttrString,
|
|
15
|
-
title:
|
|
16
|
-
description:
|
|
17
|
-
fields:
|
|
105
|
+
title: form?.title || "",
|
|
106
|
+
description: form?.description || "",
|
|
107
|
+
fields: fields,
|
|
18
108
|
actions: props?.form?.actions || {
|
|
19
|
-
buttons: []
|
|
109
|
+
buttons: [],
|
|
20
110
|
},
|
|
21
|
-
// TODO fix default values
|
|
22
111
|
formValues: state.formValues,
|
|
23
112
|
};
|
|
24
|
-
}
|
|
113
|
+
};
|
|
25
114
|
|
|
26
115
|
export const selectState = ({ state }) => {
|
|
27
116
|
return state;
|
|
28
|
-
}
|
|
117
|
+
};
|
|
29
118
|
|
|
30
|
-
export const selectFormValues = ({ state }) => {
|
|
31
|
-
|
|
32
|
-
}
|
|
119
|
+
export const selectFormValues = ({ state, props }) => {
|
|
120
|
+
const form = selectForm({ state, props });
|
|
33
121
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
122
|
+
return pick(
|
|
123
|
+
state.formValues,
|
|
124
|
+
form.fields.map((field) => field.name),
|
|
125
|
+
);
|
|
126
|
+
};
|
|
37
127
|
|
|
38
|
-
export const
|
|
39
|
-
state.formValues
|
|
40
|
-
}
|
|
128
|
+
export const getFormFieldValue = ({ state }, name) => {
|
|
129
|
+
return get(state.formValues, name);
|
|
130
|
+
};
|
|
41
131
|
|
|
42
|
-
export const
|
|
43
|
-
state.formValues
|
|
44
|
-
}
|
|
132
|
+
export const setFormValues = (state, defaultValues) => {
|
|
133
|
+
state.formValues = defaultValues || {};
|
|
134
|
+
};
|
|
45
135
|
|
|
136
|
+
export const setFormFieldValue = (state, { name, value, props }) => {
|
|
137
|
+
set(state.formValues, name, value);
|
|
138
|
+
// remove non visible values
|
|
139
|
+
const form = selectForm({ state, props });
|
|
140
|
+
const formValues = pick(
|
|
141
|
+
state.formValues,
|
|
142
|
+
form.fields.map((field) => field.name),
|
|
143
|
+
);
|
|
144
|
+
state.formValues = formValues;
|
|
145
|
+
};
|