@rettangoli/ui 0.1.2-rc3 → 0.1.2-rc31
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 +115 -43
- package/dist/rettangoli-iife-ui.min.js +187 -67
- package/package.json +5 -3
- package/src/cli/buildSvg.js +86 -0
- package/src/cli/index.js +1 -0
- package/src/common.js +19 -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 +173 -25
- package/src/components/form/form.store.js +176 -22
- package/src/components/form/form.view.yaml +217 -33
- package/src/components/pageOutline/pageOutline.handlers.js +57 -17
- package/src/components/pageOutline/pageOutline.store.js +46 -1
- package/src/components/pageOutline/pageOutline.view.yaml +7 -5
- 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 +116 -11
- package/src/components/select/select.store.js +84 -18
- package/src/components/select/select.view.yaml +40 -10
- package/src/components/sidebar/sidebar.view.yaml +1 -1
- package/src/components/sliderInput/sliderInput.handlers.js +41 -0
- package/src/components/sliderInput/sliderInput.store.js +18 -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/tooltip/tooltip.handlers.js +0 -0
- package/src/components/tooltip/tooltip.store.js +12 -0
- package/src/components/tooltip/tooltip.view.yaml +27 -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/button.js +10 -0
- package/src/primitives/colorPicker.js +9 -0
- package/src/primitives/dialog.js +254 -0
- package/src/primitives/input.js +41 -11
- package/src/primitives/popover.js +280 -0
- package/src/primitives/slider.js +18 -9
- package/src/primitives/svg.js +2 -0
- package/src/primitives/textarea.js +25 -1
- package/src/styles/cursorStyles.js +38 -2
- 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
|
@@ -1,45 +1,199 @@
|
|
|
1
|
+
import { parseAndRender } from "jempl";
|
|
2
|
+
|
|
3
|
+
const encode = (input) => {
|
|
4
|
+
function escapeHtml(text) {
|
|
5
|
+
const map = {
|
|
6
|
+
'&': '&',
|
|
7
|
+
'<': '<',
|
|
8
|
+
'>': '>',
|
|
9
|
+
'"': '"',
|
|
10
|
+
"'": '''
|
|
11
|
+
};
|
|
12
|
+
return text.replace(/[&<>"']/g, char => map[char]);
|
|
13
|
+
}
|
|
14
|
+
if (input === undefined || input === null) {
|
|
15
|
+
return ""
|
|
16
|
+
}
|
|
17
|
+
return `"${escapeHtml(String(input))}"`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pick(obj, keys) {
|
|
21
|
+
return keys.reduce((acc, key) => {
|
|
22
|
+
if (key in obj) acc[key] = obj[key];
|
|
23
|
+
return acc;
|
|
24
|
+
}, {});
|
|
25
|
+
}
|
|
26
|
+
|
|
1
27
|
export const INITIAL_STATE = Object.freeze({
|
|
2
|
-
formValues: {}
|
|
28
|
+
formValues: {},
|
|
29
|
+
tooltipState: {
|
|
30
|
+
open: false,
|
|
31
|
+
x: 0,
|
|
32
|
+
y: 0,
|
|
33
|
+
content: ''
|
|
34
|
+
},
|
|
3
35
|
});
|
|
4
36
|
|
|
5
|
-
|
|
37
|
+
// Lodash-like utility functions for nested property access
|
|
38
|
+
const get = (obj, path, defaultValue = undefined) => {
|
|
39
|
+
if (!path) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const keys = path.split(/[\[\].]/).filter((key) => key !== "");
|
|
43
|
+
let current = obj;
|
|
44
|
+
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
if (current === null || current === undefined || !(key in current)) {
|
|
47
|
+
return defaultValue;
|
|
48
|
+
}
|
|
49
|
+
current = current[key];
|
|
50
|
+
}
|
|
51
|
+
return current;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const set = (obj, path, value) => {
|
|
55
|
+
const keys = path.split(/[\[\].]/).filter((key) => key !== "");
|
|
56
|
+
|
|
57
|
+
// If path contains array notation, delete the original flat key
|
|
58
|
+
if (path.includes("[") && path in obj) {
|
|
59
|
+
delete obj[path];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let current = obj;
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
65
|
+
const key = keys[i];
|
|
66
|
+
if (
|
|
67
|
+
!(key in current) ||
|
|
68
|
+
typeof current[key] !== "object" ||
|
|
69
|
+
current[key] === null
|
|
70
|
+
) {
|
|
71
|
+
// Check if next key is a number to create array
|
|
72
|
+
const nextKey = keys[i + 1];
|
|
73
|
+
const isArrayIndex = /^\d+$/.test(nextKey);
|
|
74
|
+
current[key] = isArrayIndex ? [] : {};
|
|
75
|
+
}
|
|
76
|
+
current = current[key];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
current[keys[keys.length - 1]] = value;
|
|
80
|
+
return obj;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const blacklistedAttrs = ["id", "class", "style", "slot"];
|
|
6
84
|
|
|
7
85
|
const stringifyAttrs = (attrs) => {
|
|
8
|
-
return Object.entries(attrs)
|
|
9
|
-
|
|
86
|
+
return Object.entries(attrs)
|
|
87
|
+
.filter(([key]) => !blacklistedAttrs.includes(key))
|
|
88
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
89
|
+
.join(" ");
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const selectForm = ({ state, props }) => {
|
|
93
|
+
const { form } = props;
|
|
94
|
+
const { context } = props;
|
|
95
|
+
|
|
96
|
+
if (context) {
|
|
97
|
+
return parseAndRender(form, context);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return form;
|
|
101
|
+
};
|
|
102
|
+
|
|
10
103
|
|
|
11
104
|
export const toViewData = ({ state, props, attrs }) => {
|
|
12
105
|
const containerAttrString = stringifyAttrs(attrs);
|
|
106
|
+
const defaultValues = props.defaultValues || {};
|
|
107
|
+
|
|
108
|
+
const form = selectForm({ state, props });
|
|
109
|
+
const fields = structuredClone(form.fields || []);
|
|
110
|
+
fields.forEach((field) => {
|
|
111
|
+
// Use formValues from state if available, otherwise fall back to defaultValues from props
|
|
112
|
+
const defaultValue = get(state.formValues, field.name) ?? get(defaultValues, field.name)
|
|
113
|
+
if (["popover-input", "select", "read-only-text"].includes(field.inputType)) {
|
|
114
|
+
field.defaultValue = defaultValue
|
|
115
|
+
} else {
|
|
116
|
+
field.defaultValue = encode(defaultValue);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (["inputText"].includes(field.inputType)) {
|
|
120
|
+
field.placeholder = encode(field.placeholder)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (field.inputType === "image") {
|
|
124
|
+
const src = field.src;
|
|
125
|
+
// Only set imageSrc if src exists and is not empty
|
|
126
|
+
field.imageSrc = src && src.trim() ? src : null;
|
|
127
|
+
// Set placeholder text
|
|
128
|
+
field.placeholderText = field.placeholder || "No Image";
|
|
129
|
+
}
|
|
130
|
+
if (field.inputType === "waveform") {
|
|
131
|
+
const waveformData = field.waveformData;
|
|
132
|
+
// Only set waveformData if it exists
|
|
133
|
+
field.waveformData = waveformData || null;
|
|
134
|
+
// Set placeholder text
|
|
135
|
+
field.placeholderText = field.placeholder || "No Waveform";
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
13
139
|
return {
|
|
140
|
+
key: attrs?.key,
|
|
14
141
|
containerAttrString,
|
|
15
|
-
title:
|
|
16
|
-
description:
|
|
17
|
-
fields:
|
|
142
|
+
title: form?.title || "",
|
|
143
|
+
description: form?.description || "",
|
|
144
|
+
fields: fields,
|
|
18
145
|
actions: props?.form?.actions || {
|
|
19
|
-
buttons: []
|
|
146
|
+
buttons: [],
|
|
20
147
|
},
|
|
21
|
-
// TODO fix default values
|
|
22
148
|
formValues: state.formValues,
|
|
149
|
+
tooltipState: state.tooltipState,
|
|
23
150
|
};
|
|
24
|
-
}
|
|
151
|
+
};
|
|
25
152
|
|
|
26
153
|
export const selectState = ({ state }) => {
|
|
27
154
|
return state;
|
|
28
|
-
}
|
|
155
|
+
};
|
|
29
156
|
|
|
30
|
-
export const selectFormValues = ({ state }) => {
|
|
31
|
-
|
|
32
|
-
}
|
|
157
|
+
export const selectFormValues = ({ state, props }) => {
|
|
158
|
+
const form = selectForm({ state, props });
|
|
33
159
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
160
|
+
return pick(
|
|
161
|
+
state.formValues,
|
|
162
|
+
form.fields.map((field) => field.name),
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const getFormFieldValue = ({ state }, name) => {
|
|
167
|
+
return get(state.formValues, name);
|
|
168
|
+
};
|
|
37
169
|
|
|
38
|
-
export const
|
|
170
|
+
export const setFormValues = (state, defaultValues) => {
|
|
39
171
|
state.formValues = defaultValues || {};
|
|
40
|
-
}
|
|
172
|
+
};
|
|
41
173
|
|
|
42
|
-
export const setFormFieldValue = (state, {
|
|
43
|
-
state.formValues
|
|
44
|
-
|
|
174
|
+
export const setFormFieldValue = (state, { name, value, props }) => {
|
|
175
|
+
set(state.formValues, name, value);
|
|
176
|
+
// remove non visible values
|
|
177
|
+
const form = selectForm({ state, props });
|
|
178
|
+
const formValues = pick(
|
|
179
|
+
state.formValues,
|
|
180
|
+
form.fields.map((field) => field.name),
|
|
181
|
+
);
|
|
182
|
+
state.formValues = formValues;
|
|
183
|
+
};
|
|
45
184
|
|
|
185
|
+
export const showTooltip = (state, { x, y, content }) => {
|
|
186
|
+
state.tooltipState = {
|
|
187
|
+
open: true,
|
|
188
|
+
x: x,
|
|
189
|
+
y: y,
|
|
190
|
+
content: content
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const hideTooltip = (state) => {
|
|
195
|
+
state.tooltipState = {
|
|
196
|
+
...state.tooltipState,
|
|
197
|
+
open: false
|
|
198
|
+
};
|
|
199
|
+
};
|
|
@@ -8,6 +8,8 @@ propsSchema:
|
|
|
8
8
|
properties:
|
|
9
9
|
defaultValues:
|
|
10
10
|
type: object
|
|
11
|
+
context:
|
|
12
|
+
type: object
|
|
11
13
|
form:
|
|
12
14
|
type: object
|
|
13
15
|
properties:
|
|
@@ -21,7 +23,7 @@ propsSchema:
|
|
|
21
23
|
anyOf:
|
|
22
24
|
- type: object
|
|
23
25
|
properties:
|
|
24
|
-
|
|
26
|
+
name:
|
|
25
27
|
type: string
|
|
26
28
|
label:
|
|
27
29
|
type: string
|
|
@@ -31,14 +33,19 @@ propsSchema:
|
|
|
31
33
|
const: inputText
|
|
32
34
|
placeholder:
|
|
33
35
|
type: string
|
|
36
|
+
tooltip:
|
|
37
|
+
type: object
|
|
38
|
+
properties:
|
|
39
|
+
content:
|
|
40
|
+
type: string
|
|
34
41
|
required:
|
|
35
|
-
-
|
|
42
|
+
- name
|
|
36
43
|
- label
|
|
37
44
|
- inputType
|
|
38
45
|
additionalProperties: false
|
|
39
46
|
- type: object
|
|
40
47
|
properties:
|
|
41
|
-
|
|
48
|
+
name:
|
|
42
49
|
type: string
|
|
43
50
|
label:
|
|
44
51
|
type: string
|
|
@@ -48,30 +55,39 @@ propsSchema:
|
|
|
48
55
|
const: select
|
|
49
56
|
placeholder:
|
|
50
57
|
type: string
|
|
58
|
+
noClear:
|
|
59
|
+
type: boolean
|
|
60
|
+
addOption:
|
|
61
|
+
type: object
|
|
62
|
+
properties:
|
|
63
|
+
label:
|
|
64
|
+
type: string
|
|
51
65
|
options:
|
|
52
66
|
type: array
|
|
53
67
|
items:
|
|
54
68
|
type: object
|
|
55
69
|
properties:
|
|
56
|
-
id:
|
|
57
|
-
type: string
|
|
58
70
|
label:
|
|
59
71
|
type: string
|
|
60
72
|
value:
|
|
61
73
|
type: any
|
|
62
74
|
required:
|
|
63
|
-
- id
|
|
64
75
|
- label
|
|
65
76
|
- value
|
|
77
|
+
tooltip:
|
|
78
|
+
type: object
|
|
79
|
+
properties:
|
|
80
|
+
content:
|
|
81
|
+
type: string
|
|
66
82
|
required:
|
|
67
|
-
-
|
|
83
|
+
- name
|
|
68
84
|
- label
|
|
69
85
|
- inputType
|
|
70
86
|
- options
|
|
71
87
|
additionalProperties: false
|
|
72
88
|
- type: object
|
|
73
89
|
properties:
|
|
74
|
-
|
|
90
|
+
name:
|
|
75
91
|
type: string
|
|
76
92
|
label:
|
|
77
93
|
type: string
|
|
@@ -81,14 +97,19 @@ propsSchema:
|
|
|
81
97
|
const: colorPicker
|
|
82
98
|
value:
|
|
83
99
|
type: string
|
|
100
|
+
tooltip:
|
|
101
|
+
type: object
|
|
102
|
+
properties:
|
|
103
|
+
content:
|
|
104
|
+
type: string
|
|
84
105
|
required:
|
|
85
|
-
-
|
|
106
|
+
- name
|
|
86
107
|
- label
|
|
87
108
|
- inputType
|
|
88
109
|
additionalProperties: false
|
|
89
110
|
- type: object
|
|
90
111
|
properties:
|
|
91
|
-
|
|
112
|
+
name:
|
|
92
113
|
type: string
|
|
93
114
|
label:
|
|
94
115
|
type: string
|
|
@@ -104,8 +125,119 @@ propsSchema:
|
|
|
104
125
|
type: number
|
|
105
126
|
value:
|
|
106
127
|
type: number
|
|
128
|
+
tooltip:
|
|
129
|
+
type: object
|
|
130
|
+
properties:
|
|
131
|
+
content:
|
|
132
|
+
type: string
|
|
107
133
|
required:
|
|
108
|
-
-
|
|
134
|
+
- name
|
|
135
|
+
- label
|
|
136
|
+
- inputType
|
|
137
|
+
additionalProperties: false
|
|
138
|
+
- type: object
|
|
139
|
+
properties:
|
|
140
|
+
name:
|
|
141
|
+
type: string
|
|
142
|
+
label:
|
|
143
|
+
type: string
|
|
144
|
+
description:
|
|
145
|
+
type: string
|
|
146
|
+
inputType:
|
|
147
|
+
const: slider-input
|
|
148
|
+
min:
|
|
149
|
+
type: number
|
|
150
|
+
max:
|
|
151
|
+
type: number
|
|
152
|
+
step:
|
|
153
|
+
type: number
|
|
154
|
+
value:
|
|
155
|
+
type: number
|
|
156
|
+
tooltip:
|
|
157
|
+
type: object
|
|
158
|
+
properties:
|
|
159
|
+
content:
|
|
160
|
+
type: string
|
|
161
|
+
required:
|
|
162
|
+
- name
|
|
163
|
+
- label
|
|
164
|
+
- inputType
|
|
165
|
+
additionalProperties: false
|
|
166
|
+
- type: object
|
|
167
|
+
properties:
|
|
168
|
+
name:
|
|
169
|
+
type: string
|
|
170
|
+
label:
|
|
171
|
+
type: string
|
|
172
|
+
description:
|
|
173
|
+
type: string
|
|
174
|
+
inputType:
|
|
175
|
+
const: image
|
|
176
|
+
width:
|
|
177
|
+
type: number
|
|
178
|
+
height:
|
|
179
|
+
type: number
|
|
180
|
+
placeholder:
|
|
181
|
+
type: string
|
|
182
|
+
tooltip:
|
|
183
|
+
type: object
|
|
184
|
+
properties:
|
|
185
|
+
content:
|
|
186
|
+
type: string
|
|
187
|
+
required:
|
|
188
|
+
- name
|
|
189
|
+
- label
|
|
190
|
+
- inputType
|
|
191
|
+
additionalProperties: false
|
|
192
|
+
- type: object
|
|
193
|
+
properties:
|
|
194
|
+
name:
|
|
195
|
+
type: string
|
|
196
|
+
label:
|
|
197
|
+
type: string
|
|
198
|
+
description:
|
|
199
|
+
type: string
|
|
200
|
+
inputType:
|
|
201
|
+
const: waveform
|
|
202
|
+
width:
|
|
203
|
+
type: number
|
|
204
|
+
height:
|
|
205
|
+
type: number
|
|
206
|
+
placeholder:
|
|
207
|
+
type: string
|
|
208
|
+
defaultValue:
|
|
209
|
+
type: object
|
|
210
|
+
waveformData:
|
|
211
|
+
type: object
|
|
212
|
+
tooltip:
|
|
213
|
+
type: object
|
|
214
|
+
properties:
|
|
215
|
+
content:
|
|
216
|
+
type: string
|
|
217
|
+
required:
|
|
218
|
+
- name
|
|
219
|
+
- label
|
|
220
|
+
- inputType
|
|
221
|
+
additionalProperties: false
|
|
222
|
+
- type: object
|
|
223
|
+
properties:
|
|
224
|
+
name:
|
|
225
|
+
type: string
|
|
226
|
+
label:
|
|
227
|
+
type: string
|
|
228
|
+
description:
|
|
229
|
+
type: string
|
|
230
|
+
inputType:
|
|
231
|
+
const: popover-input
|
|
232
|
+
placeholder:
|
|
233
|
+
type: string
|
|
234
|
+
tooltip:
|
|
235
|
+
type: object
|
|
236
|
+
properties:
|
|
237
|
+
content:
|
|
238
|
+
type: string
|
|
239
|
+
required:
|
|
240
|
+
- name
|
|
109
241
|
- label
|
|
110
242
|
- inputType
|
|
111
243
|
additionalProperties: false
|
|
@@ -130,6 +262,12 @@ refs:
|
|
|
130
262
|
eventListeners:
|
|
131
263
|
click:
|
|
132
264
|
handler: handleActionClick
|
|
265
|
+
tooltip-icon-*:
|
|
266
|
+
eventListeners:
|
|
267
|
+
mouseenter:
|
|
268
|
+
handler: handleTooltipMouseEnter
|
|
269
|
+
mouseleave:
|
|
270
|
+
handler: handleTooltipMouseLeave
|
|
133
271
|
input-*:
|
|
134
272
|
eventListeners:
|
|
135
273
|
input-change:
|
|
@@ -138,6 +276,8 @@ refs:
|
|
|
138
276
|
eventListeners:
|
|
139
277
|
select-change:
|
|
140
278
|
handler: handleSelectChange
|
|
279
|
+
add-option-selected:
|
|
280
|
+
handler: handleSelectAddOption
|
|
141
281
|
colorpicker-*:
|
|
142
282
|
eventListeners:
|
|
143
283
|
colorpicker-change:
|
|
@@ -146,30 +286,74 @@ refs:
|
|
|
146
286
|
eventListeners:
|
|
147
287
|
slider-change:
|
|
148
288
|
handler: handleSliderChange
|
|
289
|
+
slider-input-*:
|
|
290
|
+
eventListeners:
|
|
291
|
+
slider-input-value-change:
|
|
292
|
+
handler: handleSliderInputChange
|
|
293
|
+
image-*:
|
|
294
|
+
eventListeners:
|
|
295
|
+
click:
|
|
296
|
+
handler: handleImageClick
|
|
297
|
+
contextmenu:
|
|
298
|
+
handler: handleImageClick
|
|
299
|
+
waveform-*:
|
|
300
|
+
eventListeners:
|
|
301
|
+
click:
|
|
302
|
+
handler: handleWaveformClick
|
|
303
|
+
contextmenu:
|
|
304
|
+
handler: handleWaveformClick
|
|
305
|
+
popover-input-*:
|
|
306
|
+
eventListeners:
|
|
307
|
+
input-change:
|
|
308
|
+
handler: handlePopoverInputChange
|
|
149
309
|
|
|
150
310
|
events:
|
|
151
311
|
form-change: {}
|
|
312
|
+
extra-event: {}
|
|
313
|
+
action-click: {}
|
|
152
314
|
|
|
153
315
|
template:
|
|
154
|
-
- rtgl-view w=f
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
316
|
+
- rtgl-view w=f p=md g=lg ${containerAttrString}:
|
|
317
|
+
- rtgl-view g=sm w=f:
|
|
318
|
+
- rtgl-text s=lg: ${title}
|
|
319
|
+
- rtgl-text c=mu-fg: ${description}
|
|
320
|
+
- rtgl-view g=lg w=f:
|
|
321
|
+
- $for field, i in fields:
|
|
322
|
+
- rtgl-view g=md w=f:
|
|
323
|
+
- rtgl-view g=sm:
|
|
324
|
+
- rtgl-view d=h g=md av=c:
|
|
325
|
+
- rtgl-text: ${field.label}
|
|
326
|
+
- $if field.tooltip:
|
|
327
|
+
- rtgl-svg#tooltip-icon-${field.name} svg="info" wh=16 c=mu-fg cur=help ml=xs:
|
|
328
|
+
- rtgl-text s=sm c=mu-fg: ${field.description}
|
|
329
|
+
- $if field.inputType == "read-only-text":
|
|
330
|
+
- rtgl-text s=sm: ${field.defaultValue}
|
|
331
|
+
- $if field.inputType == "inputText":
|
|
332
|
+
- rtgl-input#input-${field.name} key=${key} w=f placeholder=${field.placeholder} value=${field.defaultValue}:
|
|
333
|
+
- $if field.inputType == "popover-input":
|
|
334
|
+
- rtgl-popover-input#popover-input-${field.name} label="${field.label}" .defaultValue=fields[${i}].defaultValue:
|
|
335
|
+
- $if field.inputType == "select":
|
|
336
|
+
- rtgl-select#select-${field.name} key=${key} w=f .options=fields[${i}].options .placeholder=fields[${i}].placeholder .selectedValue=fields[${i}].defaultValue ?no-clear=fields[${i}].noClear .addOption=fields[${i}].addOption:
|
|
337
|
+
- $if field.inputType == "colorPicker":
|
|
338
|
+
- rtgl-color-picker#colorpicker-${field.name} key=${key} value=${field.defaultValue}:
|
|
339
|
+
- $if field.inputType == "slider":
|
|
340
|
+
- rtgl-slider#slider-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step} value=${field.defaultValue}:
|
|
341
|
+
- $if field.inputType == "slider-input":
|
|
342
|
+
- rtgl-slider-input#slider-input-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step} defaultValue=${field.defaultValue}:
|
|
343
|
+
- $if field.inputType == "image" && field.imageSrc:
|
|
344
|
+
- rtgl-image#image-${field.name} src=${field.imageSrc} w=${field.width} h=${field.height} cur=p:
|
|
345
|
+
- $if field.inputType == "image" && !field.imageSrc:
|
|
346
|
+
- rtgl-view#image-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
|
|
347
|
+
- rtgl-text c=mu-fg ta=c: ${field.placeholderText}
|
|
348
|
+
- $if field.inputType == "waveform" && field.waveformData:
|
|
349
|
+
- rtgl-waveform#waveform-${field.name} .waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=p:
|
|
350
|
+
- $if field.inputType == "waveform" && !field.waveformData:
|
|
351
|
+
- rtgl-view#waveform-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
|
|
352
|
+
- rtgl-text c=mu-fg ta=c: ${field.placeholder}
|
|
353
|
+
- $if field.inputType == "slot":
|
|
354
|
+
- 'slot#slot-${field.slotName} name=${field.slot} style="display: contents;"':
|
|
355
|
+
- rtgl-view g=sm w=f:
|
|
356
|
+
- rtgl-view d=h ah=e g=sm w=f:
|
|
357
|
+
- $for button, i in actions.buttons:
|
|
358
|
+
- rtgl-button#action-${button.id}: ${button.content}
|
|
359
|
+
- rtgl-tooltip ?open=${tooltipState.open} x=${tooltipState.x} y=${tooltipState.y} placement="top" content="${tooltipState.content}":
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
5
5
|
* @param {*} headingElements
|
|
6
|
+
* @param {*} offsetTop
|
|
6
7
|
* @param {*} deps
|
|
7
8
|
*/
|
|
8
|
-
const updateToLatestCurrentId = (headingElements, deps) => {
|
|
9
|
+
const updateToLatestCurrentId = (headingElements, offsetTop, deps) => {
|
|
9
10
|
const { store, render } = deps;
|
|
10
11
|
|
|
11
12
|
let currentHeadingId;
|
|
@@ -14,7 +15,9 @@ const updateToLatestCurrentId = (headingElements, deps) => {
|
|
|
14
15
|
headingElements.forEach((heading) => {
|
|
15
16
|
const rect = heading.getBoundingClientRect();
|
|
16
17
|
|
|
17
|
-
if
|
|
18
|
+
// A heading is "current" if it's at or above the offset line
|
|
19
|
+
// We want the heading that's closest to the offset but still above it
|
|
20
|
+
if (rect.top <= offsetTop) {
|
|
18
21
|
if (rect.top > closestTopPosition) {
|
|
19
22
|
closestTopPosition = rect.top;
|
|
20
23
|
currentHeadingId = heading.id;
|
|
@@ -22,46 +25,83 @@ const updateToLatestCurrentId = (headingElements, deps) => {
|
|
|
22
25
|
}
|
|
23
26
|
});
|
|
24
27
|
|
|
28
|
+
// If no heading is above the threshold, select the first visible heading below it
|
|
29
|
+
if (!currentHeadingId) {
|
|
30
|
+
let lowestTop = Infinity;
|
|
31
|
+
headingElements.forEach((heading) => {
|
|
32
|
+
const rect = heading.getBoundingClientRect();
|
|
33
|
+
if (rect.top > offsetTop && rect.top < lowestTop) {
|
|
34
|
+
lowestTop = rect.top;
|
|
35
|
+
currentHeadingId = heading.id;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
if (currentHeadingId && currentHeadingId !== store.selectCurrentId()) {
|
|
26
41
|
store.setCurrentId(currentHeadingId);
|
|
27
42
|
render();
|
|
28
43
|
}
|
|
29
44
|
};
|
|
30
45
|
|
|
31
|
-
const startListening = (contentContainer, deps) => {
|
|
46
|
+
const startListening = (contentContainer, scrollContainer, offsetTop, deps) => {
|
|
32
47
|
const { store, render } = deps;
|
|
33
48
|
|
|
34
49
|
// Extract headings
|
|
35
|
-
const headings = contentContainer.querySelectorAll("rtgl-text[id]");
|
|
50
|
+
const headings = contentContainer.querySelectorAll("h1[id], h2[id], h3[id], h4[id], rtgl-text[id]");
|
|
36
51
|
const headingElements = Array.from(headings);
|
|
37
52
|
|
|
38
|
-
const items = headingElements.map((heading) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
const items = headingElements.map((heading) => {
|
|
54
|
+
let level = 1;
|
|
55
|
+
const tagName = heading.tagName.toLowerCase();
|
|
56
|
+
|
|
57
|
+
if (tagName === 'h1') level = 1;
|
|
58
|
+
else if (tagName === 'h2') level = 2;
|
|
59
|
+
else if (tagName === 'h3') level = 3;
|
|
60
|
+
else if (tagName === 'h4') level = 4;
|
|
61
|
+
else if (tagName === 'rtgl-text') {
|
|
62
|
+
// For rtgl-text, check if it has a data-level attribute or default to 1
|
|
63
|
+
level = parseInt(heading.getAttribute('data-level') || '1', 10);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
id: heading.id,
|
|
68
|
+
href: `#${heading.id}`,
|
|
69
|
+
title: heading.textContent,
|
|
70
|
+
level: level
|
|
71
|
+
};
|
|
72
|
+
});
|
|
43
73
|
|
|
44
74
|
store.setItems(items);
|
|
45
|
-
updateToLatestCurrentId(headingElements, deps);
|
|
75
|
+
updateToLatestCurrentId(headingElements, offsetTop, deps);
|
|
46
76
|
render();
|
|
47
77
|
|
|
48
|
-
const boundCheckCurrentHeading = updateToLatestCurrentId.bind(this, headingElements, deps);
|
|
78
|
+
const boundCheckCurrentHeading = updateToLatestCurrentId.bind(this, headingElements, offsetTop, deps);
|
|
49
79
|
|
|
50
|
-
// Add scroll listener to the
|
|
51
|
-
|
|
80
|
+
// Add scroll listener to the scroll container
|
|
81
|
+
scrollContainer.addEventListener("scroll", boundCheckCurrentHeading, {
|
|
52
82
|
passive: true,
|
|
53
83
|
});
|
|
54
84
|
|
|
55
85
|
return () => {
|
|
56
|
-
|
|
86
|
+
scrollContainer.removeEventListener("scroll", boundCheckCurrentHeading);
|
|
57
87
|
}
|
|
58
88
|
};
|
|
59
89
|
|
|
60
|
-
export const
|
|
90
|
+
export const handleBeforeMount = (deps) => {
|
|
61
91
|
const { attrs } = deps;
|
|
62
92
|
requestAnimationFrame(() => {
|
|
63
|
-
const targetElement = document.getElementById(attrs['target-id'])
|
|
64
|
-
|
|
93
|
+
const targetElement = document.getElementById(attrs['target-id']);
|
|
94
|
+
|
|
95
|
+
// Get scroll container - default to window for page scroll if not specified
|
|
96
|
+
let scrollContainer = window;
|
|
97
|
+
if (attrs['scroll-container-id']) {
|
|
98
|
+
scrollContainer = document.getElementById(attrs['scroll-container-id']) || window;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get offset top - default to 100px if not specified
|
|
102
|
+
const offsetTop = parseInt(attrs['offset-top'] || '100', 10);
|
|
103
|
+
|
|
104
|
+
const stopListening = startListening(targetElement, scrollContainer, offsetTop, deps);
|
|
65
105
|
return () => {
|
|
66
106
|
stopListening();
|
|
67
107
|
}
|