@hyperfixi/behaviors 2.0.0 → 2.2.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 (189) hide show
  1. package/dist/all.browser.global.js +35 -6
  2. package/dist/all.browser.global.js.map +1 -1
  3. package/dist/autodismiss.browser.global.js +3 -0
  4. package/dist/autodismiss.browser.global.js.map +1 -0
  5. package/dist/behaviors/autodismiss.cjs +177 -0
  6. package/dist/behaviors/autodismiss.cjs.map +1 -0
  7. package/dist/behaviors/autodismiss.d.cts +15 -0
  8. package/dist/behaviors/autodismiss.d.ts +15 -0
  9. package/dist/behaviors/autodismiss.js +15 -0
  10. package/dist/behaviors/autodismiss.js.map +1 -0
  11. package/dist/behaviors/clickoutside.cjs +123 -0
  12. package/dist/behaviors/clickoutside.cjs.map +1 -0
  13. package/dist/behaviors/clickoutside.d.cts +15 -0
  14. package/dist/behaviors/clickoutside.d.ts +15 -0
  15. package/dist/behaviors/clickoutside.js +15 -0
  16. package/dist/behaviors/clickoutside.js.map +1 -0
  17. package/dist/behaviors/clipboard.cjs +193 -0
  18. package/dist/behaviors/clipboard.cjs.map +1 -0
  19. package/dist/behaviors/clipboard.d.cts +15 -0
  20. package/dist/behaviors/clipboard.d.ts +15 -0
  21. package/dist/behaviors/clipboard.js +15 -0
  22. package/dist/behaviors/clipboard.js.map +1 -0
  23. package/dist/behaviors/draggable.cjs +49 -7
  24. package/dist/behaviors/draggable.cjs.map +1 -1
  25. package/dist/behaviors/draggable.d.cts +2 -1
  26. package/dist/behaviors/draggable.d.ts +2 -1
  27. package/dist/behaviors/draggable.js +2 -1
  28. package/dist/behaviors/focustrap.cjs +191 -0
  29. package/dist/behaviors/focustrap.cjs.map +1 -0
  30. package/dist/behaviors/focustrap.d.cts +15 -0
  31. package/dist/behaviors/focustrap.d.ts +15 -0
  32. package/dist/behaviors/focustrap.js +15 -0
  33. package/dist/behaviors/focustrap.js.map +1 -0
  34. package/dist/behaviors/removable.cjs +9 -2
  35. package/dist/behaviors/removable.cjs.map +1 -1
  36. package/dist/behaviors/removable.d.cts +1 -1
  37. package/dist/behaviors/removable.d.ts +1 -1
  38. package/dist/behaviors/removable.js +2 -1
  39. package/dist/behaviors/resizable.cjs +54 -7
  40. package/dist/behaviors/resizable.cjs.map +1 -1
  41. package/dist/behaviors/resizable.d.cts +1 -1
  42. package/dist/behaviors/resizable.d.ts +1 -1
  43. package/dist/behaviors/resizable.js +2 -1
  44. package/dist/behaviors/scrollreveal.cjs +149 -0
  45. package/dist/behaviors/scrollreveal.cjs.map +1 -0
  46. package/dist/behaviors/scrollreveal.d.cts +15 -0
  47. package/dist/behaviors/scrollreveal.d.ts +15 -0
  48. package/dist/behaviors/scrollreveal.js +15 -0
  49. package/dist/behaviors/scrollreveal.js.map +1 -0
  50. package/dist/behaviors/sortable.cjs +49 -7
  51. package/dist/behaviors/sortable.cjs.map +1 -1
  52. package/dist/behaviors/sortable.d.cts +1 -1
  53. package/dist/behaviors/sortable.d.ts +1 -1
  54. package/dist/behaviors/sortable.js +2 -1
  55. package/dist/behaviors/tabs.cjs +247 -0
  56. package/dist/behaviors/tabs.cjs.map +1 -0
  57. package/dist/behaviors/tabs.d.cts +15 -0
  58. package/dist/behaviors/tabs.d.ts +15 -0
  59. package/dist/behaviors/tabs.js +15 -0
  60. package/dist/behaviors/tabs.js.map +1 -0
  61. package/dist/behaviors/toggleable.cjs +36 -7
  62. package/dist/behaviors/toggleable.cjs.map +1 -1
  63. package/dist/behaviors/toggleable.d.cts +2 -1
  64. package/dist/behaviors/toggleable.d.ts +2 -1
  65. package/dist/behaviors/toggleable.js +2 -1
  66. package/dist/chunk-244WSZOL.js +67 -0
  67. package/dist/chunk-244WSZOL.js.map +1 -0
  68. package/dist/chunk-2NSKQMNM.js +99 -0
  69. package/dist/chunk-2NSKQMNM.js.map +1 -0
  70. package/dist/chunk-64DIQGQI.js +37 -0
  71. package/dist/chunk-64DIQGQI.js.map +1 -0
  72. package/dist/chunk-6TJXDCAQ.js +11 -0
  73. package/dist/chunk-6TJXDCAQ.js.map +1 -0
  74. package/dist/chunk-7JDV2THA.js +62 -0
  75. package/dist/chunk-7JDV2THA.js.map +1 -0
  76. package/dist/chunk-AFGA54SA.js +116 -0
  77. package/dist/chunk-AFGA54SA.js.map +1 -0
  78. package/dist/chunk-DYHBKDKB.js +62 -0
  79. package/dist/chunk-DYHBKDKB.js.map +1 -0
  80. package/dist/chunk-GPTHAUWN.js +58 -0
  81. package/dist/chunk-GPTHAUWN.js.map +1 -0
  82. package/dist/chunk-GVJPA2EK.js +68 -0
  83. package/dist/chunk-GVJPA2EK.js.map +1 -0
  84. package/dist/chunk-H2MCHR5A.js +89 -0
  85. package/dist/chunk-H2MCHR5A.js.map +1 -0
  86. package/dist/{chunk-2NQHIHD5.js → chunk-H5Y5ARYG.js} +6 -3
  87. package/dist/chunk-H5Y5ARYG.js.map +1 -0
  88. package/dist/chunk-JKBWIJFI.js +155 -0
  89. package/dist/chunk-JKBWIJFI.js.map +1 -0
  90. package/dist/chunk-QKDYW4VM.js +70 -0
  91. package/dist/chunk-QKDYW4VM.js.map +1 -0
  92. package/dist/chunk-RB7OB6OD.js +51 -0
  93. package/dist/chunk-RB7OB6OD.js.map +1 -0
  94. package/dist/chunk-RPERQTFU.js +75 -0
  95. package/dist/chunk-RPERQTFU.js.map +1 -0
  96. package/dist/chunk-S4QDV2MH.js +75 -0
  97. package/dist/chunk-S4QDV2MH.js.map +1 -0
  98. package/dist/chunk-WTUVPVSD.js +80 -0
  99. package/dist/chunk-WTUVPVSD.js.map +1 -0
  100. package/dist/chunk-YHQRFUWV.js +62 -0
  101. package/dist/chunk-YHQRFUWV.js.map +1 -0
  102. package/dist/chunk-ZQQZTMCJ.js +64 -0
  103. package/dist/chunk-ZQQZTMCJ.js.map +1 -0
  104. package/dist/clickoutside.browser.global.js +12 -0
  105. package/dist/clickoutside.browser.global.js.map +1 -0
  106. package/dist/clipboard.browser.global.js +3 -0
  107. package/dist/clipboard.browser.global.js.map +1 -0
  108. package/dist/core.browser.global.js +3 -3
  109. package/dist/core.browser.global.js.map +1 -1
  110. package/dist/draggable.browser.global.js +2 -2
  111. package/dist/draggable.browser.global.js.map +1 -1
  112. package/dist/focustrap.browser.global.js +9 -0
  113. package/dist/focustrap.browser.global.js.map +1 -0
  114. package/dist/index.cjs +1270 -50
  115. package/dist/index.cjs.map +1 -1
  116. package/dist/index.d.cts +13 -7
  117. package/dist/index.d.ts +13 -7
  118. package/dist/index.js +155 -48
  119. package/dist/index.js.map +1 -1
  120. package/dist/loaders.cjs +1151 -30
  121. package/dist/loaders.cjs.map +1 -1
  122. package/dist/loaders.js +9 -3
  123. package/dist/registry.d.cts +1 -1
  124. package/dist/registry.d.ts +1 -1
  125. package/dist/removable.browser.global.js +2 -2
  126. package/dist/removable.browser.global.js.map +1 -1
  127. package/dist/resizable.browser.global.js +2 -2
  128. package/dist/resizable.browser.global.js.map +1 -1
  129. package/dist/schemas/index.cjs +343 -1
  130. package/dist/schemas/index.cjs.map +1 -1
  131. package/dist/schemas/index.d.cts +45 -3
  132. package/dist/schemas/index.d.ts +45 -3
  133. package/dist/schemas/index.js +34 -4
  134. package/dist/schemas/index.js.map +1 -1
  135. package/dist/scrollreveal.browser.global.js +11 -0
  136. package/dist/scrollreveal.browser.global.js.map +1 -0
  137. package/dist/sortable.browser.global.js +2 -2
  138. package/dist/sortable.browser.global.js.map +1 -1
  139. package/dist/tabs.browser.global.js +3 -0
  140. package/dist/tabs.browser.global.js.map +1 -0
  141. package/dist/toggleable.browser.global.js +2 -2
  142. package/dist/toggleable.browser.global.js.map +1 -1
  143. package/dist/{types-CCc_2wAb.d.cts → types-DCCtVQS5.d.cts} +1 -1
  144. package/dist/{types-CCc_2wAb.d.ts → types-DCCtVQS5.d.ts} +1 -1
  145. package/package.json +45 -2
  146. package/src/behaviors/autodismiss.test.ts +61 -0
  147. package/src/behaviors/autodismiss.ts +107 -0
  148. package/src/behaviors/clickoutside.test.ts +52 -0
  149. package/src/behaviors/clickoutside.ts +77 -0
  150. package/src/behaviors/clipboard.test.ts +67 -0
  151. package/src/behaviors/clipboard.ts +130 -0
  152. package/src/behaviors/draggable.ts +62 -15
  153. package/src/behaviors/focustrap.test.ts +59 -0
  154. package/src/behaviors/focustrap.ts +158 -0
  155. package/src/behaviors/index.ts +37 -0
  156. package/src/behaviors/integration.test.ts +120 -45
  157. package/src/behaviors/removable.ts +4 -5
  158. package/src/behaviors/resizable.ts +63 -14
  159. package/src/behaviors/scrollreveal.test.ts +59 -0
  160. package/src/behaviors/scrollreveal.ts +78 -0
  161. package/src/behaviors/sortable.ts +61 -14
  162. package/src/behaviors/tabs.test.ts +81 -0
  163. package/src/behaviors/tabs.ts +196 -0
  164. package/src/behaviors/toggleable.ts +45 -13
  165. package/src/generated/metadata.ts +38 -7
  166. package/src/generated/types.ts +5 -5
  167. package/src/index.test.ts +143 -5
  168. package/src/index.ts +65 -13
  169. package/src/loaders.ts +18 -0
  170. package/src/registry.test.ts +26 -5
  171. package/src/schemas/autodismiss.schema.ts +66 -0
  172. package/src/schemas/clickoutside.schema.ts +39 -0
  173. package/src/schemas/clipboard.schema.ts +72 -0
  174. package/src/schemas/focustrap.schema.ts +53 -0
  175. package/src/schemas/index.ts +18 -0
  176. package/src/schemas/scrollreveal.schema.ts +60 -0
  177. package/src/schemas/tabs.schema.ts +66 -0
  178. package/src/schemas/types.ts +13 -1
  179. package/dist/chunk-2NQHIHD5.js.map +0 -1
  180. package/dist/chunk-3Q652JQB.js +0 -37
  181. package/dist/chunk-3Q652JQB.js.map +0 -1
  182. package/dist/chunk-HMRPQWQU.js +0 -37
  183. package/dist/chunk-HMRPQWQU.js.map +0 -1
  184. package/dist/chunk-MEP6B7XW.js +0 -37
  185. package/dist/chunk-MEP6B7XW.js.map +0 -1
  186. package/dist/chunk-URBOGGW6.js +0 -32
  187. package/dist/chunk-URBOGGW6.js.map +0 -1
  188. package/dist/chunk-WU37DFAL.js +0 -37
  189. package/dist/chunk-WU37DFAL.js.map +0 -1
