@bquery/bquery 1.5.0 → 1.7.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 +193 -23
- package/dist/a11y/announce.d.ts +43 -0
- package/dist/a11y/announce.d.ts.map +1 -0
- package/dist/a11y/audit.d.ts +42 -0
- package/dist/a11y/audit.d.ts.map +1 -0
- package/dist/a11y/index.d.ts +53 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/media-preferences.d.ts +77 -0
- package/dist/a11y/media-preferences.d.ts.map +1 -0
- package/dist/a11y/roving-tab-index.d.ts +38 -0
- package/dist/a11y/roving-tab-index.d.ts.map +1 -0
- package/dist/a11y/skip-link.d.ts +37 -0
- package/dist/a11y/skip-link.d.ts.map +1 -0
- package/dist/a11y/trap-focus.d.ts +49 -0
- package/dist/a11y/trap-focus.d.ts.map +1 -0
- package/dist/a11y/types.d.ts +152 -0
- package/dist/a11y/types.d.ts.map +1 -0
- package/dist/a11y-C5QOVvRn.js +421 -0
- package/dist/a11y-C5QOVvRn.js.map +1 -0
- package/dist/a11y.es.mjs +14 -0
- package/dist/component/component.d.ts +13 -5
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/html.d.ts +40 -3
- package/dist/component/html.d.ts.map +1 -1
- package/dist/component/index.d.ts +3 -2
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/library.d.ts.map +1 -1
- package/dist/component/scope.d.ts +138 -0
- package/dist/component/scope.d.ts.map +1 -0
- package/dist/component/types.d.ts +184 -17
- package/dist/component/types.d.ts.map +1 -1
- package/dist/component-CuuTijA6.js +684 -0
- package/dist/component-CuuTijA6.js.map +1 -0
- package/dist/component.es.mjs +10 -6
- package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
- package/dist/config-BW35FKuA.js.map +1 -0
- package/dist/constraints-3lV9yyBw.js +100 -0
- package/dist/constraints-3lV9yyBw.js.map +1 -0
- package/dist/core/collection.d.ts +48 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +92 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/env.d.ts +18 -0
- package/dist/core/env.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/shared.d.ts +8 -0
- package/dist/core/shared.d.ts.map +1 -1
- package/dist/core/utils/index.d.ts +52 -41
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core-Cjl7GUu8.js +717 -0
- package/dist/core-Cjl7GUu8.js.map +1 -0
- package/dist/{core-DPdbItcq.js → core-DnlyjbF2.js} +1 -1
- package/dist/{core-DPdbItcq.js.map → core-DnlyjbF2.js.map} +1 -1
- package/dist/core.es.mjs +45 -44
- package/dist/custom-directives-7wAShnnd.js +9 -0
- package/dist/custom-directives-7wAShnnd.js.map +1 -0
- package/dist/devtools/devtools.d.ts +212 -0
- package/dist/devtools/devtools.d.ts.map +1 -0
- package/dist/devtools/index.d.ts +20 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/types.d.ts +69 -0
- package/dist/devtools/types.d.ts.map +1 -0
- package/dist/devtools-D2fQLhDN.js +122 -0
- package/dist/devtools-D2fQLhDN.js.map +1 -0
- package/dist/devtools.es.mjs +19 -0
- package/dist/dnd/draggable.d.ts +51 -0
- package/dist/dnd/draggable.d.ts.map +1 -0
- package/dist/dnd/droppable.d.ts +38 -0
- package/dist/dnd/droppable.d.ts.map +1 -0
- package/dist/dnd/index.d.ts +47 -0
- package/dist/dnd/index.d.ts.map +1 -0
- package/dist/dnd/sortable.d.ts +43 -0
- package/dist/dnd/sortable.d.ts.map +1 -0
- package/dist/dnd/types.d.ts +250 -0
- package/dist/dnd/types.d.ts.map +1 -0
- package/dist/dnd-B8EgyzaI.js +244 -0
- package/dist/dnd-B8EgyzaI.js.map +1 -0
- package/dist/dnd.es.mjs +6 -0
- package/dist/env-NeVmr4Gf.js +19 -0
- package/dist/env-NeVmr4Gf.js.map +1 -0
- package/dist/forms/create-form.d.ts +49 -0
- package/dist/forms/create-form.d.ts.map +1 -0
- package/dist/forms/index.d.ts +39 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/types.d.ts +139 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +179 -0
- package/dist/forms/validators.d.ts.map +1 -0
- package/dist/forms-C3yovgH9.js +141 -0
- package/dist/forms-C3yovgH9.js.map +1 -0
- package/dist/forms.es.mjs +14 -0
- package/dist/full.d.ts +37 -9
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +186 -91
- package/dist/full.iife.js +47 -31
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +47 -31
- package/dist/full.umd.js.map +1 -1
- package/dist/i18n/formatting.d.ts +40 -0
- package/dist/i18n/formatting.d.ts.map +1 -0
- package/dist/i18n/i18n.d.ts +48 -0
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/translate.d.ts +83 -0
- package/dist/i18n/translate.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +156 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n-BnnhTFOS.js +89 -0
- package/dist/i18n-BnnhTFOS.js.map +1 -0
- package/dist/i18n.es.mjs +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.mjs +233 -138
- package/dist/media/battery.d.ts +35 -0
- package/dist/media/battery.d.ts.map +1 -0
- package/dist/media/breakpoints.d.ts +51 -0
- package/dist/media/breakpoints.d.ts.map +1 -0
- package/dist/media/clipboard.d.ts +30 -0
- package/dist/media/clipboard.d.ts.map +1 -0
- package/dist/media/device-sensors.d.ts +54 -0
- package/dist/media/device-sensors.d.ts.map +1 -0
- package/dist/media/geolocation.d.ts +38 -0
- package/dist/media/geolocation.d.ts.map +1 -0
- package/dist/media/index.d.ts +42 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/media-query.d.ts +36 -0
- package/dist/media/media-query.d.ts.map +1 -0
- package/dist/media/network.d.ts +35 -0
- package/dist/media/network.d.ts.map +1 -0
- package/dist/media/types.d.ts +173 -0
- package/dist/media/types.d.ts.map +1 -0
- package/dist/media/viewport.d.ts +32 -0
- package/dist/media/viewport.d.ts.map +1 -0
- package/dist/media-Di2Ta22s.js +340 -0
- package/dist/media-Di2Ta22s.js.map +1 -0
- package/dist/media.es.mjs +12 -0
- package/dist/motion/index.d.ts +7 -3
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/morph.d.ts +27 -0
- package/dist/motion/morph.d.ts.map +1 -0
- package/dist/motion/parallax.d.ts +30 -0
- package/dist/motion/parallax.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +36 -3
- package/dist/motion/reduced-motion.d.ts.map +1 -1
- package/dist/motion/types.d.ts +58 -0
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/motion/typewriter.d.ts +31 -0
- package/dist/motion/typewriter.d.ts.map +1 -0
- package/dist/motion-qPj_TYGv.js +530 -0
- package/dist/motion-qPj_TYGv.js.map +1 -0
- package/dist/motion.es.mjs +27 -23
- package/dist/mount-SM07RUa6.js +403 -0
- package/dist/mount-SM07RUa6.js.map +1 -0
- package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
- package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
- package/dist/{platform-B7JhGBc7.js → platform-CPbCprb6.js} +3 -3
- package/dist/platform-CPbCprb6.js.map +1 -0
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin/index.d.ts +22 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/registry.d.ts +108 -0
- package/dist/plugin/registry.d.ts.map +1 -0
- package/dist/plugin/types.d.ts +110 -0
- package/dist/plugin/types.d.ts.map +1 -0
- package/dist/plugin-cPoOHFLY.js +64 -0
- package/dist/plugin-cPoOHFLY.js.map +1 -0
- package/dist/plugin.es.mjs +9 -0
- package/dist/reactive/computed.d.ts +7 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive-Cfv0RK6x.js +233 -0
- package/dist/reactive-Cfv0RK6x.js.map +1 -0
- package/dist/reactive.es.mjs +18 -17
- package/dist/registry-CWf368tT.js +26 -0
- package/dist/registry-CWf368tT.js.map +1 -0
- package/dist/router/bq-link.d.ts +112 -0
- package/dist/router/bq-link.d.ts.map +1 -0
- package/dist/router/constraints.d.ts +9 -0
- package/dist/router/constraints.d.ts.map +1 -0
- package/dist/router/index.d.ts +14 -6
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/match.d.ts +0 -1
- package/dist/router/match.d.ts.map +1 -1
- package/dist/router/path-pattern.d.ts +14 -0
- package/dist/router/path-pattern.d.ts.map +1 -0
- package/dist/router/query.d.ts.map +1 -1
- package/dist/router/router.d.ts +3 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/types.d.ts +48 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/use-route.d.ts +50 -0
- package/dist/router/use-route.d.ts.map +1 -0
- package/dist/router/utils.d.ts +3 -0
- package/dist/router/utils.d.ts.map +1 -1
- package/dist/router-BrthaP_z.js +473 -0
- package/dist/router-BrthaP_z.js.map +1 -0
- package/dist/router.es.mjs +13 -10
- package/dist/{sanitize-jyJ2ryE2.js → sanitize-B1V4JswB.js} +95 -83
- package/dist/sanitize-B1V4JswB.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize.d.ts +4 -1
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-html.d.ts +53 -0
- package/dist/security/trusted-html.d.ts.map +1 -0
- package/dist/security.es.mjs +10 -9
- package/dist/ssr/hydrate.d.ts +65 -0
- package/dist/ssr/hydrate.d.ts.map +1 -0
- package/dist/ssr/index.d.ts +59 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/render.d.ts +62 -0
- package/dist/ssr/render.d.ts.map +1 -0
- package/dist/ssr/serialize.d.ts +118 -0
- package/dist/ssr/serialize.d.ts.map +1 -0
- package/dist/ssr/types.d.ts +70 -0
- package/dist/ssr/types.d.ts.map +1 -0
- package/dist/ssr-B2qd_WBB.js +248 -0
- package/dist/ssr-B2qd_WBB.js.map +1 -0
- package/dist/ssr.es.mjs +9 -0
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/define-store.d.ts +1 -1
- package/dist/store/define-store.d.ts.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +1 -1
- package/dist/store/mapping.d.ts.map +1 -1
- package/dist/store/persisted.d.ts +38 -4
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/store/types.d.ts +140 -3
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/utils.d.ts +2 -2
- package/dist/store/utils.d.ts.map +1 -1
- package/dist/store/watch.d.ts +1 -1
- package/dist/store/watch.d.ts.map +1 -1
- package/dist/store-DWpyH6p5.js +338 -0
- package/dist/store-DWpyH6p5.js.map +1 -0
- package/dist/store.es.mjs +11 -10
- package/dist/storybook/index.d.ts +37 -0
- package/dist/storybook/index.d.ts.map +1 -0
- package/dist/storybook.es.mjs +151 -0
- package/dist/storybook.es.mjs.map +1 -0
- package/dist/testing/index.d.ts +23 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testing.d.ts +156 -0
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/types.d.ts +134 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing-CsqjNUyy.js +224 -0
- package/dist/testing-CsqjNUyy.js.map +1 -0
- package/dist/testing.es.mjs +9 -0
- package/dist/type-guards-Do9DWgNp.js +44 -0
- package/dist/type-guards-Do9DWgNp.js.map +1 -0
- package/dist/untrack-DJVQQ2WM.js +33 -0
- package/dist/untrack-DJVQQ2WM.js.map +1 -0
- package/dist/view/custom-directives.d.ts +20 -0
- package/dist/view/custom-directives.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts.map +1 -1
- package/dist/view/process.d.ts.map +1 -1
- package/dist/view.es.mjs +11 -10
- package/package.json +52 -11
- package/src/a11y/announce.ts +131 -0
- package/src/a11y/audit.ts +314 -0
- package/src/a11y/index.ts +68 -0
- package/src/a11y/media-preferences.ts +255 -0
- package/src/a11y/roving-tab-index.ts +164 -0
- package/src/a11y/skip-link.ts +255 -0
- package/src/a11y/trap-focus.ts +184 -0
- package/src/a11y/types.ts +183 -0
- package/src/component/component.ts +345 -65
- package/src/component/html.ts +153 -53
- package/src/component/index.ts +12 -2
- package/src/component/library.ts +66 -28
- package/src/component/scope.ts +212 -0
- package/src/component/types.ts +238 -19
- package/src/core/collection.ts +707 -628
- package/src/core/element.ts +981 -774
- package/src/core/env.ts +60 -0
- package/src/core/index.ts +49 -48
- package/src/core/shared.ts +62 -13
- package/src/core/utils/index.ts +148 -83
- package/src/devtools/devtools.ts +410 -0
- package/src/devtools/index.ts +48 -0
- package/src/devtools/types.ts +104 -0
- package/src/dnd/draggable.ts +296 -0
- package/src/dnd/droppable.ts +228 -0
- package/src/dnd/index.ts +62 -0
- package/src/dnd/sortable.ts +307 -0
- package/src/dnd/types.ts +293 -0
- package/src/forms/create-form.ts +278 -0
- package/src/forms/index.ts +65 -0
- package/src/forms/types.ts +154 -0
- package/src/forms/validators.ts +265 -0
- package/src/full.ts +260 -3
- package/src/i18n/formatting.ts +67 -0
- package/src/i18n/i18n.ts +200 -0
- package/src/i18n/index.ts +67 -0
- package/src/i18n/translate.ts +182 -0
- package/src/i18n/types.ts +171 -0
- package/src/index.ts +108 -36
- package/src/media/battery.ts +116 -0
- package/src/media/breakpoints.ts +131 -0
- package/src/media/clipboard.ts +80 -0
- package/src/media/device-sensors.ts +158 -0
- package/src/media/geolocation.ts +119 -0
- package/src/media/index.ts +76 -0
- package/src/media/media-query.ts +92 -0
- package/src/media/network.ts +115 -0
- package/src/media/types.ts +177 -0
- package/src/media/viewport.ts +84 -0
- package/src/motion/index.ts +57 -48
- package/src/motion/morph.ts +151 -0
- package/src/motion/parallax.ts +120 -0
- package/src/motion/reduced-motion.ts +66 -17
- package/src/motion/transition.ts +97 -97
- package/src/motion/types.ts +63 -0
- package/src/motion/typewriter.ts +164 -0
- package/src/platform/announcer.ts +208 -208
- package/src/platform/config.ts +163 -163
- package/src/platform/cookies.ts +165 -165
- package/src/platform/index.ts +39 -39
- package/src/platform/meta.ts +168 -168
- package/src/plugin/index.ts +37 -0
- package/src/plugin/registry.ts +269 -0
- package/src/plugin/types.ts +137 -0
- package/src/reactive/async-data.ts +486 -486
- package/src/reactive/computed.ts +130 -92
- package/src/reactive/index.ts +37 -37
- package/src/reactive/signal.ts +29 -29
- package/src/router/bq-link.ts +279 -0
- package/src/router/constraints.ts +201 -0
- package/src/router/index.ts +49 -41
- package/src/router/match.ts +312 -106
- package/src/router/path-pattern.ts +52 -0
- package/src/router/query.ts +38 -35
- package/src/router/router.ts +402 -211
- package/src/router/types.ts +139 -93
- package/src/router/use-route.ts +68 -0
- package/src/router/utils.ts +157 -116
- package/src/security/constants.ts +211 -211
- package/src/security/index.ts +12 -10
- package/src/security/sanitize.ts +6 -2
- package/src/security/trusted-html.ts +71 -0
- package/src/ssr/hydrate.ts +82 -0
- package/src/ssr/index.ts +70 -0
- package/src/ssr/render.ts +508 -0
- package/src/ssr/serialize.ts +296 -0
- package/src/ssr/types.ts +81 -0
- package/src/store/create-store.ts +467 -329
- package/src/store/define-store.ts +2 -1
- package/src/store/index.ts +27 -22
- package/src/store/mapping.ts +2 -1
- package/src/store/persisted.ts +249 -61
- package/src/store/types.ts +247 -94
- package/src/store/utils.ts +135 -141
- package/src/store/watch.ts +2 -1
- package/src/storybook/index.ts +480 -0
- package/src/testing/index.ts +42 -0
- package/src/testing/testing.ts +593 -0
- package/src/testing/types.ts +170 -0
- package/src/view/custom-directives.ts +30 -0
- package/src/view/evaluate.ts +292 -290
- package/src/view/process.ts +108 -92
- package/dist/component-CY5MVoYN.js +0 -531
- package/dist/component-CY5MVoYN.js.map +0 -1
- package/dist/config-DRmZZno3.js.map +0 -1
- package/dist/core-CK2Mfpf4.js +0 -648
- package/dist/core-CK2Mfpf4.js.map +0 -1
- package/dist/motion-C5DRdPnO.js +0 -415
- package/dist/motion-C5DRdPnO.js.map +0 -1
- package/dist/platform-B7JhGBc7.js.map +0 -1
- package/dist/reactive-BDya-ia8.js +0 -253
- package/dist/reactive-BDya-ia8.js.map +0 -1
- package/dist/router-CijiICxt.js +0 -188
- package/dist/router-CijiICxt.js.map +0 -1
- package/dist/sanitize-jyJ2ryE2.js.map +0 -1
- package/dist/store-CPK9E62U.js +0 -262
- package/dist/store-CPK9E62U.js.map +0 -1
- package/dist/view-Cdi0g-qo.js +0 -396
- package/dist/view-Cdi0g-qo.js.map +0 -1
package/src/motion/types.ts
CHANGED
|
@@ -206,3 +206,66 @@ export interface ScrollAnimateOptions extends AnimateOptions {
|
|
|
206
206
|
* Cleanup function for scroll animations.
|
|
207
207
|
*/
|
|
208
208
|
export type ScrollAnimateCleanup = () => void;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Options for the `morphElement` FLIP-based morph animation.
|
|
212
|
+
*/
|
|
213
|
+
export interface MorphOptions {
|
|
214
|
+
/** Animation duration in milliseconds (default: 300) */
|
|
215
|
+
duration?: number;
|
|
216
|
+
/** CSS easing function (default: 'ease') */
|
|
217
|
+
easing?: string;
|
|
218
|
+
/** Whether to respect prefers-reduced-motion (default: true) */
|
|
219
|
+
respectReducedMotion?: boolean;
|
|
220
|
+
/** Callback when morph completes */
|
|
221
|
+
onComplete?: () => void;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Options for the `parallax` scroll-linked effect.
|
|
226
|
+
*/
|
|
227
|
+
export interface ParallaxOptions {
|
|
228
|
+
/** Parallax speed multiplier (default: 0.5). Values < 1 are slower, > 1 are faster. */
|
|
229
|
+
speed?: number;
|
|
230
|
+
/** Direction of the parallax effect (default: 'vertical') */
|
|
231
|
+
direction?: 'vertical' | 'horizontal' | 'both';
|
|
232
|
+
/** Whether to respect prefers-reduced-motion (default: true) */
|
|
233
|
+
respectReducedMotion?: boolean;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Cleanup function for the parallax effect.
|
|
238
|
+
*/
|
|
239
|
+
export type ParallaxCleanup = () => void;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Options for the `typewriter` text animation.
|
|
243
|
+
*/
|
|
244
|
+
export interface TypewriterOptions {
|
|
245
|
+
/** Time in milliseconds between each character (default: 50) */
|
|
246
|
+
speed?: number;
|
|
247
|
+
/** Initial delay before starting, in milliseconds (default: 0) */
|
|
248
|
+
delay?: number;
|
|
249
|
+
/** Whether to show a blinking cursor (default: false) */
|
|
250
|
+
cursor?: boolean;
|
|
251
|
+
/** Cursor character (default: '|') */
|
|
252
|
+
cursorChar?: string;
|
|
253
|
+
/** Whether to loop the animation (default: false) */
|
|
254
|
+
loop?: boolean;
|
|
255
|
+
/** Pause duration at end before looping, in ms (default: 1000) */
|
|
256
|
+
loopDelay?: number;
|
|
257
|
+
/** Whether to respect prefers-reduced-motion (default: true) */
|
|
258
|
+
respectReducedMotion?: boolean;
|
|
259
|
+
/** Callback when typing completes (per-loop) */
|
|
260
|
+
onComplete?: () => void;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Controls for an active typewriter animation.
|
|
265
|
+
*/
|
|
266
|
+
export interface TypewriterControls {
|
|
267
|
+
/** Stop the animation and clean up */
|
|
268
|
+
stop(): void;
|
|
269
|
+
/** Promise that resolves when the animation finishes, or when a looping animation is stopped */
|
|
270
|
+
done: Promise<void>;
|
|
271
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Character-by-character typewriter text animation.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/motion
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { prefersReducedMotion } from './reduced-motion';
|
|
8
|
+
import type { TypewriterControls, TypewriterOptions } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Animate text appearing character by character in an element.
|
|
12
|
+
*
|
|
13
|
+
* @param element - The element to type text into
|
|
14
|
+
* @param text - The text to display
|
|
15
|
+
* @param options - Typewriter configuration
|
|
16
|
+
* @returns Controls with `.stop()` to cancel and `.done` promise
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const tw = typewriter(
|
|
21
|
+
* document.querySelector('#output')!,
|
|
22
|
+
* 'Hello, world!',
|
|
23
|
+
* { speed: 80, cursor: true },
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* // Wait for it to finish:
|
|
27
|
+
* await tw.done;
|
|
28
|
+
*
|
|
29
|
+
* // Or cancel early:
|
|
30
|
+
* tw.stop();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export const typewriter = (
|
|
34
|
+
element: HTMLElement,
|
|
35
|
+
text: string,
|
|
36
|
+
options: TypewriterOptions = {}
|
|
37
|
+
): TypewriterControls => {
|
|
38
|
+
const {
|
|
39
|
+
speed = 50,
|
|
40
|
+
delay = 0,
|
|
41
|
+
cursor = false,
|
|
42
|
+
cursorChar = '|',
|
|
43
|
+
loop = false,
|
|
44
|
+
loopDelay = 1000,
|
|
45
|
+
respectReducedMotion = true,
|
|
46
|
+
onComplete,
|
|
47
|
+
} = options;
|
|
48
|
+
|
|
49
|
+
if (typeof document === 'undefined') {
|
|
50
|
+
return {
|
|
51
|
+
stop: () => {},
|
|
52
|
+
done: Promise.resolve(),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const el = element;
|
|
57
|
+
let stopped = false;
|
|
58
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
59
|
+
let cursorEl: HTMLSpanElement | null = null;
|
|
60
|
+
let cursorTimer: ReturnType<typeof setInterval> | null = null;
|
|
61
|
+
let resolvePromise: (() => void) | null = null;
|
|
62
|
+
|
|
63
|
+
// Add cursor element if enabled
|
|
64
|
+
const setupCursor = () => {
|
|
65
|
+
if (!cursor) return;
|
|
66
|
+
cursorEl = document.createElement('span');
|
|
67
|
+
cursorEl.setAttribute('aria-hidden', 'true');
|
|
68
|
+
cursorEl.textContent = cursorChar;
|
|
69
|
+
el.appendChild(cursorEl);
|
|
70
|
+
|
|
71
|
+
// Blink the cursor
|
|
72
|
+
let visible = true;
|
|
73
|
+
cursorTimer = setInterval(() => {
|
|
74
|
+
if (cursorEl) {
|
|
75
|
+
visible = !visible;
|
|
76
|
+
cursorEl.style.opacity = visible ? '1' : '0';
|
|
77
|
+
}
|
|
78
|
+
}, 530);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const removeCursor = () => {
|
|
82
|
+
if (cursorTimer !== null) {
|
|
83
|
+
clearInterval(cursorTimer);
|
|
84
|
+
cursorTimer = null;
|
|
85
|
+
}
|
|
86
|
+
if (cursorEl && cursorEl.parentNode) {
|
|
87
|
+
cursorEl.parentNode.removeChild(cursorEl);
|
|
88
|
+
cursorEl = null;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const stop = () => {
|
|
93
|
+
if (stopped) return;
|
|
94
|
+
stopped = true;
|
|
95
|
+
if (timer !== null) {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
timer = null;
|
|
98
|
+
}
|
|
99
|
+
removeCursor();
|
|
100
|
+
// Resolve the done promise so callers awaiting it are unblocked
|
|
101
|
+
resolvePromise?.();
|
|
102
|
+
resolvePromise = null;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// If reduced motion, show text instantly
|
|
106
|
+
if (respectReducedMotion && prefersReducedMotion()) {
|
|
107
|
+
el.textContent = text;
|
|
108
|
+
onComplete?.();
|
|
109
|
+
return {
|
|
110
|
+
stop: () => {},
|
|
111
|
+
done: Promise.resolve(),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const done = new Promise<void>((resolve) => {
|
|
116
|
+
resolvePromise = resolve;
|
|
117
|
+
|
|
118
|
+
const typeLoop = () => {
|
|
119
|
+
let charIndex = 0;
|
|
120
|
+
el.textContent = '';
|
|
121
|
+
setupCursor();
|
|
122
|
+
const textNode = document.createTextNode('');
|
|
123
|
+
|
|
124
|
+
if (cursorEl) {
|
|
125
|
+
el.insertBefore(textNode, cursorEl);
|
|
126
|
+
} else {
|
|
127
|
+
el.appendChild(textNode);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const typeNextChar = () => {
|
|
131
|
+
if (stopped) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (charIndex < text.length) {
|
|
135
|
+
textNode.data = text.slice(0, charIndex + 1);
|
|
136
|
+
charIndex++;
|
|
137
|
+
timer = setTimeout(typeNextChar, speed);
|
|
138
|
+
} else {
|
|
139
|
+
// Typing complete for this iteration
|
|
140
|
+
onComplete?.();
|
|
141
|
+
|
|
142
|
+
if (loop && !stopped) {
|
|
143
|
+
timer = setTimeout(() => {
|
|
144
|
+
if (!stopped) {
|
|
145
|
+
removeCursor();
|
|
146
|
+
typeLoop();
|
|
147
|
+
}
|
|
148
|
+
}, loopDelay);
|
|
149
|
+
} else {
|
|
150
|
+
removeCursor();
|
|
151
|
+
resolve();
|
|
152
|
+
resolvePromise = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
timer = setTimeout(typeNextChar, delay);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
typeLoop();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return { stop, done };
|
|
164
|
+
};
|
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Accessibility live-region announcer helpers.
|
|
3
|
-
*
|
|
4
|
-
* @module bquery/platform
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { effect, signal, type Signal } from '../reactive/signal';
|
|
8
|
-
import { getBqueryConfig } from './config';
|
|
9
|
-
|
|
10
|
-
/** Options for creating an announcer. */
|
|
11
|
-
export interface UseAnnouncerOptions {
|
|
12
|
-
/** Live region politeness. */
|
|
13
|
-
politeness?: 'polite' | 'assertive';
|
|
14
|
-
/** Whether the live region should be atomic. */
|
|
15
|
-
atomic?: boolean;
|
|
16
|
-
/** Delay before applying the message. */
|
|
17
|
-
delay?: number;
|
|
18
|
-
/** Delay after which the message is cleared automatically. */
|
|
19
|
-
clearDelay?: number;
|
|
20
|
-
/** Optional element id for the live region. */
|
|
21
|
-
id?: string;
|
|
22
|
-
/** Optional CSS class name. */
|
|
23
|
-
className?: string;
|
|
24
|
-
/** Optional container used to append the live region. */
|
|
25
|
-
container?: HTMLElement;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Runtime options for a single announcement. */
|
|
29
|
-
export interface AnnounceOptions {
|
|
30
|
-
/** Override politeness for this specific announcement. */
|
|
31
|
-
politeness?: 'polite' | 'assertive';
|
|
32
|
-
/** Override the message delay for this specific announcement. */
|
|
33
|
-
delay?: number;
|
|
34
|
-
/** Override the auto-clear delay for this specific announcement. */
|
|
35
|
-
clearDelay?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Returned announcer API. */
|
|
39
|
-
export interface AnnouncerHandle {
|
|
40
|
-
/** The live region element or null outside the DOM. */
|
|
41
|
-
element: HTMLElement | null;
|
|
42
|
-
/** Reactive message signal. */
|
|
43
|
-
message: Signal<string>;
|
|
44
|
-
/** Announce a message to assistive technologies. */
|
|
45
|
-
announce: (value: string, options?: AnnounceOptions) => void;
|
|
46
|
-
/** Clear the current announcement. */
|
|
47
|
-
clear: () => void;
|
|
48
|
-
/** Remove the live region if it was created by this announcer. */
|
|
49
|
-
destroy: () => void;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const visuallyHiddenStyle = [
|
|
53
|
-
'position:absolute',
|
|
54
|
-
'width:1px',
|
|
55
|
-
'height:1px',
|
|
56
|
-
'padding:0',
|
|
57
|
-
'margin:-1px',
|
|
58
|
-
'overflow:hidden',
|
|
59
|
-
'clip:rect(0, 0, 0, 0)',
|
|
60
|
-
'white-space:nowrap',
|
|
61
|
-
'border:0',
|
|
62
|
-
].join(';');
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Create or reuse an accessible live region.
|
|
66
|
-
*
|
|
67
|
-
* @param options - Live region configuration
|
|
68
|
-
* @returns An announcer handle with announce(), clear(), and destroy()
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```ts
|
|
72
|
-
* const announcer = useAnnouncer();
|
|
73
|
-
* announcer.announce('Saved successfully');
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
export const useAnnouncer = (options: UseAnnouncerOptions = {}): AnnouncerHandle => {
|
|
77
|
-
const defaults = getBqueryConfig().announcer;
|
|
78
|
-
const resolvedOptions: Required<
|
|
79
|
-
Pick<UseAnnouncerOptions, 'politeness' | 'atomic' | 'delay' | 'clearDelay'>
|
|
80
|
-
> &
|
|
81
|
-
UseAnnouncerOptions = {
|
|
82
|
-
politeness: defaults?.politeness ?? 'polite',
|
|
83
|
-
atomic: defaults?.atomic ?? true,
|
|
84
|
-
delay: defaults?.delay ?? 16,
|
|
85
|
-
clearDelay: defaults?.clearDelay ?? 1000,
|
|
86
|
-
...options,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const message = signal('');
|
|
90
|
-
|
|
91
|
-
if (typeof document === 'undefined') {
|
|
92
|
-
return {
|
|
93
|
-
element: null,
|
|
94
|
-
message,
|
|
95
|
-
announce(value: string) {
|
|
96
|
-
message.value = value;
|
|
97
|
-
},
|
|
98
|
-
clear() {
|
|
99
|
-
message.value = '';
|
|
100
|
-
},
|
|
101
|
-
destroy() {
|
|
102
|
-
message.value = '';
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const existing = resolvedOptions.id ? document.getElementById(resolvedOptions.id) : null;
|
|
108
|
-
const element = (existing ?? document.createElement('div')) as HTMLElement;
|
|
109
|
-
const created = !existing;
|
|
110
|
-
|
|
111
|
-
if (resolvedOptions.id) {
|
|
112
|
-
element.id = resolvedOptions.id;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (resolvedOptions.className) {
|
|
116
|
-
element.className = resolvedOptions.className;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
element.setAttribute('aria-live', resolvedOptions.politeness);
|
|
120
|
-
element.setAttribute('aria-atomic', String(resolvedOptions.atomic));
|
|
121
|
-
element.setAttribute('role', resolvedOptions.politeness === 'assertive' ? 'alert' : 'status');
|
|
122
|
-
element.setAttribute('data-bquery-announcer', 'true');
|
|
123
|
-
if (!element.getAttribute('style')) {
|
|
124
|
-
element.setAttribute('style', visuallyHiddenStyle);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (created) {
|
|
128
|
-
const parent = resolvedOptions.container ?? document.body ?? document.documentElement;
|
|
129
|
-
if (!parent) {
|
|
130
|
-
return {
|
|
131
|
-
element: null,
|
|
132
|
-
message,
|
|
133
|
-
announce(value: string) {
|
|
134
|
-
message.value = value;
|
|
135
|
-
},
|
|
136
|
-
clear() {
|
|
137
|
-
message.value = '';
|
|
138
|
-
},
|
|
139
|
-
destroy() {
|
|
140
|
-
message.value = '';
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
parent.appendChild(element);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const disposeMessageEffect = effect(() => {
|
|
148
|
-
element.textContent = message.value;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
let messageTimer: ReturnType<typeof setTimeout> | undefined;
|
|
152
|
-
let clearTimer: ReturnType<typeof setTimeout> | undefined;
|
|
153
|
-
let destroyed = false;
|
|
154
|
-
|
|
155
|
-
const clearTimers = (): void => {
|
|
156
|
-
if (messageTimer) {
|
|
157
|
-
clearTimeout(messageTimer);
|
|
158
|
-
messageTimer = undefined;
|
|
159
|
-
}
|
|
160
|
-
if (clearTimer) {
|
|
161
|
-
clearTimeout(clearTimer);
|
|
162
|
-
clearTimer = undefined;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const clear = (): void => {
|
|
167
|
-
if (destroyed) return;
|
|
168
|
-
clearTimers();
|
|
169
|
-
message.value = '';
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const announce = (value: string, announceOptions: AnnounceOptions = {}): void => {
|
|
173
|
-
if (destroyed) return;
|
|
174
|
-
const politeness = announceOptions.politeness ?? resolvedOptions.politeness;
|
|
175
|
-
const delay = announceOptions.delay ?? resolvedOptions.delay;
|
|
176
|
-
const clearDelay = announceOptions.clearDelay ?? resolvedOptions.clearDelay;
|
|
177
|
-
|
|
178
|
-
clearTimers();
|
|
179
|
-
|
|
180
|
-
element.setAttribute('aria-live', politeness);
|
|
181
|
-
element.setAttribute('role', politeness === 'assertive' ? 'alert' : 'status');
|
|
182
|
-
message.value = '';
|
|
183
|
-
|
|
184
|
-
messageTimer = setTimeout(() => {
|
|
185
|
-
if (destroyed) return;
|
|
186
|
-
message.value = value;
|
|
187
|
-
if (clearDelay > 0) {
|
|
188
|
-
clearTimer = setTimeout(() => {
|
|
189
|
-
if (destroyed) return;
|
|
190
|
-
message.value = '';
|
|
191
|
-
}, clearDelay);
|
|
192
|
-
}
|
|
193
|
-
}, delay);
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const destroy = (): void => {
|
|
197
|
-
if (destroyed) return;
|
|
198
|
-
destroyed = true;
|
|
199
|
-
clearTimers();
|
|
200
|
-
message.value = '';
|
|
201
|
-
disposeMessageEffect();
|
|
202
|
-
if (created) {
|
|
203
|
-
element.remove();
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
return { element, message, announce, clear, destroy };
|
|
208
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility live-region announcer helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/platform
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { effect, signal, type Signal } from '../reactive/signal';
|
|
8
|
+
import { getBqueryConfig } from './config';
|
|
9
|
+
|
|
10
|
+
/** Options for creating an announcer. */
|
|
11
|
+
export interface UseAnnouncerOptions {
|
|
12
|
+
/** Live region politeness. */
|
|
13
|
+
politeness?: 'polite' | 'assertive';
|
|
14
|
+
/** Whether the live region should be atomic. */
|
|
15
|
+
atomic?: boolean;
|
|
16
|
+
/** Delay before applying the message. */
|
|
17
|
+
delay?: number;
|
|
18
|
+
/** Delay after which the message is cleared automatically. */
|
|
19
|
+
clearDelay?: number;
|
|
20
|
+
/** Optional element id for the live region. */
|
|
21
|
+
id?: string;
|
|
22
|
+
/** Optional CSS class name. */
|
|
23
|
+
className?: string;
|
|
24
|
+
/** Optional container used to append the live region. */
|
|
25
|
+
container?: HTMLElement;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Runtime options for a single announcement. */
|
|
29
|
+
export interface AnnounceOptions {
|
|
30
|
+
/** Override politeness for this specific announcement. */
|
|
31
|
+
politeness?: 'polite' | 'assertive';
|
|
32
|
+
/** Override the message delay for this specific announcement. */
|
|
33
|
+
delay?: number;
|
|
34
|
+
/** Override the auto-clear delay for this specific announcement. */
|
|
35
|
+
clearDelay?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Returned announcer API. */
|
|
39
|
+
export interface AnnouncerHandle {
|
|
40
|
+
/** The live region element or null outside the DOM. */
|
|
41
|
+
element: HTMLElement | null;
|
|
42
|
+
/** Reactive message signal. */
|
|
43
|
+
message: Signal<string>;
|
|
44
|
+
/** Announce a message to assistive technologies. */
|
|
45
|
+
announce: (value: string, options?: AnnounceOptions) => void;
|
|
46
|
+
/** Clear the current announcement. */
|
|
47
|
+
clear: () => void;
|
|
48
|
+
/** Remove the live region if it was created by this announcer. */
|
|
49
|
+
destroy: () => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const visuallyHiddenStyle = [
|
|
53
|
+
'position:absolute',
|
|
54
|
+
'width:1px',
|
|
55
|
+
'height:1px',
|
|
56
|
+
'padding:0',
|
|
57
|
+
'margin:-1px',
|
|
58
|
+
'overflow:hidden',
|
|
59
|
+
'clip:rect(0, 0, 0, 0)',
|
|
60
|
+
'white-space:nowrap',
|
|
61
|
+
'border:0',
|
|
62
|
+
].join(';');
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create or reuse an accessible live region.
|
|
66
|
+
*
|
|
67
|
+
* @param options - Live region configuration
|
|
68
|
+
* @returns An announcer handle with announce(), clear(), and destroy()
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const announcer = useAnnouncer();
|
|
73
|
+
* announcer.announce('Saved successfully');
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export const useAnnouncer = (options: UseAnnouncerOptions = {}): AnnouncerHandle => {
|
|
77
|
+
const defaults = getBqueryConfig().announcer;
|
|
78
|
+
const resolvedOptions: Required<
|
|
79
|
+
Pick<UseAnnouncerOptions, 'politeness' | 'atomic' | 'delay' | 'clearDelay'>
|
|
80
|
+
> &
|
|
81
|
+
UseAnnouncerOptions = {
|
|
82
|
+
politeness: defaults?.politeness ?? 'polite',
|
|
83
|
+
atomic: defaults?.atomic ?? true,
|
|
84
|
+
delay: defaults?.delay ?? 16,
|
|
85
|
+
clearDelay: defaults?.clearDelay ?? 1000,
|
|
86
|
+
...options,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const message = signal('');
|
|
90
|
+
|
|
91
|
+
if (typeof document === 'undefined') {
|
|
92
|
+
return {
|
|
93
|
+
element: null,
|
|
94
|
+
message,
|
|
95
|
+
announce(value: string) {
|
|
96
|
+
message.value = value;
|
|
97
|
+
},
|
|
98
|
+
clear() {
|
|
99
|
+
message.value = '';
|
|
100
|
+
},
|
|
101
|
+
destroy() {
|
|
102
|
+
message.value = '';
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const existing = resolvedOptions.id ? document.getElementById(resolvedOptions.id) : null;
|
|
108
|
+
const element = (existing ?? document.createElement('div')) as HTMLElement;
|
|
109
|
+
const created = !existing;
|
|
110
|
+
|
|
111
|
+
if (resolvedOptions.id) {
|
|
112
|
+
element.id = resolvedOptions.id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (resolvedOptions.className) {
|
|
116
|
+
element.className = resolvedOptions.className;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
element.setAttribute('aria-live', resolvedOptions.politeness);
|
|
120
|
+
element.setAttribute('aria-atomic', String(resolvedOptions.atomic));
|
|
121
|
+
element.setAttribute('role', resolvedOptions.politeness === 'assertive' ? 'alert' : 'status');
|
|
122
|
+
element.setAttribute('data-bquery-announcer', 'true');
|
|
123
|
+
if (!element.getAttribute('style')) {
|
|
124
|
+
element.setAttribute('style', visuallyHiddenStyle);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (created) {
|
|
128
|
+
const parent = resolvedOptions.container ?? document.body ?? document.documentElement;
|
|
129
|
+
if (!parent) {
|
|
130
|
+
return {
|
|
131
|
+
element: null,
|
|
132
|
+
message,
|
|
133
|
+
announce(value: string) {
|
|
134
|
+
message.value = value;
|
|
135
|
+
},
|
|
136
|
+
clear() {
|
|
137
|
+
message.value = '';
|
|
138
|
+
},
|
|
139
|
+
destroy() {
|
|
140
|
+
message.value = '';
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
parent.appendChild(element);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const disposeMessageEffect = effect(() => {
|
|
148
|
+
element.textContent = message.value;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let messageTimer: ReturnType<typeof setTimeout> | undefined;
|
|
152
|
+
let clearTimer: ReturnType<typeof setTimeout> | undefined;
|
|
153
|
+
let destroyed = false;
|
|
154
|
+
|
|
155
|
+
const clearTimers = (): void => {
|
|
156
|
+
if (messageTimer) {
|
|
157
|
+
clearTimeout(messageTimer);
|
|
158
|
+
messageTimer = undefined;
|
|
159
|
+
}
|
|
160
|
+
if (clearTimer) {
|
|
161
|
+
clearTimeout(clearTimer);
|
|
162
|
+
clearTimer = undefined;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const clear = (): void => {
|
|
167
|
+
if (destroyed) return;
|
|
168
|
+
clearTimers();
|
|
169
|
+
message.value = '';
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const announce = (value: string, announceOptions: AnnounceOptions = {}): void => {
|
|
173
|
+
if (destroyed) return;
|
|
174
|
+
const politeness = announceOptions.politeness ?? resolvedOptions.politeness;
|
|
175
|
+
const delay = announceOptions.delay ?? resolvedOptions.delay;
|
|
176
|
+
const clearDelay = announceOptions.clearDelay ?? resolvedOptions.clearDelay;
|
|
177
|
+
|
|
178
|
+
clearTimers();
|
|
179
|
+
|
|
180
|
+
element.setAttribute('aria-live', politeness);
|
|
181
|
+
element.setAttribute('role', politeness === 'assertive' ? 'alert' : 'status');
|
|
182
|
+
message.value = '';
|
|
183
|
+
|
|
184
|
+
messageTimer = setTimeout(() => {
|
|
185
|
+
if (destroyed) return;
|
|
186
|
+
message.value = value;
|
|
187
|
+
if (clearDelay > 0) {
|
|
188
|
+
clearTimer = setTimeout(() => {
|
|
189
|
+
if (destroyed) return;
|
|
190
|
+
message.value = '';
|
|
191
|
+
}, clearDelay);
|
|
192
|
+
}
|
|
193
|
+
}, delay);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const destroy = (): void => {
|
|
197
|
+
if (destroyed) return;
|
|
198
|
+
destroyed = true;
|
|
199
|
+
clearTimers();
|
|
200
|
+
message.value = '';
|
|
201
|
+
disposeMessageEffect();
|
|
202
|
+
if (created) {
|
|
203
|
+
element.remove();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return { element, message, announce, clear, destroy };
|
|
208
|
+
};
|