@sc4rfurryx/proteusjs 1.0.0 → 1.1.1

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 (65) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +331 -77
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/adapters/react.d.ts +140 -0
  5. package/dist/adapters/react.esm.js +849 -0
  6. package/dist/adapters/react.esm.js.map +1 -0
  7. package/dist/adapters/svelte.d.ts +181 -0
  8. package/dist/adapters/svelte.esm.js +909 -0
  9. package/dist/adapters/svelte.esm.js.map +1 -0
  10. package/dist/adapters/vue.d.ts +205 -0
  11. package/dist/adapters/vue.esm.js +873 -0
  12. package/dist/adapters/vue.esm.js.map +1 -0
  13. package/dist/modules/a11y-audit.d.ts +31 -0
  14. package/dist/modules/a11y-audit.esm.js +64 -0
  15. package/dist/modules/a11y-audit.esm.js.map +1 -0
  16. package/dist/modules/a11y-primitives.d.ts +36 -0
  17. package/dist/modules/a11y-primitives.esm.js +114 -0
  18. package/dist/modules/a11y-primitives.esm.js.map +1 -0
  19. package/dist/modules/anchor.d.ts +30 -0
  20. package/dist/modules/anchor.esm.js +219 -0
  21. package/dist/modules/anchor.esm.js.map +1 -0
  22. package/dist/modules/container.d.ts +60 -0
  23. package/dist/modules/container.esm.js +194 -0
  24. package/dist/modules/container.esm.js.map +1 -0
  25. package/dist/modules/perf.d.ts +82 -0
  26. package/dist/modules/perf.esm.js +257 -0
  27. package/dist/modules/perf.esm.js.map +1 -0
  28. package/dist/modules/popover.d.ts +33 -0
  29. package/dist/modules/popover.esm.js +191 -0
  30. package/dist/modules/popover.esm.js.map +1 -0
  31. package/dist/modules/scroll.d.ts +43 -0
  32. package/dist/modules/scroll.esm.js +195 -0
  33. package/dist/modules/scroll.esm.js.map +1 -0
  34. package/dist/modules/transitions.d.ts +35 -0
  35. package/dist/modules/transitions.esm.js +120 -0
  36. package/dist/modules/transitions.esm.js.map +1 -0
  37. package/dist/modules/typography.d.ts +72 -0
  38. package/dist/modules/typography.esm.js +168 -0
  39. package/dist/modules/typography.esm.js.map +1 -0
  40. package/dist/proteus.cjs.js +1554 -12
  41. package/dist/proteus.cjs.js.map +1 -1
  42. package/dist/proteus.d.ts +516 -12
  43. package/dist/proteus.esm.js +1545 -12
  44. package/dist/proteus.esm.js.map +1 -1
  45. package/dist/proteus.esm.min.js +3 -3
  46. package/dist/proteus.esm.min.js.map +1 -1
  47. package/dist/proteus.js +1554 -12
  48. package/dist/proteus.js.map +1 -1
  49. package/dist/proteus.min.js +3 -3
  50. package/dist/proteus.min.js.map +1 -1
  51. package/package.json +69 -7
  52. package/src/adapters/react.ts +264 -0
  53. package/src/adapters/svelte.ts +321 -0
  54. package/src/adapters/vue.ts +268 -0
  55. package/src/index.ts +33 -6
  56. package/src/modules/a11y-audit/index.ts +84 -0
  57. package/src/modules/a11y-primitives/index.ts +152 -0
  58. package/src/modules/anchor/index.ts +259 -0
  59. package/src/modules/container/index.ts +230 -0
  60. package/src/modules/perf/index.ts +291 -0
  61. package/src/modules/popover/index.ts +238 -0
  62. package/src/modules/scroll/index.ts +251 -0
  63. package/src/modules/transitions/index.ts +145 -0
  64. package/src/modules/typography/index.ts +239 -0
  65. package/src/utils/version.ts +1 -1
