js-kompo-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/app.js +22 -0
- package/components/VlApexChart.vue +89 -0
- package/components/VlCKEditorTemplate.vue +52 -0
- package/components/VlCKEditorWithVars.vue +80 -0
- package/components/VlCollapsible.vue +91 -0
- package/components/VlCopyBox.vue +61 -0
- package/components/VlCreditCardInput.vue +104 -0
- package/components/VlDateMonthDay.vue +327 -0
- package/components/VlDateTextInput.vue +151 -0
- package/components/VlInternationalPhoneInput.vue +902 -0
- package/components/VlPartialDate.vue +283 -0
- package/components/VlPasswordInput.vue +358 -0
- package/components/VlQrCodeScanner.vue +91 -0
- package/components/VlSwipeableTab.vue +35 -0
- package/components/VlSwipeableTabs.vue +325 -0
- package/components/VlText.vue +72 -0
- package/components/VlTutorialStepBuilder.vue +671 -0
- package/components/VlValidatedInput.vue +262 -0
- package/components/data/countries.js +253 -0
- package/components/getAllDefaultComponents.js +12 -0
- package/docs/architecture.md +71 -0
- package/docs/tutorials.md +247 -0
- package/docs/usage.md +128 -0
- package/functions/index.js +7 -0
- package/functions/inputs.js +64 -0
- package/functions/loading.js +20 -0
- package/kompo-ckeditor-1.0.17/index.js +5 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditor.vue +36 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditorExtended.vue +66 -0
- package/kompo-ckeditor-1.0.17/komponents/EditableCKEditor.vue +42 -0
- package/kompo-ckeditor-1.0.17/komponents/TranslatableEditor.vue +28 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditor.js +291 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditorUtilities.js +52 -0
- package/kompo-ckeditor-1.0.17/mixins/CkUploadAdapter.js +95 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/en.js +1 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/fr.js +1 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockCommand.js +100 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockEditing.js +225 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockUi.js +21 -0
- package/kompo-ckeditor-1.0.17/plugins/MentionPlugin.js +118 -0
- package/orchestrator/KompoJsBuilder.js +333 -0
- package/package.json +25 -0
- package/raw_animations/count-up-animation.js +220 -0
- package/raw_animations/magic-poof.js +210 -0
- package/tutorials/TutorialConfig.js +34 -0
- package/tutorials/TutorialEngine.js +448 -0
- package/tutorials/index.js +2 -0
- package/tutorials/modules/AutoNext.js +86 -0
- package/tutorials/modules/Avatar.js +94 -0
- package/tutorials/modules/Bubble.js +617 -0
- package/tutorials/modules/Cursor.js +315 -0
- package/tutorials/modules/Highlight.js +282 -0
- package/tutorials/modules/HoverForcer.js +250 -0
- package/tutorials/modules/Mobile.js +148 -0
- package/tutorials/modules/Options.js +98 -0
- package/tutorials/modules/Overlay.js +142 -0
- package/tutorials/modules/Scroll.js +37 -0
- package/tutorials/modules/Typewriter.js +132 -0
- package/tutorials/step-builder/step-builder-bridge.js +436 -0
- package/tutorials/step-builder/utils/bezier-editor.js +379 -0
- package/tutorials/step-builder/utils/code-generator.js +127 -0
- package/tutorials/step-builder/utils/element-picker.js +125 -0
- package/tutorials/tutorial.scss +816 -0
- package/tutorials/utils/selectors.js +225 -0
- package/tutorials/utils/stability.js +76 -0
- package/tutorials/utils/svg.js +46 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector utility functions.
|
|
3
|
+
* Pure helpers for resolving element references, computing element positions,
|
|
4
|
+
* and generating unique CSS selectors for DOM elements.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a reference selector like "highlight:N" or "hover:N" into an actual
|
|
9
|
+
* CSS selector using the step's highlight/hover configuration.
|
|
10
|
+
*
|
|
11
|
+
* - "highlight:N" resolves to the Nth element across all highlight groups
|
|
12
|
+
* - "hover:N" resolves to the Nth hover selector
|
|
13
|
+
* - All other strings are returned as-is
|
|
14
|
+
*
|
|
15
|
+
* @param {string} selector - A CSS selector, "screen:x,y", "highlight:N", or "hover:N"
|
|
16
|
+
* @param {Object} [step] - Current step object for resolving references
|
|
17
|
+
* @returns {string} Resolved CSS selector (or the original string if no match)
|
|
18
|
+
*/
|
|
19
|
+
export function resolveRef(selector, step) {
|
|
20
|
+
if (!step || typeof selector !== 'string') return selector;
|
|
21
|
+
|
|
22
|
+
// highlight:N -- resolve to the Nth element across highlight groups
|
|
23
|
+
const hlMatch = selector.match(/^highlight:(\d+)$/);
|
|
24
|
+
if (hlMatch && step.highlight?.groups) {
|
|
25
|
+
const hlIdx = parseInt(hlMatch[1], 10);
|
|
26
|
+
const allSelectors = [];
|
|
27
|
+
for (const g of step.highlight.groups) {
|
|
28
|
+
const elems = g.elements || g;
|
|
29
|
+
if (Array.isArray(elems)) {
|
|
30
|
+
allSelectors.push(...elems);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (allSelectors[hlIdx]) return allSelectors[hlIdx];
|
|
34
|
+
console.warn(
|
|
35
|
+
`TutorialEngine: highlight:${hlIdx} not found, ${allSelectors.length} elements available`
|
|
36
|
+
);
|
|
37
|
+
return selector;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// hover:N -- resolve to the Nth hover selector
|
|
41
|
+
const hvMatch = selector.match(/^hover:(\d+)$/);
|
|
42
|
+
if (hvMatch && step.hover) {
|
|
43
|
+
const hvIdx = parseInt(hvMatch[1], 10);
|
|
44
|
+
const hoverList = Array.isArray(step.hover) ? step.hover : [step.hover];
|
|
45
|
+
if (hoverList[hvIdx]) return hoverList[hvIdx];
|
|
46
|
+
console.warn(
|
|
47
|
+
`TutorialEngine: hover:${hvIdx} not found, ${hoverList.length} elements available`
|
|
48
|
+
);
|
|
49
|
+
return selector;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return selector;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolve an element's position from a selector string.
|
|
57
|
+
*
|
|
58
|
+
* Supports two formats:
|
|
59
|
+
* - "screen:x,y" for absolute screen coordinates (returned directly)
|
|
60
|
+
* - Any CSS selector, which is queried and its bounding rect used
|
|
61
|
+
*
|
|
62
|
+
* The `anchor` parameter controls where within the element's bounding box
|
|
63
|
+
* the point is taken (0,0 = top-left, 1,1 = bottom-right, default 0.5,0.5 = center).
|
|
64
|
+
* The result is clamped to the viewport.
|
|
65
|
+
*
|
|
66
|
+
* Reference selectors ("highlight:N", "hover:N") are resolved via `resolveRef`
|
|
67
|
+
* before querying.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} selector - CSS selector or "screen:x,y"
|
|
70
|
+
* @param {{x: number, y: number}} [anchor] - Anchor ratios (0-1), defaults to center (0.5, 0.5)
|
|
71
|
+
* @param {Object} [step] - Current step object for resolving references
|
|
72
|
+
* @returns {{x: number, y: number}|null} Position in pixels, or null if element not found
|
|
73
|
+
*/
|
|
74
|
+
export function resolveElementCenter(selector, anchor, step) {
|
|
75
|
+
// Resolve highlight:N / hover:N references first
|
|
76
|
+
const resolved = resolveRef(selector, step);
|
|
77
|
+
|
|
78
|
+
// "screen:x,y" -- direct absolute coordinates
|
|
79
|
+
if (typeof resolved === 'string' && resolved.startsWith('screen:')) {
|
|
80
|
+
const parts = resolved.replace('screen:', '').split(',');
|
|
81
|
+
return { x: parseFloat(parts[0]), y: parseFloat(parts[1]) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const el = document.querySelector(resolved);
|
|
85
|
+
if (!el) {
|
|
86
|
+
console.warn('TutorialEngine.resolveElementCenter: element not found for selector', resolved);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const rect = el.getBoundingClientRect();
|
|
91
|
+
const ax = anchor?.x ?? 0.5;
|
|
92
|
+
const ay = anchor?.y ?? 0.5;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
x: Math.max(0, Math.min(window.innerWidth, rect.left + rect.width * ax)),
|
|
96
|
+
y: Math.max(0, Math.min(window.innerHeight, rect.top + rect.height * ay)),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate the best unique CSS selector for a given DOM element.
|
|
102
|
+
*
|
|
103
|
+
* Tries strategies in order of preference:
|
|
104
|
+
* 1. Element ID (#id)
|
|
105
|
+
* 2. Unique single class (.class)
|
|
106
|
+
* 3. Tag + class (tag.class)
|
|
107
|
+
* 4. Unique data attribute ([data-foo="bar"])
|
|
108
|
+
* 5. Ancestor with ID + relative child path (#ancestor > tag.class > ...)
|
|
109
|
+
* 6. Fallback: nth-child path from body (tag:nth-child(n) > ...)
|
|
110
|
+
*
|
|
111
|
+
* Classes containing special characters (colons, percent signs, brackets, slashes)
|
|
112
|
+
* are filtered out to avoid issues with Tailwind-style utility classes.
|
|
113
|
+
*
|
|
114
|
+
* @param {HTMLElement} el - Target DOM element
|
|
115
|
+
* @returns {string} A CSS selector that uniquely identifies the element
|
|
116
|
+
*/
|
|
117
|
+
export function bestSelector(el) {
|
|
118
|
+
if (!el || el === document.body || el === document.documentElement) return 'body';
|
|
119
|
+
if (el.id) return `#${el.id}`;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Safely count how many elements match a selector.
|
|
123
|
+
* Returns -1 if the selector is invalid.
|
|
124
|
+
*/
|
|
125
|
+
const safeQSA = (s) => {
|
|
126
|
+
try {
|
|
127
|
+
return document.querySelectorAll(s).length;
|
|
128
|
+
} catch {
|
|
129
|
+
return -1;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Filter out classes with special characters that break selectors
|
|
135
|
+
* (Tailwind hover:, focus:, brackets, etc.)
|
|
136
|
+
*/
|
|
137
|
+
const safeClasses = (classList) =>
|
|
138
|
+
Array.from(classList).filter((c) => !/[:%\[\]\/]/.test(c));
|
|
139
|
+
|
|
140
|
+
// Strategy 1: unique single class
|
|
141
|
+
const classes = safeClasses(el.classList);
|
|
142
|
+
const tag = el.tagName.toLowerCase();
|
|
143
|
+
|
|
144
|
+
if (classes.length) {
|
|
145
|
+
for (const cls of classes) {
|
|
146
|
+
const sel = `.${cls}`;
|
|
147
|
+
if (safeQSA(sel) === 1) return sel;
|
|
148
|
+
}
|
|
149
|
+
// Strategy 2: tag + single class
|
|
150
|
+
for (const cls of classes) {
|
|
151
|
+
const sel = `${tag}.${cls}`;
|
|
152
|
+
if (safeQSA(sel) === 1) return sel;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Strategy 3: unique data attribute
|
|
157
|
+
for (const attr of el.attributes) {
|
|
158
|
+
if (attr.name.startsWith('data-')) {
|
|
159
|
+
const escapedVal = attr.value.replace(/"/g, '\\"');
|
|
160
|
+
const sel = `[${attr.name}="${escapedVal}"]`;
|
|
161
|
+
if (safeQSA(sel) === 1) return sel;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Strategy 4: ancestor with ID + relative child path
|
|
166
|
+
let ancestor = el.parentElement;
|
|
167
|
+
while (ancestor && ancestor !== document.body) {
|
|
168
|
+
if (ancestor.id) {
|
|
169
|
+
const relPath = [];
|
|
170
|
+
let cur = el;
|
|
171
|
+
|
|
172
|
+
while (cur && cur !== ancestor) {
|
|
173
|
+
const parent = cur.parentElement;
|
|
174
|
+
if (!parent) break;
|
|
175
|
+
|
|
176
|
+
const siblings = Array.from(parent.children);
|
|
177
|
+
const idx = siblings.indexOf(cur) + 1;
|
|
178
|
+
const curTag = cur.tagName.toLowerCase();
|
|
179
|
+
|
|
180
|
+
// Try class-based segment first for readability
|
|
181
|
+
const curClasses = safeClasses(cur.classList);
|
|
182
|
+
let found = false;
|
|
183
|
+
|
|
184
|
+
for (const cls of curClasses) {
|
|
185
|
+
const segment = `${curTag}.${cls}`;
|
|
186
|
+
const full = `#${ancestor.id} > ${[segment, ...relPath].join(' > ')}`;
|
|
187
|
+
if (safeQSA(full) === 1) {
|
|
188
|
+
relPath.unshift(segment);
|
|
189
|
+
found = true;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!found) {
|
|
195
|
+
relPath.unshift(`${curTag}:nth-child(${idx})`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
cur = parent;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const sel = `#${ancestor.id} > ${relPath.join(' > ')}`;
|
|
202
|
+
if (safeQSA(sel) === 1) return sel;
|
|
203
|
+
}
|
|
204
|
+
ancestor = ancestor.parentElement;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Strategy 5 (fallback): nth-child path from body
|
|
208
|
+
const path = [];
|
|
209
|
+
let current = el;
|
|
210
|
+
while (current && current !== document.body) {
|
|
211
|
+
const parent = current.parentElement;
|
|
212
|
+
if (!parent) break;
|
|
213
|
+
|
|
214
|
+
const children = Array.from(parent.children);
|
|
215
|
+
const index = children.indexOf(current) + 1;
|
|
216
|
+
const currentTag = current.tagName.toLowerCase();
|
|
217
|
+
path.unshift(`${currentTag}:nth-child(${index})`);
|
|
218
|
+
current = parent;
|
|
219
|
+
|
|
220
|
+
const sel = path.join(' > ');
|
|
221
|
+
if (safeQSA(sel) === 1) return sel;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return path.join(' > ');
|
|
225
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM stability utility.
|
|
3
|
+
* Polls element positions/sizes until they stop changing,
|
|
4
|
+
* useful for waiting out CSS transitions before measuring layout.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wait for all highlight target elements to be stable (position/size not changing)
|
|
9
|
+
* before calling the callback.
|
|
10
|
+
*
|
|
11
|
+
* "Stable" means the bounding rects of all target elements have not changed
|
|
12
|
+
* for 3 consecutive checks (150ms). Gives up after 30 attempts (1.5s).
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} config - Highlight configuration object
|
|
15
|
+
* @param {Array} config.groups - Array of group objects, each with an `elements`
|
|
16
|
+
* array of CSS selector strings (or the group itself may be a selector array)
|
|
17
|
+
* @param {Function} callback - Called once all elements are stable (or timeout is reached)
|
|
18
|
+
* @returns {Function} cancel - Call to stop polling early
|
|
19
|
+
*/
|
|
20
|
+
export function waitForStableHighlight(config, callback) {
|
|
21
|
+
// Collect all CSS selectors from the highlight groups
|
|
22
|
+
const allSelectors = [];
|
|
23
|
+
for (const g of config.groups || []) {
|
|
24
|
+
const elems = g.elements || g;
|
|
25
|
+
if (Array.isArray(elems)) {
|
|
26
|
+
allSelectors.push(...elems);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let lastRects = null;
|
|
31
|
+
let stableCount = 0;
|
|
32
|
+
const maxAttempts = 30; // 30 * 50ms = 1.5s max
|
|
33
|
+
let attempts = 0;
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
let timerId = null;
|
|
36
|
+
|
|
37
|
+
/** Take a snapshot of all elements' bounding rects as a comparable string. */
|
|
38
|
+
const snapshot = () =>
|
|
39
|
+
allSelectors
|
|
40
|
+
.map((sel) => {
|
|
41
|
+
const el = document.querySelector(sel);
|
|
42
|
+
if (!el) return '0,0,0,0';
|
|
43
|
+
const r = el.getBoundingClientRect();
|
|
44
|
+
return `${Math.round(r.left)},${Math.round(r.top)},${Math.round(r.width)},${Math.round(r.height)}`;
|
|
45
|
+
})
|
|
46
|
+
.join('|');
|
|
47
|
+
|
|
48
|
+
/** Check if rects are stable; if not, schedule another check. */
|
|
49
|
+
const check = () => {
|
|
50
|
+
if (cancelled) return;
|
|
51
|
+
attempts++;
|
|
52
|
+
const current = snapshot();
|
|
53
|
+
|
|
54
|
+
if (current === lastRects) {
|
|
55
|
+
stableCount++;
|
|
56
|
+
} else {
|
|
57
|
+
stableCount = 0;
|
|
58
|
+
}
|
|
59
|
+
lastRects = current;
|
|
60
|
+
|
|
61
|
+
// Stable for 3 consecutive checks (150ms) or max attempts reached
|
|
62
|
+
if (stableCount >= 3 || attempts >= maxAttempts) {
|
|
63
|
+
callback();
|
|
64
|
+
} else {
|
|
65
|
+
timerId = setTimeout(check, 50);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Start after a minimum initial delay for CSS transitions to begin
|
|
70
|
+
timerId = setTimeout(check, 50);
|
|
71
|
+
|
|
72
|
+
return function cancel() {
|
|
73
|
+
cancelled = true;
|
|
74
|
+
if (timerId) clearTimeout(timerId);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG utility functions.
|
|
3
|
+
* Pure helpers for creating SVG elements and transforming SVG paths.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create an SVG element with the given tag and attributes.
|
|
8
|
+
* @param {string} tag - SVG element tag name (e.g. 'rect', 'path', 'circle')
|
|
9
|
+
* @param {Object} [attrs={}] - Key-value pairs to set as attributes
|
|
10
|
+
* @returns {SVGElement}
|
|
11
|
+
*/
|
|
12
|
+
export function createSvgEl(tag, attrs = {}) {
|
|
13
|
+
const el = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
|
14
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
15
|
+
el.setAttribute(key, value);
|
|
16
|
+
}
|
|
17
|
+
return el;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert a normalized SVG path (0-1 range) to absolute pixel coordinates.
|
|
22
|
+
*
|
|
23
|
+
* Coordinate pairs in the path string are interpreted as normalized values
|
|
24
|
+
* within the bounding box defined by `from` and `to`. Each pair (nx, ny) is
|
|
25
|
+
* mapped to (from.x + nx * dx, from.y + ny * dy).
|
|
26
|
+
*
|
|
27
|
+
* @param {string} svgPath - SVG path string with normalized coordinate pairs
|
|
28
|
+
* @param {{x: number, y: number}} from - Start position in pixels
|
|
29
|
+
* @param {{x: number, y: number}} to - End position in pixels
|
|
30
|
+
* @returns {string} SVG path with absolute pixel coordinates
|
|
31
|
+
*/
|
|
32
|
+
export function denormalizeSvgPath(svgPath, from, to) {
|
|
33
|
+
const dx = to.x - from.x;
|
|
34
|
+
const dy = to.y - from.y;
|
|
35
|
+
|
|
36
|
+
return svgPath.replace(
|
|
37
|
+
/([-]?[0-9]*\.?[0-9]+)\s*,\s*([-]?[0-9]*\.?[0-9]+)/g,
|
|
38
|
+
(_, xStr, yStr) => {
|
|
39
|
+
const nx = parseFloat(xStr);
|
|
40
|
+
const ny = parseFloat(yStr);
|
|
41
|
+
const px = from.x + nx * dx;
|
|
42
|
+
const py = from.y + ny * dy;
|
|
43
|
+
return `${px.toFixed(1)},${py.toFixed(1)}`;
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|