@blukavantui/community 1.0.1 → 1.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 (304) hide show
  1. package/README.md +11 -5
  2. package/dist/build-audit.json +234 -44
  3. package/dist/checkbox.css +1 -0
  4. package/dist/checkbox.d.ts +2 -0
  5. package/dist/checkbox.js +1 -0
  6. package/dist/chunk-2BSBXDOT.js +1 -0
  7. package/dist/chunk-4LHIQROI.js +0 -0
  8. package/dist/chunk-5ENZVZGF.js +0 -0
  9. package/dist/chunk-6T677NSC.js +1 -0
  10. package/dist/chunk-7CEOCZ5L.js +0 -0
  11. package/dist/chunk-7DXFNK7C.js +1 -0
  12. package/dist/chunk-AL2ETLJV.js +1 -0
  13. package/dist/chunk-BN3PDE5G.js +0 -0
  14. package/dist/chunk-CNIIMGT3.js +0 -0
  15. package/dist/chunk-CSTCEBEK.js +1 -0
  16. package/dist/chunk-DRZM2AL3.js +0 -0
  17. package/dist/chunk-EOKNLCNS.js +1 -0
  18. package/dist/chunk-EPPETNK6.js +1 -0
  19. package/dist/chunk-EPPSBHZR.js +1 -0
  20. package/dist/chunk-EPZGLHPD.js +0 -0
  21. package/dist/chunk-FNX33PDD.js +0 -0
  22. package/dist/{chunk-NTFHNL7T.js → chunk-G6OW4OZM.js} +1 -1
  23. package/dist/chunk-GS67ZBLI.js +1 -0
  24. package/dist/chunk-HO6CYWED.js +0 -0
  25. package/dist/{chunk-6EHKOVZK.js → chunk-HVR2UDFX.js} +1 -1
  26. package/dist/chunk-IJ62ILKE.js +0 -0
  27. package/dist/chunk-IKYBFMHI.js +1 -0
  28. package/dist/chunk-IO22GWON.js +0 -0
  29. package/dist/chunk-KVOA55NE.js +0 -0
  30. package/dist/{chunk-MZBB5ROJ.js → chunk-LF2HELEZ.js} +1 -1
  31. package/dist/chunk-MYVLBALF.js +1 -0
  32. package/dist/{chunk-UEOCR2C3.js → chunk-OLDA73XG.js} +2 -2
  33. package/dist/chunk-RWBLICYX.js +0 -0
  34. package/dist/chunk-RYD6RHRX.js +0 -0
  35. package/dist/chunk-T45FJNEQ.js +1 -0
  36. package/dist/chunk-TZSX4OTV.js +1 -0
  37. package/dist/chunk-UN2SLVFG.js +1 -0
  38. package/dist/{chunk-GK3INPAZ.js → chunk-V2YPIC6Z.js} +1 -1
  39. package/dist/chunk-V4ZQLQ7L.js +0 -0
  40. package/dist/chunk-VAM3FPCS.js +0 -0
  41. package/dist/chunk-VJVFHL6P.js +0 -0
  42. package/dist/chunk-VSTKXZYY.js +0 -0
  43. package/dist/chunk-W3H7XTA7.js +1 -0
  44. package/dist/chunk-WG4US4JH.js +1 -0
  45. package/dist/chunk-WGSTNXAQ.js +0 -0
  46. package/dist/chunk-WJHCVECU.js +1 -0
  47. package/dist/chunk-XVMJG26X.js +1 -0
  48. package/dist/chunk-Y4AXWTAD.js +0 -0
  49. package/dist/chunk-YCLR3IE3.js +0 -0
  50. package/dist/chunk-YLFXSHKZ.js +0 -0
  51. package/dist/chunk-ZDGZGAYG.js +0 -0
  52. package/dist/columns2.css +1 -0
  53. package/dist/columns2.d.ts +2 -0
  54. package/dist/columns2.js +1 -0
  55. package/dist/columns3.css +1 -0
  56. package/dist/columns3.d.ts +2 -0
  57. package/dist/columns3.js +1 -0
  58. package/dist/columns4.css +1 -0
  59. package/dist/columns4.d.ts +2 -0
  60. package/dist/columns4.js +1 -0
  61. package/dist/command-palette.js +1 -1
  62. package/dist/component-motion.css +39 -0
  63. package/dist/component-motion.d.ts +45 -0
  64. package/dist/component-motion.js +1 -0
  65. package/dist/content.js +1 -1
  66. package/dist/core.css +8 -0
  67. package/dist/core.js +1 -1
  68. package/dist/data-motion.css +4 -0
  69. package/dist/data-motion.d.ts +23 -0
  70. package/dist/data-motion.js +1 -0
  71. package/dist/data.css +63 -0
  72. package/dist/data.d.ts +1 -0
  73. package/dist/data.js +1 -1
  74. package/dist/date-picker.css +5 -5
  75. package/dist/date-picker.js +1 -1
  76. package/dist/date-range-picker.css +5 -5
  77. package/dist/date-range-picker.js +1 -1
  78. package/dist/date-time-picker.css +5 -5
  79. package/dist/date-time-picker.js +1 -1
  80. package/dist/draft-components.json +77 -0
  81. package/dist/drawer.js +1 -1
  82. package/dist/essentials.css +8 -0
  83. package/dist/feedback.js +1 -1
  84. package/dist/form-field.css +1 -0
  85. package/dist/form-field.d.ts +2 -0
  86. package/dist/form-field.js +1 -0
  87. package/dist/form-layout/choice.d.ts +15 -0
  88. package/dist/form-layout/layout.d.ts +12 -0
  89. package/dist/form-layout/select.d.ts +3 -0
  90. package/dist/form-layout/shared.d.ts +21 -0
  91. package/dist/form-layout/table.d.ts +20 -0
  92. package/dist/form-layout/text.d.ts +3 -0
  93. package/dist/form-layout/types.d.ts +151 -0
  94. package/dist/form-layout.css +62 -0
  95. package/dist/form-layout.d.ts +12 -0
  96. package/dist/form-layout.js +1 -0
  97. package/dist/index.d.ts +1 -0
  98. package/dist/index.js +1 -1
  99. package/dist/inputs.css +83 -20
  100. package/dist/inputs.d.ts +8 -0
  101. package/dist/inputs.js +1 -1
  102. package/dist/layout.css +63 -0
  103. package/dist/layout.d.ts +3 -0
  104. package/dist/layout.js +1 -1
  105. package/dist/modal.js +1 -1
  106. package/dist/motion/action-feedback.d.ts +14 -0
  107. package/dist/motion/border-glow.d.ts +12 -0
  108. package/dist/motion/button.d.ts +11 -0
  109. package/dist/motion/component-settings.d.ts +51 -0
  110. package/dist/motion/controller.d.ts +33 -0
  111. package/dist/motion/effects.d.ts +30 -0
  112. package/dist/motion/feedback.d.ts +6 -0
  113. package/dist/motion/loading.d.ts +9 -0
  114. package/dist/motion/optional.d.ts +35 -0
  115. package/dist/motion/photo.d.ts +7 -0
  116. package/dist/motion/presence.d.ts +12 -0
  117. package/dist/motion/scene.d.ts +10 -0
  118. package/dist/motion/screen.d.ts +6 -0
  119. package/dist/motion/scroll-reveal.d.ts +13 -0
  120. package/dist/motion/settings-group.d.ts +14 -0
  121. package/dist/motion/stagger.d.ts +12 -0
  122. package/dist/motion/structure.d.ts +18 -0
  123. package/dist/motion/tabs.d.ts +19 -0
  124. package/dist/motion/tokens.d.ts +11 -0
  125. package/dist/motion/value.d.ts +21 -0
  126. package/dist/motion/view-transition.d.ts +14 -0
  127. package/dist/motion-enhancers.d.ts +12 -0
  128. package/dist/motion-enhancers.js +1 -0
  129. package/dist/motion-settings.d.ts +3 -0
  130. package/dist/motion-settings.js +1 -0
  131. package/dist/motion.css +1076 -0
  132. package/dist/motion.d.ts +20 -0
  133. package/dist/motion.js +1 -0
  134. package/dist/native/date-picker.js +1 -1
  135. package/dist/native/date-range-picker.js +1 -1
  136. package/dist/native/date-time-picker.js +1 -1
  137. package/dist/native/form-layout.js +1 -0
  138. package/dist/native/time-picker.js +1 -1
  139. package/dist/native-components/form-layout.d.ts +1 -0
  140. package/dist/native-motion-components.d.ts +3 -0
  141. package/dist/native-motion-content.d.ts +1 -0
  142. package/dist/native-motion-foundations.d.ts +3 -0
  143. package/dist/native-motion-layout.d.ts +3 -0
  144. package/dist/native-motion-pilot.d.ts +34 -0
  145. package/dist/native-motion.d.ts +71 -0
  146. package/dist/notification.js +1 -1
  147. package/dist/pagination.js +1 -1
  148. package/dist/radio-button.css +1 -0
  149. package/dist/radio-button.d.ts +2 -0
  150. package/dist/radio-button.js +1 -0
  151. package/dist/radio-group.css +1 -0
  152. package/dist/radio-group.d.ts +2 -0
  153. package/dist/radio-group.js +1 -0
  154. package/dist/select.css +1 -0
  155. package/dist/select.d.ts +2 -0
  156. package/dist/select.js +1 -0
  157. package/dist/sidebar.js +1 -1
  158. package/dist/sizes.json +416 -112
  159. package/dist/skeleton.css +8 -0
  160. package/dist/styles.css +1213 -20
  161. package/dist/switch.css +1 -0
  162. package/dist/switch.d.ts +2 -0
  163. package/dist/switch.js +1 -0
  164. package/dist/table.css +1 -0
  165. package/dist/table.d.ts +2 -0
  166. package/dist/table.js +1 -0
  167. package/dist/text-area.css +1 -0
  168. package/dist/text-area.d.ts +2 -0
  169. package/dist/text-area.js +1 -0
  170. package/dist/text-input.css +1 -0
  171. package/dist/text-input.d.ts +2 -0
  172. package/dist/text-input.js +1 -0
  173. package/dist/time-picker.css +5 -5
  174. package/dist/time-picker.js +1 -1
  175. package/dist/tree-view.js +1 -1
  176. package/dist/web-motion/chip-ripple.d.ts +6 -0
  177. package/dist/web-motion/choice-ripple.d.ts +6 -0
  178. package/dist/web-motion/content.d.ts +9 -0
  179. package/dist/web-motion/data-rows.d.ts +12 -0
  180. package/dist/web-motion/fields.d.ts +6 -0
  181. package/dist/web-motion/navigation.d.ts +6 -0
  182. package/dist/web-motion/notifications.d.ts +6 -0
  183. package/dist/web-motion/select-ripple.d.ts +6 -0
  184. package/dist/web-motion/statistic.d.ts +13 -0
  185. package/dist/web-motion/surfaces.d.ts +10 -0
  186. package/dist/web-motion/tree.d.ts +6 -0
  187. package/docs/FORM-LAYOUT-ESSENTIALS.md +122 -0
  188. package/docs/PUBLISHING.md +129 -25
  189. package/draft-components.json +77 -0
  190. package/edition.json +44 -7
  191. package/npm-shrinkwrap.json +2 -2
  192. package/package.json +102 -10
  193. package/scripts/build-motion-css.mjs +36 -0
  194. package/scripts/build-motion-styles.mjs +29 -0
  195. package/scripts/build.mjs +20 -8
  196. package/scripts/motion-entries.mjs +2 -0
  197. package/src/checkbox.ts +2 -0
  198. package/src/columns2.ts +2 -0
  199. package/src/columns3.ts +2 -0
  200. package/src/columns4.ts +2 -0
  201. package/src/component-motion.ts +101 -0
  202. package/src/data-motion.ts +60 -0
  203. package/src/data.ts +1 -0
  204. package/src/form-field.ts +2 -0
  205. package/src/form-layout/choice.ts +82 -0
  206. package/src/form-layout/layout.ts +40 -0
  207. package/src/form-layout/select.ts +97 -0
  208. package/src/form-layout/shared.ts +57 -0
  209. package/src/form-layout/table.ts +86 -0
  210. package/src/form-layout/text.ts +73 -0
  211. package/src/form-layout/types.ts +59 -0
  212. package/src/form-layout.ts +12 -0
  213. package/src/index.ts +2 -0
  214. package/src/inputs.ts +8 -0
  215. package/src/layout.ts +3 -0
  216. package/src/motion/action-feedback.ts +38 -0
  217. package/src/motion/border-glow.ts +41 -0
  218. package/src/motion/button.ts +56 -0
  219. package/src/motion/component-settings.ts +147 -0
  220. package/src/motion/controller.ts +191 -0
  221. package/src/motion/effects.ts +75 -0
  222. package/src/motion/feedback.ts +41 -0
  223. package/src/motion/loading.ts +40 -0
  224. package/src/motion/optional.ts +123 -0
  225. package/src/motion/photo.ts +19 -0
  226. package/src/motion/presence.ts +44 -0
  227. package/src/motion/scene.ts +103 -0
  228. package/src/motion/screen.ts +15 -0
  229. package/src/motion/scroll-reveal.ts +49 -0
  230. package/src/motion/settings-group.ts +28 -0
  231. package/src/motion/stagger.ts +26 -0
  232. package/src/motion/structure.ts +75 -0
  233. package/src/motion/tabs.ts +105 -0
  234. package/src/motion/tokens.ts +2 -0
  235. package/src/motion/value.ts +61 -0
  236. package/src/motion/view-transition.ts +57 -0
  237. package/src/motion-enhancers.ts +12 -0
  238. package/src/motion-settings.ts +3 -0
  239. package/src/motion.ts +20 -0
  240. package/src/native-components/form-layout.ts +21 -0
  241. package/src/native-motion-components.ts +76 -0
  242. package/src/native-motion-content.ts +10 -0
  243. package/src/native-motion-foundations.ts +56 -0
  244. package/src/native-motion-layout.ts +35 -0
  245. package/src/native-motion-pilot.ts +151 -0
  246. package/src/native-motion.ts +188 -0
  247. package/src/radio-button.ts +2 -0
  248. package/src/radio-group.ts +2 -0
  249. package/src/runtime/date-picker.js +21 -12
  250. package/src/runtime/date-range-picker.js +21 -12
  251. package/src/runtime/date-time-picker.js +21 -12
  252. package/src/runtime/time-picker.js +21 -12
  253. package/src/select.ts +2 -0
  254. package/src/switch.ts +2 -0
  255. package/src/table.ts +2 -0
  256. package/src/text-area.ts +2 -0
  257. package/src/text-input.ts +2 -0
  258. package/src/types.ts +0 -12
  259. package/src/web-motion/chip-ripple.ts +31 -0
  260. package/src/web-motion/choice-ripple.ts +30 -0
  261. package/src/web-motion/content.ts +74 -0
  262. package/src/web-motion/data-rows.ts +106 -0
  263. package/src/web-motion/fields.ts +52 -0
  264. package/src/web-motion/navigation.ts +62 -0
  265. package/src/web-motion/notifications.ts +67 -0
  266. package/src/web-motion/select-ripple.ts +23 -0
  267. package/src/web-motion/statistic.ts +23 -0
  268. package/src/web-motion/surfaces.ts +137 -0
  269. package/src/web-motion/tree.ts +44 -0
  270. package/styles/checkbox.css +1 -0
  271. package/styles/columns2.css +1 -0
  272. package/styles/columns3.css +1 -0
  273. package/styles/columns4.css +1 -0
  274. package/styles/component-motion.css +39 -0
  275. package/styles/data-motion.css +4 -0
  276. package/styles/date-picker.css +5 -5
  277. package/styles/date-range-picker.css +5 -5
  278. package/styles/date-time-picker.css +5 -5
  279. package/styles/form-field.css +1 -0
  280. package/styles/form-layout.css +62 -0
  281. package/styles/motion-optional.css +15 -0
  282. package/styles/motion-presets.css +92 -0
  283. package/styles/motion-tokens.css +6 -0
  284. package/styles/motion.css +125 -0
  285. package/styles/radio-button.css +1 -0
  286. package/styles/radio-group.css +1 -0
  287. package/styles/select.css +1 -0
  288. package/styles/skeleton.css +8 -0
  289. package/styles/switch.css +1 -0
  290. package/styles/table.css +1 -0
  291. package/styles/text-area.css +1 -0
  292. package/styles/text-input.css +1 -0
  293. package/styles/time-picker.css +5 -5
  294. package/test/component-motion-settings.test.mjs +35 -0
  295. package/test/content-overlay-motion.test.mjs +36 -0
  296. package/test/form-layout.test.mjs +28 -0
  297. package/test/isolation.test.mjs +2 -2
  298. package/test/motion.test.mjs +215 -0
  299. package/dist/chunk-2TCNAQC6.js +0 -1
  300. package/dist/chunk-CTLZIWMX.js +0 -1
  301. package/dist/chunk-MHHJZYXW.js +0 -1
  302. package/dist/chunk-XDYBNO4P.js +0 -1
  303. /package/dist/{chunk-H7ETPJVW.js → chunk-3PODK22R.js} +0 -0
  304. /package/dist/{chunk-SIU7DSOA.js → chunk-KBWB4OIF.js} +0 -0