package/src/index.ts CHANGED
@@ -71,6 +71,43 @@ export {
71
71
  default as Resizable,
72
72
  } from './behaviors/resizable';
73
73
 
74
+ export {
75
+ clipboardSource,
76
+ clipboardMetadata,
77
+ registerClipboard,
78
+ default as Clipboard,
79
+ } from './behaviors/clipboard';
80
+
81
+ export {
82
+ autoDismissSource,
83
+ autoDismissMetadata,
84
+ registerAutoDismiss,
85
+ default as AutoDismiss,
86
+ } from './behaviors/autodismiss';
87
+
88
+ export {
89
+ clickOutsideSource,
90
+ clickOutsideMetadata,
91
+ registerClickOutside,
92
+ default as ClickOutside,
93
+ } from './behaviors/clickoutside';
94
+
95
+ export {
96
+ focusTrapSource,
97
+ focusTrapMetadata,
98
+ registerFocusTrap,
99
+ default as FocusTrap,
100
+ } from './behaviors/focustrap';
101
+
102
+ export {
103
+ scrollRevealSource,
104
+ scrollRevealMetadata,
105
+ registerScrollReveal,
106
+ default as ScrollReveal,
107
+ } from './behaviors/scrollreveal';
108
+
109
+ export { tabsSource, tabsMetadata, registerTabs, default as Tabs } from './behaviors/tabs';
110
+
74
111
  // =============================================================================
