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.
Files changed (67) hide show
  1. package/README.md +1 -0
  2. package/app.js +22 -0
  3. package/components/VlApexChart.vue +89 -0
  4. package/components/VlCKEditorTemplate.vue +52 -0
  5. package/components/VlCKEditorWithVars.vue +80 -0
  6. package/components/VlCollapsible.vue +91 -0
  7. package/components/VlCopyBox.vue +61 -0
  8. package/components/VlCreditCardInput.vue +104 -0
  9. package/components/VlDateMonthDay.vue +327 -0
  10. package/components/VlDateTextInput.vue +151 -0
  11. package/components/VlInternationalPhoneInput.vue +902 -0
  12. package/components/VlPartialDate.vue +283 -0
  13. package/components/VlPasswordInput.vue +358 -0
  14. package/components/VlQrCodeScanner.vue +91 -0
  15. package/components/VlSwipeableTab.vue +35 -0
  16. package/components/VlSwipeableTabs.vue +325 -0
  17. package/components/VlText.vue +72 -0
  18. package/components/VlTutorialStepBuilder.vue +671 -0
  19. package/components/VlValidatedInput.vue +262 -0
  20. package/components/data/countries.js +253 -0
  21. package/components/getAllDefaultComponents.js +12 -0
  22. package/docs/architecture.md +71 -0
  23. package/docs/tutorials.md +247 -0
  24. package/docs/usage.md +128 -0
  25. package/functions/index.js +7 -0
  26. package/functions/inputs.js +64 -0
  27. package/functions/loading.js +20 -0
  28. package/kompo-ckeditor-1.0.17/index.js +5 -0
  29. package/kompo-ckeditor-1.0.17/komponents/CKEditor.vue +36 -0
  30. package/kompo-ckeditor-1.0.17/komponents/CKEditorExtended.vue +66 -0
  31. package/kompo-ckeditor-1.0.17/komponents/EditableCKEditor.vue +42 -0
  32. package/kompo-ckeditor-1.0.17/komponents/TranslatableEditor.vue +28 -0
  33. package/kompo-ckeditor-1.0.17/mixins/CKEditor.js +291 -0
  34. package/kompo-ckeditor-1.0.17/mixins/CKEditorUtilities.js +52 -0
  35. package/kompo-ckeditor-1.0.17/mixins/CkUploadAdapter.js +95 -0
  36. package/kompo-ckeditor-1.0.17/mixins/translations/en.js +1 -0
  37. package/kompo-ckeditor-1.0.17/mixins/translations/fr.js +1 -0
  38. package/kompo-ckeditor-1.0.17/plugins/JsonBlockCommand.js +100 -0
  39. package/kompo-ckeditor-1.0.17/plugins/JsonBlockEditing.js +225 -0
  40. package/kompo-ckeditor-1.0.17/plugins/JsonBlockUi.js +21 -0
  41. package/kompo-ckeditor-1.0.17/plugins/MentionPlugin.js +118 -0
  42. package/orchestrator/KompoJsBuilder.js +333 -0
  43. package/package.json +25 -0
  44. package/raw_animations/count-up-animation.js +220 -0
  45. package/raw_animations/magic-poof.js +210 -0
  46. package/tutorials/TutorialConfig.js +34 -0
  47. package/tutorials/TutorialEngine.js +448 -0
  48. package/tutorials/index.js +2 -0
  49. package/tutorials/modules/AutoNext.js +86 -0
  50. package/tutorials/modules/Avatar.js +94 -0
  51. package/tutorials/modules/Bubble.js +617 -0
  52. package/tutorials/modules/Cursor.js +315 -0
  53. package/tutorials/modules/Highlight.js +282 -0
  54. package/tutorials/modules/HoverForcer.js +250 -0
  55. package/tutorials/modules/Mobile.js +148 -0
  56. package/tutorials/modules/Options.js +98 -0
  57. package/tutorials/modules/Overlay.js +142 -0
  58. package/tutorials/modules/Scroll.js +37 -0
  59. package/tutorials/modules/Typewriter.js +132 -0
  60. package/tutorials/step-builder/step-builder-bridge.js +436 -0
  61. package/tutorials/step-builder/utils/bezier-editor.js +379 -0
  62. package/tutorials/step-builder/utils/code-generator.js +127 -0
  63. package/tutorials/step-builder/utils/element-picker.js +125 -0
  64. package/tutorials/tutorial.scss +816 -0
  65. package/tutorials/utils/selectors.js +225 -0
  66. package/tutorials/utils/stability.js +76 -0
  67. 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
+ }