@@ -0,0 +1,188 @@
1
+ /** Private, unpublished O11 1.2 integration. Enhances stable native wrappers; never owns their children. */
2
+ import { createMotionController, type MotionController, type MotionEffect } from './motion/controller.js';
3
+ import { createComponentMotionScope, validateComponentMotion, type ComponentMotionSettings } from './motion/component-settings.js';
4
+ import { motionEffects, motionOrigins, type MotionOrigin } from './motion/effects.js';
5
+ import { createRipple } from './motion/optional.js';
6
+ import { createPressFeedback } from './motion/feedback.js';
7
+ import { createMotionPresence } from './motion/presence.js';
8
+ import { enhanceField } from './web-motion/fields.js';
9
+ import { nativeComponentAdapter } from './native-motion-components.js';
10
+ import { nativeFoundationAdapter } from './native-motion-foundations.js';
11
+ import { observeSurface } from './web-motion/surfaces.js';
12
+
13
+ export interface NativeMotionOptions {
14
+ EnableMotion?: boolean;
15
+ Preference?: 'System' | 'Reduced' | 'Off';
16
+ UseDefaultDuration?: boolean;
17
+ DurationMs?: number;
18
+ Easing?: ComponentMotionSettings['easing'];
19
+ Effect?: MotionEffect; Origin?: MotionOrigin; Phase?: 'enter'|'exit';
20
+ ReplayKey?: string;
21
+ ActivePanel?: string;
22
+ FocusFallbackId?: string;
23
+ Ripple?: boolean;
24
+ Value?: number; Precision?: number; Locale?: string;
25
+ IsVisible?: boolean; IsActive?: boolean; Paused?: boolean;
26
+ Once?: boolean; Order?: string; Columns?: number; GapMs?: number;
27
+ LoopDurationMs?: number; Decoration?: string;
28
+ RowKeysJson?: string; RowsJson?: string; ColumnsJson?: string; RowKey?: string; Page?: number; PageSize?: number; SortKey?: string; SortDirection?: string; Loading?: boolean;
29
+ Progress?: number; Label?: string; Variant?: string;
30
+ }
31
+ export type Kind = string;
32
+ export type Adapter = { update(p: NativeMotionOptions): void; capture?(): void; destroy(): void };
33
+ type Owner = { root: HTMLElement; kind: Kind; parent?: Owner; controller?: MotionController; scope?: ReturnType<typeof createComponentMotionScope>; adapter: Adapter; signature: string; priorKind: string | null; options: NativeMotionOptions };
34
+
35
+ export function nativeMotionSettings(p: NativeMotionOptions): ComponentMotionSettings {
36
+ const preference = p.Preference ?? 'System';
37
+ if (!['System', 'Reduced', 'Off'].includes(preference)) throw Error('Preference must be System, Reduced or Off.');
38
+ if (p.Effect !== undefined && !(motionEffects as readonly string[]).includes(p.Effect) && p.Effect !== 'scale') throw Error('Unknown motion effect.');
39
+ if (p.UseDefaultDuration === false && p.DurationMs === undefined) throw Error('DurationMs is required when UseDefaultDuration is False.');
40
+ if(p.Origin!==undefined&&!(p.Origin in motionOrigins))throw Error('Unknown transform origin.');
41
+ if(p.Phase!==undefined&&!['enter','exit'].includes(p.Phase))throw Error('Unknown motion phase.');
42
+ return validateComponentMotion({ mode: preference === 'System' ? 'inherit' : preference.toLowerCase() as 'reduced' | 'off',
43
+ duration: p.UseDefaultDuration === false ? p.DurationMs : undefined, easing: p.Easing });
44
+ }
45
+
46
+ /** Multiple producer scripts share this registry, never overlapping body controllers. */
47
+ export function createNativeMotionRuntime() {
48
+ const extensions = new Map<string, (root: HTMLElement, motion: MotionController) => Adapter>();
49
+ const owners = new Map<string, Owner>();
50
+ const hosts = new WeakMap<HTMLElement, string>();
51
+ function actionOwner(id:string) {
52
+ if(owners.has(id))return owners.get(id);
53
+ // Consumers can pass the Id of their own Container inside a provider;
54
+ // they never need access to a producer Block's private widget names.
55
+ for(let el=document.getElementById(id);el;el=el.parentElement){const owner=owners.get(hosts.get(el)||'');if(owner)return owner;}
56
+ }
57
+ function parentOf(root: HTMLElement): Owner | undefined {
58
+ for (let el = root.parentElement; el; el = el.parentElement) {
59
+ const owner = owners.get(hosts.get(el) || '');
60
+ if (owner?.kind === 'provider') return owner;
61
+ }
62
+ }
63
+ function destroy(id: string) {
64
+ const owner = owners.get(id); if (!owner) return;
65
+ // Children release their adapters before a screen provider disappears.
66
+ for (const [childId, child] of owners) if (child.parent === owner) destroy(childId);
67
+ owner.adapter.destroy(); owner.scope?.destroy(); owner.controller?.destroy();
68
+ if (owner.priorKind === null) owner.root.removeAttribute('data-blu-native-motion');
69
+ else owner.root.setAttribute('data-blu-native-motion', owner.priorKind);
70
+ owners.delete(id); if (hosts.get(owner.root) === id) hosts.delete(owner.root);
71
+ }
72
+ function makeAdapter(kind: Kind, root: HTMLElement, motion: MotionController): Adapter {
73
+ const extension = extensions.get(kind);
74
+ if(extension) return extension(root, motion);
75
+ const foundation = nativeFoundationAdapter(kind, root, motion);
76
+ if(foundation) return foundation;
77
+ const component = nativeComponentAdapter(kind, root, motion);
78
+ if(component) return component;
79
+ if (kind === 'chart') return {update() {},destroy() {}};
80
+ if (kind === 'modal') {
81
+ // The original modal owns focus, events and slots. Only its JS-created
82
+ // surface is enhanced; closing copies are inert, unnamed visual decoration.
83
+ const surface = observeSurface(root, motion, 'modal');
84
+ return { update() { surface.sync(); }, capture: surface.capture, destroy: surface.destroy };
85
+ }
86
+ if (kind === 'button') {
87
+ const press = createPressFeedback(root, motion);
88
+ let ripple: ReturnType<typeof createRipple> | undefined;
89
+ let busy = root.getAttribute('aria-busy'), face: HTMLElement | null = null;
90
+ const refresh = () => {
91
+ const next = root.getAttribute('aria-busy');
92
+ if (next === busy) return;
93
+ busy = next; if (face) motion.cancel(face);
94
+ face = root.querySelector<HTMLElement>('.blu-button-content');
95
+ // Native If widgets still own the label/spinner. Fade the stable face only.
96
+ if (face) void motion.play(face, 'fade', { duration: 350 });
97
+ };
98
+ const observer = new MutationObserver(refresh);
99
+ observer.observe(root, { attributes: true, attributeFilter: ['aria-busy'], childList: true, subtree: true });
100
+ return { update(p) { if(p.Ripple && !ripple) ripple=createRipple(root,motion); else if(!p.Ripple && ripple){ripple.destroy();ripple=undefined;} refresh(); }, destroy() { ripple?.destroy(); observer.disconnect(); if (face) motion.cancel(face); press.destroy(); } };
101
+ }
102
+ if (kind === 'field') {
103
+ let target: HTMLElement | null = null, field: ReturnType<typeof enhanceField> | undefined;
104
+ return { update() {
105
+ const next = root.matches('.blu-field') ? root : root.querySelector<HTMLElement>('.blu-field');
106
+ if (next !== target) { field?.destroy(); target = next; field = target ? enhanceField(target, motion) : undefined; }
107
+ field?.sync();
108
+ }, destroy() { field?.destroy(); } };
109
+ }
110
+ if (kind === 'workspace') {
111
+ const panels = Array.from(root.children).filter((el): el is HTMLElement => el instanceof HTMLElement && el.classList.contains('blu-native-workspace-panel'));
112
+ if (panels.length !== 2) throw Error('WorkspaceTransition requires two stable native panel wrappers.');
113
+ const presence = panels.map(el => createMotionPresence(el, motion));
114
+ let active = -1, dead = false, revision = 0;
115
+ return { update(p) {
116
+ const next = p.ActivePanel === 'Second' ? 1 : 0;
117
+ if (next === active) return;
118
+ const old = active; active = next; const own = ++revision;
119
+ const fallback = (p.FocusFallbackId ? root.ownerDocument.getElementById(p.FocusFallbackId) : root) || root;
120
+ if (old < 0) { panels.forEach((panel, i) => { panel.hidden = i !== next; panel.inert = i !== next; }); return; }
121
+ // Existing values and nodes stay in place; application state already changed.
122
+ if (panels[old].contains(root.ownerDocument.activeElement)) fallback.focus({ preventScroll: true });
123
+ if (panels[old].contains(root.ownerDocument.activeElement)) root.focus({ preventScroll: true });
124
+ const options = { effect: p.Effect || 'fade' as MotionEffect, duration: 650, focusFallback: root };
125
+ void presence[old].hide(options).then(() => { if (!dead && own === revision) panels[old].inert = true; });
126
+ void presence[next].show(options);
127
+ }, destroy() { dead = true; ++revision; presence.forEach(p => p.destroy()); } };
128
+ }
129
+ if(!['container','provider'].includes(kind)) throw Error('Unsupported native motion adapter: '+kind);
130
+ let replay: string | undefined;
131
+ return { update(p) {
132
+ if (kind !== 'container' || p.ReplayKey === replay) return;
133
+ replay = p.ReplayKey;
134
+ void motion.play(root, p.Effect || 'fade', { duration: 750, origin:p.Origin,phase:p.Phase });
135
+ }, destroy() { motion.cancel(root); } };
136
+ }
137
+ function sync(kind: Kind, hostId: string, p: NativeMotionOptions, ownerId = hostId): string {
138
+ try {
139
+ if (!ownerId) throw Error('A stable native motion owner is required.');
140
+ // These two foundation Blocks own their empty visual slot. Disabling
141
+ // motion must leave a static icon/progress indicator, not erase it.
142
+ if(p.EnableMotion!==true&&(kind==='icon'||kind==='loading'))p={...p,EnableMotion:true,Preference:'Off'};
143
+ if (p.EnableMotion !== true) { destroy(ownerId); return ''; }
144
+ const settings = nativeMotionSettings(p); // Reject invalid changes before touching the old adapter.
145
+ const root = document.getElementById(hostId); if (!root) { destroy(ownerId); return 'Native motion host is not rendered yet.'; }
146
+ const parent = parentOf(root), previous = owners.get(ownerId), occupant = hosts.get(root);
147
+ if (occupant && occupant !== ownerId) throw Error('Native motion host already has an owner.');
148
+ if (previous && (previous.root !== root || previous.kind !== kind || previous.parent !== parent)) destroy(ownerId);
149
+ let owner = owners.get(ownerId);
150
+ const signature = JSON.stringify(settings);
151
+ if (!owner) {
152
+ const controller = parent ? undefined : createMotionController(root, 'system');
153
+ const base = parent?.scope?.motion || parent?.controller || controller!;
154
+ // A private controller owns the root attribute; its component scope is destroyed first.
155
+ const scope = createComponentMotionScope(root, base, settings);
156
+ const priorKind = root.getAttribute('data-blu-native-motion');
157
+ root.setAttribute('data-blu-native-motion', kind);
158
+ try {
159
+ owner = { root, kind, parent, controller, scope, adapter: makeAdapter(kind, root, scope.motion), signature, priorKind, options: { ...p } };
160
+ owners.set(ownerId, owner); hosts.set(root, ownerId);
161
+ } catch (error) { scope.destroy(); controller?.destroy(); if (priorKind === null) root.removeAttribute('data-blu-native-motion'); else root.setAttribute('data-blu-native-motion', priorKind); throw error; }
162
+ } else if (owner.signature !== signature) { owner.scope!.update(settings); owner.signature = signature; }
163
+ const preference = settings.mode === 'inherit' ? 'system' : settings.mode || 'system';
164
+ if (owner.controller && owner.controller.preference !== preference) owner.controller.setPreference(preference);
165
+ owner.options = { ...p };
166
+ owner.adapter.update({ ...p, ReplayKey: p.ReplayKey ?? '' });
167
+ // O11 can ready inner Blocks before their enclosing provider.
168
+ if (kind === 'provider') for (const [childId, child] of Array.from(owners)) {
169
+ if (child !== owner && root.contains(child.root) && child.parent !== parentOf(child.root)) sync(child.kind, child.root.id, child.options, childId);
170
+ }
171
+ return '';
172
+ } catch (error) { return error instanceof Error ? error.message : String(error); }
173
+ }
174
+ return { version: '1.2.0-local-full', sync, destroy,
175
+ register(kind: string, build: (root: HTMLElement, motion: MotionController) => Adapter) { if(extensions.has(kind)) return; extensions.set(kind,build); },
176
+ play(id: string, targetId: string, effect: MotionEffect = 'fade', phase: 'enter'|'exit' = 'enter') { nativeMotionSettings({Effect:effect,Phase:phase});const owner=actionOwner(id),target=document.getElementById(targetId); if(!owner?.scope || !target || !owner.root.contains(target))return Promise.resolve('cancelled'); return owner.scope.motion.play(target,effect,{phase}); },
177
+ cancel(id: string, targetId: string) { const owner=actionOwner(id),target=document.getElementById(targetId); if(target&&owner?.root.contains(target)) owner.scope?.motion.cancel(target); },
178
+ setPreference(id:string,preference:NativeMotionOptions['Preference']) {const owner=actionOwner(id);return owner?sync(owner.kind,owner.root.id,{...owner.options,Preference:preference},hosts.get(owner.root)):'Native motion owner is unavailable.';},
179
+ capture: (id: string) => owners.get(id)?.adapter.capture?.(),
180
+ /** Read-only counts for lifecycle checks; does not expose application data. */
181
+ inspect: () => ({ owners: owners.size, kinds: Array.from(owners.values(), o => o.kind) }),
182
+ };
183
+ }
184
+
185
+ declare global { interface Window { BluUIMotion12?: ReturnType<typeof createNativeMotionRuntime>; BluUIMotion12Plugins?: Array<(runtime:ReturnType<typeof createNativeMotionRuntime>)=>void> } }
186
+ if (typeof window !== 'undefined' && !window.BluUIMotion12) window.BluUIMotion12 = createNativeMotionRuntime();
187
+
188
+ if(typeof window!=="undefined"&&window.BluUIMotion12)for(const install of window.BluUIMotion12Plugins?.splice(0)||[])install(window.BluUIMotion12);
@@ -0,0 +1,2 @@
1
+ export {createRadioButton} from './form-layout/choice.js';
2
+ export type * from './form-layout/types.js';
@@ -0,0 +1,2 @@
1
+ export {createRadioGroup} from './form-layout/choice.js';
2
+ export type * from './form-layout/types.js';
@@ -1,4 +1,4 @@
1
- // ../blu-grid/docs/scripts/advanced/shared.js
1
+ // docs/scripts/advanced/shared.js
2
2
  import { state, register } from "./kernel.js";