75
112
  // Types
76
113
  // =============================================================================
@@ -157,7 +194,15 @@ import { registerRemovable } from './behaviors/removable';
157
194
  import { registerToggleable } from './behaviors/toggleable';
158
195
  import { registerSortable } from './behaviors/sortable';
159
196
  import { registerResizable } from './behaviors/resizable';
160
- import type { LokaScriptInstance, LokaScriptWindow } from './schemas/types';
197
+ import { registerClipboard } from './behaviors/clipboard';
198
+ import { registerAutoDismiss } from './behaviors/autodismiss';
199
+ import { registerClickOutside } from './behaviors/clickoutside';
200
+ import { registerFocusTrap } from './behaviors/focustrap';
201
+ import { registerScrollReveal } from './behaviors/scrollreveal';
202
+ import { registerTabs } from './behaviors/tabs';
203
+ import type { LokaScriptInstance } from './schemas/types';
204
+ import { resolveRuntime } from './schemas/types';
205
+ import type { LokaScriptWindow } from './schemas/types';
161
206
 
162
207
  /**
163
208
  * Register all behaviors with HyperFixi.
@@ -178,6 +223,12 @@ export async function registerAll(hyperfixi?: LokaScriptInstance): Promise<void>
178
223
  registerToggleable(hyperfixi),
179
224
  registerSortable(hyperfixi),
180
225
  registerResizable(hyperfixi),
226
+ registerClipboard(hyperfixi),
227
+ registerAutoDismiss(hyperfixi),
228
+ registerClickOutside(hyperfixi),
229
+ registerFocusTrap(hyperfixi),
230
+ registerScrollReveal(hyperfixi),
231
+ registerTabs(hyperfixi),
181
232
  ]);
182
233
  }
183
234
 
@@ -192,28 +243,29 @@ export async function registerAll(hyperfixi?: LokaScriptInstance): Promise<void>
192
243
  export let ready: Promise<void> | null = null;
193
244
 
194
245
  // Auto-register all behaviors when loaded in browser with hyperfixi available
195
- if (typeof window !== 'undefined' && (window as unknown as LokaScriptWindow).lokascript) {
246
+ const _runtime = resolveRuntime();
247
+ if (_runtime) {
196
248
  const lsWin = window as unknown as LokaScriptWindow;
197
- ready = registerAll();
249
+ ready = registerAll(_runtime);
198
250
 
199
- lsWin.__lokascript_behaviors_ready = ready;
251
+ lsWin.__hyperfixi_behaviors_ready = ready;
200
252
 
201
253
  ready
202
254
  .then(() => {
203
- const hyperfixi = lsWin.lokascript as LokaScriptInstance & {
255
+ const hf = _runtime as LokaScriptInstance & {
204
256
  attributeProcessor?: { scanAndProcessAll: () => Promise<void> };
205
257
  processNode?: (node: Document) => void;
206
258
  };
207
- const savedPromise = lsWin.__lokascript_behaviors_ready;
208
- delete lsWin.__lokascript_behaviors_ready;
259
+ const savedPromise = lsWin.__hyperfixi_behaviors_ready;
260
+ delete lsWin.__hyperfixi_behaviors_ready;
209
261
 
210
- if (hyperfixi.attributeProcessor?.scanAndProcessAll) {
211
- hyperfixi.attributeProcessor.scanAndProcessAll().finally(() => {
212
- lsWin.__lokascript_behaviors_ready = savedPromise;
262
+ if (hf.attributeProcessor?.scanAndProcessAll) {
263
+ hf.attributeProcessor.scanAndProcessAll().finally(() => {
264
+ lsWin.__hyperfixi_behaviors_ready = savedPromise;
213
265
  });
214
- } else if (hyperfixi.processNode) {
215
- hyperfixi.processNode(document);
216
- lsWin.__lokascript_behaviors_ready = savedPromise;
266
+ } else if (hf.processNode) {
267
+ hf.processNode(document);
268
+ lsWin.__hyperfixi_behaviors_ready = savedPromise;
217
269
  }
218
270
  })
219
271
  .catch(err => {
package/src/loaders.ts CHANGED
@@ -23,6 +23,12 @@ import { sortableSchema } from './schemas/sortable.schema';
23
23
  import { resizableSchema } from './schemas/resizable.schema';
24
24
  import { removableSchema } from './schemas/removable.schema';
25
25
  import { toggleableSchema } from './schemas/toggleable.schema';
26
+ import { clipboardSchema } from './schemas/clipboard.schema';
27
+ import { autoDismissSchema } from './schemas/autodismiss.schema';
28
+ import { clickOutsideSchema } from './schemas/clickoutside.schema';
29
+ import { focusTrapSchema } from './schemas/focustrap.schema';
30
+ import { scrollRevealSchema } from './schemas/scrollreveal.schema';
31
+ import { tabsSchema } from './schemas/tabs.schema';
26
32
 
27
33
  // Register all schemas
28
34
  registerSchema(draggableSchema);
@@ -30,6 +36,12 @@ registerSchema(sortableSchema);
30
36
  registerSchema(resizableSchema);
31
37
  registerSchema(removableSchema);
32
38
  registerSchema(toggleableSchema);
39
+ registerSchema(clipboardSchema);
40
+ registerSchema(autoDismissSchema);
41
+ registerSchema(clickOutsideSchema);
42
+ registerSchema(focusTrapSchema);
43
+ registerSchema(scrollRevealSchema);
44
+ registerSchema(tabsSchema);
33
45
 
34
46
  // Register lazy loaders - behaviors are only loaded when requested
35
47
  registerLoader('Draggable', () => import('./behaviors/draggable'));
@@ -37,3 +49,9 @@ registerLoader('Sortable', () => import('./behaviors/sortable'));
37
49
  registerLoader('Resizable', () => import('./behaviors/resizable'));
38
50
  registerLoader('Removable', () => import('./behaviors/removable'));
39
51
  registerLoader('Toggleable', () => import('./behaviors/toggleable'));
52
+ registerLoader('Clipboard', () => import('./behaviors/clipboard'));
53
+ registerLoader('AutoDismiss', () => import('./behaviors/autodismiss'));
54
+ registerLoader('ClickOutside', () => import('./behaviors/clickoutside'));
55
+ registerLoader('FocusTrap', () => import('./behaviors/focustrap'));
56
+ registerLoader('ScrollReveal', () => import('./behaviors/scrollreveal'));
57
+ registerLoader('Tabs', () => import('./behaviors/tabs'));
@@ -175,17 +175,38 @@ describe('registry', () => {
175
175
  });
176
176
 
177
177
  describe('registerWithRuntime', () => {
178
- it('should load and call module.register', async () => {
178
+ it('should load and call module.register (imperative behavior)', async () => {
179
179
  const mockInstance = {
180
180
  compileSync: vi.fn().mockReturnValue({ ok: true, ast: {} }),
181
181
  execute: vi.fn().mockResolvedValue(undefined),
182
182
  createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
183
183
  };
184
184
 
185
- // Load first so module is available
186
185
  await loadBehavior('Draggable');
187
-
188
186
  await registerWithRuntime('Draggable', mockInstance);
187
+
188
+ // Imperative behaviors use execute with synthetic node, not compileSync
189
+ expect(mockInstance.execute).toHaveBeenCalledWith(
190
+ expect.objectContaining({
191
+ type: 'behavior',
192
+ name: 'Draggable',
193
+ imperativeInstaller: expect.any(Function),
194
+ }),
195
+ expect.objectContaining({ locals: expect.any(Map), globals: expect.any(Map) })
196
+ );
197
+ });
198
+
199
+ it('should load and call module.register (compiled behavior)', async () => {
200
+ const mockInstance = {
201
+ compileSync: vi.fn().mockReturnValue({ ok: true, ast: {} }),
202
+ execute: vi.fn().mockResolvedValue(undefined),
203
+ createContext: vi.fn().mockReturnValue({ locals: new Map(), globals: new Map() }),
204
+ };
205
+
206
+ await loadBehavior('Removable');
207
+ await registerWithRuntime('Removable', mockInstance);
208
+
209
+ // Source-compiled behaviors use compileSync
189
210
  expect(mockInstance.compileSync).toHaveBeenCalled();
190
211
  });
191
212
  });
@@ -200,8 +221,8 @@ describe('registry', () => {
200
221
 
201
222
  await loadAll();
202
223
  await registerAllWithRuntime(mockInstance);
203
- // Each behavior calls compileSync once
204
- expect(mockInstance.compileSync.mock.calls.length).toBeGreaterThanOrEqual(5);
224
+ // execute called for each behavior (imperative + compiled)
225
+ expect(mockInstance.execute.mock.calls.length).toBeGreaterThanOrEqual(10);
205
226
  });
206
227
  });
207
228
  });
@@ -0,0 +1,66 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ /**
4
+ * AutoDismiss Behavior Schema
5
+ *
6
+ * Auto-removes flash messages and toasts after a configurable delay.
7
+ * Supports pause-on-hover and fade effect.
8
+ */
9
+ export const autoDismissSchema: BehaviorSchema = {
10
+ name: 'AutoDismiss',
11
+ category: 'ui',
12
+ tier: 'core',
13
+ version: '1.0.0',
14
+ description: 'Auto-removes elements after a delay with optional pause-on-hover',
15
+ parameters: [
16
+ {
17
+ name: 'delay',
18
+ type: 'number',
19
+ optional: true,
20
+ default: 5000,
21
+ description: 'Time in ms before the element is removed',
22
+ },
23
+ {
24
+ name: 'pauseOnHover',
25
+ type: 'boolean',
26
+ optional: true,
27
+ default: true,
28
+ description: 'Pause the countdown when the mouse hovers over the element',
29
+ },
30
+ {
31
+ name: 'effect',
32
+ type: 'string',
33
+ optional: true,
34
+ default: 'none',
35
+ enum: ['fade', 'none'],
36
+ description: 'Transition effect before removal: "fade" or "none"',
37
+ },
38
+ ],
39
+ events: [
40
+ { name: 'autodismiss:start', description: 'Fired when the countdown timer starts' },
41
+ {
42
+ name: 'autodismiss:dismissed',
43
+ description: 'Fired before removal (cancelable via preventDefault)',
44
+ },
45
+ { name: 'autodismiss:paused', description: 'Fired when the timer is paused on hover' },
46
+ { name: 'autodismiss:resumed', description: 'Fired when the timer resumes on mouseleave' },
47
+ ],
48
+ source: [
49
+ 'behavior AutoDismiss(delay, pauseOnHover, effect)',
50
+ ' init',
51
+ ' if delay is undefined set delay to 5000',
52
+ ' if pauseOnHover is undefined set pauseOnHover to true',
53
+ ' if effect is undefined set effect to "none"',
54
+ ' end',
55
+ ' on load',
56
+ ' trigger autodismiss:start',
57
+ ' wait delay ms',
58
+ ' trigger autodismiss:dismissed',
59
+ ' if effect is "fade"',
60
+ ' transition opacity to 0 over 300ms',
61
+ ' end',
62
+ ' remove me',
63
+ ' end',
64
+ 'end',
65
+ ].join('\n'),
66
+ };
@@ -0,0 +1,39 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ /**
4
+ * ClickOutside Behavior Schema
5
+ *
6
+ * Fires an event when the user clicks outside the element.
7
+ * Uses pointerdown instead of click to avoid timing issues.
8
+ */
9
+ export const clickOutsideSchema: BehaviorSchema = {
10
+ name: 'ClickOutside',
11
+ category: 'ui',
12
+ tier: 'core',
13
+ version: '1.0.0',
14
+ description: 'Fires an event when the user clicks outside the element',
15
+ parameters: [
16
+ {
17
+ name: 'active',
18
+ type: 'boolean',
19
+ optional: true,
20
+ default: true,
21
+ description: 'Whether the listener is active (can be toggled programmatically)',
22
+ },
23
+ ],
24
+ events: [
25
+ { name: 'clickoutside', description: 'Fired when a pointer press occurs outside the element' },
26
+ ],
27
+ requirements: ['Uses pointerdown instead of click to avoid timing issues with removed elements'],
28
+ source: `
29
+ behavior ClickOutside(active)
30
+ init
31
+ if active is undefined set active to true
32
+ end
33
+ on pointerdown from document
34
+ if active and not me.contains(event.target)
35
+ trigger clickoutside on me
36
+ end
37
+ end
38
+ end`.trim(),
39
+ };
@@ -0,0 +1,72 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ /**
4
+ * Clipboard Behavior Schema
5
+ *
6
+ * Copies text to clipboard on click with visual feedback.
7
+ * Uses the Clipboard API with execCommand fallback.
8
+ */
9
+ export const clipboardSchema: BehaviorSchema = {
10
+ name: 'Clipboard',
11
+ category: 'ui',
12
+ tier: 'core',
13
+ version: '1.0.0',
14
+ description: 'Copies text to clipboard on click with visual feedback',
15
+ parameters: [
16
+ {
17
+ name: 'text',
18
+ type: 'string',
19
+ optional: true,
20
+ description: 'Literal text to copy (takes priority over source)',
21
+ },
22
+ {
23
+ name: 'source',
24
+ type: 'selector',
25
+ optional: true,
26
+ default: 'me',
27
+ description: 'Element whose textContent or value is copied',
28
+ },
29
+ {
30
+ name: 'feedback',
31
+ type: 'selector',
32
+ optional: true,
33
+ description: 'Element to show .copied feedback on (defaults to the installed element)',
34
+ },
35
+ {
36
+ name: 'feedbackDuration',
37
+ type: 'number',
38
+ optional: true,
39
+ default: 2000,
40
+ description: 'Duration in ms to show .copied class feedback',
41
+ },
42
+ ],
43
+ events: [
44
+ { name: 'clipboard:copied', description: 'Fired after text is copied successfully' },
45
+ { name: 'clipboard:error', description: 'Fired when clipboard write fails' },
46
+ ],
47
+ requirements: [
48
+ 'navigator.clipboard.writeText() requires HTTPS or localhost',
49
+ 'Falls back to document.execCommand("copy") for older browsers',
50
+ ],
51
+ source: [
52
+ 'behavior Clipboard(text, source, feedback, feedbackDuration)',
53
+ ' init',
54
+ ' if feedbackDuration is undefined set feedbackDuration to 2000',
55
+ ' if source is undefined set source to me',
56
+ ' end',
57
+ ' on click',
58
+ ' if text is not undefined',
59
+ ' set copyText to text',
60
+ ' else',
61
+ " set copyText to source's value or source's textContent",
62
+ ' end',
63
+ ' js(copyText) navigator.clipboard.writeText(copyText) end',
64
+ ' set feedbackEl to feedback or me',
65
+ ' add .copied to feedbackEl',
66
+ ' wait feedbackDuration ms',
67
+ ' remove .copied from feedbackEl',
68
+ ' trigger clipboard:copied',
69
+ ' end',
70
+ 'end',
71
+ ].join('\n'),
72
+ };
@@ -0,0 +1,53 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ /**
4
+ * FocusTrap Behavior Schema
5
+ *
6
+ * Confines Tab navigation inside an element and restores focus on release.
7
+ * Manages aria-modal for accessibility compliance.
8
+ */
9
+ export const focusTrapSchema: BehaviorSchema = {
10
+ name: 'FocusTrap',
11
+ category: 'ui',
12
+ tier: 'core',
13
+ version: '1.0.0',
14
+ description: 'Confines Tab navigation inside an element with ARIA support',
15
+ parameters: [
16
+ {
17
+ name: 'active',
18
+ type: 'boolean',
19
+ optional: true,
20
+ default: true,
21
+ description: 'Whether the trap is active on install',
22
+ },
23
+ {
24
+ name: 'initialFocus',
25
+ type: 'selector',
26
+ optional: true,
27
+ description: 'Element to focus when the trap activates',
28
+ },
29
+ {
30
+ name: 'returnFocus',
31
+ type: 'boolean',
32
+ optional: true,
33
+ default: true,
34
+ description: 'Restore focus to the previously focused element on deactivation',
35
+ },
36
+ ],
37
+ events: [
38
+ { name: 'focustrap:activated', description: 'Fired when the trap is activated' },
39
+ { name: 'focustrap:deactivated', description: 'Fired when the trap is deactivated' },
40
+ ],
41
+ requirements: [
42
+ 'Sets aria-modal="true" on the element when active',
43
+ 'Listens for focustrap:activate and focustrap:deactivate custom events for programmatic control',
44
+ ],
45
+ source: `
46
+ behavior FocusTrap(active, initialFocus, returnFocus)
47
+ init
48
+ if active is undefined set active to true
49
+ if returnFocus is undefined set returnFocus to true
50
+ end
51
+ -- Imperative installer handles Tab key trapping and focus management
52
+ end`.trim(),
53
+ };
@@ -22,6 +22,12 @@ export { sortableSchema } from './sortable.schema';
22
22
  export { resizableSchema } from './resizable.schema';
