@syntrologie/adapt-overlays 0.0.0-semantically-released
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/editor.d.ts +22 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +18 -0
- package/dist/highlight.d.ts +19 -0
- package/dist/highlight.d.ts.map +1 -0
- package/dist/highlight.js +156 -0
- package/dist/modal.d.ts +11 -0
- package/dist/modal.d.ts.map +1 -0
- package/dist/modal.js +194 -0
- package/dist/runtime.d.ts +70 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +267 -0
- package/dist/sanitizer.d.ts +8 -0
- package/dist/sanitizer.d.ts.map +1 -0
- package/dist/sanitizer.js +75 -0
- package/dist/schema.d.ts +197 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +74 -0
- package/dist/tooltip.d.ts +20 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +220 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/package.json +52 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Overlays - Runtime Module
|
|
3
|
+
*
|
|
4
|
+
* Visual overlay actions: highlight, pulse, badge, tooltip.
|
|
5
|
+
*/
|
|
6
|
+
import { showHighlight } from './highlight';
|
|
7
|
+
import { showTooltip } from './tooltip';
|
|
8
|
+
import { sanitizeHtml } from './sanitizer';
|
|
9
|
+
import { executeModal } from './modal';
|
|
10
|
+
// Re-export executeModal for use by other packages
|
|
11
|
+
export { executeModal };
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Executors
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Execute a highlight action
|
|
17
|
+
*/
|
|
18
|
+
export const executeHighlight = async (action, context) => {
|
|
19
|
+
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
20
|
+
if (!anchorEl) {
|
|
21
|
+
throw new Error(`Anchor not found: ${action.anchorId}`);
|
|
22
|
+
}
|
|
23
|
+
const handle = showHighlight(anchorEl, context.overlayRoot, {
|
|
24
|
+
paddingPx: action.style?.paddingPx ?? 12,
|
|
25
|
+
radiusPx: action.style?.radiusPx ?? 12,
|
|
26
|
+
scrimOpacity: action.style?.scrimOpacity ?? 0.55,
|
|
27
|
+
ringColor: action.style?.color,
|
|
28
|
+
blocking: false,
|
|
29
|
+
onClickOutside: false,
|
|
30
|
+
onEsc: false,
|
|
31
|
+
});
|
|
32
|
+
context.publishEvent('action.applied', {
|
|
33
|
+
id: context.generateId(),
|
|
34
|
+
kind: 'overlays:highlight',
|
|
35
|
+
anchorId: action.anchorId,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
cleanup: () => {
|
|
39
|
+
handle.destroy();
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Execute a pulse action
|
|
45
|
+
*/
|
|
46
|
+
export const executePulse = async (action, context) => {
|
|
47
|
+
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
48
|
+
if (!anchorEl) {
|
|
49
|
+
throw new Error(`Anchor not found: ${action.anchorId}`);
|
|
50
|
+
}
|
|
51
|
+
const duration = action.duration ?? 2000;
|
|
52
|
+
const className = 'syntro-pulse-effect';
|
|
53
|
+
// Inject pulse animation styles if not already present
|
|
54
|
+
if (!document.querySelector('[data-syntro-pulse-styles]')) {
|
|
55
|
+
const style = document.createElement('style');
|
|
56
|
+
style.setAttribute('data-syntro-pulse-styles', '');
|
|
57
|
+
style.textContent = `
|
|
58
|
+
@keyframes syntro-pulse {
|
|
59
|
+
0%, 100% {
|
|
60
|
+
box-shadow: 0 0 0 0 rgba(79, 70, 229, 0.4);
|
|
61
|
+
}
|
|
62
|
+
50% {
|
|
63
|
+
box-shadow: 0 0 0 8px rgba(79, 70, 229, 0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
.${className} {
|
|
67
|
+
animation: syntro-pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
document.head.appendChild(style);
|
|
71
|
+
}
|
|
72
|
+
// Add pulse class
|
|
73
|
+
anchorEl.classList.add(className);
|
|
74
|
+
// Set up auto-remove timeout
|
|
75
|
+
const timeoutId = setTimeout(() => {
|
|
76
|
+
anchorEl.classList.remove(className);
|
|
77
|
+
}, duration);
|
|
78
|
+
context.publishEvent('action.applied', {
|
|
79
|
+
id: context.generateId(),
|
|
80
|
+
kind: 'overlays:pulse',
|
|
81
|
+
anchorId: action.anchorId,
|
|
82
|
+
duration,
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
cleanup: () => {
|
|
86
|
+
clearTimeout(timeoutId);
|
|
87
|
+
anchorEl.classList.remove(className);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Execute a badge action
|
|
93
|
+
*/
|
|
94
|
+
export const executeBadge = async (action, context) => {
|
|
95
|
+
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
96
|
+
if (!anchorEl) {
|
|
97
|
+
throw new Error(`Anchor not found: ${action.anchorId}`);
|
|
98
|
+
}
|
|
99
|
+
// Create badge element
|
|
100
|
+
const badge = document.createElement('div');
|
|
101
|
+
badge.className = 'syntro-badge';
|
|
102
|
+
badge.textContent = action.content;
|
|
103
|
+
badge.setAttribute('data-syntro-badge', action.anchorId);
|
|
104
|
+
// Inject badge styles if not present
|
|
105
|
+
if (!document.querySelector('[data-syntro-badge-styles]')) {
|
|
106
|
+
const style = document.createElement('style');
|
|
107
|
+
style.setAttribute('data-syntro-badge-styles', '');
|
|
108
|
+
style.textContent = `
|
|
109
|
+
.syntro-badge {
|
|
110
|
+
position: absolute;
|
|
111
|
+
padding: 2px 6px;
|
|
112
|
+
font-size: 12px;
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
line-height: 1;
|
|
115
|
+
color: white;
|
|
116
|
+
background: var(--syntro-accent, #4f46e5);
|
|
117
|
+
border-radius: 9999px;
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
z-index: 2147483646;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
document.head.appendChild(style);
|
|
124
|
+
}
|
|
125
|
+
// Position badge relative to anchor
|
|
126
|
+
const position = action.position ?? 'top-right';
|
|
127
|
+
// Ensure anchor has relative positioning for badge
|
|
128
|
+
const originalPosition = anchorEl.style.position;
|
|
129
|
+
if (getComputedStyle(anchorEl).position === 'static') {
|
|
130
|
+
anchorEl.style.position = 'relative';
|
|
131
|
+
}
|
|
132
|
+
// Append to anchor for relative positioning
|
|
133
|
+
anchorEl.appendChild(badge);
|
|
134
|
+
// Position based on config
|
|
135
|
+
switch (position) {
|
|
136
|
+
case 'top-left':
|
|
137
|
+
Object.assign(badge.style, { top: '-8px', left: '-8px' });
|
|
138
|
+
break;
|
|
139
|
+
case 'top-right':
|
|
140
|
+
Object.assign(badge.style, { top: '-8px', right: '-8px' });
|
|
141
|
+
break;
|
|
142
|
+
case 'bottom-left':
|
|
143
|
+
Object.assign(badge.style, { bottom: '-8px', left: '-8px' });
|
|
144
|
+
break;
|
|
145
|
+
case 'bottom-right':
|
|
146
|
+
Object.assign(badge.style, { bottom: '-8px', right: '-8px' });
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
context.publishEvent('action.applied', {
|
|
150
|
+
id: context.generateId(),
|
|
151
|
+
kind: 'overlays:badge',
|
|
152
|
+
anchorId: action.anchorId,
|
|
153
|
+
content: action.content,
|
|
154
|
+
position,
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
cleanup: () => {
|
|
158
|
+
badge.remove();
|
|
159
|
+
if (originalPosition !== undefined) {
|
|
160
|
+
anchorEl.style.position = originalPosition;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
updateFn: (changes) => {
|
|
164
|
+
if ('content' in changes && typeof changes.content === 'string') {
|
|
165
|
+
badge.textContent = changes.content;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Execute a tooltip action
|
|
172
|
+
*/
|
|
173
|
+
export const executeTooltip = async (action, context) => {
|
|
174
|
+
const anchorEl = context.resolveAnchor(action.anchorId);
|
|
175
|
+
if (!anchorEl) {
|
|
176
|
+
throw new Error(`Anchor not found: ${action.anchorId}`);
|
|
177
|
+
}
|
|
178
|
+
// Build tooltip HTML
|
|
179
|
+
const content = action.content;
|
|
180
|
+
let html = '';
|
|
181
|
+
if (content.title) {
|
|
182
|
+
html += `<div class="syntro-tt-title">${sanitizeHtml(content.title)}</div>`;
|
|
183
|
+
}
|
|
184
|
+
html += `<div class="syntro-tt-body">${sanitizeHtml(content.body)}</div>`;
|
|
185
|
+
// Add CTA buttons if provided (new multi-button support)
|
|
186
|
+
if (content.ctaButtons && content.ctaButtons.length > 0) {
|
|
187
|
+
html += `<div class="syntro-tt-actions">`;
|
|
188
|
+
for (const btn of content.ctaButtons) {
|
|
189
|
+
const isPrimary = btn.primary ?? false;
|
|
190
|
+
html += `
|
|
191
|
+
<button
|
|
192
|
+
class="syntro-tt-btn ${isPrimary ? 'syntro-tt-btn-primary' : ''}"
|
|
193
|
+
data-syntro-action="${sanitizeHtml(btn.actionId)}"
|
|
194
|
+
>
|
|
195
|
+
${sanitizeHtml(btn.label)}
|
|
196
|
+
</button>
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
html += `</div>`;
|
|
200
|
+
}
|
|
201
|
+
else if (content.cta) {
|
|
202
|
+
// Legacy single CTA support
|
|
203
|
+
html += `<div class="syntro-tt-actions">
|
|
204
|
+
<button class="syntro-tt-btn syntro-tt-btn-primary" data-syntro-action="cta">
|
|
205
|
+
${sanitizeHtml(content.cta.label)}
|
|
206
|
+
</button>
|
|
207
|
+
</div>`;
|
|
208
|
+
}
|
|
209
|
+
const handle = showTooltip(anchorEl, context.overlayRoot, {
|
|
210
|
+
html,
|
|
211
|
+
placement: action.placement ?? 'top',
|
|
212
|
+
trigger: action.trigger ?? 'immediate',
|
|
213
|
+
onAction: (actionId) => {
|
|
214
|
+
if (actionId === 'cta' && content.cta) {
|
|
215
|
+
context.publishEvent('action.cta_clicked', {
|
|
216
|
+
anchorId: action.anchorId,
|
|
217
|
+
ctaLabel: content.cta.label,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
else if (content.ctaButtons) {
|
|
221
|
+
const clickedBtn = content.ctaButtons.find((b) => b.actionId === actionId);
|
|
222
|
+
if (clickedBtn) {
|
|
223
|
+
context.publishEvent('action.tooltip_cta_clicked', {
|
|
224
|
+
anchorId: action.anchorId,
|
|
225
|
+
actionId,
|
|
226
|
+
label: clickedBtn.label,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
context.publishEvent('action.applied', {
|
|
233
|
+
id: context.generateId(),
|
|
234
|
+
kind: 'overlays:tooltip',
|
|
235
|
+
anchorId: action.anchorId,
|
|
236
|
+
trigger: action.trigger ?? 'immediate',
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
cleanup: () => {
|
|
240
|
+
handle.destroy();
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// Executor Definitions for Registration
|
|
246
|
+
// ============================================================================
|
|
247
|
+
/**
|
|
248
|
+
* All executors provided by this app.
|
|
249
|
+
* These are registered with the runtime's ExecutorRegistry.
|
|
250
|
+
*/
|
|
251
|
+
export const executors = [
|
|
252
|
+
{ kind: 'overlays:highlight', executor: executeHighlight },
|
|
253
|
+
{ kind: 'overlays:pulse', executor: executePulse },
|
|
254
|
+
{ kind: 'overlays:badge', executor: executeBadge },
|
|
255
|
+
{ kind: 'overlays:tooltip', executor: executeTooltip },
|
|
256
|
+
{ kind: 'overlays:modal', executor: executeModal },
|
|
257
|
+
];
|
|
258
|
+
/**
|
|
259
|
+
* App runtime manifest.
|
|
260
|
+
*/
|
|
261
|
+
export const runtime = {
|
|
262
|
+
id: 'adaptive-overlays',
|
|
263
|
+
version: '1.0.0',
|
|
264
|
+
name: 'Overlays',
|
|
265
|
+
description: 'Tooltips, highlights, badges, modals, and visual overlays',
|
|
266
|
+
executors,
|
|
267
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../src/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqDjD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML Sanitizer
|
|
3
|
+
*
|
|
4
|
+
* Sanitizes HTML to prevent XSS attacks.
|
|
5
|
+
* Uses native Sanitizer API when available, falls back to whitelist approach.
|
|
6
|
+
*/
|
|
7
|
+
const ALLOWED_TAGS = new Set([
|
|
8
|
+
'b',
|
|
9
|
+
'strong',
|
|
10
|
+
'i',
|
|
11
|
+
'em',
|
|
12
|
+
'u',
|
|
13
|
+
'span',
|
|
14
|
+
'div',
|
|
15
|
+
'p',
|
|
16
|
+
'br',
|
|
17
|
+
'ul',
|
|
18
|
+
'ol',
|
|
19
|
+
'li',
|
|
20
|
+
'code',
|
|
21
|
+
'pre',
|
|
22
|
+
'small',
|
|
23
|
+
'sup',
|
|
24
|
+
'sub',
|
|
25
|
+
'a',
|
|
26
|
+
'button',
|
|
27
|
+
]);
|
|
28
|
+
export function sanitizeHtml(html) {
|
|
29
|
+
// Try native Sanitizer API first
|
|
30
|
+
const hasNative = typeof window.Sanitizer === 'function';
|
|
31
|
+
if (hasNative) {
|
|
32
|
+
try {
|
|
33
|
+
const s = new window.Sanitizer({});
|
|
34
|
+
const frag = s.sanitizeToFragment(html);
|
|
35
|
+
const div = document.createElement('div');
|
|
36
|
+
div.append(frag);
|
|
37
|
+
return div.innerHTML;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Fall through to manual sanitizer
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Conservative fallback sanitizer
|
|
44
|
+
const tpl = document.createElement('template');
|
|
45
|
+
tpl.innerHTML = html;
|
|
46
|
+
const root = tpl.content;
|
|
47
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
|
|
48
|
+
const toRemove = [];
|
|
49
|
+
while (walker.nextNode()) {
|
|
50
|
+
const el = walker.currentNode;
|
|
51
|
+
const tag = el.tagName.toLowerCase();
|
|
52
|
+
if (!ALLOWED_TAGS.has(tag)) {
|
|
53
|
+
toRemove.push(el);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Remove dangerous attributes
|
|
57
|
+
for (const attr of Array.from(el.attributes)) {
|
|
58
|
+
const name = attr.name.toLowerCase();
|
|
59
|
+
const value = attr.value.trim().toLowerCase();
|
|
60
|
+
const isEvent = name.startsWith('on');
|
|
61
|
+
const isJsUrl = (name === 'href' || name === 'src') && value.startsWith('javascript:');
|
|
62
|
+
if (isEvent || isJsUrl) {
|
|
63
|
+
el.removeAttribute(attr.name);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Remove disallowed elements but keep their children
|
|
68
|
+
for (const el of toRemove) {
|
|
69
|
+
while (el.firstChild) {
|
|
70
|
+
el.parentNode?.insertBefore(el.firstChild, el);
|
|
71
|
+
}
|
|
72
|
+
el.remove();
|
|
73
|
+
}
|
|
74
|
+
return tpl.innerHTML;
|
|
75
|
+
}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Overlays - Config Schema
|
|
3
|
+
*
|
|
4
|
+
* Zod schema for validating overlays app configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* Overlays app config schema.
|
|
9
|
+
* Defines the structure for overlay configurations in a canvas config.
|
|
10
|
+
*/
|
|
11
|
+
export declare const configSchema: z.ZodObject<{
|
|
12
|
+
/** Highlight configurations */
|
|
13
|
+
highlights: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14
|
+
anchorId: z.ZodString;
|
|
15
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
color: z.ZodOptional<z.ZodString>;
|
|
17
|
+
paddingPx: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
radiusPx: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
scrimOpacity: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
color?: string | undefined;
|
|
22
|
+
paddingPx?: number | undefined;
|
|
23
|
+
radiusPx?: number | undefined;
|
|
24
|
+
scrimOpacity?: number | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
color?: string | undefined;
|
|
27
|
+
paddingPx?: number | undefined;
|
|
28
|
+
radiusPx?: number | undefined;
|
|
29
|
+
scrimOpacity?: number | undefined;
|
|
30
|
+
}>>;
|
|
31
|
+
}, "strip", z.ZodTypeAny, {
|
|
32
|
+
anchorId: string;
|
|
33
|
+
style?: {
|
|
34
|
+
color?: string | undefined;
|
|
35
|
+
paddingPx?: number | undefined;
|
|
36
|
+
radiusPx?: number | undefined;
|
|
37
|
+
scrimOpacity?: number | undefined;
|
|
38
|
+
} | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
anchorId: string;
|
|
41
|
+
style?: {
|
|
42
|
+
color?: string | undefined;
|
|
43
|
+
paddingPx?: number | undefined;
|
|
44
|
+
radiusPx?: number | undefined;
|
|
45
|
+
scrimOpacity?: number | undefined;
|
|
46
|
+
} | undefined;
|
|
47
|
+
}>, "many">>;
|
|
48
|
+
/** Tooltip configurations */
|
|
49
|
+
tooltips: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
50
|
+
anchorId: z.ZodString;
|
|
51
|
+
content: z.ZodObject<{
|
|
52
|
+
title: z.ZodOptional<z.ZodString>;
|
|
53
|
+
body: z.ZodString;
|
|
54
|
+
cta: z.ZodOptional<z.ZodObject<{
|
|
55
|
+
label: z.ZodString;
|
|
56
|
+
action: z.ZodUnknown;
|
|
57
|
+
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
label: string;
|
|
59
|
+
action?: unknown;
|
|
60
|
+
}, {
|
|
61
|
+
label: string;
|
|
62
|
+
action?: unknown;
|
|
63
|
+
}>>;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
body: string;
|
|
66
|
+
title?: string | undefined;
|
|
67
|
+
cta?: {
|
|
68
|
+
label: string;
|
|
69
|
+
action?: unknown;
|
|
70
|
+
} | undefined;
|
|
71
|
+
}, {
|
|
72
|
+
body: string;
|
|
73
|
+
title?: string | undefined;
|
|
74
|
+
cta?: {
|
|
75
|
+
label: string;
|
|
76
|
+
action?: unknown;
|
|
77
|
+
} | undefined;
|
|
78
|
+
}>;
|
|
79
|
+
trigger: z.ZodOptional<z.ZodEnum<["immediate", "hover", "click"]>>;
|
|
80
|
+
placement: z.ZodOptional<z.ZodEnum<["top", "top-start", "top-end", "bottom", "bottom-start", "bottom-end", "left", "left-start", "left-end", "right", "right-start", "right-end"]>>;
|
|
81
|
+
}, "strip", z.ZodTypeAny, {
|
|
82
|
+
anchorId: string;
|
|
83
|
+
content: {
|
|
84
|
+
body: string;
|
|
85
|
+
title?: string | undefined;
|
|
86
|
+
cta?: {
|
|
87
|
+
label: string;
|
|
88
|
+
action?: unknown;
|
|
89
|
+
} | undefined;
|
|
90
|
+
};
|
|
91
|
+
trigger?: "immediate" | "hover" | "click" | undefined;
|
|
92
|
+
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | undefined;
|
|
93
|
+
}, {
|
|
94
|
+
anchorId: string;
|
|
95
|
+
content: {
|
|
96
|
+
body: string;
|
|
97
|
+
title?: string | undefined;
|
|
98
|
+
cta?: {
|
|
99
|
+
label: string;
|
|
100
|
+
action?: unknown;
|
|
101
|
+
} | undefined;
|
|
102
|
+
};
|
|
103
|
+
trigger?: "immediate" | "hover" | "click" | undefined;
|
|
104
|
+
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | undefined;
|
|
105
|
+
}>, "many">>;
|
|
106
|
+
/** Badge configurations */
|
|
107
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
108
|
+
anchorId: z.ZodString;
|
|
109
|
+
content: z.ZodString;
|
|
110
|
+
position: z.ZodOptional<z.ZodEnum<["top-left", "top-right", "bottom-left", "bottom-right"]>>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
anchorId: string;
|
|
113
|
+
content: string;
|
|
114
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
115
|
+
}, {
|
|
116
|
+
anchorId: string;
|
|
117
|
+
content: string;
|
|
118
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
119
|
+
}>, "many">>;
|
|
120
|
+
/** Pulse configurations */
|
|
121
|
+
pulses: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
122
|
+
anchorId: z.ZodString;
|
|
123
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
124
|
+
}, "strip", z.ZodTypeAny, {
|
|
125
|
+
anchorId: string;
|
|
126
|
+
duration?: number | undefined;
|
|
127
|
+
}, {
|
|
128
|
+
anchorId: string;
|
|
129
|
+
duration?: number | undefined;
|
|
130
|
+
}>, "many">>;
|
|
131
|
+
}, "strip", z.ZodTypeAny, {
|
|
132
|
+
highlights?: {
|
|
133
|
+
anchorId: string;
|
|
134
|
+
style?: {
|
|
135
|
+
color?: string | undefined;
|
|
136
|
+
paddingPx?: number | undefined;
|
|
137
|
+
radiusPx?: number | undefined;
|
|
138
|
+
scrimOpacity?: number | undefined;
|
|
139
|
+
} | undefined;
|
|
140
|
+
}[] | undefined;
|
|
141
|
+
tooltips?: {
|
|
142
|
+
anchorId: string;
|
|
143
|
+
content: {
|
|
144
|
+
body: string;
|
|
145
|
+
title?: string | undefined;
|
|
146
|
+
cta?: {
|
|
147
|
+
label: string;
|
|
148
|
+
action?: unknown;
|
|
149
|
+
} | undefined;
|
|
150
|
+
};
|
|
151
|
+
trigger?: "immediate" | "hover" | "click" | undefined;
|
|
152
|
+
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | undefined;
|
|
153
|
+
}[] | undefined;
|
|
154
|
+
badges?: {
|
|
155
|
+
anchorId: string;
|
|
156
|
+
content: string;
|
|
157
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
158
|
+
}[] | undefined;
|
|
159
|
+
pulses?: {
|
|
160
|
+
anchorId: string;
|
|
161
|
+
duration?: number | undefined;
|
|
162
|
+
}[] | undefined;
|
|
163
|
+
}, {
|
|
164
|
+
highlights?: {
|
|
165
|
+
anchorId: string;
|
|
166
|
+
style?: {
|
|
167
|
+
color?: string | undefined;
|
|
168
|
+
paddingPx?: number | undefined;
|
|
169
|
+
radiusPx?: number | undefined;
|
|
170
|
+
scrimOpacity?: number | undefined;
|
|
171
|
+
} | undefined;
|
|
172
|
+
}[] | undefined;
|
|
173
|
+
tooltips?: {
|
|
174
|
+
anchorId: string;
|
|
175
|
+
content: {
|
|
176
|
+
body: string;
|
|
177
|
+
title?: string | undefined;
|
|
178
|
+
cta?: {
|
|
179
|
+
label: string;
|
|
180
|
+
action?: unknown;
|
|
181
|
+
} | undefined;
|
|
182
|
+
};
|
|
183
|
+
trigger?: "immediate" | "hover" | "click" | undefined;
|
|
184
|
+
placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end" | undefined;
|
|
185
|
+
}[] | undefined;
|
|
186
|
+
badges?: {
|
|
187
|
+
anchorId: string;
|
|
188
|
+
content: string;
|
|
189
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | undefined;
|
|
190
|
+
}[] | undefined;
|
|
191
|
+
pulses?: {
|
|
192
|
+
anchorId: string;
|
|
193
|
+
duration?: number | undefined;
|
|
194
|
+
}[] | undefined;
|
|
195
|
+
}>;
|
|
196
|
+
export type OverlaysConfig = z.infer<typeof configSchema>;
|
|
197
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiB/B,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoC7B,2BAA2B;;;;;;;;;;;;;;IAW3B,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS3B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Overlays - Config Schema
|
|
3
|
+
*
|
|
4
|
+
* Zod schema for validating overlays app configuration.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* Overlays app config schema.
|
|
9
|
+
* Defines the structure for overlay configurations in a canvas config.
|
|
10
|
+
*/
|
|
11
|
+
export const configSchema = z.object({
|
|
12
|
+
/** Highlight configurations */
|
|
13
|
+
highlights: z
|
|
14
|
+
.array(z.object({
|
|
15
|
+
anchorId: z.string(),
|
|
16
|
+
style: z
|
|
17
|
+
.object({
|
|
18
|
+
color: z.string().optional(),
|
|
19
|
+
paddingPx: z.number().optional(),
|
|
20
|
+
radiusPx: z.number().optional(),
|
|
21
|
+
scrimOpacity: z.number().optional(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
}))
|
|
25
|
+
.optional(),
|
|
26
|
+
/** Tooltip configurations */
|
|
27
|
+
tooltips: z
|
|
28
|
+
.array(z.object({
|
|
29
|
+
anchorId: z.string(),
|
|
30
|
+
content: z.object({
|
|
31
|
+
title: z.string().optional(),
|
|
32
|
+
body: z.string(),
|
|
33
|
+
cta: z
|
|
34
|
+
.object({
|
|
35
|
+
label: z.string(),
|
|
36
|
+
action: z.unknown(), // ActionStep - validated separately
|
|
37
|
+
})
|
|
38
|
+
.optional(),
|
|
39
|
+
}),
|
|
40
|
+
trigger: z.enum(['immediate', 'hover', 'click']).optional(),
|
|
41
|
+
placement: z
|
|
42
|
+
.enum([
|
|
43
|
+
'top',
|
|
44
|
+
'top-start',
|
|
45
|
+
'top-end',
|
|
46
|
+
'bottom',
|
|
47
|
+
'bottom-start',
|
|
48
|
+
'bottom-end',
|
|
49
|
+
'left',
|
|
50
|
+
'left-start',
|
|
51
|
+
'left-end',
|
|
52
|
+
'right',
|
|
53
|
+
'right-start',
|
|
54
|
+
'right-end',
|
|
55
|
+
])
|
|
56
|
+
.optional(),
|
|
57
|
+
}))
|
|
58
|
+
.optional(),
|
|
59
|
+
/** Badge configurations */
|
|
60
|
+
badges: z
|
|
61
|
+
.array(z.object({
|
|
62
|
+
anchorId: z.string(),
|
|
63
|
+
content: z.string(),
|
|
64
|
+
position: z.enum(['top-left', 'top-right', 'bottom-left', 'bottom-right']).optional(),
|
|
65
|
+
}))
|
|
66
|
+
.optional(),
|
|
67
|
+
/** Pulse configurations */
|
|
68
|
+
pulses: z
|
|
69
|
+
.array(z.object({
|
|
70
|
+
anchorId: z.string(),
|
|
71
|
+
duration: z.number().optional(),
|
|
72
|
+
}))
|
|
73
|
+
.optional(),
|
|
74
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tooltip Overlay
|
|
3
|
+
*
|
|
4
|
+
* Creates a positioned tooltip near an element using Floating UI.
|
|
5
|
+
*/
|
|
6
|
+
import { type Placement } from '@floating-ui/dom';
|
|
7
|
+
export type TooltipHandle = {
|
|
8
|
+
destroy(): void;
|
|
9
|
+
el: HTMLElement;
|
|
10
|
+
};
|
|
11
|
+
export interface TooltipOptions {
|
|
12
|
+
html: string;
|
|
13
|
+
placement?: Placement | 'auto';
|
|
14
|
+
offsetPx?: number;
|
|
15
|
+
blocking?: boolean;
|
|
16
|
+
trigger?: 'immediate' | 'hover' | 'click';
|
|
17
|
+
onAction?: (actionId: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function showTooltip(anchorEl: HTMLElement, overlayRoot: HTMLElement, opts: TooltipOptions): TooltipHandle;
|
|
20
|
+
//# sourceMappingURL=tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EASL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAC;AAG1B,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,IAAI,IAAI,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAyCD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,cAAc,GACnB,aAAa,CAkNf"}
|