@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/editor.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Overlays - Editor Module
|
|
3
|
+
*
|
|
4
|
+
* Editor panel for configuring visual overlays.
|
|
5
|
+
*/
|
|
6
|
+
import type { EditorPanelProps } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Overlays editor panel component.
|
|
9
|
+
*/
|
|
10
|
+
export declare function OverlaysEditor({ config: _config, onChange: _onChange, editor: _editor, }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/**
|
|
12
|
+
* Editor module configuration.
|
|
13
|
+
*/
|
|
14
|
+
export declare const editor: {
|
|
15
|
+
panel: {
|
|
16
|
+
title: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
component: typeof OverlaysEditor;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,OAAO,GAChB,EAAE,gBAAgB,2CAOlB;AAED;;GAEG;AACH,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAC"}
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Overlays editor panel component.
|
|
4
|
+
*/
|
|
5
|
+
export function OverlaysEditor({ config: _config, onChange: _onChange, editor: _editor, }) {
|
|
6
|
+
return (_jsxs("div", { className: "syntro-overlays-editor", children: [_jsx("p", { children: "Overlays editor coming soon..." }), _jsx("p", { children: "Configure tooltips, highlights, badges, and pulse effects." })] }));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Editor module configuration.
|
|
10
|
+
*/
|
|
11
|
+
export const editor = {
|
|
12
|
+
panel: {
|
|
13
|
+
title: 'Overlays',
|
|
14
|
+
icon: '💬',
|
|
15
|
+
description: 'Tooltips and visual highlights',
|
|
16
|
+
},
|
|
17
|
+
component: OverlaysEditor,
|
|
18
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Highlight Overlay
|
|
3
|
+
*
|
|
4
|
+
* Creates a spotlight effect around an element with a scrim backdrop.
|
|
5
|
+
*/
|
|
6
|
+
export type HighlightHandle = {
|
|
7
|
+
destroy(): void;
|
|
8
|
+
};
|
|
9
|
+
export interface HighlightOptions {
|
|
10
|
+
paddingPx?: number;
|
|
11
|
+
radiusPx?: number;
|
|
12
|
+
scrimOpacity?: number;
|
|
13
|
+
ringColor?: string;
|
|
14
|
+
blocking?: boolean;
|
|
15
|
+
onClickOutside?: boolean;
|
|
16
|
+
onEsc?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function showHighlight(anchorEl: HTMLElement, overlayRoot: HTMLElement, opts?: HighlightOptions): HighlightHandle;
|
|
19
|
+
//# sourceMappingURL=highlight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE,gBAAgB,GACtB,eAAe,CAkKjB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Highlight Overlay
|
|
3
|
+
*
|
|
4
|
+
* Creates a spotlight effect around an element with a scrim backdrop.
|
|
5
|
+
*/
|
|
6
|
+
const supportsPathClip = typeof CSS !== 'undefined' && CSS.supports?.('clip-path', "path('M0 0 H1 V1 Z')");
|
|
7
|
+
export function showHighlight(anchorEl, overlayRoot, opts) {
|
|
8
|
+
const padding = opts?.paddingPx ?? 12;
|
|
9
|
+
const radius = opts?.radiusPx ?? 12;
|
|
10
|
+
const opacity = Math.min(Math.max(opts?.scrimOpacity ?? 0.55, 0), 1);
|
|
11
|
+
const ringColor = opts?.ringColor ?? 'var(--syntro-ring, #5b8cff)';
|
|
12
|
+
const blocking = opts?.blocking ?? false;
|
|
13
|
+
const onClickOutside = opts?.onClickOutside ?? true;
|
|
14
|
+
const onEsc = opts?.onEsc ?? true;
|
|
15
|
+
const rootStyles = getComputedStyle(document.documentElement);
|
|
16
|
+
const tokenScrim = rootStyles.getPropertyValue('--syntro-spotlight-backdrop').trim();
|
|
17
|
+
const tokenRing = rootStyles.getPropertyValue('--syntro-ring').trim();
|
|
18
|
+
const scrim = document.createElement('div');
|
|
19
|
+
scrim.className = 'syntro-spotlight-scrim';
|
|
20
|
+
const needsPointerEvents = blocking || onClickOutside;
|
|
21
|
+
Object.assign(scrim.style, {
|
|
22
|
+
position: 'fixed',
|
|
23
|
+
inset: '0',
|
|
24
|
+
zIndex: '2147483646',
|
|
25
|
+
pointerEvents: needsPointerEvents ? 'auto' : 'none',
|
|
26
|
+
background: tokenScrim || `rgba(2, 6, 23, ${opacity})`,
|
|
27
|
+
transition: 'opacity 220ms ease',
|
|
28
|
+
opacity: '0',
|
|
29
|
+
});
|
|
30
|
+
overlayRoot.appendChild(scrim);
|
|
31
|
+
requestAnimationFrame(() => (scrim.style.opacity = '1'));
|
|
32
|
+
const ring = document.createElement('div');
|
|
33
|
+
ring.className = 'syntro-spotlight-ring';
|
|
34
|
+
Object.assign(ring.style, {
|
|
35
|
+
position: 'fixed',
|
|
36
|
+
pointerEvents: 'none',
|
|
37
|
+
borderRadius: `${radius}px`,
|
|
38
|
+
border: `2px solid ${ringColor || tokenRing || '#5b8cff'}`,
|
|
39
|
+
boxShadow: `0 0 0 4px rgba(255,255,255,0.35)`,
|
|
40
|
+
zIndex: '2147483647',
|
|
41
|
+
transition: 'all 220ms cubic-bezier(0.16,1,0.3,1)',
|
|
42
|
+
});
|
|
43
|
+
overlayRoot.appendChild(ring);
|
|
44
|
+
const fallbackSlices = [];
|
|
45
|
+
if (!supportsPathClip) {
|
|
46
|
+
for (let i = 0; i < 4; i++) {
|
|
47
|
+
const slice = document.createElement('div');
|
|
48
|
+
slice.style.position = 'fixed';
|
|
49
|
+
slice.style.background = 'inherit';
|
|
50
|
+
fallbackSlices.push(slice);
|
|
51
|
+
scrim.appendChild(slice);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const setClipPath = (path) => {
|
|
55
|
+
scrim.style.clipPath = path;
|
|
56
|
+
scrim.style.webkitClipPath = path;
|
|
57
|
+
};
|
|
58
|
+
const update = () => {
|
|
59
|
+
const rect = anchorEl.getBoundingClientRect();
|
|
60
|
+
const x = Math.max(0, rect.left - padding);
|
|
61
|
+
const y = Math.max(0, rect.top - padding);
|
|
62
|
+
const w = Math.min(window.innerWidth, rect.width + padding * 2);
|
|
63
|
+
const h = Math.min(window.innerHeight, rect.height + padding * 2);
|
|
64
|
+
Object.assign(ring.style, {
|
|
65
|
+
left: `${x}px`,
|
|
66
|
+
top: `${y}px`,
|
|
67
|
+
width: `${w}px`,
|
|
68
|
+
height: `${h}px`,
|
|
69
|
+
});
|
|
70
|
+
if (supportsPathClip) {
|
|
71
|
+
const vw = window.innerWidth;
|
|
72
|
+
const vh = window.innerHeight;
|
|
73
|
+
const r = Math.min(radius, w / 2, h / 2);
|
|
74
|
+
const outer = `M 0 0 L ${vw} 0 L ${vw} ${vh} L 0 ${vh} Z`;
|
|
75
|
+
const inner = `M ${x + r} ${y} ` +
|
|
76
|
+
`A ${r} ${r} 0 0 0 ${x} ${y + r} ` +
|
|
77
|
+
`L ${x} ${y + h - r} ` +
|
|
78
|
+
`A ${r} ${r} 0 0 0 ${x + r} ${y + h} ` +
|
|
79
|
+
`L ${x + w - r} ${y + h} ` +
|
|
80
|
+
`A ${r} ${r} 0 0 0 ${x + w} ${y + h - r} ` +
|
|
81
|
+
`L ${x + w} ${y + r} ` +
|
|
82
|
+
`A ${r} ${r} 0 0 0 ${x + w - r} ${y} ` +
|
|
83
|
+
`L ${x + r} ${y} ` +
|
|
84
|
+
`Z`;
|
|
85
|
+
setClipPath(`path('${outer} ${inner}')`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const [top, right, bottom, left] = fallbackSlices;
|
|
89
|
+
Object.assign(top.style, {
|
|
90
|
+
left: '0px',
|
|
91
|
+
top: '0px',
|
|
92
|
+
width: '100vw',
|
|
93
|
+
height: `${y}px`,
|
|
94
|
+
});
|
|
95
|
+
Object.assign(bottom.style, {
|
|
96
|
+
left: '0px',
|
|
97
|
+
top: `${y + h}px`,
|
|
98
|
+
width: '100vw',
|
|
99
|
+
height: `${Math.max(0, window.innerHeight - (y + h))}px`,
|
|
100
|
+
});
|
|
101
|
+
Object.assign(left.style, {
|
|
102
|
+
left: '0px',
|
|
103
|
+
top: `${y}px`,
|
|
104
|
+
width: `${x}px`,
|
|
105
|
+
height: `${h}px`,
|
|
106
|
+
});
|
|
107
|
+
Object.assign(right.style, {
|
|
108
|
+
left: `${x + w}px`,
|
|
109
|
+
top: `${y}px`,
|
|
110
|
+
width: `${Math.max(0, window.innerWidth - (x + w))}px`,
|
|
111
|
+
height: `${h}px`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const ro = new ResizeObserver(() => requestAnimationFrame(update));
|
|
116
|
+
ro.observe(anchorEl);
|
|
117
|
+
const onScroll = () => requestAnimationFrame(update);
|
|
118
|
+
const onResize = () => requestAnimationFrame(update);
|
|
119
|
+
window.addEventListener('scroll', onScroll, true);
|
|
120
|
+
window.addEventListener('resize', onResize);
|
|
121
|
+
const onKey = (e) => {
|
|
122
|
+
if (e.key === 'Escape' && onEsc)
|
|
123
|
+
handle.destroy();
|
|
124
|
+
};
|
|
125
|
+
if (onEsc) {
|
|
126
|
+
window.addEventListener('keydown', onKey);
|
|
127
|
+
}
|
|
128
|
+
const onClick = (event) => {
|
|
129
|
+
if (blocking) {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
event.stopPropagation();
|
|
132
|
+
}
|
|
133
|
+
else if (onClickOutside) {
|
|
134
|
+
handle.destroy();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
scrim.addEventListener('click', onClick);
|
|
138
|
+
const handle = {
|
|
139
|
+
destroy() {
|
|
140
|
+
ro.disconnect();
|
|
141
|
+
window.removeEventListener('scroll', onScroll, true);
|
|
142
|
+
window.removeEventListener('resize', onResize);
|
|
143
|
+
if (onEsc) {
|
|
144
|
+
window.removeEventListener('keydown', onKey);
|
|
145
|
+
}
|
|
146
|
+
scrim.removeEventListener('click', onClick);
|
|
147
|
+
scrim.style.opacity = '0';
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
scrim.remove();
|
|
150
|
+
ring.remove();
|
|
151
|
+
}, 220);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
update();
|
|
155
|
+
return handle;
|
|
156
|
+
}
|
package/dist/modal.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Executor
|
|
3
|
+
*
|
|
4
|
+
* Displays a centered modal dialog with optional CTA buttons.
|
|
5
|
+
*/
|
|
6
|
+
import type { ModalAction, ActionExecutor } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* Execute a modal action
|
|
9
|
+
*/
|
|
10
|
+
export declare const executeModal: ActionExecutor<ModalAction>;
|
|
11
|
+
//# sourceMappingURL=modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAkB,cAAc,EAAE,MAAM,SAAS,CAAC;AAG3E;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAoNpD,CAAC"}
|
package/dist/modal.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Executor
|
|
3
|
+
*
|
|
4
|
+
* Displays a centered modal dialog with optional CTA buttons.
|
|
5
|
+
*/
|
|
6
|
+
import { sanitizeHtml } from './sanitizer';
|
|
7
|
+
/**
|
|
8
|
+
* Execute a modal action
|
|
9
|
+
*/
|
|
10
|
+
export const executeModal = async (action, context) => {
|
|
11
|
+
const { content, size = 'md', blocking = false, scrim, dismiss, ctaButtons } = action;
|
|
12
|
+
// Create scrim backdrop
|
|
13
|
+
const scrimEl = document.createElement('div');
|
|
14
|
+
scrimEl.className = 'syntro-modal-scrim';
|
|
15
|
+
scrimEl.style.cssText = `
|
|
16
|
+
position: fixed;
|
|
17
|
+
inset: 0;
|
|
18
|
+
background: rgba(0, 0, 0, ${scrim?.opacity ?? 0.6});
|
|
19
|
+
z-index: 2147483645;
|
|
20
|
+
opacity: 0;
|
|
21
|
+
transition: opacity 200ms ease-out;
|
|
22
|
+
`;
|
|
23
|
+
context.overlayRoot.appendChild(scrimEl);
|
|
24
|
+
// Create modal container
|
|
25
|
+
const modal = document.createElement('div');
|
|
26
|
+
modal.className = `syntro-modal syntro-modal-${size}`;
|
|
27
|
+
modal.setAttribute('role', 'dialog');
|
|
28
|
+
modal.setAttribute('aria-modal', 'true');
|
|
29
|
+
// Size-based max-widths
|
|
30
|
+
const sizeMap = { sm: '360px', md: '480px', lg: '640px' };
|
|
31
|
+
modal.style.cssText = `
|
|
32
|
+
position: fixed;
|
|
33
|
+
top: 50%;
|
|
34
|
+
left: 50%;
|
|
35
|
+
transform: translate(-50%, -50%) scale(0.95);
|
|
36
|
+
max-width: ${sizeMap[size]};
|
|
37
|
+
width: 90%;
|
|
38
|
+
background: white;
|
|
39
|
+
border-radius: 12px;
|
|
40
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
41
|
+
z-index: 2147483646;
|
|
42
|
+
opacity: 0;
|
|
43
|
+
transition: opacity 200ms ease-out, transform 200ms ease-out;
|
|
44
|
+
padding: 24px;
|
|
45
|
+
`;
|
|
46
|
+
// Build modal HTML content
|
|
47
|
+
let html = '';
|
|
48
|
+
if (content.title) {
|
|
49
|
+
html += `<h2 class="syntro-modal-title" style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: #111827;">${sanitizeHtml(content.title)}</h2>`;
|
|
50
|
+
}
|
|
51
|
+
html += `<div class="syntro-modal-body" style="color: #4b5563; line-height: 1.5;">${sanitizeHtml(content.body)}</div>`;
|
|
52
|
+
// Add close button if enabled
|
|
53
|
+
if (dismiss?.closeButton !== false) {
|
|
54
|
+
html += `
|
|
55
|
+
<button class="syntro-modal-close" data-syntro-action="dismiss" style="
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: 16px;
|
|
58
|
+
right: 16px;
|
|
59
|
+
background: none;
|
|
60
|
+
border: none;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
padding: 4px;
|
|
63
|
+
color: #6b7280;
|
|
64
|
+
" aria-label="Close">
|
|
65
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
66
|
+
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
|
67
|
+
</svg>
|
|
68
|
+
</button>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
// Add CTA buttons
|
|
72
|
+
if (ctaButtons && ctaButtons.length > 0) {
|
|
73
|
+
html += `<div class="syntro-modal-actions" style="display: flex; gap: 12px; margin-top: 24px; justify-content: flex-end;">`;
|
|
74
|
+
for (const btn of ctaButtons) {
|
|
75
|
+
const isPrimary = btn.primary ?? false;
|
|
76
|
+
html += `
|
|
77
|
+
<button
|
|
78
|
+
class="syntro-modal-btn ${isPrimary ? 'syntro-modal-btn-primary' : ''}"
|
|
79
|
+
data-syntro-action="${sanitizeHtml(btn.actionId)}"
|
|
80
|
+
style="
|
|
81
|
+
padding: 10px 20px;
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
font-size: 14px;
|
|
84
|
+
font-weight: 500;
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
transition: background 150ms ease;
|
|
87
|
+
${isPrimary
|
|
88
|
+
? 'background: #4f46e5; color: white; border: none;'
|
|
89
|
+
: 'background: white; color: #374151; border: 1px solid #d1d5db;'}
|
|
90
|
+
"
|
|
91
|
+
>
|
|
92
|
+
${sanitizeHtml(btn.label)}
|
|
93
|
+
</button>
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
html += `</div>`;
|
|
97
|
+
}
|
|
98
|
+
modal.innerHTML = html;
|
|
99
|
+
context.overlayRoot.appendChild(modal);
|
|
100
|
+
// Track state for waitFor
|
|
101
|
+
let actionClicked = null;
|
|
102
|
+
// Handle action button clicks
|
|
103
|
+
const actionBtns = modal.querySelectorAll('[data-syntro-action]');
|
|
104
|
+
const actionHandler = (e) => {
|
|
105
|
+
const btn = e.currentTarget;
|
|
106
|
+
const actionId = btn.getAttribute('data-syntro-action');
|
|
107
|
+
if (actionId) {
|
|
108
|
+
actionClicked = actionId;
|
|
109
|
+
context.publishEvent('action.modal_cta_clicked', {
|
|
110
|
+
actionId,
|
|
111
|
+
});
|
|
112
|
+
if (actionId === 'dismiss') {
|
|
113
|
+
handle.destroy();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
actionBtns.forEach((btn) => btn.addEventListener('click', actionHandler));
|
|
118
|
+
// Handle escape key
|
|
119
|
+
const onKey = (e) => {
|
|
120
|
+
if (e.key === 'Escape' && dismiss?.onEsc !== false) {
|
|
121
|
+
handle.destroy();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
window.addEventListener('keydown', onKey);
|
|
125
|
+
// Handle click outside (on scrim)
|
|
126
|
+
const onScrimClick = () => {
|
|
127
|
+
if (!blocking) {
|
|
128
|
+
handle.destroy();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
scrimEl.addEventListener('click', onScrimClick);
|
|
132
|
+
// Blocking mode - make siblings inert
|
|
133
|
+
const originalInert = [];
|
|
134
|
+
if (blocking) {
|
|
135
|
+
Array.from(document.body.children).forEach((el) => {
|
|
136
|
+
if (el !== context.overlayRoot && el.getAttribute('inert') === null) {
|
|
137
|
+
el.setAttribute('inert', '');
|
|
138
|
+
originalInert.push(el);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Set up timeout if specified
|
|
143
|
+
let timeoutId;
|
|
144
|
+
if (dismiss?.timeoutMs) {
|
|
145
|
+
timeoutId = setTimeout(() => {
|
|
146
|
+
handle.destroy();
|
|
147
|
+
}, dismiss.timeoutMs);
|
|
148
|
+
}
|
|
149
|
+
// Focus first focusable element
|
|
150
|
+
const focusableEls = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
151
|
+
if (focusableEls.length > 0) {
|
|
152
|
+
requestAnimationFrame(() => focusableEls[0].focus());
|
|
153
|
+
}
|
|
154
|
+
// Fade in
|
|
155
|
+
requestAnimationFrame(() => {
|
|
156
|
+
scrimEl.style.opacity = '1';
|
|
157
|
+
modal.style.opacity = '1';
|
|
158
|
+
modal.style.transform = 'translate(-50%, -50%) scale(1)';
|
|
159
|
+
});
|
|
160
|
+
context.publishEvent('action.applied', {
|
|
161
|
+
id: context.generateId(),
|
|
162
|
+
kind: 'overlays:modal',
|
|
163
|
+
size,
|
|
164
|
+
blocking,
|
|
165
|
+
});
|
|
166
|
+
const handle = {
|
|
167
|
+
destroy() {
|
|
168
|
+
if (timeoutId) {
|
|
169
|
+
clearTimeout(timeoutId);
|
|
170
|
+
}
|
|
171
|
+
window.removeEventListener('keydown', onKey);
|
|
172
|
+
scrimEl.removeEventListener('click', onScrimClick);
|
|
173
|
+
actionBtns.forEach((btn) => btn.removeEventListener('click', actionHandler));
|
|
174
|
+
// Restore inert state
|
|
175
|
+
originalInert.forEach((el) => el.removeAttribute('inert'));
|
|
176
|
+
// Fade out then remove
|
|
177
|
+
modal.style.opacity = '0';
|
|
178
|
+
modal.style.transform = 'translate(-50%, -50%) scale(0.95)';
|
|
179
|
+
scrimEl.style.opacity = '0';
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
modal.remove();
|
|
182
|
+
scrimEl.remove();
|
|
183
|
+
}, 200);
|
|
184
|
+
context.publishEvent('action.modal_dismissed', {
|
|
185
|
+
actionClicked,
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
cleanup: () => {
|
|
191
|
+
handle.destroy();
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Overlays - Runtime Module
|
|
3
|
+
*
|
|
4
|
+
* Visual overlay actions: highlight, pulse, badge, tooltip.
|
|
5
|
+
*/
|
|
6
|
+
import type { HighlightAction, PulseAction, BadgeAction, TooltipAction, ActionExecutor } from './types';
|
|
7
|
+
import { executeModal } from './modal';
|
|
8
|
+
export { executeModal };
|
|
9
|
+
/**
|
|
10
|
+
* Execute a highlight action
|
|
11
|
+
*/
|
|
12
|
+
export declare const executeHighlight: ActionExecutor<HighlightAction>;
|
|
13
|
+
/**
|
|
14
|
+
* Execute a pulse action
|
|
15
|
+
*/
|
|
16
|
+
export declare const executePulse: ActionExecutor<PulseAction>;
|
|
17
|
+
/**
|
|
18
|
+
* Execute a badge action
|
|
19
|
+
*/
|
|
20
|
+
export declare const executeBadge: ActionExecutor<BadgeAction>;
|
|
21
|
+
/**
|
|
22
|
+
* Execute a tooltip action
|
|
23
|
+
*/
|
|
24
|
+
export declare const executeTooltip: ActionExecutor<TooltipAction>;
|
|
25
|
+
/**
|
|
26
|
+
* All executors provided by this app.
|
|
27
|
+
* These are registered with the runtime's ExecutorRegistry.
|
|
28
|
+
*/
|
|
29
|
+
export declare const executors: readonly [{
|
|
30
|
+
readonly kind: "overlays:highlight";
|
|
31
|
+
readonly executor: ActionExecutor<HighlightAction>;
|
|
32
|
+
}, {
|
|
33
|
+
readonly kind: "overlays:pulse";
|
|
34
|
+
readonly executor: ActionExecutor<PulseAction>;
|
|
35
|
+
}, {
|
|
36
|
+
readonly kind: "overlays:badge";
|
|
37
|
+
readonly executor: ActionExecutor<BadgeAction>;
|
|
38
|
+
}, {
|
|
39
|
+
readonly kind: "overlays:tooltip";
|
|
40
|
+
readonly executor: ActionExecutor<TooltipAction>;
|
|
41
|
+
}, {
|
|
42
|
+
readonly kind: "overlays:modal";
|
|
43
|
+
readonly executor: ActionExecutor<import("./types").ModalAction>;
|
|
44
|
+
}];
|
|
45
|
+
/**
|
|
46
|
+
* App runtime manifest.
|
|
47
|
+
*/
|
|
48
|
+
export declare const runtime: {
|
|
49
|
+
id: string;
|
|
50
|
+
version: string;
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
executors: readonly [{
|
|
54
|
+
readonly kind: "overlays:highlight";
|
|
55
|
+
readonly executor: ActionExecutor<HighlightAction>;
|
|
56
|
+
}, {
|
|
57
|
+
readonly kind: "overlays:pulse";
|
|
58
|
+
readonly executor: ActionExecutor<PulseAction>;
|
|
59
|
+
}, {
|
|
60
|
+
readonly kind: "overlays:badge";
|
|
61
|
+
readonly executor: ActionExecutor<BadgeAction>;
|
|
62
|
+
}, {
|
|
63
|
+
readonly kind: "overlays:tooltip";
|
|
64
|
+
readonly executor: ActionExecutor<TooltipAction>;
|
|
65
|
+
}, {
|
|
66
|
+
readonly kind: "overlays:modal";
|
|
67
|
+
readonly executor: ActionExecutor<import("./types").ModalAction>;
|
|
68
|
+
}];
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,WAAW,EACX,aAAa,EAEb,cAAc,EACf,MAAM,SAAS,CAAC;AAIjB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,YAAY,EAAE,CAAC;AAMxB;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,eAAe,CA8B5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAqDpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,WAAW,CAsFpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA4ExD,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;EAMZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;CAMnB,CAAC"}
|