23
23
  export { removableSchema } from './removable.schema';
24
24
  export { toggleableSchema } from './toggleable.schema';
25
+ export { clipboardSchema } from './clipboard.schema';
26
+ export { autoDismissSchema } from './autodismiss.schema';
27
+ export { clickOutsideSchema } from './clickoutside.schema';
28
+ export { focusTrapSchema } from './focustrap.schema';
29
+ export { scrollRevealSchema } from './scrollreveal.schema';
30
+ export { tabsSchema } from './tabs.schema';
25
31
 
26
32
  // All schemas as a collection
27
33
  import { draggableSchema } from './draggable.schema';
@@ -29,6 +35,12 @@ import { sortableSchema } from './sortable.schema';
29
35
  import { resizableSchema } from './resizable.schema';
30
36
  import { removableSchema } from './removable.schema';
31
37
  import { toggleableSchema } from './toggleable.schema';
38
+ import { clipboardSchema } from './clipboard.schema';
39
+ import { autoDismissSchema } from './autodismiss.schema';
40
+ import { clickOutsideSchema } from './clickoutside.schema';
41
+ import { focusTrapSchema } from './focustrap.schema';
42
+ import { scrollRevealSchema } from './scrollreveal.schema';
43
+ import { tabsSchema } from './tabs.schema';
32
44
  import type { BehaviorSchema } from './types';