3
3
  var shared = state("advanced", () => ({ instances: /* @__PURE__ */ new Map(), serial: 0 }));
4
4
  var uid = () => `blu-advanced-${++shared.serial}`;
@@ -91,7 +91,7 @@ function createAPI(kind, init2, update2) {
91
91
  }, destroy }));
92
92
  }
93
93
 
94
- // ../blu-grid/docs/scripts/advanced/date-utils.js
94
+ // docs/scripts/advanced/date-utils.js
95
95
  var pad = (n) => String(n).padStart(2, "0");
96
96
  function dateValue(d) {
97
97
  return String(d.getFullYear()).padStart(4, "0") + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
@@ -172,7 +172,7 @@ function validateRange(start, end, options = {}) {
172
172
  return "";
173
173
  }
174
174
 
175
- // ../blu-grid/docs/scripts/advanced/pickers.js
175
+ // docs/scripts/advanced/pickers.js
176
176
  var formatOptions = ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY"];
177
177
  var today = () => dateValue(/* @__PURE__ */ new Date());
178
178
  var datePart = (value) => String(value || "").split("T")[0];
@@ -256,14 +256,15 @@ function positionPopup(s) {
256
256
  const below = Math.max(0, top + height - edge - anchor.bottom - gap), above = Math.max(0, anchor.top - gap - top - edge);
257
257
  const natural = panel.scrollHeight + 2, up = natural > below && above > below, room = Math.max(0, height - edge * 2);
258
258
  panel.style.maxHeight = room + "px";
259
- panel.style.left = Math.max(left + edge, Math.min(anchor.left, left + width - edge - panelWidth)) + "px";
259
+ const fields = s.fieldRow.getBoundingClientRect();
260
+ panel.style.left = Math.max(left + edge, Math.min(fields.left + (fields.width - panelWidth) / 2, left + width - edge - panelWidth)) + "px";
260
261
  const preferred = up ? anchor.top - gap - natural : anchor.bottom + gap;
261
262
  panel.style.top = Math.max(top + edge, Math.min(preferred, top + height - edge - Math.min(natural, room))) + "px";
262
263
  panel.dataset.placement = up ? "top" : "bottom";
263
264
  }
264
265
  function focusPopup(s) {
265
266
  const timeField = s.timeOnly || s.dateTime && s.anchor === s.fields[1].input;
266
- (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus();
267
+ (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus({ preventScroll: true });
267
268
  }
268
269
  function commit(s, first, end, time, keepOpen = false) {
269
270
  const issue = validate(s, first, end, time);
@@ -287,7 +288,7 @@ function commit(s, first, end, time, keepOpen = false) {
287
288
  s.inputSignature = JSON.stringify(s.range ? [s.start, s.end] : [s.value]);
288
289
  paintInputs(s);
289
290
  if (!keepOpen) close(s);
290
- if (returnToInput) s.fields[0].input.focus();
291
+ if (returnToInput) s.fields[0].input.focus({ preventScroll: true });
291
292
  s.notice.textContent = s.range ? first ? labelDate(s, first) + " to " + labelDate(s, end) : "Date range cleared" : value ? "Selected " + (s.timeOnly ? formatTime(value, s.cycle) : labelDate(s, first) + (s.hasTime ? " at " + formatTime(time, s.cycle) : "")) : "Selection cleared";
292
293
  if (changed) {
293
294
  if (s.range) emit(s, "Change", first, end);
@@ -391,11 +392,18 @@ function appendOption(select, value, label) {
391
392
  return option;
392
393
  }
393
394
  function selectField(s, label, name) {
394
- const wrap = node("label", "blu-picker__select-field"), caption = node("span", "", label), select = node("select", "blu-picker__input");
395
+ const wrap = node("label", "blu-picker__select-field"), caption = node("span", "blu-picker__select-caption", label), select = node("select", "blu-picker__input");
395
396
  select.id = s.uid + "-" + name;
396
397
  wrap.append(caption, select);
397
398
  return { wrap, select };
398
399
  }
400
+ function focusPopupButton(event) {
401
+ if (event.button !== 0) return;
402
+ const target = event.target.closest?.("button");
403
+ if (!target || target.disabled) return;
404
+ event.preventDefault();
405
+ target.focus({ preventScroll: true });
406
+ }
399
407
  function focusDay(s, value) {
400
408
  const min = datePart(s.options.Min) || "0001-01-01", max = datePart(s.options.Max) || "9999-12-31";
401
409
  value = value < min ? min : value > max ? max : value;
@@ -403,7 +411,7 @@ function focusDay(s, value) {
403
411
  s.focusDate = value;
404
412
  s.month = value.slice(0, 7);
405
413
  paintCalendar(s);
406
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
414
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
407
415
  }
408
416
  function keyDay(s, event, value) {
409
417
  if (event.altKey || event.ctrlKey || event.metaKey) return;
@@ -441,7 +449,7 @@ function chooseDay(s, value) {
441
449
  return;
442
450
  }
443
451
  paintCalendar(s);
444
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
452
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
445
453
  paintDraft(s);
446
454
  }
447
455
  function paintDraft(s) {
@@ -594,6 +602,7 @@ function init(s) {
594
602
  attr(s.dialog, "aria-modal", "false");
595
603
  if (s.dialog.showPopover) attr(s.dialog, "popover", "manual");
596
604
  s.root.append(s.dialog);
605
+ s.dialog.addEventListener("mousedown", focusPopupButton);
597
606
  s.title = node("h2", "blu-advanced-sr");
598
607
  s.title.id = s.uid + "-title";
599
608
  s.dialog.append(s.title);
@@ -606,7 +615,7 @@ function init(s) {
606
615
  const month = selectField(s, "Month", "month");
607
616
  s.monthSelect = month.select;
608
617
  const year = node("label", "blu-picker__select-field");
609
- year.append(node("span", "", "Year"));
618
+ year.append(node("span", "blu-picker__select-caption", "Year"));
610
619
  s.yearInput = node("input", "blu-picker__input");
611
620
  s.yearInput.type = "number";
612
621
  s.yearInput.step = "1";
@@ -804,8 +813,8 @@ function update(s) {
804
813
  else {
805
814
  paintCalendar(s);
806
815
  paintTimes(s);
807
- if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus();
808
- else if (focusId) document.getElementById(focusId)?.focus();
816
+ if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus({ preventScroll: true });
817
+ else if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });
809
818
  positionPopup(s);
810
819
  }
811
820
  }
@@ -1,4 +1,4 @@
1
- // ../blu-grid/docs/scripts/advanced/shared.js
1
+ // docs/scripts/advanced/shared.js
2
2
  import { state, register } from "./kernel.js";
3
3
  var shared = state("advanced", () => ({ instances: /* @__PURE__ */ new Map(), serial: 0 }));
4
4
  var uid = () => `blu-advanced-${++shared.serial}`;
@@ -91,7 +91,7 @@ function createAPI(kind, init2, update2) {
91
91
  }, destroy }));
92
92
  }
93
93
 
94
- // ../blu-grid/docs/scripts/advanced/date-utils.js
94
+ // docs/scripts/advanced/date-utils.js
95
95
  var pad = (n) => String(n).padStart(2, "0");
96
96
  function dateValue(d) {
97
97
  return String(d.getFullYear()).padStart(4, "0") + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
@@ -172,7 +172,7 @@ function validateRange(start, end, options = {}) {
172
172
  return "";
173
173
  }
174
174
 
175
- // ../blu-grid/docs/scripts/advanced/pickers.js
175
+ // docs/scripts/advanced/pickers.js
176
176
  var formatOptions = ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY"];
177
177
  var today = () => dateValue(/* @__PURE__ */ new Date());
178
178
  var datePart = (value) => String(value || "").split("T")[0];
@@ -256,14 +256,15 @@ function positionPopup(s) {
256
256
  const below = Math.max(0, top + height - edge - anchor.bottom - gap), above = Math.max(0, anchor.top - gap - top - edge);
257
257
  const natural = panel.scrollHeight + 2, up = natural > below && above > below, room = Math.max(0, height - edge * 2);
258
258
  panel.style.maxHeight = room + "px";
259
- panel.style.left = Math.max(left + edge, Math.min(anchor.left, left + width - edge - panelWidth)) + "px";
259
+ const fields = s.fieldRow.getBoundingClientRect();
260
+ panel.style.left = Math.max(left + edge, Math.min(fields.left + (fields.width - panelWidth) / 2, left + width - edge - panelWidth)) + "px";
260
261
  const preferred = up ? anchor.top - gap - natural : anchor.bottom + gap;
261
262
  panel.style.top = Math.max(top + edge, Math.min(preferred, top + height - edge - Math.min(natural, room))) + "px";
262
263
  panel.dataset.placement = up ? "top" : "bottom";
263
264
  }
264
265
  function focusPopup(s) {
265
266
  const timeField = s.timeOnly || s.dateTime && s.anchor === s.fields[1].input;
266
- (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus();
267
+ (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus({ preventScroll: true });
267
268
  }
268
269
  function commit(s, first, end, time, keepOpen = false) {
269
270
  const issue = validate(s, first, end, time);
@@ -287,7 +288,7 @@ function commit(s, first, end, time, keepOpen = false) {
287
288
  s.inputSignature = JSON.stringify(s.range ? [s.start, s.end] : [s.value]);
288
289
  paintInputs(s);
289
290
  if (!keepOpen) close(s);
290
- if (returnToInput) s.fields[0].input.focus();
291
+ if (returnToInput) s.fields[0].input.focus({ preventScroll: true });
291
292
  s.notice.textContent = s.range ? first ? labelDate(s, first) + " to " + labelDate(s, end) : "Date range cleared" : value ? "Selected " + (s.timeOnly ? formatTime(value, s.cycle) : labelDate(s, first) + (s.hasTime ? " at " + formatTime(time, s.cycle) : "")) : "Selection cleared";
292
293
  if (changed) {
293
294
  if (s.range) emit(s, "Change", first, end);
@@ -391,11 +392,18 @@ function appendOption(select, value, label) {
391
392
  return option;
392
393
  }
393
394
  function selectField(s, label, name) {
394
- const wrap = node("label", "blu-picker__select-field"), caption = node("span", "", label), select = node("select", "blu-picker__input");
395
+ const wrap = node("label", "blu-picker__select-field"), caption = node("span", "blu-picker__select-caption", label), select = node("select", "blu-picker__input");
395
396
  select.id = s.uid + "-" + name;
396
397
  wrap.append(caption, select);
397
398
  return { wrap, select };
398
399
  }
400
+ function focusPopupButton(event) {
401
+ if (event.button !== 0) return;
402
+ const target = event.target.closest?.("button");
403
+ if (!target || target.disabled) return;
404
+ event.preventDefault();
405
+ target.focus({ preventScroll: true });
406
+ }
399
407
  function focusDay(s, value) {
400
408
  const min = datePart(s.options.Min) || "0001-01-01", max = datePart(s.options.Max) || "9999-12-31";
401
409
  value = value < min ? min : value > max ? max : value;
@@ -403,7 +411,7 @@ function focusDay(s, value) {
403
411
  s.focusDate = value;
404
412
  s.month = value.slice(0, 7);
405
413
  paintCalendar(s);
406
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
414
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
407
415
  }
408
416
  function keyDay(s, event, value) {
409
417
  if (event.altKey || event.ctrlKey || event.metaKey) return;
@@ -441,7 +449,7 @@ function chooseDay(s, value) {
441
449
  return;
442
450
  }
443
451
  paintCalendar(s);
444
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
452
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
445
453
  paintDraft(s);
446
454
  }
447
455
  function paintDraft(s) {
@@ -594,6 +602,7 @@ function init(s) {
594
602
  attr(s.dialog, "aria-modal", "false");
595
603
  if (s.dialog.showPopover) attr(s.dialog, "popover", "manual");
596
604
  s.root.append(s.dialog);
605
+ s.dialog.addEventListener("mousedown", focusPopupButton);
597
606
  s.title = node("h2", "blu-advanced-sr");
598
607
  s.title.id = s.uid + "-title";
599
608
  s.dialog.append(s.title);
@@ -606,7 +615,7 @@ function init(s) {
606
615
  const month = selectField(s, "Month", "month");
607
616
  s.monthSelect = month.select;
608
617
  const year = node("label", "blu-picker__select-field");
609
- year.append(node("span", "", "Year"));
618
+ year.append(node("span", "blu-picker__select-caption", "Year"));
610
619
  s.yearInput = node("input", "blu-picker__input");
611
620
  s.yearInput.type = "number";
612
621
  s.yearInput.step = "1";
@@ -804,8 +813,8 @@ function update(s) {
804
813
  else {
805
814
  paintCalendar(s);
806
815
  paintTimes(s);
807
- if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus();
808
- else if (focusId) document.getElementById(focusId)?.focus();
816
+ if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus({ preventScroll: true });
817
+ else if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });
809
818
  positionPopup(s);
810
819
  }
811
820
  }
@@ -1,4 +1,4 @@
1
- // ../blu-grid/docs/scripts/advanced/shared.js
1
+ // docs/scripts/advanced/shared.js
2
2
  import { state, register } from "./kernel.js";
3
3
  var shared = state("advanced", () => ({ instances: /* @__PURE__ */ new Map(), serial: 0 }));
4
4
  var uid = () => `blu-advanced-${++shared.serial}`;
@@ -91,7 +91,7 @@ function createAPI(kind, init2, update2) {
91
91
  }, destroy }));
92
92
  }
93
93
 
94
- // ../blu-grid/docs/scripts/advanced/date-utils.js
94
+ // docs/scripts/advanced/date-utils.js
95
95
  var pad = (n) => String(n).padStart(2, "0");
96
96
  function dateValue(d) {
97
97
  return String(d.getFullYear()).padStart(4, "0") + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
@@ -172,7 +172,7 @@ function validateRange(start, end, options = {}) {
172
172
  return "";
173
173
  }
174
174
 
175
- // ../blu-grid/docs/scripts/advanced/pickers.js
175
+ // docs/scripts/advanced/pickers.js
176
176
  var formatOptions = ["YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY"];
177
177
  var today = () => dateValue(/* @__PURE__ */ new Date());
178
178
  var datePart = (value) => String(value || "").split("T")[0];
@@ -256,14 +256,15 @@ function positionPopup(s) {
256
256
  const below = Math.max(0, top + height - edge - anchor.bottom - gap), above = Math.max(0, anchor.top - gap - top - edge);
257
257
  const natural = panel.scrollHeight + 2, up = natural > below && above > below, room = Math.max(0, height - edge * 2);
258
258
  panel.style.maxHeight = room + "px";
259
- panel.style.left = Math.max(left + edge, Math.min(anchor.left, left + width - edge - panelWidth)) + "px";
259
+ const fields = s.fieldRow.getBoundingClientRect();
260
+ panel.style.left = Math.max(left + edge, Math.min(fields.left + (fields.width - panelWidth) / 2, left + width - edge - panelWidth)) + "px";
260
261
  const preferred = up ? anchor.top - gap - natural : anchor.bottom + gap;
261
262
  panel.style.top = Math.max(top + edge, Math.min(preferred, top + height - edge - Math.min(natural, room))) + "px";
262
263
  panel.dataset.placement = up ? "top" : "bottom";
263
264
  }
264
265
  function focusPopup(s) {
265
266
  const timeField = s.timeOnly || s.dateTime && s.anchor === s.fields[1].input;
266
- (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus();
267
+ (timeField ? s.hours : s.calendar.querySelector('[data-date="' + s.focusDate + '"]') || s.monthSelect).focus({ preventScroll: true });
267
268
  }
268
269
  function commit(s, first, end, time, keepOpen = false) {
269
270
  const issue = validate(s, first, end, time);
@@ -287,7 +288,7 @@ function commit(s, first, end, time, keepOpen = false) {
287
288
  s.inputSignature = JSON.stringify(s.range ? [s.start, s.end] : [s.value]);
288
289
  paintInputs(s);
289
290
  if (!keepOpen) close(s);
290
- if (returnToInput) s.fields[0].input.focus();
291
+ if (returnToInput) s.fields[0].input.focus({ preventScroll: true });
291
292
  s.notice.textContent = s.range ? first ? labelDate(s, first) + " to " + labelDate(s, end) : "Date range cleared" : value ? "Selected " + (s.timeOnly ? formatTime(value, s.cycle) : labelDate(s, first) + (s.hasTime ? " at " + formatTime(time, s.cycle) : "")) : "Selection cleared";
292
293
  if (changed) {
293
294
  if (s.range) emit(s, "Change", first, end);
@@ -391,11 +392,18 @@ function appendOption(select, value, label) {
391
392
  return option;
392
393
  }
393
394
  function selectField(s, label, name) {
394
- const wrap = node("label", "blu-picker__select-field"), caption = node("span", "", label), select = node("select", "blu-picker__input");
395
+ const wrap = node("label", "blu-picker__select-field"), caption = node("span", "blu-picker__select-caption", label), select = node("select", "blu-picker__input");
395
396
  select.id = s.uid + "-" + name;
396
397
  wrap.append(caption, select);
397
398
  return { wrap, select };
398
399
  }
400
+ function focusPopupButton(event) {
401
+ if (event.button !== 0) return;
402
+ const target = event.target.closest?.("button");
403
+ if (!target || target.disabled) return;
404
+ event.preventDefault();
405
+ target.focus({ preventScroll: true });
406
+ }
399
407
  function focusDay(s, value) {
400
408
  const min = datePart(s.options.Min) || "0001-01-01", max = datePart(s.options.Max) || "9999-12-31";
401
409
  value = value < min ? min : value > max ? max : value;
@@ -403,7 +411,7 @@ function focusDay(s, value) {
403
411
  s.focusDate = value;
404
412
  s.month = value.slice(0, 7);
405
413
  paintCalendar(s);
406
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
414
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
407
415
  }
408
416
  function keyDay(s, event, value) {
409
417
  if (event.altKey || event.ctrlKey || event.metaKey) return;
@@ -441,7 +449,7 @@ function chooseDay(s, value) {
441
449
  return;
442
450
  }
443
451
  paintCalendar(s);
444
- s.calendar.querySelector('[data-date="' + value + '"]')?.focus();
452
+ s.calendar.querySelector('[data-date="' + value + '"]')?.focus({ preventScroll: true });
445
453
  paintDraft(s);
446
454
  }
447
455
  function paintDraft(s) {
@@ -594,6 +602,7 @@ function init(s) {
594
602
  attr(s.dialog, "aria-modal", "false");
595
603
  if (s.dialog.showPopover) attr(s.dialog, "popover", "manual");
596
604
  s.root.append(s.dialog);
605
+ s.dialog.addEventListener("mousedown", focusPopupButton);
597
606
  s.title = node("h2", "blu-advanced-sr");
598
607
  s.title.id = s.uid + "-title";
599
608
  s.dialog.append(s.title);
@@ -606,7 +615,7 @@ function init(s) {
606
615
  const month = selectField(s, "Month", "month");
607
616
  s.monthSelect = month.select;
608
617
  const year = node("label", "blu-picker__select-field");
609
- year.append(node("span", "", "Year"));
618
+ year.append(node("span", "blu-picker__select-caption", "Year"));
610
619
  s.yearInput = node("input", "blu-picker__input");
611
620
  s.yearInput.type = "number";
612
621
  s.yearInput.step = "1";
@@ -804,8 +813,8 @@ function update(s) {
804
813
  else {
805
814
  paintCalendar(s);
806
815
  paintTimes(s);
807
- if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus();
808
- else if (focusId) document.getElementById(focusId)?.focus();
816
+ if (focusDate) s.calendar.querySelector('[data-date="' + focusDate + '"]')?.focus({ preventScroll: true });
817
+ else if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });
809
818
  positionPopup(s);
810
819
  }
811
820
  }