@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.
- package/README.md +11 -5
- package/dist/build-audit.json +234 -44
- package/dist/checkbox.css +1 -0
- package/dist/checkbox.d.ts +2 -0
- package/dist/checkbox.js +1 -0
- package/dist/chunk-2BSBXDOT.js +1 -0
- package/dist/chunk-4LHIQROI.js +0 -0
- package/dist/chunk-5ENZVZGF.js +0 -0
- package/dist/chunk-6T677NSC.js +1 -0
- package/dist/chunk-7CEOCZ5L.js +0 -0
- package/dist/chunk-7DXFNK7C.js +1 -0
- package/dist/chunk-AL2ETLJV.js +1 -0
- package/dist/chunk-BN3PDE5G.js +0 -0
- package/dist/chunk-CNIIMGT3.js +0 -0
- package/dist/chunk-CSTCEBEK.js +1 -0
- package/dist/chunk-DRZM2AL3.js +0 -0
- package/dist/chunk-EOKNLCNS.js +1 -0
- package/dist/chunk-EPPETNK6.js +1 -0
- package/dist/chunk-EPPSBHZR.js +1 -0
- package/dist/chunk-EPZGLHPD.js +0 -0
- package/dist/chunk-FNX33PDD.js +0 -0
- package/dist/{chunk-NTFHNL7T.js → chunk-G6OW4OZM.js} +1 -1
- package/dist/chunk-GS67ZBLI.js +1 -0
- package/dist/chunk-HO6CYWED.js +0 -0
- package/dist/{chunk-6EHKOVZK.js → chunk-HVR2UDFX.js} +1 -1
- package/dist/chunk-IJ62ILKE.js +0 -0
- package/dist/chunk-IKYBFMHI.js +1 -0
- package/dist/chunk-IO22GWON.js +0 -0
- package/dist/chunk-KVOA55NE.js +0 -0
- package/dist/{chunk-MZBB5ROJ.js → chunk-LF2HELEZ.js} +1 -1
- package/dist/chunk-MYVLBALF.js +1 -0
- package/dist/{chunk-UEOCR2C3.js → chunk-OLDA73XG.js} +2 -2
- package/dist/chunk-RWBLICYX.js +0 -0
- package/dist/chunk-RYD6RHRX.js +0 -0
- package/dist/chunk-T45FJNEQ.js +1 -0
- package/dist/chunk-TZSX4OTV.js +1 -0
- package/dist/chunk-UN2SLVFG.js +1 -0
- package/dist/{chunk-GK3INPAZ.js → chunk-V2YPIC6Z.js} +1 -1
- package/dist/chunk-V4ZQLQ7L.js +0 -0
- package/dist/chunk-VAM3FPCS.js +0 -0
- package/dist/chunk-VJVFHL6P.js +0 -0
- package/dist/chunk-VSTKXZYY.js +0 -0
- package/dist/chunk-W3H7XTA7.js +1 -0
- package/dist/chunk-WG4US4JH.js +1 -0
- package/dist/chunk-WGSTNXAQ.js +0 -0
- package/dist/chunk-WJHCVECU.js +1 -0
- package/dist/chunk-XVMJG26X.js +1 -0
- package/dist/chunk-Y4AXWTAD.js +0 -0
- package/dist/chunk-YCLR3IE3.js +0 -0
- package/dist/chunk-YLFXSHKZ.js +0 -0
- package/dist/chunk-ZDGZGAYG.js +0 -0
- package/dist/columns2.css +1 -0
- package/dist/columns2.d.ts +2 -0
- package/dist/columns2.js +1 -0
- package/dist/columns3.css +1 -0
- package/dist/columns3.d.ts +2 -0
- package/dist/columns3.js +1 -0
- package/dist/columns4.css +1 -0
- package/dist/columns4.d.ts +2 -0
- package/dist/columns4.js +1 -0
- package/dist/command-palette.js +1 -1
- package/dist/component-motion.css +39 -0
- package/dist/component-motion.d.ts +45 -0
- package/dist/component-motion.js +1 -0
- package/dist/content.js +1 -1
- package/dist/core.css +8 -0
- package/dist/core.js +1 -1
- package/dist/data-motion.css +4 -0
- package/dist/data-motion.d.ts +23 -0
- package/dist/data-motion.js +1 -0
- package/dist/data.css +63 -0
- package/dist/data.d.ts +1 -0
- package/dist/data.js +1 -1
- package/dist/date-picker.css +5 -5
- package/dist/date-picker.js +1 -1
- package/dist/date-range-picker.css +5 -5
- package/dist/date-range-picker.js +1 -1
- package/dist/date-time-picker.css +5 -5
- package/dist/date-time-picker.js +1 -1
- package/dist/draft-components.json +77 -0
- package/dist/drawer.js +1 -1
- package/dist/essentials.css +8 -0
- package/dist/feedback.js +1 -1
- package/dist/form-field.css +1 -0
- package/dist/form-field.d.ts +2 -0
- package/dist/form-field.js +1 -0
- package/dist/form-layout/choice.d.ts +15 -0
- package/dist/form-layout/layout.d.ts +12 -0
- package/dist/form-layout/select.d.ts +3 -0
- package/dist/form-layout/shared.d.ts +21 -0
- package/dist/form-layout/table.d.ts +20 -0
- package/dist/form-layout/text.d.ts +3 -0
- package/dist/form-layout/types.d.ts +151 -0
- package/dist/form-layout.css +62 -0
- package/dist/form-layout.d.ts +12 -0
- package/dist/form-layout.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/inputs.css +83 -20
- package/dist/inputs.d.ts +8 -0
- package/dist/inputs.js +1 -1
- package/dist/layout.css +63 -0
- package/dist/layout.d.ts +3 -0
- package/dist/layout.js +1 -1
- package/dist/modal.js +1 -1
- package/dist/motion/action-feedback.d.ts +14 -0
- package/dist/motion/border-glow.d.ts +12 -0
- package/dist/motion/button.d.ts +11 -0
- package/dist/motion/component-settings.d.ts +51 -0
- package/dist/motion/controller.d.ts +33 -0
- package/dist/motion/effects.d.ts +30 -0
- package/dist/motion/feedback.d.ts +6 -0
- package/dist/motion/loading.d.ts +9 -0
- package/dist/motion/optional.d.ts +35 -0
- package/dist/motion/photo.d.ts +7 -0
- package/dist/motion/presence.d.ts +12 -0
- package/dist/motion/scene.d.ts +10 -0
- package/dist/motion/screen.d.ts +6 -0
- package/dist/motion/scroll-reveal.d.ts +13 -0
- package/dist/motion/settings-group.d.ts +14 -0
- package/dist/motion/stagger.d.ts +12 -0
- package/dist/motion/structure.d.ts +18 -0
- package/dist/motion/tabs.d.ts +19 -0
- package/dist/motion/tokens.d.ts +11 -0
- package/dist/motion/value.d.ts +21 -0
- package/dist/motion/view-transition.d.ts +14 -0
- package/dist/motion-enhancers.d.ts +12 -0
- package/dist/motion-enhancers.js +1 -0
- package/dist/motion-settings.d.ts +3 -0
- package/dist/motion-settings.js +1 -0
- package/dist/motion.css +1076 -0
- package/dist/motion.d.ts +20 -0
- package/dist/motion.js +1 -0
- package/dist/native/date-picker.js +1 -1
- package/dist/native/date-range-picker.js +1 -1
- package/dist/native/date-time-picker.js +1 -1
- package/dist/native/form-layout.js +1 -0
- package/dist/native/time-picker.js +1 -1
- package/dist/native-components/form-layout.d.ts +1 -0
- package/dist/native-motion-components.d.ts +3 -0
- package/dist/native-motion-content.d.ts +1 -0
- package/dist/native-motion-foundations.d.ts +3 -0
- package/dist/native-motion-layout.d.ts +3 -0
- package/dist/native-motion-pilot.d.ts +34 -0
- package/dist/native-motion.d.ts +71 -0
- package/dist/notification.js +1 -1
- package/dist/pagination.js +1 -1
- package/dist/radio-button.css +1 -0
- package/dist/radio-button.d.ts +2 -0
- package/dist/radio-button.js +1 -0
- package/dist/radio-group.css +1 -0
- package/dist/radio-group.d.ts +2 -0
- package/dist/radio-group.js +1 -0
- package/dist/select.css +1 -0
- package/dist/select.d.ts +2 -0
- package/dist/select.js +1 -0
- package/dist/sidebar.js +1 -1
- package/dist/sizes.json +416 -112
- package/dist/skeleton.css +8 -0
- package/dist/styles.css +1213 -20
- package/dist/switch.css +1 -0
- package/dist/switch.d.ts +2 -0
- package/dist/switch.js +1 -0
- package/dist/table.css +1 -0
- package/dist/table.d.ts +2 -0
- package/dist/table.js +1 -0
- package/dist/text-area.css +1 -0
- package/dist/text-area.d.ts +2 -0
- package/dist/text-area.js +1 -0
- package/dist/text-input.css +1 -0
- package/dist/text-input.d.ts +2 -0
- package/dist/text-input.js +1 -0
- package/dist/time-picker.css +5 -5
- package/dist/time-picker.js +1 -1
- package/dist/tree-view.js +1 -1
- package/dist/web-motion/chip-ripple.d.ts +6 -0
- package/dist/web-motion/choice-ripple.d.ts +6 -0
- package/dist/web-motion/content.d.ts +9 -0
- package/dist/web-motion/data-rows.d.ts +12 -0
- package/dist/web-motion/fields.d.ts +6 -0
- package/dist/web-motion/navigation.d.ts +6 -0
- package/dist/web-motion/notifications.d.ts +6 -0
- package/dist/web-motion/select-ripple.d.ts +6 -0
- package/dist/web-motion/statistic.d.ts +13 -0
- package/dist/web-motion/surfaces.d.ts +10 -0
- package/dist/web-motion/tree.d.ts +6 -0
- package/docs/FORM-LAYOUT-ESSENTIALS.md +122 -0
- package/docs/PUBLISHING.md +129 -25
- package/draft-components.json +77 -0
- package/edition.json +44 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +102 -10
- package/scripts/build-motion-css.mjs +36 -0
- package/scripts/build-motion-styles.mjs +29 -0
- package/scripts/build.mjs +20 -8
- package/scripts/motion-entries.mjs +2 -0
- package/src/checkbox.ts +2 -0
- package/src/columns2.ts +2 -0
- package/src/columns3.ts +2 -0
- package/src/columns4.ts +2 -0
- package/src/component-motion.ts +101 -0
- package/src/data-motion.ts +60 -0
- package/src/data.ts +1 -0
- package/src/form-field.ts +2 -0
- package/src/form-layout/choice.ts +82 -0
- package/src/form-layout/layout.ts +40 -0
- package/src/form-layout/select.ts +97 -0
- package/src/form-layout/shared.ts +57 -0
- package/src/form-layout/table.ts +86 -0
- package/src/form-layout/text.ts +73 -0
- package/src/form-layout/types.ts +59 -0
- package/src/form-layout.ts +12 -0
- package/src/index.ts +2 -0
- package/src/inputs.ts +8 -0
- package/src/layout.ts +3 -0
- package/src/motion/action-feedback.ts +38 -0
- package/src/motion/border-glow.ts +41 -0
- package/src/motion/button.ts +56 -0
- package/src/motion/component-settings.ts +147 -0
- package/src/motion/controller.ts +191 -0
- package/src/motion/effects.ts +75 -0
- package/src/motion/feedback.ts +41 -0
- package/src/motion/loading.ts +40 -0
- package/src/motion/optional.ts +123 -0
- package/src/motion/photo.ts +19 -0
- package/src/motion/presence.ts +44 -0
- package/src/motion/scene.ts +103 -0
- package/src/motion/screen.ts +15 -0
- package/src/motion/scroll-reveal.ts +49 -0
- package/src/motion/settings-group.ts +28 -0
- package/src/motion/stagger.ts +26 -0
- package/src/motion/structure.ts +75 -0
- package/src/motion/tabs.ts +105 -0
- package/src/motion/tokens.ts +2 -0
- package/src/motion/value.ts +61 -0
- package/src/motion/view-transition.ts +57 -0
- package/src/motion-enhancers.ts +12 -0
- package/src/motion-settings.ts +3 -0
- package/src/motion.ts +20 -0
- package/src/native-components/form-layout.ts +21 -0
- package/src/native-motion-components.ts +76 -0
- package/src/native-motion-content.ts +10 -0
- package/src/native-motion-foundations.ts +56 -0
- package/src/native-motion-layout.ts +35 -0
- package/src/native-motion-pilot.ts +151 -0
- package/src/native-motion.ts +188 -0
- package/src/radio-button.ts +2 -0
- package/src/radio-group.ts +2 -0
- package/src/runtime/date-picker.js +21 -12
- package/src/runtime/date-range-picker.js +21 -12
- package/src/runtime/date-time-picker.js +21 -12
- package/src/runtime/time-picker.js +21 -12
- package/src/select.ts +2 -0
- package/src/switch.ts +2 -0
- package/src/table.ts +2 -0
- package/src/text-area.ts +2 -0
- package/src/text-input.ts +2 -0
- package/src/types.ts +0 -12
- package/src/web-motion/chip-ripple.ts +31 -0
- package/src/web-motion/choice-ripple.ts +30 -0
- package/src/web-motion/content.ts +74 -0
- package/src/web-motion/data-rows.ts +106 -0
- package/src/web-motion/fields.ts +52 -0
- package/src/web-motion/navigation.ts +62 -0
- package/src/web-motion/notifications.ts +67 -0
- package/src/web-motion/select-ripple.ts +23 -0
- package/src/web-motion/statistic.ts +23 -0
- package/src/web-motion/surfaces.ts +137 -0
- package/src/web-motion/tree.ts +44 -0
- package/styles/checkbox.css +1 -0
- package/styles/columns2.css +1 -0
- package/styles/columns3.css +1 -0
- package/styles/columns4.css +1 -0
- package/styles/component-motion.css +39 -0
- package/styles/data-motion.css +4 -0
- package/styles/date-picker.css +5 -5
- package/styles/date-range-picker.css +5 -5
- package/styles/date-time-picker.css +5 -5
- package/styles/form-field.css +1 -0
- package/styles/form-layout.css +62 -0
- package/styles/motion-optional.css +15 -0
- package/styles/motion-presets.css +92 -0
- package/styles/motion-tokens.css +6 -0
- package/styles/motion.css +125 -0
- package/styles/radio-button.css +1 -0
- package/styles/radio-group.css +1 -0
- package/styles/select.css +1 -0
- package/styles/skeleton.css +8 -0
- package/styles/switch.css +1 -0
- package/styles/table.css +1 -0
- package/styles/text-area.css +1 -0
- package/styles/text-input.css +1 -0
- package/styles/time-picker.css +5 -5
- package/test/component-motion-settings.test.mjs +35 -0
- package/test/content-overlay-motion.test.mjs +36 -0
- package/test/form-layout.test.mjs +28 -0
- package/test/isolation.test.mjs +2 -2
- package/test/motion.test.mjs +215 -0
- package/dist/chunk-2TCNAQC6.js +0 -1
- package/dist/chunk-CTLZIWMX.js +0 -1
- package/dist/chunk-MHHJZYXW.js +0 -1
- package/dist/chunk-XDYBNO4P.js +0 -1
- /package/dist/{chunk-H7ETPJVW.js → chunk-3PODK22R.js} +0 -0
- /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);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
}
|