33
45
 
34
46
  /**
@@ -40,6 +52,12 @@ export const ALL_SCHEMAS: Record<string, BehaviorSchema> = {
40
52
  Resizable: resizableSchema,
41
53
  Removable: removableSchema,
42
54
  Toggleable: toggleableSchema,
55
+ Clipboard: clipboardSchema,
56
+ AutoDismiss: autoDismissSchema,
57
+ ClickOutside: clickOutsideSchema,
58
+ FocusTrap: focusTrapSchema,
59
+ ScrollReveal: scrollRevealSchema,
60
+ Tabs: tabsSchema,
43
61
  };
44
62
 
45
63
  /**
@@ -0,0 +1,60 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ /**
4
+ * ScrollReveal Behavior Schema
5
+ *
6
+ * Adds a class or fires events when the element enters or exits the viewport.
7
+ * Uses the IntersectionObserver web standard API.
8
+ */
9
+ export const scrollRevealSchema: BehaviorSchema = {
10
+ name: 'ScrollReveal',
11
+ category: 'layout',
12
+ tier: 'common',
13
+ version: '1.0.0',
14
+ description: 'Adds a class when the element enters the viewport via IntersectionObserver',
15
+ parameters: [
16
+ {
17
+ name: 'cls',
18
+ type: 'string',
19
+ optional: true,
20
+ default: 'revealed',
21
+ description: 'CSS class added when element enters viewport',
22
+ },
23
+ {
24
+ name: 'threshold',
25
+ type: 'number',
26
+ optional: true,
27
+ default: 0.1,
28
+ description: 'Intersection ratio threshold (0-1) for triggering',
29
+ },
30
+ {
31
+ name: 'once',
32
+ type: 'boolean',
33
+ optional: true,
34
+ default: true,
35
+ description: 'If true, disconnect observer after first intersection',
36
+ },
37
+ {
38
+ name: 'rootMargin',
39
+ type: 'string',
40
+ optional: true,
41
+ default: '0px',
42
+ description: 'CSS margin around the viewport for triggering earlier or later',
43
+ },
44
+ ],
45
+ events: [
46
+ { name: 'scrollreveal:enter', description: 'Fired when the element enters the viewport' },
47
+ { name: 'scrollreveal:exit', description: 'Fired when the element exits the viewport' },
48
+ ],
49
+ requirements: ['Uses IntersectionObserver API (supported in all modern browsers)'],
50
+ source: `
51
+ behavior ScrollReveal(cls, threshold, once, rootMargin)
52
+ init
53
+ if cls is undefined set cls to "revealed"
54
+ if threshold is undefined set threshold to 0.1
55
+ if once is undefined set once to true
56
+ if rootMargin is undefined set rootMargin to "0px"
57
+ end
58
+ -- Imperative installer handles IntersectionObserver setup
59
+ end`.trim(),
60
+ };
@@ -0,0 +1,66 @@
1
+ import type { BehaviorSchema } from './types';
2
+
3
+ export const tabsSchema: BehaviorSchema = {
4
+ name: 'Tabs',
5
+ category: 'ui',
6
+ tier: 'core',
7
+ version: '1.0.0',
8
+ description:
9
+ 'WAI-ARIA compliant tabs with roving tabindex keyboard navigation. ' +
10
+ 'Auto-wires role, aria-selected, aria-controls, and aria-labelledby attributes.',
11
+ parameters: [
12
+ {
13
+ name: 'orientation',
14
+ type: 'string',
15
+ optional: true,
16
+ default: 'horizontal',
17
+ enum: ['horizontal', 'vertical'],
18
+ description: 'Arrow key direction: horizontal (Left/Right) or vertical (Up/Down)',
19
+ },
20
+ {
21
+ name: 'activeTab',
22
+ type: 'number',
23
+ optional: true,
24
+ default: 0,
25
+ description: 'Initial active tab index (zero-based)',
26
+ },
27
+ {
28
+ name: 'wrap',
29
+ type: 'boolean',
30
+ optional: true,
31
+ default: true,
32
+ description: 'Wrap to first/last tab when navigating past the end',
33
+ },
34
+ {
35
+ name: 'activeClass',
36
+ type: 'string',
37
+ optional: true,
38
+ default: 'active',
39
+ description: 'CSS class applied to the active tab and its panel',
40
+ },
41
+ ],
42
+ events: [
43
+ {
44
+ name: 'tabs:change',
45
+ description: 'Fired before tab selection changes (cancelable via preventDefault)',
46
+ },
47
+ { name: 'tabs:changed', description: 'Fired after tab selection has changed' },
48
+ ],
49
+ requirements: [
50
+ 'Sets role="tablist" on the tab container',
51
+ 'Sets role="tab" on tab buttons and role="tabpanel" on content panels',
52
+ 'Manages aria-selected, aria-controls, aria-labelledby, and tabindex',
53
+ 'Keyboard: Arrow keys navigate, Home/End jump to first/last, Tab exits tablist',
54
+ ],
55
+ source: [
56
+ 'behavior Tabs(orientation, activeTab, wrap, activeClass)',
57
+ ' init',
58
+ ' if orientation is undefined set orientation to "horizontal"',
59
+ ' if activeTab is undefined set activeTab to 0',
60
+ ' if wrap is undefined set wrap to true',
61
+ ' if activeClass is undefined set activeClass to "active"',
62
+ ' end',
63
+ ' -- Imperative installer handles ARIA wiring and keyboard navigation',
64
+ 'end',
65
+ ].join('\n'),
66
+ };
@@ -100,8 +100,20 @@ export interface LokaScriptInstance {
100
100
  /**
101
101
  * Typed window interface for behavior auto-registration.
102
102
  * Avoids `(window as any)` casts throughout the codebase.
103
+ * Checks both `window.lokascript` and `window.hyperfixi` for the runtime instance.
103
104
  */
104
105
  export interface LokaScriptWindow {
105
106
  lokascript?: LokaScriptInstance;
106
- __lokascript_behaviors_ready?: Promise<void> | undefined;
107
+ hyperfixi?: LokaScriptInstance;
108
+ __hyperfixi_behaviors_ready?: Promise<void> | undefined;
109
+ }
110
+
111
+ /**
112
+ * Resolve the LokaScript runtime from window globals.
113
+ * Checks both `window.lokascript` and `window.hyperfixi`.
114
+ */
115
+ export function resolveRuntime(): LokaScriptInstance | null {
116
+ if (typeof window === 'undefined') return null;
117
+ const win = window as unknown as LokaScriptWindow;
118
+ return win.lokascript || win.hyperfixi || null;
107
119
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/behaviors/removable.ts"],"sourcesContent":["/**\n * Removable Behavior\n *\n * A behavior that removes an element when a trigger is clicked.\n * Supports optional confirmation and transition effects.\n *\n * @example\n * ```html\n * <div _=\"install Removable\">Click to remove</div>\n *\n * <div _=\"install Removable(confirm: true)\">With confirmation</div>\n * ```\n */\n\nimport { removableSchema } from '../schemas/removable.schema';\nimport type { LokaScriptInstance, LokaScriptWindow } from '../schemas/types';\n\n// Re-export schema-derived values for backwards compatibility\nexport const removableSource = removableSchema.source;\nexport const removableMetadata = removableSchema;\n\n/**\n * Register the Removable behavior with LokaScript.\n */\nexport async function registerRemovable(hyperfixi?: LokaScriptInstance): Promise<void> {\n const hf =\n hyperfixi ||\n (typeof window !== 'undefined' ? (window as unknown as LokaScriptWindow).lokascript : null);\n\n if (!hf) {\n throw new Error(\n 'LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors.'\n );\n }\n\n const result = hf.compileSync(removableSchema.source, { traditional: true });\n\n if (!result.ok) {\n throw new Error(`Failed to compile Removable behavior: ${JSON.stringify(result.errors)}`);\n }\n\n const ctx = hf.createContext ? hf.createContext() : { locals: new Map(), globals: new Map() };\n await hf.execute(result.ast, ctx);\n}\n\n// Auto-register when loaded as a script tag\nif (typeof window !== 'undefined' && (window as unknown as LokaScriptWindow).lokascript) {\n registerRemovable().catch(console.error);\n}\n\nexport default {\n source: removableSchema.source,\n metadata: removableSchema,\n register: registerRemovable,\n};\n"],"mappings":";;;;;AAkBO,IAAM,kBAAkB,gBAAgB;AACxC,IAAM,oBAAoB;AAKjC,eAAsB,kBAAkB,WAA+C;AACrF,QAAM,KACJ,cACC,OAAO,WAAW,cAAe,OAAuC,aAAa;AAExF,MAAI,CAAC,IAAI;AACP,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,GAAG,YAAY,gBAAgB,QAAQ,EAAE,aAAa,KAAK,CAAC;AAE3E,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI,MAAM,yCAAyC,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE;AAAA,EAC1F;AAEA,QAAM,MAAM,GAAG,gBAAgB,GAAG,cAAc,IAAI,EAAE,QAAQ,oBAAI,IAAI,GAAG,SAAS,oBAAI,IAAI,EAAE;AAC5F,QAAM,GAAG,QAAQ,OAAO,KAAK,GAAG;AAClC;AAGA,IAAI,OAAO,WAAW,eAAgB,OAAuC,YAAY;AACvF,oBAAkB,EAAE,MAAM,QAAQ,KAAK;AACzC;AAEA,IAAO,oBAAQ;AAAA,EACb,QAAQ,gBAAgB;AAAA,EACxB,UAAU;AAAA,EACV,UAAU;AACZ;","names":[]}
@@ -1,37 +0,0 @@
1
- import {
2
- toggleableSchema
3
- } from "./chunk-WUHHMDAA.js";
4
-
5
- // src/behaviors/toggleable.ts
6
- var toggleableSource = toggleableSchema.source;
7
- var toggleableMetadata = toggleableSchema;
8
- async function registerToggleable(hyperfixi) {
9
- const hf = hyperfixi || (typeof window !== "undefined" ? window.lokascript : null);
10
- if (!hf) {
11
- throw new Error(
12
- "LokaScript not found. Make sure @hyperfixi/core is loaded before registering behaviors."
13
- );
14
- }
15
- const result = hf.compileSync(toggleableSchema.source, { traditional: true });
16
- if (!result.ok) {
17
- throw new Error(`Failed to compile Toggleable behavior: ${JSON.stringify(result.errors)}`);
18
- }
19
- const ctx = hf.createContext ? hf.createContext() : { locals: /* @__PURE__ */ new Map(), globals: /* @__PURE__ */ new Map() };
20
- await hf.execute(result.ast, ctx);
21
- }
22
- if (typeof window !== "undefined" && window.lokascript) {
23
- registerToggleable().catch(console.error);
24
- }
25
- var toggleable_default = {
26
- source: toggleableSchema.source,
27
- metadata: toggleableSchema,
28
- register: registerToggleable
29
- };
30
-
31
- export {
32
- toggleableSource,
33
- toggleableMetadata,
34
- registerToggleable,
35
- toggleable_default
36
- };
37
- //# sourceMappingURL=chunk-3Q652JQB.js.map