package/package.json CHANGED
@@ -1,11 +1,67 @@
1
1
  {
2
2
  "name": "@sc4rfurryx/proteusjs",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "description": "The Modern Web Development Framework for Accessible, Responsive, and High-Performance Applications. Intelligent container queries, fluid typography, WCAG compliance, and performance optimization.",
6
6
  "main": "dist/proteus.js",
7
7
  "module": "dist/proteus.esm.js",
8
8
  "types": "dist/proteus.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/proteus.d.ts",
12
+ "import": "./dist/proteus.esm.js",
13
+ "require": "./dist/proteus.cjs.js"
14
+ },
15
+ "./transitions": {
16
+ "types": "./dist/modules/transitions.d.ts",
17
+ "import": "./dist/modules/transitions.esm.js"
18
+ },
19
+ "./scroll": {
20
+ "types": "./dist/modules/scroll.d.ts",
21
+ "import": "./dist/modules/scroll.esm.js"
22
+ },
23
+ "./anchor": {
24
+ "types": "./dist/modules/anchor.d.ts",
25
+ "import": "./dist/modules/anchor.esm.js"
26
+ },
27
+ "./popover": {
28
+ "types": "./dist/modules/popover.d.ts",
29
+ "import": "./dist/modules/popover.esm.js"
30
+ },
31
+ "./container": {
32
+ "types": "./dist/modules/container.d.ts",
33
+ "import": "./dist/modules/container.esm.js"
34
+ },
35
+ "./typography": {
36
+ "types": "./dist/modules/typography.d.ts",
37
+ "import": "./dist/modules/typography.esm.js"
38
+ },
39
+ "./a11y-audit": {
40
+ "types": "./dist/modules/a11y-audit.d.ts",
41
+ "import": "./dist/modules/a11y-audit.esm.js"
42
+ },
43
+ "./a11y-primitives": {
44
+ "types": "./dist/modules/a11y-primitives.d.ts",
45
+ "import": "./dist/modules/a11y-primitives.esm.js"
46
+ },
47
+ "./perf": {
48
+ "types": "./dist/modules/perf.d.ts",
49
+ "import": "./dist/modules/perf.esm.js"
50
+ },
51
+ "./adapters/react": {
52
+ "types": "./dist/adapters/react.d.ts",
53
+ "import": "./dist/adapters/react.esm.js"
54
+ },
55
+ "./adapters/vue": {
56
+ "types": "./dist/adapters/vue.d.ts",
57
+ "import": "./dist/adapters/vue.esm.js"
58
+ },
59
+ "./adapters/svelte": {
60
+ "types": "./dist/adapters/svelte.d.ts",
61
+ "import": "./dist/adapters/svelte.esm.js"
62
+ }
63
+ },
64
+ "sideEffects": false,
9
65
  "files": [
10
66
  "dist",
11
67
  "src",
@@ -27,13 +83,19 @@
27
83
  "lint": "eslint src --ext .ts,.tsx",
28
84
  "lint:fix": "eslint src --ext .ts,.tsx --fix",
29
85
  "typecheck": "tsc --noEmit",
86
+ "typecheck:adapters": "tsc --noEmit -p tsconfig.adapters.json",
30
87
  "validate": "npm run typecheck && npm run lint",
31
88
  "size-check": "echo 'Bundle size: ~50KB gzipped'",
32
89
  "benchmark": "vitest run tests/benchmarks/performance-benchmark.ts",
33
90
  "accessibility": "vitest run tests/validation/accessibility-validation.test.ts",
34
91
  "docs:build": "typedoc src/index.ts",
35
- "prepublishOnly": "npm run validate && npm run build:prod",
36
- "release": "npm run validate && npm run build:prod && npm publish"
92
+ "prepublishOnly": "npm run build:prod",
93
+ "release": "npm run validate && npm run build:prod && npm publish",
94
+ "release:patch": "node scripts/release.js patch",
95
+ "release:minor": "node scripts/release.js minor",
96
+ "release:major": "node scripts/release.js major",
97
+ "release:dry": "node scripts/release.js patch --dry-run",
98
+ "publish:dev": "node scripts/publish-dev.js"
37
99
  },
