@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,125 @@
|
|
|
1
|
+
/* Optional web-only 1.2 draft. Import after the existing theme/button styles. */
|
|
2
|
+
@import './motion-presets.css';
|
|
3
|
+
@import './motion-tokens.css';
|
|
4
|
+
@import './motion-optional.css';
|
|
5
|
+
:root {
|
|
6
|
+
--blu-motion-duration: var(--blu-motion-normal);
|
|
7
|
+
--blu-motion-delay: 0ms;
|
|
8
|
+
--blu-motion-distance: 12px;
|
|
9
|
+
--blu-motion-ease: cubic-bezier(.2, .8, .2, 1);
|
|
10
|
+
}
|
|
11
|
+
.blu-motion-fast { --blu-motion-duration: var(--blu-motion-fast); }
|
|
12
|
+
.blu-motion-slow { --blu-motion-duration: var(--blu-motion-slow); }
|
|
13
|
+
.blu-motion-delay-1 { --blu-motion-delay: 60ms; }
|
|
14
|
+
.blu-motion-delay-2 { --blu-motion-delay: 120ms; }
|
|
15
|
+
.blu-motion-delay-3 { --blu-motion-delay: 180ms; }
|
|
16
|
+
.blu-motion-spin { animation: blu-motion-spin 800ms linear infinite; }
|
|
17
|
+
.blu-motion-pulse { animation: blu-motion-pulse 900ms ease-in-out 3; }
|
|
18
|
+
.blu-motion-hover-lift { transition: transform var(--blu-motion-duration) var(--blu-motion-ease), box-shadow var(--blu-motion-duration) var(--blu-motion-ease); }
|
|
19
|
+
@media (hover: hover) { .blu-motion-hover-lift:hover { transform: translateY(calc(-1 * var(--blu-motion-lift-distance, 4px))); box-shadow: 0 8px 20px #28334b12; } }
|
|
20
|
+
.blu-motion-hover-lift:is(:focus-visible, [data-blu-lifted="true"]) { transform: translateY(calc(-1 * var(--blu-motion-lift-distance, 4px))); box-shadow: 0 8px 20px #28334b12; }
|
|
21
|
+
/* Requires one inner wrapper. Content remains in the DOM; the owner manages inert/hidden and focus. */
|
|
22
|
+
.blu-motion-expand { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--blu-motion-duration) var(--blu-motion-ease); }
|
|
23
|
+
.blu-motion-expand[data-open="true"] { grid-template-rows: 1fr; }
|
|
24
|
+
.blu-motion-expand > * { min-height: 0; overflow: hidden; }
|
|
25
|
+
@keyframes blu-motion-spin { to { transform: rotate(360deg); } }
|
|
26
|
+
@keyframes blu-motion-pulse { 50% { opacity: .45; } }
|
|
27
|
+
|
|
28
|
+
.blu-motion-button.blu-button { display: inline-grid; gap: 0; transition-duration: var(--blu-motion-duration); }
|
|
29
|
+
.blu-motion-button__face, .blu-motion-button__reserve { grid-area: 1 / 1; padding-inline: 24px; }
|
|
30
|
+
.blu-motion-button__face { position: relative; display: inline-flex; align-items: center; justify-content: center; }
|
|
31
|
+
.blu-motion-button__reserve { visibility: hidden; pointer-events: none; }
|
|
32
|
+
.blu-motion-button .blu-button__spinner { position: absolute; inset-inline-start: 0; }
|
|
33
|
+
.blu-motion-button[data-spinner-position="Trailing"] .blu-button__spinner { inset-inline-start: auto; inset-inline-end: 0; }
|
|
34
|
+
.blu-motion-button .blu-button__spinner[hidden] { display: none; }
|
|
35
|
+
.blu-motion-button.blu-button--loading { pointer-events: auto; }
|
|
36
|
+
.blu-motion-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
37
|
+
|
|
38
|
+
.blu-loading { display: flex; align-items: center; gap: 10px; font: inherit; color: inherit; }
|
|
39
|
+
.blu-loading [hidden] { display: none !important; }
|
|
40
|
+
.blu-loading__visual { width: 20px; height: 20px; flex-shrink: 0; }
|
|
41
|
+
.blu-loading[data-variant="Spinner"] .blu-loading__visual { border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: blu-motion-spin 800ms linear infinite; }
|
|
42
|
+
.blu-loading[data-variant="Spinner"] i { display: none; }
|
|
43
|
+
.blu-loading[data-variant="Dots"] .blu-loading__visual { display: flex; align-items: center; width: 32px; gap: 3px; }
|
|
44
|
+
.blu-loading[data-variant="Dots"] i { width: 7px; height: 7px; background: currentColor; border-radius: 50%; animation: blu-motion-pulse 900ms ease-in-out infinite; }
|
|
45
|
+
.blu-loading[data-variant="Dots"] i:nth-child(2) { animation-delay: 150ms; }
|
|
46
|
+
.blu-loading[data-variant="Dots"] i:nth-child(3) { animation-delay: 300ms; }
|
|
47
|
+
.blu-loading progress { width: 100%; height: 8px; accent-color: var(--bluui-primary, #5145e5); border: 0; border-radius: 8px; background: #e7e8f1; overflow: hidden; }
|
|
48
|
+
.blu-loading progress::-webkit-progress-bar { background: #e7e8f1; border-radius: 8px; }
|
|
49
|
+
.blu-loading progress::-webkit-progress-value { background: var(--bluui-primary, #5145e5); border-radius: 8px; }
|
|
50
|
+
.blu-loading progress::-moz-progress-bar { background: var(--bluui-primary, #5145e5); }
|
|
51
|
+
.blu-loading[data-variant="Bar"] { flex-direction: column; align-items: stretch; }
|
|
52
|
+
.blu-loading__bar { height: 8px; overflow: hidden; border-radius: 8px; background: #e7e8f1; }
|
|
53
|
+
.blu-loading__bar::after { content: ''; display: block; width: 35%; height: 100%; background: var(--bluui-primary, #5145e5); border-radius: inherit; animation: blu-motion-progress 1.2s ease-in-out infinite alternate; }
|
|
54
|
+
@keyframes blu-motion-progress { to { transform: translateX(185%); } }
|
|
55
|
+
|
|
56
|
+
/* Explicit off/reduced scopes and per-element opt-out; never hide useful content. */
|
|
57
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off),
|
|
58
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off) * {
|
|
59
|
+
--blu-motion-duration: 0ms;
|
|
60
|
+
--blu-motion-delay: 0ms;
|
|
61
|
+
}
|
|
62
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off) :is([class*="blu-motion-"], .blu-button__spinner, .blu-loading__visual, .blu-loading i),
|
|
63
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off)[class*="blu-motion-"],
|
|
64
|
+
.blu-motion-off { animation: none !important; transition: none !important; }
|
|
65
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off) .blu-loading__bar::after { animation: none !important; }
|
|
66
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off) .blu-motion-hover-lift:is(:hover, :focus-visible, [data-blu-lifted="true"]) { transform: none; }
|
|
67
|
+
@media (prefers-reduced-motion: reduce) {
|
|
68
|
+
[class*="blu-motion-"], .blu-button__spinner, .blu-loading__visual, .blu-loading i, .blu-loading__bar::after { animation: none !important; transition: none !important; }
|
|
69
|
+
.blu-motion-hover-lift:is(:hover, :focus-visible, [data-blu-lifted="true"]) { transform: none; }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Press state is finite and never changes a control's activation semantics. */
|
|
73
|
+
.blu-motion-press[data-blu-pressed="true"] { transform: scale(.97); }
|
|
74
|
+
.blu-motion-highlight { animation: blu-motion-highlight 1400ms ease-in-out 1; }
|
|
75
|
+
.blu-motion-nudge { animation: blu-motion-nudge 280ms var(--blu-motion-ease) 1; }
|
|
76
|
+
@keyframes blu-motion-highlight { 45% { box-shadow: 0 0 0 4px var(--blu-motion-highlight, #7770ed40); } }
|
|
77
|
+
@keyframes blu-motion-nudge { 25% { transform: translateX(-3px); } 60% { transform: translateX(3px); } }
|
|
78
|
+
:is([data-blu-motion="off"], [data-blu-motion="reduced"], .blu-motion-off) .blu-motion-press[data-blu-pressed="true"],
|
|
79
|
+
.blu-motion-press.blu-motion-off[data-blu-pressed="true"] { transform: none; }
|
|
80
|
+
@media (prefers-reduced-motion: reduce) { .blu-motion-press[data-blu-pressed="true"] { transform: none; } }
|
|
81
|
+
|
|
82
|
+
.blu-motion-photo-frame { position:relative;overflow:hidden;isolation:isolate; }
|
|
83
|
+
.blu-motion-photo-frame > img { display:block;width:100%;height:100%;object-fit:cover; }
|
|
84
|
+
|
|
85
|
+
/* A masked border, not an overlay on the content. The inner square rotates;
|
|
86
|
+
the outer mask and soft halo stay aligned to the original rounded box. */
|
|
87
|
+
.blu-motion-border-glow{position:absolute;inset:-1px;border-radius:inherit;pointer-events:none!important;z-index:1;filter:drop-shadow(0 0 4px var(--blu-glow-halo,#9b85ee80))}
|
|
88
|
+
.blu-motion-border-glow__rim{position:absolute;inset:0;padding:var(--blu-glow-width,2px);border-radius:inherit;overflow:hidden;pointer-events:none!important;-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}
|
|
89
|
+
.blu-motion-border-glow__beam{position:absolute;left:50%;top:50%;width:var(--blu-glow-size,1800px);height:var(--blu-glow-size,1800px);background:conic-gradient(transparent 0deg,transparent 240deg,var(--blu-glow-tail,#b5a4ef60) 265deg,var(--blu-glow-color,#8060dd) 295deg,var(--blu-glow-tip,#d7c6ff) 315deg,transparent 340deg);transform:translate(-50%,-50%) rotate(0turn);animation:blu-border-orbit var(--blu-glow-duration,12s) linear infinite}
|
|
90
|
+
.blu-motion-border-glow[data-paused=true] .blu-motion-border-glow__beam{animation-play-state:paused}
|
|
91
|
+
@keyframes blu-border-orbit{from{transform:translate(-50%,-50%) rotate(0turn)}to{transform:translate(-50%,-50%) rotate(1turn)}}
|
|
92
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) .blu-motion-border-glow{filter:none}
|
|
93
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) .blu-motion-border-glow__rim{background:var(--blu-glow-static,#b5a4ef80)}
|
|
94
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) .blu-motion-border-glow__beam{animation:none!important;display:none}
|
|
95
|
+
@media(prefers-reduced-motion:reduce){.blu-motion-border-glow{filter:none}.blu-motion-border-glow__rim{background:var(--blu-glow-static,#b5a4ef80)}.blu-motion-border-glow__beam{animation:none!important;display:none}}
|
|
96
|
+
@media(forced-colors:active){.blu-motion-border-glow{display:none}}
|
|
97
|
+
|
|
98
|
+
/* Opt-in content adapters. Hover/focus lift composes with collection positioning. */
|
|
99
|
+
[data-blu-content-motion] .blu-profile-card{transition:translate var(--blu-component-motion-duration,450ms) var(--blu-component-motion-ease,ease-in-out)}
|
|
100
|
+
[data-blu-content-motion] .blu-profile-card:is(:hover,:focus-within){translate:0 -4px}
|
|
101
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion] .blu-profile-card,
|
|
102
|
+
[data-blu-content-motion]:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) .blu-profile-card{translate:none;transition:none}
|
|
103
|
+
[data-blu-content-motion] :is(.blu-tree-select__check,.blu-video-player__heatmap){transition-duration:var(--blu-component-motion-duration,450ms);transition-timing-function:var(--blu-component-motion-ease,ease-in-out)}
|
|
104
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion] :is(.blu-tree-select__check,.blu-video-player__heatmap){transition:none}
|
|
105
|
+
@media(prefers-reduced-motion:reduce){[data-blu-content-motion] .blu-profile-card{translate:none;transition:none}}
|
|
106
|
+
|
|
107
|
+
/* Optional ripple stays clipped to the selectable part of a chip. */
|
|
108
|
+
[data-blu-content-motion=chip] .blu-chip__select{border-radius:inherit}
|
|
109
|
+
[data-blu-content-motion=skeleton] .blu-skeleton--animated::after{animation-duration:1800ms;animation-iteration-count:2}
|
|
110
|
+
[data-blu-component-motion=statistic] .blu-statistic__loading{animation-duration:1800ms;animation-iteration-count:2}
|
|
111
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion=skeleton] .blu-skeleton--animated::after{animation:none!important;display:none}
|
|
112
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-component-motion=statistic] .blu-statistic__loading{animation:none!important}
|
|
113
|
+
@media(prefers-reduced-motion:reduce){[data-blu-content-motion=skeleton] .blu-skeleton--animated::after{animation:none!important;display:none}[data-blu-component-motion=statistic] .blu-statistic__loading{animation:none!important}}
|
|
114
|
+
|
|
115
|
+
/* Rich media hover must obey the same scope as its lightbox. */
|
|
116
|
+
[data-blu-content-motion=media-image] img{transition-duration:var(--blu-component-motion-duration,650ms);transition-timing-function:var(--blu-component-motion-ease,ease-in-out)}
|
|
117
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion=media-image] img{transition:none!important;transform:none!important}
|
|
118
|
+
/* Legacy content CSS follows the opt-in controller as well as system preference. */
|
|
119
|
+
[data-blu-content-motion] .blu-button{transition-duration:var(--blu-component-motion-duration,280ms)}
|
|
120
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion] *,
|
|
121
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion] *::before,
|
|
122
|
+
:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) [data-blu-content-motion] *::after,
|
|
123
|
+
[data-blu-content-motion]:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) *,
|
|
124
|
+
[data-blu-content-motion]:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) *::before,
|
|
125
|
+
[data-blu-content-motion]:is([data-blu-motion=reduced],[data-blu-motion=off],.blu-motion-off) *::after{transition:none!important;animation:none!important}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
package/styles/skeleton.css
CHANGED
|
@@ -67,3 +67,11 @@ The base shape remains visible when the animated overlay is removed. */
|
|
|
67
67
|
-servicestudio-width: 180px;
|
|
68
68
|
-servicestudio-height: 20px;
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
/* The opt-in native motion adapter owns the shimmer. Avoid a second legacy
|
|
72
|
+
wave, including when the adapter is Reduced or Off. Removing the adapter
|
|
73
|
+
restores the existing IsAnimated behavior without changing its contract. */
|
|
74
|
+
.blu-skeleton[data-blu-native-motion="loading-skeleton"]::after {
|
|
75
|
+
animation: none !important;
|
|
76
|
+
display: none !important;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
package/styles/table.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './form-layout.css';
|
package/styles/time-picker.css
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
.blu-picker [hidden],.blu-picker__dialog [hidden] {display:none!important;}
|
|
7
7
|
.blu-picker__fields {display:flex;flex-wrap:wrap;gap:12px;}
|
|
8
8
|
.blu-picker__field {flex:1 1 180px;min-width:0;}
|
|
9
|
-
.blu-picker__field label,.blu-picker__select-
|
|
9
|
+
.blu-picker__field label,.blu-picker__select-caption {display:block;margin-bottom:5px;color:var(--bluui-text,#182230);font-weight:600;}
|
|
10
10
|
.blu-picker__input {display:block;width:100%;min-width:0;min-height:42px;padding:9px 11px;border:1px solid var(--bluui-control-border,#69788d);border-radius:9px;background:var(--bluui-surface,#fff);color:var(--bluui-text,#182230);font:inherit;line-height:1.4;}
|
|
11
11
|
.blu-picker__field-control {position:relative;}
|
|
12
12
|
.blu-picker__field-control>.blu-picker__input {padding-right:38px;}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
.blu-picker__dialog::backdrop {background:transparent;pointer-events:none;}
|
|
30
30
|
.blu-picker__dialog-head {display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:14px;}
|
|
31
31
|
.blu-picker__dialog-head h2 {font-size:16px;line-height:1.4;font-family:var(--blu-font-heading-family,inherit);margin:0;overflow-wrap:anywhere;}
|
|
32
|
-
.blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr)
|
|
32
|
+
.blu-picker__calendar-nav {display:grid;grid-template-columns:32px minmax(0,1fr) 88px 32px;align-items:end;gap:6px;margin-top:0;}
|
|
33
33
|
.blu-picker__calendar-nav>.blu-picker__button {height:36px;padding:2px;font-size:22px;}
|
|
34
34
|
.blu-picker__select-field {min-width:0;}
|
|
35
35
|
.blu-picker__caption {font-size:15px;text-align:center;margin:16px 0 8px;}
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
/* Compact popup controls; editable text stays at 16px to avoid iOS focus zoom. */
|
|
55
55
|
.blu-picker__dialog .blu-picker__input {min-height:36px;padding:5px 7px;font-size:16px;line-height:1.2;}
|
|
56
56
|
.blu-picker__dialog .blu-picker__button {min-height:34px;min-width:32px;padding:5px 9px;font-size:13px;line-height:1.25;}
|
|
57
|
-
.blu-picker__dialog .blu-picker__select-
|
|
58
|
-
.blu-picker__calendar-nav .blu-picker__select-
|
|
57
|
+
.blu-picker__dialog .blu-picker__select-caption {font-size:11px;margin-bottom:2px;}
|
|
58
|
+
.blu-picker__calendar-nav .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
|
|
59
59
|
.blu-picker__dialog .blu-picker__error {margin-top:4px;font-size:12px;line-height:1.3;}
|
|
60
60
|
@media(pointer:coarse){
|
|
61
61
|
.blu-picker__field-control>.blu-picker__input {font-size:16px;min-height:44px;}
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
.blu-picker__dialog[data-compact=true] .blu-picker__calendar td {height:24px;}
|
|
73
73
|
.blu-picker__dialog[data-compact=true] .blu-picker__day {min-height:24px;padding:3px 0;}
|
|
74
74
|
.blu-picker__dialog[data-compact=true] .blu-picker__time-area {margin:4px 0;gap:6px;}
|
|
75
|
-
.blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-
|
|
75
|
+
.blu-picker__dialog[data-compact=true] .blu-picker__time-area .blu-picker__select-caption {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
|
|
76
76
|
.blu-picker__dialog[data-compact=true] .blu-picker__selection {position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0;}
|
|
77
77
|
.blu-picker__dialog[data-compact=true] .blu-picker__popup-actions {margin-top:4px;gap:4px;}
|
|
78
78
|
.blu-picker__dialog[data-compact=true] .blu-picker__presets {margin-bottom:4px;gap:4px;}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {build} from 'esbuild';
|
|
4
|
+
const built=await build({entryPoints:['src/motion/component-settings.ts'],bundle:true,format:'esm',platform:'node',write:false});
|
|
5
|
+
const {validateComponentMotion}=await import('data:text/javascript;base64,'+Buffer.from(built.outputFiles[0].text).toString('base64'));
|
|
6
|
+
test('component settings preserve explicit zero and omit undefined inheritance values',()=>{
|
|
7
|
+
assert.deepEqual(validateComponentMotion({duration:0,easing:undefined}),{duration:0});
|
|
8
|
+
assert.deepEqual(validateComponentMotion({mode:'inherit',duration:2000,easing:'ease-in-out'}),{mode:'inherit',duration:2000,easing:'ease-in-out'});
|
|
9
|
+
});
|
|
10
|
+
test('invalid settings cannot silently disable or produce unbounded component motion',()=>{
|
|
11
|
+
for(const invalid of [null,[],{duration:NaN},{duration:Infinity},{duration:-1},{duration:2001},{duration:'750'},{mode:'full'},{easing:'constructor'}])assert.throws(()=>validateComponentMotion(invalid));
|
|
12
|
+
});
|
|
13
|
+
test('validated settings are detached from later caller changes',()=>{
|
|
14
|
+
const source={duration:750,easing:'linear'}, settings=validateComponentMotion(source);source.duration=1000;
|
|
15
|
+
assert.equal(settings.duration,750);assert.equal(source.duration,1000);
|
|
16
|
+
});
|
|
17
|
+
const grouped=await build({entryPoints:['src/motion/settings-group.ts'],bundle:true,format:'esm',platform:'node',write:false});
|
|
18
|
+
const {createMotionSettingsGroup}=await import('data:text/javascript;base64,'+Buffer.from(grouped.outputFiles[0].text).toString('base64'));
|
|
19
|
+
test('collection configuration preserves instance overrides, resets inheritance and detaches disposed instances',()=>{
|
|
20
|
+
const group=createMotionSettingsGroup({duration:750,mode:'reduced'});let a,b;
|
|
21
|
+
const first=group.bind(value=>{a=value;}),second=group.bind(value=>{b=value;});
|
|
22
|
+
first.setMotion({duration:1200,mode:'off'});second.setMotion();
|
|
23
|
+
group.configure({duration:450,easing:'linear'});
|
|
24
|
+
assert.deepEqual(a,{duration:1200,easing:'linear',mode:'off'});assert.deepEqual(b,{duration:450,easing:'linear'});
|
|
25
|
+
first.setMotion({mode:'inherit'});assert.deepEqual(a,b);
|
|
26
|
+
second.destroy();group.configure({duration:1000});assert.equal(b.duration,450);assert.equal(a.duration,1000);
|
|
27
|
+
assert.throws(()=>second.setMotion());
|
|
28
|
+
});
|
|
29
|
+
test('invalid collection defaults and overrides are rejected before changing any member',()=>{
|
|
30
|
+
const group=createMotionSettingsGroup({duration:750});let current;
|
|
31
|
+
const member=group.bind(value=>{current=value;});member.setMotion();
|
|
32
|
+
assert.throws(()=>group.configure({duration:NaN}));assert.throws(()=>member.setMotion({mode:'full'}));
|
|
33
|
+
assert.deepEqual(current,{duration:750});assert.deepEqual(group.settings,{duration:750});
|
|
34
|
+
const detached=group.settings;detached.duration=1000;assert.equal(group.settings.duration,750);
|
|
35
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {build} from 'esbuild';
|
|
4
|
+
const result=await build({entryPoints:['src/web-motion/content.ts'],bundle:true,format:'esm',platform:'node',write:false});
|
|
5
|
+
const {enhanceContentMotion}=await import('data:text/javascript;base64,'+Buffer.from(result.outputFiles[0].text).toString('base64'));
|
|
6
|
+
|
|
7
|
+
function fixture(mode='full') {
|
|
8
|
+
const watchers=[],jobs=[];
|
|
9
|
+
globalThis.MutationObserver=class {constructor(fn){this.fn=fn;} observe(root,options){this.root=root;this.options=options;watchers.push(this);} disconnect(){this.off=true;}};
|
|
10
|
+
globalThis.cancelAnimationFrame=()=>{};
|
|
11
|
+
const rect={left:0,top:0,width:100,height:100};
|
|
12
|
+
const attrs=new Map(),styles=new Map();
|
|
13
|
+
const layer={hidden:true,inert:false,style:{getPropertyValue:k=>styles.get(k)?.[0]||'',getPropertyPriority:k=>styles.get(k)?.[1]||'',setProperty:(k,v,p='')=>styles.set(k,[v,p]),removeProperty:k=>styles.delete(k),get pointerEvents(){return styles.get('pointer-events')?.[0]||'';},set pointerEvents(v){styles.set('pointer-events',[v,'']);}},getAttribute:k=>attrs.get(k)??null,setAttribute:(k,v)=>attrs.set(k,v),removeAttribute:k=>attrs.delete(k),closest:()=>inner};
|
|
14
|
+
const root=()=>{const a=new Map();return {getBoundingClientRect:()=>rect,querySelectorAll:s=>s==='.blu-overlay,.blu-tour'?[layer]:[],addEventListener(){},removeEventListener(){},getAttribute:k=>a.get(k)??null,setAttribute:(k,v)=>a.set(k,v),removeAttribute:k=>a.delete(k),contains:()=>true};};
|
|
15
|
+
const outer=root(),inner=root();
|
|
16
|
+
const motion={mode,cancel(){},play(){return Promise.resolve('finished');},resolveOptions:()=>({duration:750}),animate(){let done;const p=new Promise(r=>done=r);jobs.push(done);return p;}};
|
|
17
|
+
const collection=enhanceContentMotion(outer,motion,'collection-layout');
|
|
18
|
+
const profile=enhanceContentMotion(inner,motion,'profile-card');
|
|
19
|
+
const change=hidden=>{layer.hidden=hidden;for(const w of watchers)if(!w.off&&w.options.attributeFilter?.length===1)w.fn();};
|
|
20
|
+
const finish=async()=>{for(const done of jobs.splice(0))done('finished');await Promise.resolve();await Promise.resolve();};
|
|
21
|
+
return {layer,styles,attrs,jobs,change,finish,destroy(){profile.destroy();collection.destroy();}};
|
|
22
|
+
}
|
|
23
|
+
test('a nested profile has one exit owner and remains visually hidden after close',async()=>{
|
|
24
|
+
const f=fixture();f.change(false);f.change(true);
|
|
25
|
+
assert.equal(f.jobs.length,1,'the collection must not animate its child dialog');
|
|
26
|
+
assert.equal(f.layer.inert,true);assert.equal(f.attrs.get('aria-hidden'),'true');
|
|
27
|
+
await f.finish();assert.equal(f.layer.hidden,true);assert.equal(f.styles.has('display'),false);assert.equal(f.layer.inert,false);assert.equal(f.attrs.has('aria-hidden'),false);f.destroy();
|
|
28
|
+
});
|
|
29
|
+
test('quick reopen and destroy restore exit styles without hiding the reopened dialog',async()=>{
|
|
30
|
+
const f=fixture();f.change(false);f.change(true);f.change(false);await f.finish();
|
|
31
|
+
assert.equal(f.layer.hidden,false);assert.equal(f.styles.has('display'),false);
|
|
32
|
+
f.change(true);f.destroy();await f.finish();assert.equal(f.styles.has('display'),false);assert.equal(f.layer.inert,false);
|
|
33
|
+
});
|
|
34
|
+
test('motion off closes immediately without temporary visibility styles',()=>{
|
|
35
|
+
const f=fixture('off');f.change(false);f.change(true);assert.equal(f.jobs.length,0);assert.equal(f.styles.has('display'),false);f.destroy();
|
|
36
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {build} from 'esbuild';
|
|
4
|
+
import {readFile} from 'node:fs/promises';
|
|
5
|
+
import vm from 'node:vm';
|
|
6
|
+
const built=await build({entryPoints:['src/form-layout/table.ts'],bundle:true,format:'esm',platform:'node',write:false});
|
|
7
|
+
const {tableRows}=await import('data:text/javascript;base64,'+Buffer.from(built.outputFiles[0].text).toString('base64'));
|
|
8
|
+
const options={Caption:'Records',Columns:[{Key:'value',Header:'Value',Sortable:true}],Rows:[{value:10},{value:null},{value:2},{value:2}],SortKey:'value'};
|
|
9
|
+
test('Table sorts numerically without mutating records, keeps equal rows stable, and keeps nulls last',()=>{
|
|
10
|
+
const sorted=tableRows(options).rows;assert.deepEqual(sorted.map(r=>r.value),[2,2,10,null]);assert.equal(sorted[0],options.Rows[2]);assert.equal(sorted[1],options.Rows[3]);assert.deepEqual(options.Rows.map(r=>r.value),[10,null,2,2]);assert.deepEqual(tableRows({...options,SortDirection:'Descending'}).rows.map(r=>r.value),[10,2,2,null]);
|
|
11
|
+
});
|
|
12
|
+
test('Table ignores non-sortable keys and safely clamps paging after data shrinks',()=>{
|
|
13
|
+
assert.deepEqual(tableRows({...options,Columns:[{Key:'value',Header:'Value'}]}).rows,options.Rows);
|
|
14
|
+
assert.deepEqual(tableRows({...options,PageSize:2,Page:100}),{rows:[options.Rows[0],options.Rows[1]],page:2,pages:2,total:4,start:3,end:4});
|
|
15
|
+
assert.equal(tableRows({...options,Rows:[],Page:20,PageSize:5}).page,1);assert.equal(tableRows({...options,Page:NaN,PageSize:Infinity}).page,1);
|
|
16
|
+
});
|
|
17
|
+
test('every draft component has a selective entry and the Table does not load AG Grid',async()=>{
|
|
18
|
+
const draft=JSON.parse(await readFile('draft-components.json','utf8')),pkg=JSON.parse(await readFile('package.json','utf8'));
|
|
19
|
+
const sizes=JSON.parse(await readFile('dist/sizes.json','utf8'));
|
|
20
|
+
for(const c of draft.components){assert.ok(pkg.exports['./'+c.slug]);await readFile('dist/'+c.slug+'.d.ts');}
|
|
21
|
+
const code=(await Promise.all(sizes.typescript['table'].files.map(f=>readFile('dist/'+f,'utf8')))).join('\n');
|
|
22
|
+
assert.ok(!/ClientSideRowModel|BluGrid|ag-grid/.test(code));assert.ok(sizes.typescript['table'].bytes<20000);
|
|
23
|
+
});
|
|
24
|
+
test('draft native runtime registers independently without changing commercial globals',async()=>{
|
|
25
|
+
const commercial={untouched:true},window={BluUIRuntime:commercial,BluUITheme:{version:'1.0.0',setMode(){},getMode(){return 'Light';},mountSelector(){}}};const context=vm.createContext({window,Map,Set,WeakMap,WeakSet,Promise,console});
|
|
26
|
+
vm.runInContext(await readFile('dist/native/form-layout.js','utf8'),context);
|
|
27
|
+
assert.equal(window.BluUIRuntime,commercial);assert.equal(typeof window.BluUICommunity.sync,'function');assert.equal(typeof window.BluUICommunity.destroy,'function');
|
|
28
|
+
});
|
package/test/isolation.test.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import {readFile,readdir} from 'node:fs/promises';
|
|
|
4
4
|
import vm from 'node:vm';
|
|
5
5
|
const edition=JSON.parse(await readFile('edition.json','utf8'));
|
|
6
6
|
const sizes=JSON.parse(await readFile('dist/sizes.json','utf8'));
|
|
7
|
-
test('all seven groups expose
|
|
8
|
-
const names=Object.values(edition.nativeModules).flat();assert.equal(names.length,
|
|
7
|
+
test('all seven groups expose the declared release component set without duplicates',()=>{
|
|
8
|
+
const names=Object.values(edition.nativeModules).flat();const expected=67;assert.equal(names.length,expected);assert.equal(new Set(names).size,expected);assert.deepEqual([...names].sort(),[...edition.components].sort());assert.equal(Object.keys(edition.nativeModules).length,7);
|
|
9
9
|
});
|
|
10
10
|
test('non-grid entrypoints do not pull the AG Grid engine or unrelated component factories',async()=>{
|
|
11
11
|
for(const name of ['chat','chat-widget','float-button','date-picker','time-picker','date-time-picker','date-range-picker','font-provider','shape-mask','tree-select','transfer-list','core','layout','inputs','feedback','data']){
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { build } from 'esbuild';
|
|
4
|
+
const built = await build({ entryPoints: ['src/motion.ts'], bundle: true, format: 'esm', platform: 'node', write: false });
|
|
5
|
+
// Import must be safe without browser globals (server rendering/tooling).
|
|
6
|
+
const { createMotionController, resolveMotionMode, boundedTime, normalizeProgress, motionEffects, motionEasings, motionFrames, playStagger, staggerRanks, createScrollReveal, easeProgress } = await import('data:text/javascript;base64,' + Buffer.from(built.outputFiles[0].text).toString('base64'));
|
|
7
|
+
|
|
8
|
+
function browserFixture(reduced = false) {
|
|
9
|
+
const listeners = new Set(), attrs = new Map(), animations = [];
|
|
10
|
+
const media = { matches: reduced, addEventListener(_event, fn) { listeners.add(fn); }, removeEventListener(_event, fn) { listeners.delete(fn); } };
|
|
11
|
+
const scope = {
|
|
12
|
+
ownerDocument: { defaultView: { matchMedia: () => media, getComputedStyle: () => ({ getPropertyValue: key => ({ '--blu-motion-duration': '.28s', '--blu-motion-distance': '8px' }[key] || '') }) } },
|
|
13
|
+
contains: () => true, getAttribute: name => attrs.get(name) ?? null,
|
|
14
|
+
setAttribute: (name, value) => attrs.set(name, value), removeAttribute: name => attrs.delete(name),
|
|
15
|
+
};
|
|
16
|
+
const element = { animate(frames, options) {
|
|
17
|
+
let resolve, reject;
|
|
18
|
+
const finished = new Promise((yes, no) => { resolve = yes; reject = no; });
|
|
19
|
+
const animation = { frames, options, finished, cancel() { this.cancelled = true; reject(Error('Cancelled')); }, complete() { resolve(); } };
|
|
20
|
+
animations.push(animation); return animation;
|
|
21
|
+
} };
|
|
22
|
+
return { scope, element, animations, media, listeners, attrs, change(reduced) { media.matches = reduced; for (const fn of listeners) fn(); } };
|
|
23
|
+
}
|
|
24
|
+
test('preferences never override the device reduction and explicit Off always wins', () => {
|
|
25
|
+
for (const system of [true, false]) {
|
|
26
|
+
assert.equal(resolveMotionMode('off', system), 'off');
|
|
27
|
+
assert.equal(resolveMotionMode('reduced', system), 'reduced');
|
|
28
|
+
assert.equal(resolveMotionMode('system', system), system ? 'reduced' : 'full');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
test('timing and progress reject invalid values rather than producing endless/false states', () => {
|
|
32
|
+
assert.equal(boundedTime(Infinity, 180), 180); assert.equal(boundedTime(-2, 180), 0); assert.equal(boundedTime(90000, 180), 2000);
|
|
33
|
+
assert.equal(normalizeProgress(NaN), undefined); assert.equal(normalizeProgress(Infinity), undefined); assert.equal(normalizeProgress(-10), 0); assert.equal(normalizeProgress(130), 100); assert.equal(normalizeProgress(42.5), 42.5);
|
|
34
|
+
});
|
|
35
|
+
test('starting another effect on the same element cancels and settles the first', async () => {
|
|
36
|
+
const f = browserFixture(), motion = createMotionController(f.scope);
|
|
37
|
+
const first = motion.play(f.element, 'slide-left'), last = motion.play(f.element, 'fade');
|
|
38
|
+
assert.equal(await first, 'cancelled'); assert.equal(f.animations[0].cancelled, true);
|
|
39
|
+
assert.equal(f.animations[1].options.duration, 280);
|
|
40
|
+
f.animations[1].complete(); assert.equal(await last, 'finished');
|
|
41
|
+
assert.equal(f.animations[1].cancelled, true, 'Finished effects must release fill state'); motion.destroy();
|
|
42
|
+
});
|
|
43
|
+
test('device preference changes cancel in-flight motion; reduced effects never translate', async () => {
|
|
44
|
+
const f = browserFixture(), motion = createMotionController(f.scope);
|
|
45
|
+
const first = motion.play(f.element, 'slide-up'); f.change(true);
|
|
46
|
+
assert.equal(await first, 'cancelled'); assert.equal(f.attrs.get('data-blu-motion'), 'reduced');
|
|
47
|
+
const reduced = motion.play(f.element, 'scale', { duration: 800, delay: 300 });
|
|
48
|
+
assert.equal(f.animations[1].options.duration, 200); assert.equal(f.animations[1].options.delay, 0);
|
|
49
|
+
assert.deepEqual(f.animations[1].frames, [{ opacity: 0 }, { opacity: 1 }]);
|
|
50
|
+
f.animations[1].complete(); assert.equal(await reduced, 'finished'); motion.destroy();
|
|
51
|
+
});
|
|
52
|
+
test('Off and unsupported animation APIs commit instantly without creating animation tasks', async () => {
|
|
53
|
+
const f = browserFixture(), motion = createMotionController(f.scope, 'off');
|
|
54
|
+
assert.equal(await motion.play(f.element), 'finished'); assert.equal(f.animations.length, 0);
|
|
55
|
+
motion.setPreference('system'); assert.equal(await motion.play({}), 'finished');
|
|
56
|
+
f.element.closest = () => ({});
|
|
57
|
+
assert.equal(await motion.play(f.element, 'slide-up', { duration: 800 }), 'finished');
|
|
58
|
+
assert.equal(f.animations.length, 0); motion.destroy();
|
|
59
|
+
});
|
|
60
|
+
test('destroy cancels pending effects, restores scope attributes, detaches listeners and allows remount', async () => {
|
|
61
|
+
const f = browserFixture(); f.attrs.set('data-blu-motion', 'previous');
|
|
62
|
+
const motion = createMotionController(f.scope), task = motion.play(f.element);
|
|
63
|
+
assert.throws(() => createMotionController(f.scope), /already/);
|
|
64
|
+
motion.destroy(); motion.destroy();
|
|
65
|
+
assert.equal(await task, 'cancelled'); assert.equal(f.listeners.size, 0); assert.equal(f.attrs.get('data-blu-motion'), 'previous');
|
|
66
|
+
assert.equal(await motion.play(f.element), 'cancelled'); assert.throws(() => motion.setPreference('off'), /destroyed/);
|
|
67
|
+
createMotionController(f.scope).destroy();
|
|
68
|
+
});
|
|
69
|
+
test('all base effects support both phases with opposite opacity endpoints and named easing', async () => {
|
|
70
|
+
for (const effect of motionEffects) for (const phase of ['enter', 'exit']) for (const easing of Object.keys(motionEasings)) {
|
|
71
|
+
const f = browserFixture(), motion = createMotionController(f.scope);
|
|
72
|
+
const task = motion.play(f.element, effect, { phase, easing, duration: 450 });
|
|
73
|
+
const animation = f.animations[0];
|
|
74
|
+
assert.equal(animation.frames[0].opacity, phase === 'enter' ? 0 : 1, effect);
|
|
75
|
+
assert.equal(animation.frames.at(-1).opacity, phase === 'enter' ? 1 : 0, effect);
|
|
76
|
+
assert.equal(animation.options.easing, motionEasings[easing]);
|
|
77
|
+
assert.equal(animation.options.duration, 450);
|
|
78
|
+
animation.complete(); assert.equal(await task, 'finished'); motion.destroy();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
test('direction names describe movement for both entrance and exit', () => {
|
|
82
|
+
const expected = {
|
|
83
|
+
'slide-up': ['translateY(20px)', 'translateY(calc(-1 * 20px))'],
|
|
84
|
+
'slide-down': ['translateY(calc(-1 * 20px))', 'translateY(20px)'],
|
|
85
|
+
'slide-left': ['translateX(20px)', 'translateX(calc(-1 * 20px))'],
|
|
86
|
+
'slide-right': ['translateX(calc(-1 * 20px))', 'translateX(20px)'],
|
|
87
|
+
'zoom-in': ['scale(.92)', 'scale(1.08)'],
|
|
88
|
+
'zoom-out': ['scale(1.08)', 'scale(.92)'],
|
|
89
|
+
};
|
|
90
|
+
for (const [name, [enter, exit]] of Object.entries(expected)) {
|
|
91
|
+
assert.equal(motionFrames(name, 'enter', '20px')[0].transform, enter);
|
|
92
|
+
assert.equal(motionFrames(name, 'exit', '20px').at(-1).transform, exit);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
test('every effect reduces to a short fade; zero duration remains instant', async () => {
|
|
96
|
+
for (const effect of motionEffects) for (const phase of ['enter', 'exit']) {
|
|
97
|
+
const f = browserFixture(true), motion = createMotionController(f.scope);
|
|
98
|
+
const task = motion.play(f.element, effect, { phase, easing: 'spring', delay: 500 });
|
|
99
|
+
assert.equal(f.animations[0].options.duration, 200);
|
|
100
|
+
assert.equal(f.animations[0].options.delay, 0);
|
|
101
|
+
assert.ok(f.animations[0].frames.every(frame => !('transform' in frame)));
|
|
102
|
+
f.animations[0].complete(); await task;
|
|
103
|
+
await motion.play(f.element, effect, { duration: 0 }); assert.equal(f.animations.length, 1);
|
|
104
|
+
motion.destroy();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
test('stagger bounds total delay and removes all delays in reduced mode', async () => {
|
|
108
|
+
for (const reduced of [false, true]) {
|
|
109
|
+
const f = browserFixture(reduced), motion = createMotionController(f.scope);
|
|
110
|
+
const elements = Array.from({ length: 30 }, () => ({ animate: f.element.animate }));
|
|
111
|
+
const task = playStagger(motion, elements, 'slide-up', { gap: 120, delay: 40 });
|
|
112
|
+
const delays = f.animations.map(animation => animation.options.delay);
|
|
113
|
+
assert.equal(delays[0], reduced ? 0 : 40); assert.equal(delays.at(-1), reduced ? 0 : 500);
|
|
114
|
+
assert.ok(delays.every(delay => delay <= 500));
|
|
115
|
+
for (const animation of f.animations) animation.complete();
|
|
116
|
+
assert.ok((await task).every(result => result === 'finished')); motion.destroy();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
test('scroll reveals only intersecting targets, defaults to once, and disposes pending motion', async () => {
|
|
120
|
+
const f = browserFixture(); let callback, disconnected = false, unobserved = [], count = 0;
|
|
121
|
+
f.scope.ownerDocument.defaultView.IntersectionObserver = class {
|
|
122
|
+
constructor(fn) { callback = fn; }
|
|
123
|
+
observe() {} unobserve(element) { unobserved.push(element); } disconnect() { disconnected = true; }
|
|
124
|
+
};
|
|
125
|
+
Object.assign(f.element, { ownerDocument: f.scope.ownerDocument, isConnected: true });
|
|
126
|
+
const motion = createMotionController(f.scope);
|
|
127
|
+
const reveal = createScrollReveal([f.element], motion, { onReveal() { count++; } });
|
|
128
|
+
callback([{ target: f.element, isIntersecting: false, intersectionRatio: 0 }]); assert.equal(count, 0);
|
|
129
|
+
callback([{ target: f.element, isIntersecting: true, intersectionRatio: .1 }]); assert.equal(count, 0);
|
|
130
|
+
callback([{ target: f.element, isIntersecting: true, intersectionRatio: .4 }]); assert.equal(count, 1); assert.deepEqual(unobserved, [f.element]);
|
|
131
|
+
callback([{ target: f.element, isIntersecting: false, intersectionRatio: 0 }]);
|
|
132
|
+
callback([{ target: f.element, isIntersecting: true, intersectionRatio: 1 }]); assert.equal(count, 1);
|
|
133
|
+
reveal.destroy(); assert.equal(disconnected, true); assert.equal(f.animations[0].cancelled, true);
|
|
134
|
+
callback([{ target: f.element, isIntersecting: true, intersectionRatio: 1 }]); assert.equal(count, 1); motion.destroy();
|
|
135
|
+
});
|
|
136
|
+
test('repeat scroll reveal waits for genuine re-entry; unsupported observer leaves content untouched', () => {
|
|
137
|
+
const f = browserFixture(); let callback, count = 0;
|
|
138
|
+
f.scope.ownerDocument.defaultView.IntersectionObserver = class { constructor(fn) { callback = fn; } observe() {} unobserve() {} disconnect() {} };
|
|
139
|
+
Object.assign(f.element, { ownerDocument: f.scope.ownerDocument, isConnected: true, hidden: false });
|
|
140
|
+
const motion = createMotionController(f.scope, 'off'), reveal = createScrollReveal([f.element], motion, { once: false, onReveal() { count++; } });
|
|
141
|
+
const entry = visible => [{ target: f.element, isIntersecting: visible, intersectionRatio: visible ? 1 : 0 }];
|
|
142
|
+
callback(entry(true)); callback(entry(true)); assert.equal(count, 1);
|
|
143
|
+
callback(entry(false)); callback(entry(true)); assert.equal(count, 2);
|
|
144
|
+
assert.equal(f.animations.length, 0); assert.equal(f.element.hidden, false);
|
|
145
|
+
reveal.destroy(); delete f.scope.ownerDocument.defaultView.IntersectionObserver;
|
|
146
|
+
const unsupported = createScrollReveal([f.element], motion); assert.equal(f.element.hidden, false);
|
|
147
|
+
unsupported.destroy(); createScrollReveal([], motion).destroy(); motion.destroy();
|
|
148
|
+
});
|
|
149
|
+
test('custom frames share cancellation; reduced and Off commit without spatial animation', async () => {
|
|
150
|
+
const f = browserFixture(), motion = createMotionController(f.scope);
|
|
151
|
+
const first = motion.animate(f.element, [{ height: '0px' }, { height: '80px' }]);
|
|
152
|
+
const second = motion.animate(f.element, [{ height: '30px' }, { height: '0px' }]);
|
|
153
|
+
assert.equal(await first, 'cancelled'); f.animations[1].complete(); assert.equal(await second, 'finished');
|
|
154
|
+
motion.setPreference('reduced'); await motion.animate(f.element, [{ transform: 'scale(.5)' }, { transform: 'none' }]); assert.equal(f.animations.length, 2);
|
|
155
|
+
motion.setPreference('off'); await motion.animate(f.element, [{ opacity: 0 }, { opacity: 1 }]); assert.equal(f.animations.length, 2); motion.destroy();
|
|
156
|
+
});
|
|
157
|
+
test('numeric interpolation respects actual time, easing, retargeting and disposal', async () => {
|
|
158
|
+
const f = browserFixture(), callbacks = new Map(); let id = 0, value = 0;
|
|
159
|
+
Object.assign(f.scope.ownerDocument.defaultView, { requestAnimationFrame(fn) { callbacks.set(++id, fn); return id; }, cancelAnimationFrame(key) { callbacks.delete(key); } });
|
|
160
|
+
function tick(time) { const batch = Array.from(callbacks.values()); callbacks.clear(); batch.forEach(fn => fn(time)); }
|
|
161
|
+
const motion = createMotionController(f.scope);
|
|
162
|
+
const first = motion.tween(f.element, 0, 100, v => value = v, { duration: 200, easing: 'linear' });
|
|
163
|
+
tick(0); tick(100); assert.equal(value, 50);
|
|
164
|
+
const last = motion.tween(f.element, value, 20, v => value = v, { duration: 200, easing: 'linear' });
|
|
165
|
+
assert.equal(await first, 'cancelled'); tick(101); tick(301); assert.equal(value, 20); assert.equal(await last, 'finished'); assert.equal(callbacks.size, 0);
|
|
166
|
+
const pending = motion.tween(f.element, 20, 200, v => value = v); motion.destroy(); assert.equal(await pending, 'cancelled'); assert.equal(callbacks.size, 0);
|
|
167
|
+
});
|
|
168
|
+
test('reduced numeric interpolation finishes instantly; invalid data cannot cancel valid work', async () => {
|
|
169
|
+
const f = browserFixture(true), motion = createMotionController(f.scope); let value;
|
|
170
|
+
assert.equal(await motion.tween(f.element, 0, 42, v => value = v), 'finished'); assert.equal(value, 42);
|
|
171
|
+
assert.throws(() => motion.tween(f.element, 0, NaN, () => {}), /finite/); motion.destroy();
|
|
172
|
+
});
|
|
173
|
+
test('numeric easing matches directional CSS behavior and exact endpoints', () => {
|
|
174
|
+
for (const easing of Object.keys(motionEasings)) { assert.equal(easeProgress(0, easing), 0); assert.equal(easeProgress(1, easing), 1); }
|
|
175
|
+
assert.equal(easeProgress(.5, 'linear'), .5);
|
|
176
|
+
assert.ok(easeProgress(.5, 'ease-in') < .5); assert.ok(easeProgress(.5, 'ease-out') > .5);
|
|
177
|
+
assert.ok(Math.abs(easeProgress(.5, 'ease-in-out') - .5) < .00001);
|
|
178
|
+
});
|
|
179
|
+
test('wipe directions change opposite clip edges on entry and exit', () => {
|
|
180
|
+
for (const effect of ['wipe-up', 'wipe-down', 'wipe-left', 'wipe-right']) {
|
|
181
|
+
const enter = motionFrames(effect), exit = motionFrames(effect, 'exit');
|
|
182
|
+
assert.notEqual(enter[0].clipPath, exit[1].clipPath); assert.equal(enter[1].clipPath, 'inset(0 0 0 0)'); assert.equal(exit[0].clipPath, enter[1].clipPath);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
test('review durations up to two seconds remain intact; component default remains short', async () => {
|
|
186
|
+
const f = browserFixture(), motion = createMotionController(f.scope);
|
|
187
|
+
for (const duration of [750, 1000, 1500, 2000]) { const pending = motion.play(f.element, 'wipe-left', { duration }); assert.equal(f.animations.at(-1).options.duration, duration); f.animations.at(-1).complete(); await pending; }
|
|
188
|
+
const pending = motion.play(f.element); assert.equal(f.animations.at(-1).options.duration, 280); f.animations.at(-1).complete(); await pending; motion.destroy();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('shared default is 450ms without styles; caller zero still bypasses animation', async () => {
|
|
192
|
+
const f=browserFixture();f.scope.ownerDocument.defaultView.getComputedStyle=()=>({getPropertyValue:()=>''});
|
|
193
|
+
const motion=createMotionController(f.scope);const task=motion.play(f.element);
|
|
194
|
+
assert.equal(f.animations[0].options.duration,450);f.animations[0].complete();await task;
|
|
195
|
+
await motion.play(f.element,'fade',{duration:0});assert.equal(f.animations.length,1);motion.destroy();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('directional stagger ranks preserve stable order with even centers and incomplete grid rows', () => {
|
|
199
|
+
assert.deepEqual(staggerRanks(5, 'center'), [2,1,0,1,2]);
|
|
200
|
+
assert.deepEqual(staggerRanks(6, 'center'), [2,1,0,0,1,2]);
|
|
201
|
+
assert.deepEqual(staggerRanks(5, 'rows', 3), [0,0,0,1,1]);
|
|
202
|
+
assert.deepEqual(staggerRanks(5, 'columns', 3), [0,1,2,0,1]);
|
|
203
|
+
assert.deepEqual(staggerRanks(3, 'reverse'), [2,1,0]);
|
|
204
|
+
assert.deepEqual(staggerRanks(0, 'center'), []);
|
|
205
|
+
assert.throws(() => staggerRanks(3, 'rows', 0), /positive/);
|
|
206
|
+
assert.throws(() => staggerRanks(3, 'random'), /Unknown/);
|
|
207
|
+
});
|
|
208
|
+
test('anchored zoom applies only animation frames and disappears in reduced motion', async () => {
|
|
209
|
+
for (const reduced of [false, true]) {
|
|
210
|
+
const f = browserFixture(reduced), motion = createMotionController(f.scope);
|
|
211
|
+
const task = motion.play(f.element, 'zoom-in', { origin: 'top-right' });
|
|
212
|
+
assert.ok(f.animations[0].frames.every(frame => reduced ? !frame.transformOrigin : frame.transformOrigin === '100% 0%'));
|
|
213
|
+
f.animations[0].complete(); await task; motion.destroy();
|
|
214
|
+
}
|
|
215
|
+
});
|
package/dist/chunk-2TCNAQC6.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as X}from"./chunk-MEEQNPGM.js";import{c as G,d as Q}from"./chunk-5TEIXA4R.js";var O=G("advanced",()=>({instances:new Map,serial:0})),fe=()=>`blu-advanced-${++O.serial}`;function c(e,t,a){let r=document.createElement(e);return t&&(r.className=t),a!==void 0&&(r.textContent=String(a)),r}function u(e,t,a){return a==null?e.removeAttribute(t):e.setAttribute(t,String(a)),e}function E(e,t,a="blu-picker__button"){let r=c("button",a,e);return r.type="button",r.addEventListener("click",t),r}function I(e,t,...a){if(!e.dead)try{Promise.resolve(e.events[t]?.(...a)).catch(r=>K(e,r))}catch(r){K(e,r)}}function K(e,t){if(e.dead)return;let a=t?.message||String(t);e.notice.textContent=a,e.lastError!==a&&(e.lastError=a,I(e,"Error",a))}function Z(e,t){let a=typeof e=="string"?JSON.parse(e||"[]"):e||[];if(!Array.isArray(a))throw Error(t+" must be a list.");return a}function me(e,t,a){let r=o=>{let n=O.instances.get(o);!n||n.kind!==e||(n.dead=!0,n.dispose?.(),n.root.remove(),n.notice.remove(),n.host._bluAdvancedOwner===o&&delete n.host._bluAdvancedOwner,O.instances.delete(o))};return Q(e,()=>({sync(o,n={},m={},h){let l=document.getElementById(o);if(!l)return e+" host was not found.";let g=h||o,p=O.instances.get(g);p&&(p.host!==l||p.kind!==e)&&(p.destroy(p.key),p=null);let i=O.instances.get(l._bluAdvancedOwner);if(i&&i!==p&&i.destroy(i.key),!p){p={key:g,host:l,kind:e,uid:fe(),options:n,events:m,dead:!1,destroy:r},p.root=c("div"),p.notice=u(c("div","blu-advanced-sr"),"role","status"),u(p.notice,"aria-atomic","true"),l.append(p.root,p.notice),O.instances.set(g,p),l._bluAdvancedOwner=g;try{t(p)}catch(f){throw r(g),f}}p.options=n,p.events=m;try{return a(p),l.dataset.bluRuntimeReady="true",""}catch(f){return K(p,f),f.message||String(f)}},destroy:r}))}var v=e=>String(e).padStart(2,"0");function q(e){return String(e.getFullYear()).padStart(4,"0")+"-"+v(e.getMonth()+1)+"-"+v(e.getDate())}function b(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(String(e));if(!t||+t[1]<1||+t[1]>9999)return null;let a=new Date(2e3,+t[2]-1,+t[3],12);return a.setFullYear(+t[1]),q(a)===e?a:null}function L(e){let t=/^(\d{2}):(\d{2})$/.exec(String(e));return t&&+t[1]<24&&+t[2]<60?t[1]+":"+t[2]:null}function ee(e,t="YYYY-MM-DD"){if(e=String(e).trim(),!e)return"";let a=e;if(t!=="YYYY-MM-DD"){let r=/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(e);if(!r)return null;a=r[3]+"-"+v(+(t==="DD/MM/YYYY"?r[2]:r[1]))+"-"+v(+(t==="DD/MM/YYYY"?r[1]:r[2]))}return b(a)?a:null}function te(e,t="YYYY-MM-DD"){if(!e)return"";let a=e.split("-");return t==="MM/DD/YYYY"?a[1]+"/"+a[2]+"/"+a[0]:t==="DD/MM/YYYY"?a[2]+"/"+a[1]+"/"+a[0]:e}function he(e,t=24){if(e=String(e).trim(),!e)return"";let a=/^(\d{1,2}):(\d{2})(?:\s*(AM|PM))?$/i.exec(e);return!a||+a[2]>59||(t===12?!a[3]||+a[1]<1||+a[1]>12:a[3]||+a[1]>23)?null:v(a[3]?+a[1]%12+(a[3].toUpperCase()==="PM"?12:0):+a[1])+":"+a[2]}function T(e,t=24){if(!e)return"";let[a,r]=e.split(":").map(Number);return t===12?(a%12||12)+":"+v(r)+" "+(a<12?"AM":"PM"):v(a)+":"+v(r)}function ae(e,t){let a=b(e);return a.setDate(a.getDate()+t),q(a)}function ie(e,t){let a=b(e),r=a.getDate();return a.setDate(1),a.setMonth(a.getMonth()+t),a.setDate(Math.min(r,new Date(a.getFullYear(),a.getMonth()+1,0).getDate())),q(a)}function ge(e,t){let a=b(e),r=b(t);return Math.round((r.getTime()-a.getTime())/864e5)}function z(e,t,a,r){return b(e)?t&&e<t?"Choose "+t+" or later.":a&&e>a?"Choose "+a+" or earlier.":r?.has(e)?"This date is unavailable.":"":"Enter a real calendar date."}function oe(e,t,a={}){if(!e||!t)return"Choose both a start date and an end date.";let r=z(e,a.Min,a.Max,a.disabled)||z(t,a.Min,a.Max,a.disabled);if(r)return r;if(t<e)return"End date must be on or after start date.";let o=ge(e,t)+1;if(a.MinRangeDays&&o<a.MinRangeDays)return"Choose at least "+a.MinRangeDays+" days.";if(a.MaxRangeDays&&o>a.MaxRangeDays)return"Choose no more than "+a.MaxRangeDays+" days.";if(a.disabled){for(let n of a.disabled)if(n>=e&&n<=t)return"The range includes an unavailable date."}return""}var ye=["YYYY-MM-DD","MM/DD/YYYY","DD/MM/YYYY"],N=()=>q(new Date),M=e=>String(e||"").split("T")[0],F=e=>String(e||"").split("T")[1]||"";function w(e,t){let a=b(t);return a?new Intl.DateTimeFormat(e.locale,{dateStyle:"full"}).format(a):t}function B(e,t,a=e.fields){e.error.textContent=t,e.error.hidden=!t||e.isOpen,e.dialogError&&(e.dialogError.textContent=e.isOpen?t:"");for(let o of e.fields)u(o.input,"aria-invalid",t&&a.includes(o)?"true":null);let r=!t;(e.valid!==r||e.validationMessage!==t)&&(e.valid=r,e.validationMessage=t,I(e,"ValidationChanged",r,t))}function V(e,t){return z(t,M(e.options.Min),M(e.options.Max),e.disabledDates)}function be(e,t,a){if(!L(t))return"Enter a valid time.";let r=e.dateTime?a+"T"+t:t,o=e.options.Min,n=e.options.Max;return o&&r<o?"Choose "+o+" or later.":n&&r>n?"Choose "+n+" or earlier.":""}function de(e,t,a,r){if(e.range)return!t&&!a&&!e.options.IsRequired?"":oe(t,a,{...e.options,disabled:e.disabledDates});if(!t&&!r)return e.options.IsRequired?"A "+(e.timeOnly?"time":"date")+" is required.":"";if(!e.timeOnly){if(!t)return"Choose a date.";let o=V(e,t);if(o)return o}return e.hasTime?r?be(e,r,t):"Choose a time.":""}function le(e){e.timeOnly?e.fields[0].input.value=T(e.value,e.cycle):(e.fields[0].input.value=te(e.range?e.start:M(e.value),e.format),e.range&&(e.fields[1].input.value=te(e.end,e.format)),e.dateTime&&(e.fields[1].input.value=T(F(e.value),e.cycle)));let t=e.range?[e.start,e.end].filter(Boolean).join(" to "):e.value;e.typedDirty=!1,e.clearButton.hidden=e.options.ShowClear===!1||!t}function x(e,t=!0){if(e.isOpen){e.isOpen=!1,e.dialog.hidePopover&&e.dialog.matches(":popover-open")&&e.dialog.hidePopover(),e.dialog.hidden=!0,e.error.hidden=!e.validationMessage;for(let{input:a}of e.fields)u(a,"aria-expanded","false");t&&e.anchor?.isConnected&&!e.anchor.disabled&&e.anchor.focus({preventScroll:!0}),I(e,"OpenChanged",!1)}}function A(e){if(!e.isOpen||e.dead)return;let t=e.anchor.getBoundingClientRect(),a=window.visualViewport,r=a?.offsetLeft||0,o=a?.offsetTop||0,n=a?.width||document.documentElement.clientWidth,m=a?.height||window.innerHeight,h=6,l=8;if(t.bottom<=o||t.top>=o+m||t.right<=r||t.left>=r+n){x(e,!1);return}let g=e.dialog,p=Math.min(e.timeOnly?300:344,n-l*2);g.dataset.compact=String(m<420),g.style.width=p+"px",g.style.maxHeight="none";let i=Math.max(0,o+m-l-t.bottom-h),f=Math.max(0,t.top-h-o-l),d=g.scrollHeight+2,D=d>i&&f>i,S=Math.max(0,m-l*2);g.style.maxHeight=S+"px",g.style.left=Math.max(r+l,Math.min(t.left,r+n-l-p))+"px";let y=D?t.top-h-d:t.bottom+h;g.style.top=Math.max(o+l,Math.min(y,o+m-l-Math.min(d,S)))+"px",g.dataset.placement=D?"top":"bottom"}function ne(e){(e.timeOnly||e.dateTime&&e.anchor===e.fields[1].input?e.hours:e.calendar.querySelector('[data-date="'+e.focusDate+'"]')||e.monthSelect).focus()}function H(e,t,a,r,o=!1){let n=de(e,t,a,r);if(B(e,n),n)return e.dialogError.textContent=n,!1;let m=document.activeElement===e.clearButton&&!t&&!a&&!r,h=e.timeOnly?r:e.dateTime?t?t+"T"+r:"":t,l=e.range?e.start!==t||e.end!==a:e.value!==h;return e.range?(e.start=t,e.end=a,e.options.StartDate=t,e.options.EndDate=a):(e.value=h,e.options.Value=h),e.inputSignature=JSON.stringify(e.range?[e.start,e.end]:[e.value]),le(e),o||x(e),m&&e.fields[0].input.focus(),e.notice.textContent=e.range?t?w(e,t)+" to "+w(e,a):"Date range cleared":h?"Selected "+(e.timeOnly?T(h,e.cycle):w(e,t)+(e.hasTime?" at "+T(r,e.cycle):"")):"Selection cleared",l&&(e.range?I(e,"Change",t,a):I(e,"Change",h)),!0}function re(e,t=!1){if(e.options.IsDisabled||e.options.IsReadOnly)return;let a=e.timeOnly?"":ee(e.fields[0].input.value,e.format),r=e.range?ee(e.fields[1].input.value,e.format):"",o=e.hasTime?he(e.fields[e.timeOnly?0:1].input.value,e.cycle):"";if(a===null||r===null){B(e,"Enter a real date using "+e.format+".");return}if(o===null){B(e,e.cycle===12?"Enter a time such as 9:15 AM.":"Enter a time such as 14:30.");return}H(e,a,r,o,t)&&t&&(e.draftStart=a,e.draftEnd=r,e.draftTime=o,a&&(e.focusDate=a,e.month=a.slice(0,7)),_(e),J(e),Y(e),A(e))}function De(e){let t=e.range||e.dateTime?2:1;e.fields=[];for(let a=0;a<t;a++){let r=c("div","blu-picker__field"),o=c("label"),n=c("input","blu-picker__input");n.type="text",n.id=e.uid+"-input-"+a,o.htmlFor=n.id,n.autocomplete="off",n.spellcheck=!1,u(n,"role","combobox"),u(n,"aria-haspopup","dialog"),u(n,"aria-expanded","false"),u(n,"aria-autocomplete","none"),u(n,"aria-controls",e.uid+"-dialog"),u(n,"aria-describedby",e.uid+"-hint "+e.uid+"-error "+e.uid+"-keyboard"),n.addEventListener("input",()=>{e.typedDirty=!0}),n.addEventListener("change",()=>re(e,e.isOpen)),n.addEventListener("pointerdown",l=>{l.pointerType==="mouse"&&(e.touchOpen=!1,n.inputMode="text"),(l.pointerType==="touch"||l.pointerType==="pen")&&e.manualInput!==n&&!e.options.IsDisabled&&!e.options.IsReadOnly&&(l.preventDefault(),e.touchOpen=!0,n.inputMode="none",P(e,n,!0))}),n.addEventListener("click",l=>{e.manualInput!==n&&(e.touchOpen?(l.preventDefault(),P(e,n,!0)):P(e,n,!1))}),n.addEventListener("blur",()=>{e.manualInput===n&&(e.manualInput=null)}),n.addEventListener("keydown",l=>{l.ctrlKey||l.metaKey||l.shiftKey||(l.key==="ArrowDown"&&(l.preventDefault(),P(e,n,!0)),l.key==="Enter"&&(l.preventDefault(),!e.isOpen&&!e.typedDirty?P(e,n,!0):re(e)),l.key==="Tab"&&x(e,!1))});let m=c("div","blu-picker__field-control"),h=u(c("span","blu-picker__field-icon"),"aria-hidden","true");h.innerHTML=e.timeOnly||e.dateTime&&a?'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="8.5"/><path d="M12 7v5l3 2"/></svg>':'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="4" y="5" width="16" height="16" rx="3"/><path d="M8 3v4m8-4v4M4 10h16m-11 4h2m3 0h2m-7 3h2"/></svg>',m.append(n,h),r.append(o,m),e.fields.push({field:r,label:o,input:n}),e.fieldRow.append(r)}}function C(e,t,a){let r=c("option","",a);return r.value=String(t),e.append(r),r}function s(e,t,a){let r=c("label","blu-picker__select-field"),o=c("span","",t),n=c("select","blu-picker__input");return n.id=e.uid+"-"+a,r.append(o,n),{wrap:r,select:n}}function R(e,t){let a=M(e.options.Min)||"0001-01-01",r=M(e.options.Max)||"9999-12-31";t=t<a?a:t>r?r:t,b(t)&&(e.focusDate=t,e.month=t.slice(0,7),_(e),e.calendar.querySelector('[data-date="'+t+'"]')?.focus())}function ve(e,t,a){if(t.altKey||t.ctrlKey||t.metaKey)return;let r,o={ArrowLeft:-1,ArrowRight:1,ArrowUp:-7,ArrowDown:7};if(t.key in o)r=ae(a,o[t.key]);else if(t.key==="Home"||t.key==="End"){let n=(b(a).getDay()-e.firstDay+7)%7;r=ae(a,t.key==="Home"?-n:6-n)}else(t.key==="PageUp"||t.key==="PageDown")&&(r=ie(a,(t.key==="PageUp"?-1:1)*(t.shiftKey?12:1)));r&&(t.preventDefault(),R(e,r))}function ce(e,t){if(!V(e,t)){if(e.dialogError.textContent="",e.focusDate=t,e.range?e.rangeNext==="start"?(e.draftStart=t,e.draftEnd="",e.rangeNext="end"):(t<e.draftStart?(e.draftEnd=e.draftStart,e.draftStart=t):e.draftEnd=t,e.rangeNext="start"):e.draftStart=t,!e.hasTime&&!e.range){H(e,t,"","");return}_(e),e.calendar.querySelector('[data-date="'+t+'"]')?.focus(),Y(e)}}function Y(e){let t=e.range?e.draftStart?w(e,e.draftStart)+(e.draftEnd?" to "+w(e,e.draftEnd):" \u2014 choose an end date"):"Choose a start date":e.hasTime?(e.draftStart?w(e,e.draftStart)+" \xB7 ":"")+T(e.draftTime,e.cycle):"",a=r=>b(r)?new Intl.DateTimeFormat(e.locale,{month:"short",day:"numeric"}).format(b(r)):"";e.selectionSpeech.textContent=t,e.selectionVisual.textContent=e.range?e.draftStart?a(e.draftStart)+(e.draftEnd?" \u2013 "+a(e.draftEnd):" \u2014 choose end"):"Choose start date":e.dateTime?a(e.draftStart)+" \xB7 "+T(e.draftTime,e.cycle):e.timeOnly?T(e.draftTime,e.cycle):"",e.selection.hidden=!t}function _(e){let t=b(e.month+"-01");if(!t)return;let a=t.getFullYear(),r=t.getMonth(),o=Number(M(e.options.Min).slice(0,4))||1,n=Number(M(e.options.Max).slice(0,4))||9999;e.monthSelect.value=String(r),e.yearInput.value=String(a),u(e.yearInput,"aria-invalid",null),e.yearInput.min=String(o),e.yearInput.max=String(n);let m=new Intl.DateTimeFormat(e.locale,{month:"long",year:"numeric"}).format(t);e.caption.textContent!==m&&(e.caption.textContent=m);let h=(t.getDay()-e.firstDay+7)%7,l=new Date(a,r+1,0).getDate();if(e.focusDate.startsWith(e.month)||(e.focusDate=e.month+"-01"),!e.dayCells){let i=c("tbody");e.dayCells=[];for(let f=0;f<6;f++){let d=c("tr");for(let D=0;D<7;D++){let S=c("td"),y=E("",()=>ce(e,y.dataset.date),"blu-picker__day");y.addEventListener("keydown",k=>ve(e,k,y.dataset.date)),y.addEventListener("focus",()=>{e.focusDate=y.dataset.date;for(let k of e.dayCells)k.button.tabIndex=k.button===y?0:-1}),S.append(y),d.append(S),e.dayCells.push({cell:S,button:y})}i.append(d)}e.calendar.append(i)}for(let i=0;i<e.dayCells.length;i++){let{cell:f,button:d}=e.dayCells[i],D=i-h+1,S=D>0&&D<=l;if(d.hidden=!S,!S){d.tabIndex=-1,delete d.dataset.date,u(f,"aria-selected",null);continue}let y=e.month+"-"+v(D),k=y===e.draftStart||e.range&&y===e.draftEnd,U=e.range&&e.draftStart&&e.draftEnd&&y>e.draftStart&&y<e.draftEnd,W=V(e,y),$=!!(k||U),pe=e.range&&k?y===e.draftStart?e.draftEnd?", range starts here; "+w(e,e.draftStart)+" to "+w(e,e.draftEnd):", start date; choose an end date":", range ends here; "+w(e,e.draftStart)+" to "+w(e,e.draftEnd):"",j=w(e,y)+($?", selected":"")+pe+(W?", unavailable":"");d.dataset.date=y,d.tabIndex=y===e.focusDate?0:-1,d.textContent!==String(D)&&(d.textContent=String(D)),d.getAttribute("aria-label")!==j&&u(d,"aria-label",j),u(f,"aria-selected",String($)),u(d,"aria-disabled",W?"true":null),u(d,"aria-current",y===N()?"date":null),d.dataset.selected=String(k),d.dataset.between=String(!!U)}for(let i=0;i<6;i++)e.dayCells[i*7].cell.parentElement.hidden=i*7-h+1>l;let g=M(e.options.Min),p=M(e.options.Max);e.prev.disabled=!!g&&e.month<=g.slice(0,7)||a===1&&r===0,e.next.disabled=!!p&&e.month>=p.slice(0,7)||a===9999&&r===11,e.todayButton.hidden=e.timeOnly||e.options.ShowToday===!1,e.todayButton.disabled=!!V(e,N())}function J(e){if(!e.hasTime)return;let[t,a]=(e.draftTime||"09:00").split(":").map(Number);e.hours.replaceChildren(),e.minutes.replaceChildren();for(let r=0;r<(e.cycle===12?12:24);r++)C(e.hours,e.cycle===12?r+1:r,v(e.cycle===12?r+1:r));for(let r=0;r<60;r+=e.step)C(e.minutes,r,v(r));a%e.step&&C(e.minutes,a,v(a)+" (entered)"),e.hours.value=String(e.cycle===12?t%12||12:t),e.minutes.value=String(a),e.period.value=t<12?"AM":"PM",e.periodField.hidden=e.cycle!==12,Y(e)}function P(e,t=e.fields[0].input,a=!0){if(e.options.IsDisabled||e.options.IsReadOnly)return;if(e.anchor=t,e.manualInput=null,e.typeButton.hidden=!e.touchOpen,e.isOpen){e.range&&(e.rangeNext=t===e.fields[1].input&&e.draftStart?"end":"start"),A(e),a&&ne(e);return}e.draftStart=e.timeOnly?"":e.range?e.start:M(e.value),e.draftEnd=e.end||"",e.draftTime=e.timeOnly?e.value:F(e.value),e.hasTime&&!L(e.draftTime)&&(e.draftTime="09:00"),e.rangeNext="start",e.range&&t===e.fields[1].input&&e.draftStart&&(e.rangeNext="end");let r=e.range&&e.rangeNext==="end"?e.draftEnd:e.draftStart;e.focusDate=b(r)?r:b(e.draftStart)?e.draftStart:N();let o=M(e.options.Min),n=M(e.options.Max);e.timeOnly||(o&&e.focusDate<o&&(e.focusDate=o),n&&e.focusDate>n&&(e.focusDate=n)),e.month=e.focusDate.slice(0,7),e.dialogError.textContent="",_(e),J(e),Y(e),e.isOpen=!0,e.dialog.hidden=!1,e.dialog.showPopover&&e.dialog.showPopover(),e.dialog.scrollTop=0;for(let{input:m}of e.fields)u(m,"aria-expanded","true");A(e),a&&e.isOpen&&ne(e),I(e,"OpenChanged",!0)}function Me(e){e.range=e.kind==="DateRangePicker",e.timeOnly=e.kind==="TimePicker",e.dateTime=e.kind==="DateTimePicker",e.hasTime=e.timeOnly||e.dateTime,e.root.className="blu-picker",e.fieldRow=c("div","blu-picker__fields"),e.error=u(c("div","blu-picker__error"),"role","alert"),e.error.id=e.uid+"-error",e.error.hidden=!0,e.hint=c("div","blu-picker__hint"),e.hint.id=e.uid+"-hint",e.actions=c("div","blu-picker__actions"),e.clearButton=E("Clear",()=>H(e,"","","")),e.actions.append(e.clearButton),e.root.append(e.fieldRow,e.hint,e.actions,e.error),De(e);let t=c("span","blu-advanced-sr","Click the field or press Down Arrow to browse. Escape closes the popup.");t.id=e.uid+"-keyboard",e.root.append(t),e.dialog=c("div","blu-picker__dialog blu-picker__popup"),e.dialog.id=e.uid+"-dialog",e.dialog.hidden=!0,u(e.dialog,"role","dialog"),u(e.dialog,"aria-labelledby",e.uid+"-title"),u(e.dialog,"aria-modal","false"),e.dialog.showPopover&&u(e.dialog,"popover","manual"),e.root.append(e.dialog),e.title=c("h2","blu-advanced-sr"),e.title.id=e.uid+"-title",e.dialog.append(e.title),e.presets=c("div","blu-picker__presets"),e.dialog.append(e.presets),e.calendarArea=c("div","blu-picker__calendar-area"),e.calendarArea.hidden=e.timeOnly,e.dialog.append(e.calendarArea);let a=c("div","blu-picker__calendar-nav"),r=s(e,"Month","month");e.monthSelect=r.select;let o=c("label","blu-picker__select-field");o.append(c("span","","Year")),e.yearInput=c("input","blu-picker__input"),e.yearInput.type="number",e.yearInput.step="1",o.append(e.yearInput);let n=d=>{let D=ie(e.focusDate,d);R(e,D)};e.prev=E("\u2039",()=>n(-1)),u(e.prev,"aria-label","Previous month"),e.next=E("\u203A",()=>n(1)),u(e.next,"aria-label","Next month"),a.append(e.prev,r.wrap,o,e.next),e.calendarArea.append(a),e.monthSelect.addEventListener("change",()=>R(e,String(e.yearInput.value).padStart(4,"0")+"-"+v(+e.monthSelect.value+1)+"-01")),e.yearInput.addEventListener("change",()=>{let d=Number(e.yearInput.value);if(!Number.isInteger(d)||d<+e.yearInput.min||d>+e.yearInput.max){e.dialogError.textContent="Enter a year from "+e.yearInput.min+" to "+e.yearInput.max+".",u(e.yearInput,"aria-invalid","true");return}u(e.yearInput,"aria-invalid",null),e.dialogError.textContent="",R(e,String(d).padStart(4,"0")+"-"+v(+e.monthSelect.value+1)+"-01")}),e.caption=u(c("h3","blu-picker__caption blu-advanced-sr"),"aria-live","polite"),e.caption.id=e.uid+"-caption",e.calendarArea.append(e.caption),e.calendar=u(c("table","blu-picker__calendar"),"role","grid"),u(e.calendar,"aria-labelledby",e.caption.id),e.range&&u(e.calendar,"aria-multiselectable","true"),e.calendarArea.append(e.calendar),e.todayButton=E("Today",()=>{e.focusDate=N(),e.month=e.focusDate.slice(0,7),ce(e,e.focusDate)}),e.calendarArea.append(c("p","blu-advanced-sr","Arrow keys move by day or week. Home/End move within the week. Page Up/Down change month; hold Shift to change year.")),e.timeArea=c("div","blu-picker__time-area"),e.timeArea.hidden=!e.hasTime,e.dialog.append(e.timeArea);let m=s(e,"Hour","hour"),h=s(e,"Minute","minute"),l=s(e,"AM / PM","period");e.hours=m.select,e.minutes=h.select,e.period=l.select,e.periodField=l.wrap,C(e.period,"AM","AM"),C(e.period,"PM","PM"),e.timeArea.append(m.wrap,h.wrap,l.wrap);for(let d of[e.hours,e.minutes,e.period])d.addEventListener("change",()=>{let D=+e.hours.value;e.cycle===12&&(D=D%12+(e.period.value==="PM"?12:0)),e.draftTime=v(D)+":"+v(+e.minutes.value),e.dialogError.textContent="",Y(e)});e.selection=u(c("div","blu-picker__selection"),"role","status"),u(e.selection,"aria-atomic","true"),e.selectionSpeech=c("span","blu-advanced-sr"),e.selectionVisual=u(c("span"),"aria-hidden","true"),e.selection.append(e.selectionSpeech,e.selectionVisual),e.dialogError=u(c("div","blu-picker__error"),"role","alert"),e.dialogError.id=e.uid+"-dialog-error",u(e.yearInput,"aria-describedby",e.dialogError.id),e.apply=E("Apply",()=>H(e,e.draftStart,e.draftEnd,e.draftTime),"blu-picker__button blu-picker__button--primary"),e.apply.hidden=!e.range&&!e.hasTime,e.typeButton=E("Type",()=>{let d=e.anchor;x(e,!1),e.manualInput=d,d.inputMode="text",d.focus({preventScroll:!0})}),u(e.typeButton,"aria-label","Type a date or time"),e.typeButton.hidden=!0;let g=c("div","blu-picker__popup-actions");g.append(e.typeButton,e.todayButton,E("Cancel",()=>x(e)),e.apply),e.dialog.append(e.selection,e.dialogError,g);let p=d=>{e.isOpen&&!e.root.contains(d.target)&&x(e,!1)},i=d=>{e.isOpen&&d.key==="Escape"&&!d.defaultPrevented&&(d.preventDefault(),d.stopPropagation(),x(e))},f=d=>{!e.isOpen||e.dialog.contains(d?.target)||(cancelAnimationFrame(e.positionFrame),e.positionFrame=requestAnimationFrame(()=>A(e)))};document.addEventListener("pointerdown",p,!0),document.addEventListener("focusin",p),e.root.addEventListener("keydown",i),window.addEventListener("resize",f),document.addEventListener("scroll",f,!0),window.visualViewport?.addEventListener("resize",f),window.visualViewport?.addEventListener("scroll",f),window.ResizeObserver&&(e.popupObserver=new ResizeObserver(()=>f()),e.popupObserver.observe(e.fieldRow),e.popupObserver.observe(e.dialog)),e.dispose=()=>{x(e,!1),cancelAnimationFrame(e.positionFrame),e.popupObserver?.disconnect(),document.removeEventListener("pointerdown",p,!0),document.removeEventListener("focusin",p),e.root.removeEventListener("keydown",i),window.removeEventListener("resize",f),document.removeEventListener("scroll",f,!0),window.visualViewport?.removeEventListener("resize",f),window.visualViewport?.removeEventListener("scroll",f)}}function we(e){let t=JSON.stringify(e.options);if(e.renderSignature===t)return;let a=e.isOpen?document.activeElement:null,r=a?.dataset?.date,o=a?.id,n=e.options;if(e.label=String(n.Label||{DatePicker:"Date",TimePicker:"Time",DateTimePicker:"Date and time",DateRangePicker:"Date range"}[e.kind]),e.locale=String(n.Locale||"en-US"),new Intl.DateTimeFormat(e.locale),e.format=ye.includes(n.DateFormat)?n.DateFormat:"YYYY-MM-DD",e.cycle=Number(n.HourCycle)===12?12:24,e.step=Number(n.MinuteStep??15),!Number.isInteger(e.step)||e.step<1||e.step>60)throw Error("MinuteStep must be a whole number from 1 to 60.");if(e.firstDay=Number(n.FirstDayOfWeek??1),!Number.isInteger(e.firstDay)||e.firstDay<0||e.firstDay>6)throw Error("FirstDayOfWeek must be 0\u20136.");e.disabledDates=new Set(Z(n.DisabledDatesJson??n.DisabledDates,"DisabledDates").map(i=>typeof i=="string"?i:i.Date));for(let i of e.disabledDates)if(!b(i))throw Error("DisabledDates must contain ISO calendar dates.");for(let i of["Min","Max"])if(n[i]&&!(e.timeOnly?L(n[i]):b(M(n[i]))&&(!e.dateTime||L(F(n[i])))))throw Error(i+" must use the documented ISO value format.");if(n.Min&&n.Max&&n.Min>n.Max)throw Error("Min must be on or before Max.");for(let i of["MinRangeDays","MaxRangeDays"])if(n[i]!==void 0&&(!Number.isInteger(Number(n[i]))||Number(n[i])<0))throw Error(i+" must be a non-negative whole number.");if(n.MinRangeDays&&n.MaxRangeDays&&Number(n.MinRangeDays)>Number(n.MaxRangeDays))throw Error("MinRangeDays must not exceed MaxRangeDays.");let m=e.range?[String(n.StartDate||""),String(n.EndDate||"")]:[String(n.Value||"")],h=JSON.stringify(m),l=h!==e.inputSignature;if(e.inputSignature=h,e.range?(e.start=m[0],e.end=m[1]):e.value=m[0],e.fields.forEach((i,f)=>{i.label.textContent=e.range?e.label+(f?" \u2014 end":" \u2014 start"):e.dateTime?e.label+(f?" \u2014 time":" \u2014 date"):e.label,i.input.disabled=!!n.IsDisabled,i.input.readOnly=!!n.IsReadOnly,i.input.required=!!n.IsRequired,i.input.placeholder=String(n.Placeholder||(e.timeOnly||e.dateTime&&f?e.cycle===12?"9:00 AM":"09:00":e.format))}),e.hint.textContent=e.timeOnly?(e.cycle===12?"h:mm AM/PM":"HH:mm")+" \xB7 local time":e.format+(e.dateTime?" \xB7 "+(e.cycle===12?"h:mm AM/PM":"HH:mm")+" local time":""),e.title.textContent=e.label,e.clearButton.disabled=!!n.IsDisabled||!!n.IsReadOnly,l||e.previousFormat!==e.format||e.previousCycle!==e.cycle){le(e);let i=m.some(Boolean);B(e,i?de(e,e.timeOnly?"":e.range?e.start:M(e.value),e.end||"",e.timeOnly?e.value:F(e.value)):""),e.previousFormat=e.format,e.previousCycle=e.cycle}e.monthSelect.replaceChildren();for(let i=0;i<12;i++)C(e.monthSelect,i,new Intl.DateTimeFormat(e.locale,{month:"long"}).format(new Date(2024,i,1)));e.calendar.tHead?.remove();let g=c("thead"),p=c("tr");for(let i=0;i<7;i++){let f=new Date(2024,0,7+(i+e.firstDay)%7),d=c("th","",new Intl.DateTimeFormat(e.locale,{weekday:"short"}).format(f));d.scope="col",d.abbr=new Intl.DateTimeFormat(e.locale,{weekday:"long"}).format(f),p.append(d)}if(g.append(p),e.calendar.prepend(g),e.presets.replaceChildren(),e.presets.hidden=!e.range,e.range)for(let i of Z(n.PresetsJson??n.Presets,"Presets")){if(!i.Label||!b(i.StartDate)||!b(i.EndDate))throw Error("Presets need Label, StartDate and EndDate.");let f=E(i.Label,()=>{e.draftStart=i.StartDate,e.draftEnd=i.EndDate,e.rangeNext="start",e.focusDate=e.draftStart,e.month=e.focusDate.slice(0,7),e.dialogError.textContent="",_(e),Y(e)});f.disabled=!!oe(i.StartDate,i.EndDate,{...n,disabled:e.disabledDates}),e.presets.append(f)}e.isOpen&&(n.IsDisabled||n.IsReadOnly||l?x(e):(_(e),J(e),r?e.calendar.querySelector('[data-date="'+r+'"]')?.focus():o&&document.getElementById(o)?.focus(),A(e))),e.renderSignature=t}var ue=()=>me("DateTimePicker",Me,we);function Te(e,t={},a={}){return ue(),X(e,"DateTimePicker",t,r=>({Change:o=>(r({Value:o}),a.Change?.(o)),ValidationChanged:(o,n)=>a.ValidationChanged?.(o,n),OpenChanged:o=>a.OpenChanged?.(o),Error:o=>a.Error?.(o)}),["DisabledDates"],[])}export{Te as a};
|