38
100
  "keywords": [
39
101
  "responsive",
@@ -71,8 +133,8 @@
71
133
  "@types/node": "^22.0.0",
72
134
  "@typescript-eslint/eslint-plugin": "^8.0.0",
73
135
  "@typescript-eslint/parser": "^8.0.0",
74
- "@vitest/coverage-v8": "^2.0.0",
75
- "@vitest/ui": "^2.0.0",
136
+ "@vitest/coverage-v8": "^3.2.4",
137
+ "@vitest/ui": "^3.2.4",
76
138
  "cross-env": "^7.0.3",
77
139
  "eslint": "^9.0.0",
78
140
  "globals": "^16.3.0",
@@ -81,7 +143,7 @@
81
143
  "rollup-plugin-analyzer": "^4.0.0",
82
144
  "rollup-plugin-dts": "^6.1.1",
83
145
  "typescript": "^5.5.0",
84
- "vitest": "^2.0.0"
146
+ "vitest": "^3.2.4"
85
147
  },
86
148
  "engines": {
87
149
  "node": ">=16.0.0"
@@ -95,4 +157,4 @@
95
157
  "dependencies": {
96
158
  "tslib": "^2.8.1"
97
159
  }
98
- }
160
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * @sc4rfurryx/proteusjs/adapters/react
3
+ * React hooks and components for ProteusJS
4
+ *
5
+ * @version 1.1.0
6
+ * @author sc4rfurry
7
+ * @license MIT
8
+ */
9
+
10
+ import React, { useEffect, useRef, useCallback, RefObject, createElement } from 'react';
11
+ import { transition, TransitionOptions } from '../modules/transitions';
12
+ import { scrollAnimate, ScrollAnimateOptions } from '../modules/scroll';
13
+ import { attach as attachPopover, PopoverOptions, PopoverController } from '../modules/popover';
14
+ import { tether, TetherOptions, TetherController } from '../modules/anchor';
15
+ import { defineContainer, ContainerOptions } from '../modules/container';
16
+
17
+ /**
18
+ * Hook for view transitions
19
+ */
20
+ export function useTransition() {
21
+ return useCallback(async (
22
+ run: () => Promise<any> | any,
23
+ opts?: TransitionOptions
24
+ ) => {
25
+ return transition(run, opts);
26
+ }, []);
27
+ }
28
+
29
+ /**
30
+ * Hook for scroll-driven animations
31
+ */
32
+ export function useScrollAnimate(
33
+ ref: RefObject<HTMLElement>,
34
+ opts: ScrollAnimateOptions
35
+ ) {
36
+ useEffect(() => {
37
+ if (!ref.current) return;
38
+
39
+ scrollAnimate(ref.current, opts);
40
+
41
+ return () => {
42
+ // Cleanup would be handled by the scroll module
43
+ };
44
+ }, [ref, opts]);
45
+ }
46
+
47
+ /**
48
+ * Hook for popover functionality
49
+ */
50
+ export function usePopover(
51
+ triggerRef: RefObject<HTMLElement>,
52
+ panelRef: RefObject<HTMLElement>,
53
+ opts?: PopoverOptions
54
+ ): PopoverController | null {
55
+ const controllerRef = useRef<PopoverController | null>(null);
56
+
57
+ useEffect(() => {
58
+ if (!triggerRef.current || !panelRef.current) return;
59
+
60
+ controllerRef.current = attachPopover(triggerRef.current, panelRef.current, opts);
61
+
62
+ return () => {
63
+ if (controllerRef.current) {
64
+ controllerRef.current.destroy();
65
+ controllerRef.current = null;
66
+ }
67
+ };
68
+ }, [triggerRef, panelRef, opts]);
69
+
70
+ return controllerRef.current;
71
+ }
72
+
73
+ /**
74
+ * Hook for anchor positioning
75
+ */
76
+ export function useAnchor(
77
+ floatingRef: RefObject<HTMLElement>,
78
+ anchorRef: RefObject<HTMLElement>,
79
+ opts?: Omit<TetherOptions, 'anchor'>
80
+ ): TetherController | null {
81
+ const controllerRef = useRef<TetherController | null>(null);
82
+
83
+ useEffect(() => {
84
+ if (!floatingRef.current || !anchorRef.current) return;
85
+
86
+ controllerRef.current = tether(floatingRef.current, {
87
+ anchor: anchorRef.current,
88
+ ...opts
89
+ });
90
+
91
+ return () => {
92
+ if (controllerRef.current) {
93
+ controllerRef.current.destroy();
94
+ controllerRef.current = null;
95
+ }
96
+ };
97
+ }, [floatingRef, anchorRef, opts]);
98
+
99
+ return controllerRef.current;
100
+ }
101
+
102
+ /**
103
+ * Hook for container queries
104
+ */
105
+ export function useContainer(
106
+ ref: RefObject<HTMLElement>,
107
+ name?: string,
108
+ opts?: ContainerOptions
109
+ ) {
110
+ useEffect(() => {
111
+ if (!ref.current) return;
112
+
113
+ defineContainer(ref.current, name, opts);
114
+ }, [ref, name, opts]);
115
+ }
116
+
117
+ /**
118
+ * Hook for performance optimizations
119
+ */
120
+ export function usePerformance(ref: RefObject<HTMLElement>) {
121
+ useEffect(() => {
122
+ if (!ref.current) return;
123
+
124
+ // Apply basic performance optimizations
125
+ const element = ref.current;
126
+
127
+ // Content visibility for off-screen content
128
+ const observer = new IntersectionObserver(
129
+ (entries) => {
130
+ entries.forEach(entry => {
131
+ if (entry.isIntersecting) {
132
+ element.style.contentVisibility = 'visible';
133
+ } else {
134
+ element.style.contentVisibility = 'auto';
135
+ }
136
+ });
137
+ },
138
+ { rootMargin: '50px' }
139
+ );
140
+
141
+ observer.observe(element);
142
+
143
+ return () => {
144
+ observer.disconnect();
145
+ };
146
+ }, [ref]);
147
+ }
148
+
149
+ /**
150
+ * Hook for accessibility features
151
+ */
152
+ export function useA11y(ref: RefObject<HTMLElement>, options: {
153
+ announceChanges?: boolean;
154
+ focusManagement?: boolean;
155
+ } = {}): void {
156
+ const { announceChanges = false, focusManagement = true } = options;
157
+
158
+ useEffect(() => {
159
+ if (!ref.current) return;
160
+
161
+ const element = ref.current;
162
+
163
+ // Basic accessibility enhancements
164
+ if (focusManagement) {
165
+ // Ensure focusable elements have visible focus indicators
166
+ const focusableElements = element.querySelectorAll(
167
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
168
+ );
169
+
170
+ focusableElements.forEach((el: Element) => {
171
+ const htmlEl = el as HTMLElement;
172
+ if (!htmlEl.style.outline && !getComputedStyle(htmlEl).outline) {
173
+ htmlEl.style.outline = '2px solid transparent';
174
+ htmlEl.style.outlineOffset = '2px';
175
+
176
+ htmlEl.addEventListener('focus', () => {
177
+ htmlEl.style.outline = '2px solid #0066cc';
178
+ });
179
+
180
+ htmlEl.addEventListener('blur', () => {
181
+ htmlEl.style.outline = '2px solid transparent';
182
+ });
183
+ }
184
+ });
185
+ }
186
+
187
+ if (announceChanges) {
188
+ // Set up mutation observer for announcing changes
189
+ const observer = new MutationObserver((mutations) => {
190
+ mutations.forEach(mutation => {
191
+ if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
192
+ // Announce significant changes to screen readers
193
+ const announcement = document.createElement('div');
194
+ announcement.setAttribute('aria-live', 'polite');
195
+ announcement.setAttribute('aria-atomic', 'true');
196
+ announcement.style.position = 'absolute';
197
+ announcement.style.left = '-10000px';
198
+ announcement.textContent = 'Content updated';
199
+ document.body.appendChild(announcement);
200
+
201
+ setTimeout(() => {
202
+ document.body.removeChild(announcement);
203
+ }, 1000);
204
+ }
205
+ });
206
+ });
207
+
208
+ observer.observe(element, {
209
+ childList: true,
210
+ subtree: true
211
+ });
212
+
213
+ return () => {
214
+ observer.disconnect();
215
+ };
216
+ }
217
+ }, [ref, announceChanges, focusManagement]);
218
+ }
219
+
220
+ /**
221
+ * Higher-order component for adding ProteusJS features
222
+ */
223
+ export function withProteus<P extends object>(
224
+ Component: React.ComponentType<P>,
225
+ features: {
226
+ container?: { name?: string; options?: ContainerOptions };
227
+ performance?: boolean;
228
+ accessibility?: boolean;
229
+ } = {}
230
+ ) {
231
+ return function ProteusEnhanced(props: P) {
232
+ const ref = useRef<HTMLDivElement>(null);
233
+
234
+ if (features.container) {
235
+ useContainer(ref, features.container.name, features.container.options);
236
+ }
237
+
238
+ if (features.performance) {
239
+ usePerformance(ref);
240
+ }
241
+
242
+ if (features.accessibility) {
243
+ useA11y(ref);
244
+ }
245
+
246
+ return createElement(
247
+ 'div',
248
+ { ref },
249
+ createElement(Component, props)
250
+ );
251
+ };
252
+ }
253
+
254
+ // Export all hooks and utilities
255
+ export default {
256
+ useTransition,
257
+ useScrollAnimate,
258
+ usePopover,
259
+ useAnchor,
260
+ useContainer,
261
+ usePerformance,
262
+ useA11y,
263
+ withProteus
264
+ };
@@ -0,0 +1,321 @@
1
+ /**
2
+ * @sc4rfurryx/proteusjs/adapters/svelte
3
+ * Svelte actions and stores for ProteusJS
4
+ *
5
+ * @version 1.1.0
6
+ * @author sc4rfurry
7
+ * @license MIT
8
+ */
9
+
10
+ import { writable } from 'svelte/store';
11
+ import { transition, TransitionOptions } from '../modules/transitions';
12
+ import { scrollAnimate, ScrollAnimateOptions } from '../modules/scroll';
13
+ import { attach as attachPopover, PopoverOptions, PopoverController } from '../modules/popover';
14
+ import { tether, TetherOptions, TetherController } from '../modules/anchor';
15
+ import { defineContainer, ContainerOptions } from '../modules/container';
16
+
17
+ /**
18
+ * Svelte action for scroll animations
19
+ */
20
+ export function proteusScroll(node: HTMLElement, options: ScrollAnimateOptions) {
21
+ scrollAnimate(node, options);
22
+
23
+ return {
24
+ update(newOptions: ScrollAnimateOptions) {
25
+ // Re-apply with new options
26
+ scrollAnimate(node, newOptions);
27
+ },
28
+ destroy() {
29
+ // Cleanup handled by scroll module
30
+ }
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Svelte action for container queries
36
+ */
37
+ export function proteusContainer(
38
+ node: HTMLElement,
39
+ options: { name?: string; containerOptions?: ContainerOptions } = {}
40
+ ) {
41
+ const { name, containerOptions } = options;
42
+ defineContainer(node, name, containerOptions);
43
+
44
+ return {
45
+ update(newOptions: { name?: string; containerOptions?: ContainerOptions }) {
46
+ const { name: newName, containerOptions: newContainerOptions } = newOptions;
47
+ defineContainer(node, newName, newContainerOptions);
48
+ }
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Svelte action for popover functionality
54
+ */
55
+ export function proteusPopover(
56
+ node: HTMLElement,
57
+ options: { panel: HTMLElement | string; popoverOptions?: PopoverOptions }
58
+ ) {
59
+ let controller: PopoverController | null = null;
60
+ const { panel, popoverOptions } = options;
61
+
62
+ controller = attachPopover(node, panel, popoverOptions);
63
+
64
+ return {
65
+ update(newOptions: { panel: HTMLElement | string; popoverOptions?: PopoverOptions }) {
66
+ if (controller) {
67
+ controller.destroy();
68
+ }
69
+ controller = attachPopover(node, newOptions.panel, newOptions.popoverOptions);
70
+ },
71
+ destroy() {
72
+ if (controller) {
73
+ controller.destroy();
74
+ }
75
+ }
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Svelte action for anchor positioning
81
+ */
82
+ export function proteusAnchor(
83
+ node: HTMLElement,
84
+ options: TetherOptions
85
+ ) {
86
+ let controller: TetherController | null = null;
87
+
88
+ controller = tether(node, options);
89
+
90
+ return {
91
+ update(newOptions: TetherOptions) {
92
+ if (controller) {
93
+ controller.destroy();
94
+ }
95
+ controller = tether(node, newOptions);
96
+ },
97
+ destroy() {
98
+ if (controller) {
99
+ controller.destroy();
100
+ }
101
+ }
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Svelte action for performance optimizations
107
+ */
108
+ export function proteusPerf(node: HTMLElement) {
109
+ const observer = new IntersectionObserver(
110
+ (entries) => {
111
+ entries.forEach(entry => {
112
+ if (entry.isIntersecting) {
113
+ node.style.contentVisibility = 'visible';
114
+ } else {
115
+ node.style.contentVisibility = 'auto';
116
+ }
117
+ });
118
+ },
119
+ { rootMargin: '50px' }
120
+ );
121
+
122
+ observer.observe(node);
123
+
124
+ return {
125
+ destroy() {
126
+ observer.disconnect();
127
+ }
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Svelte action for accessibility enhancements
133
+ */
134
+ export function proteusA11y(
135
+ node: HTMLElement,
136
+ options: { announceChanges?: boolean } = {}
137
+ ) {
138
+ const { announceChanges = false } = options;
139
+
140
+ // Enhance focus indicators
141
+ const focusableElements = node.querySelectorAll(
142
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
143
+ );
144
+
145
+ const focusHandlers = new Map();
146
+
147
+ focusableElements.forEach(element => {
148
+ const htmlEl = element as HTMLElement;
149
+
150
+ const focusHandler = () => {
151
+ htmlEl.style.outline = '2px solid #0066cc';
152
+ htmlEl.style.outlineOffset = '2px';
153
+ };
154
+
155
+ const blurHandler = () => {
156
+ htmlEl.style.outline = 'none';
157
+ };
158
+
159
+ htmlEl.addEventListener('focus', focusHandler);
160
+ htmlEl.addEventListener('blur', blurHandler);
161
+
162
+ focusHandlers.set(htmlEl, { focusHandler, blurHandler });
163
+ });
164
+
165
+ let mutationObserver: MutationObserver | null = null;
166
+
167
+ if (announceChanges) {
168
+ mutationObserver = new MutationObserver(() => {
169
+ const announcement = document.createElement('div');
170
+ announcement.setAttribute('aria-live', 'polite');
171
+ announcement.style.position = 'absolute';
172
+ announcement.style.left = '-10000px';
173
+ announcement.textContent = 'Content updated';
174
+ document.body.appendChild(announcement);
175
+
176
+ setTimeout(() => {
177
+ document.body.removeChild(announcement);
178
+ }, 1000);
179
+ });
180
+
181
+ mutationObserver.observe(node, { childList: true, subtree: true });
182
+ }
183
+
184
+ return {
185
+ destroy() {
186
+ // Clean up focus handlers
187
+ focusHandlers.forEach(({ focusHandler, blurHandler }, element) => {
188
+ element.removeEventListener('focus', focusHandler);
189
+ element.removeEventListener('blur', blurHandler);
190
+ });
191
+
192
+ if (mutationObserver) {
193
+ mutationObserver.disconnect();
194
+ }
195
+ }
196
+ };
197
+ }
198
+
199
+ /**
200
+ * Store for managing popover state
201
+ */
202
+ export function createPopover(
203
+ triggerSelector: string,
204
+ panelSelector: string,
205
+ options?: PopoverOptions
206
+ ) {
207
+ const isOpen = writable(false);
208
+ let controller: PopoverController | null = null;
209
+
210
+ const initialize = () => {
211
+ const trigger = document.querySelector(triggerSelector);
212
+ const panel = document.querySelector(panelSelector);
213
+
214
+ if (trigger && panel) {
215
+ controller = attachPopover(trigger, panel, {
216
+ ...options,
217
+ onOpen: () => {
218
+ isOpen.set(true);
219
+ options?.onOpen?.();
220
+ },
221
+ onClose: () => {
222
+ isOpen.set(false);
223
+ options?.onClose?.();
224
+ }
225
+ });
226
+ }
227
+ };
228
+
229
+ const open = () => controller?.open();
230
+ const close = () => controller?.close();
231
+ const toggle = () => controller?.toggle();
232
+ const destroy = () => {
233
+ if (controller) {
234
+ controller.destroy();
235
+ controller = null;
236
+ }
237
+ };
238
+
239
+ return {
240
+ isOpen,
241
+ initialize,
242
+ open,
243
+ close,
244
+ toggle,
245
+ destroy
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Store for managing transition state
251
+ */
252
+ export function createTransition() {
253
+ const isTransitioning = writable(false);
254
+
255
+ const runTransition = async (
256
+ run: () => Promise<any> | any,
257
+ opts?: TransitionOptions
258
+ ) => {
259
+ isTransitioning.set(true);
260
+ try {
261
+ await transition(run, opts);
262
+ } finally {
263
+ isTransitioning.set(false);
264
+ }
265
+ };
266
+
267
+ return {
268
+ isTransitioning,
269
+ runTransition
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Utility function to create reactive container size store
275
+ */
276
+ export function createContainerSize(element: HTMLElement) {
277
+ const size = writable({ width: 0, height: 0 });
278
+
279
+ if ('ResizeObserver' in window) {
280
+ const resizeObserver = new ResizeObserver((entries) => {
281
+ for (const entry of entries) {
282
+ const { width, height } = entry.contentRect;
283
+ size.set({ width, height });
284
+ }
285
+ });
286
+
287
+ resizeObserver.observe(element);
288
+
289
+ return {
290
+ size,
291
+ destroy: () => resizeObserver.disconnect()
292
+ };
293
+ }
294
+
295
+ // Fallback for browsers without ResizeObserver
296
+ const updateSize = () => {
297
+ const rect = element.getBoundingClientRect();
298
+ size.set({ width: rect.width, height: rect.height });
299
+ };
300
+
301
+ updateSize();
302
+ (window as any).addEventListener('resize', updateSize);
303
+
304
+ return {
305
+ size,
306
+ destroy: () => (window as any).removeEventListener('resize', updateSize)
307
+ };
308
+ }
309
+
310
+ // Export all actions and utilities
311
+ export default {
312
+ proteusScroll,
313
+ proteusContainer,
314
+ proteusPopover,
315
+ proteusAnchor,
316
+ proteusPerf,
317
+ proteusA11y,
318
+ createPopover,
319
+ createTransition,
320
+ createContainerSize
321
+ };