bits-ui 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bits/avatar/avatar.svelte.d.ts +2 -1
- package/dist/bits/avatar/avatar.svelte.js +5 -3
- package/dist/bits/calendar/calendar.svelte.d.ts +2 -0
- package/dist/bits/calendar/calendar.svelte.js +9 -4
- package/dist/bits/combobox/components/combobox-input.svelte +2 -2
- package/dist/bits/combobox/components/combobox.svelte +5 -0
- package/dist/bits/combobox/types.d.ts +18 -1
- package/dist/bits/date-field/date-field.svelte.d.ts +3 -1
- package/dist/bits/date-field/date-field.svelte.js +15 -6
- package/dist/bits/date-range-field/date-range-field.svelte.d.ts +2 -0
- package/dist/bits/date-range-field/date-range-field.svelte.js +4 -2
- package/dist/bits/link-preview/link-preview.svelte.d.ts +2 -0
- package/dist/bits/link-preview/link-preview.svelte.js +11 -6
- package/dist/bits/menu/menu.svelte.d.ts +2 -0
- package/dist/bits/menu/menu.svelte.js +15 -10
- package/dist/bits/navigation-menu/navigation-menu.svelte.d.ts +3 -1
- package/dist/bits/navigation-menu/navigation-menu.svelte.js +21 -11
- package/dist/bits/pin-input/pin-input.svelte.d.ts +4 -2
- package/dist/bits/pin-input/pin-input.svelte.js +17 -13
- package/dist/bits/pin-input/usePasswordManager.svelte.d.ts +3 -2
- package/dist/bits/pin-input/usePasswordManager.svelte.js +6 -5
- package/dist/bits/range-calendar/range-calendar.svelte.d.ts +2 -0
- package/dist/bits/range-calendar/range-calendar.svelte.js +9 -3
- package/dist/bits/scroll-area/scroll-area.svelte.d.ts +2 -0
- package/dist/bits/scroll-area/scroll-area.svelte.js +15 -12
- package/dist/bits/select/components/select.svelte +6 -0
- package/dist/bits/select/select.svelte.d.ts +5 -1
- package/dist/bits/select/select.svelte.js +34 -18
- package/dist/bits/time-field/time-field.svelte.d.ts +3 -1
- package/dist/bits/time-field/time-field.svelte.js +15 -6
- package/dist/bits/time-range-field/time-range-field.svelte.d.ts +2 -0
- package/dist/bits/time-range-field/time-range-field.svelte.js +4 -2
- package/dist/bits/tooltip/tooltip.svelte.d.ts +2 -0
- package/dist/bits/tooltip/tooltip.svelte.js +4 -2
- package/dist/bits/utilities/floating-layer/use-floating-layer.svelte.js +3 -2
- package/dist/bits/utilities/focus-scope/use-focus-scope.svelte.js +14 -9
- package/dist/bits/utilities/portal/types.d.ts +1 -1
- package/dist/bits/utilities/text-selection-layer/use-text-selection-layer.svelte.d.ts +2 -0
- package/dist/bits/utilities/text-selection-layer/use-text-selection-layer.svelte.js +7 -7
- package/dist/internal/box-auto-reset.svelte.d.ts +7 -1
- package/dist/internal/box-auto-reset.svelte.js +11 -6
- package/dist/internal/date-time/announcer.d.ts +1 -1
- package/dist/internal/date-time/announcer.js +20 -20
- package/dist/internal/date-time/calendar-helpers.svelte.js +7 -5
- package/dist/internal/date-time/field/helpers.d.ts +8 -2
- package/dist/internal/date-time/field/helpers.js +8 -7
- package/dist/internal/date-time/field/time-helpers.d.ts +8 -2
- package/dist/internal/date-time/field/time-helpers.js +9 -9
- package/dist/internal/dom.d.ts +0 -1
- package/dist/internal/dom.js +0 -3
- package/dist/internal/focus.d.ts +2 -2
- package/dist/internal/focus.js +14 -9
- package/dist/internal/math.d.ts +0 -4
- package/dist/internal/math.js +0 -28
- package/dist/internal/tabbable.d.ts +0 -2
- package/dist/internal/tabbable.js +10 -14
- package/dist/internal/use-data-typeahead.svelte.d.ts +1 -0
- package/dist/internal/use-data-typeahead.svelte.js +4 -1
- package/dist/internal/use-dom-typeahead.svelte.d.ts +3 -1
- package/dist/internal/use-dom-typeahead.svelte.js +5 -2
- package/dist/internal/use-grace-area.svelte.js +9 -5
- package/package.json +2 -2
- package/dist/internal/dom-context.svelte.d.ts +0 -9
- package/dist/internal/dom-context.svelte.js +0 -26
- package/dist/internal/use-size.svelte.d.ts +0 -7
- package/dist/internal/use-size.svelte.js +0 -54
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CalendarDateTime, Time, ZonedDateTime } from "@internationalized/date";
|
|
2
|
-
import { onDestroyEffect, attachRef, box } from "svelte-toolbelt";
|
|
2
|
+
import { onDestroyEffect, attachRef, box, DOMContext } from "svelte-toolbelt";
|
|
3
3
|
import { onMount, untrack } from "svelte";
|
|
4
4
|
import { Context, watch } from "runed";
|
|
5
5
|
import { getAriaDisabled, getAriaHidden, getAriaInvalid, getAriaReadonly, getDataDisabled, getDataInvalid, getDataReadonly, } from "../../internal/attrs.js";
|
|
@@ -96,6 +96,7 @@ export class TimeFieldRootState {
|
|
|
96
96
|
return getDefaultHourCycle(this.locale.current);
|
|
97
97
|
});
|
|
98
98
|
rangeRoot = undefined;
|
|
99
|
+
domContext = new DOMContext(() => null);
|
|
99
100
|
constructor(props, rangeRoot) {
|
|
100
101
|
this.rangeRoot = rangeRoot;
|
|
101
102
|
/**
|
|
@@ -124,7 +125,7 @@ export class TimeFieldRootState {
|
|
|
124
125
|
this.formatter = createTimeFormatter(this.locale.current);
|
|
125
126
|
this.initialSegments = this.#initializeTimeSegmentValues();
|
|
126
127
|
this.segmentValues = this.initialSegments;
|
|
127
|
-
this.announcer = getAnnouncer();
|
|
128
|
+
this.announcer = getAnnouncer(null);
|
|
128
129
|
this.getFieldNode = this.getFieldNode.bind(this);
|
|
129
130
|
this.updateSegment = this.updateSegment.bind(this);
|
|
130
131
|
this.handleSegmentClick = this.handleSegmentClick.bind(this);
|
|
@@ -135,10 +136,10 @@ export class TimeFieldRootState {
|
|
|
135
136
|
});
|
|
136
137
|
});
|
|
137
138
|
onMount(() => {
|
|
138
|
-
this.announcer = getAnnouncer();
|
|
139
|
+
this.announcer = getAnnouncer(this.domContext.getDocument());
|
|
139
140
|
});
|
|
140
141
|
onDestroyEffect(() => {
|
|
141
|
-
removeTimeDescriptionElement(this.descriptionId);
|
|
142
|
+
removeTimeDescriptionElement(this.descriptionId, this.domContext.getDocument());
|
|
142
143
|
});
|
|
143
144
|
$effect(() => {
|
|
144
145
|
if (this.formatter.getLocale() === this.locale.current)
|
|
@@ -148,7 +149,12 @@ export class TimeFieldRootState {
|
|
|
148
149
|
$effect(() => {
|
|
149
150
|
if (this.value.current) {
|
|
150
151
|
const descriptionId = untrack(() => this.descriptionId);
|
|
151
|
-
setTimeDescription(
|
|
152
|
+
setTimeDescription({
|
|
153
|
+
id: descriptionId,
|
|
154
|
+
formatter: this.formatter,
|
|
155
|
+
value: this.#toDateValue(this.value.current),
|
|
156
|
+
doc: this.domContext.getDocument(),
|
|
157
|
+
});
|
|
152
158
|
}
|
|
153
159
|
const placeholder = untrack(() => this.placeholder.current);
|
|
154
160
|
if (this.value.current && placeholder !== this.value.current) {
|
|
@@ -446,9 +452,12 @@ export class TimeFieldRootState {
|
|
|
446
452
|
export class TimeFieldInputState {
|
|
447
453
|
opts;
|
|
448
454
|
root;
|
|
455
|
+
domContext;
|
|
449
456
|
constructor(opts, root) {
|
|
450
457
|
this.opts = opts;
|
|
451
458
|
this.root = root;
|
|
459
|
+
this.domContext = new DOMContext(opts.ref);
|
|
460
|
+
this.root.setName(this.opts.name.current);
|
|
452
461
|
$effect(() => {
|
|
453
462
|
this.root.setName(this.opts.name.current);
|
|
454
463
|
});
|
|
@@ -456,7 +465,7 @@ export class TimeFieldInputState {
|
|
|
456
465
|
#ariaDescribedBy = $derived.by(() => {
|
|
457
466
|
if (!isBrowser)
|
|
458
467
|
return undefined;
|
|
459
|
-
const doesDescriptionExist =
|
|
468
|
+
const doesDescriptionExist = this.domContext.getElementById(this.root.descriptionId);
|
|
460
469
|
if (!doesDescriptionExist)
|
|
461
470
|
return undefined;
|
|
462
471
|
return this.root.descriptionId;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Time } from "@internationalized/date";
|
|
2
|
+
import { DOMContext } from "svelte-toolbelt";
|
|
2
3
|
import { Context } from "runed";
|
|
3
4
|
import type { TimeFieldRootState } from "../time-field/time-field.svelte.js";
|
|
4
5
|
import { TimeFieldInputState } from "../time-field/time-field.svelte.js";
|
|
@@ -45,6 +46,7 @@ export declare class TimeRangeFieldRootState<T extends TimeValue = Time> {
|
|
|
45
46
|
endValueTime: Time | undefined;
|
|
46
47
|
minValueTime: Time | undefined;
|
|
47
48
|
maxValueTime: Time | undefined;
|
|
49
|
+
domContext: DOMContext;
|
|
48
50
|
constructor(opts: TimeRangeFieldRootStateProps<T>);
|
|
49
51
|
validationStatus: false | {
|
|
50
52
|
readonly reason: "custom";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { box, onDestroyEffect, attachRef } from "svelte-toolbelt";
|
|
1
|
+
import { box, onDestroyEffect, attachRef, DOMContext } from "svelte-toolbelt";
|
|
2
2
|
import { Context, watch } from "runed";
|
|
3
3
|
import { TimeFieldInputState, useTimeFieldRoot } from "../time-field/time-field.svelte.js";
|
|
4
4
|
import { useId } from "../../internal/use-id.js";
|
|
@@ -41,11 +41,13 @@ export class TimeRangeFieldRootState {
|
|
|
41
41
|
return undefined;
|
|
42
42
|
return convertTimeValueToTime(this.opts.maxValue.current);
|
|
43
43
|
});
|
|
44
|
+
domContext;
|
|
44
45
|
constructor(opts) {
|
|
45
46
|
this.opts = opts;
|
|
46
47
|
this.formatter = createTimeFormatter(this.opts.locale.current);
|
|
48
|
+
this.domContext = new DOMContext(this.opts.ref);
|
|
47
49
|
onDestroyEffect(() => {
|
|
48
|
-
removeDescriptionElement(this.descriptionId);
|
|
50
|
+
removeDescriptionElement(this.descriptionId, this.domContext.getDocument());
|
|
49
51
|
});
|
|
50
52
|
$effect(() => {
|
|
51
53
|
if (this.formatter.getLocale() === this.opts.locale.current)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DOMContext } from "svelte-toolbelt";
|
|
1
2
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
2
3
|
import type { WithRefProps } from "../../internal/types.js";
|
|
3
4
|
import type { PointerEventHandler } from "svelte/elements";
|
|
@@ -53,6 +54,7 @@ declare class TooltipTriggerState {
|
|
|
53
54
|
#private;
|
|
54
55
|
readonly opts: TooltipTriggerStateProps;
|
|
55
56
|
readonly root: TooltipRootState;
|
|
57
|
+
domContext: DOMContext;
|
|
56
58
|
constructor(opts: TooltipTriggerStateProps, root: TooltipRootState);
|
|
57
59
|
handlePointerUp: () => void;
|
|
58
60
|
props: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { box, onMountEffect, attachRef } from "svelte-toolbelt";
|
|
1
|
+
import { box, onMountEffect, attachRef, DOMContext } from "svelte-toolbelt";
|
|
2
2
|
import { on } from "svelte/events";
|
|
3
3
|
import { Context, watch } from "runed";
|
|
4
4
|
import { useTimeoutFn } from "../../internal/use-timeout-fn.svelte.js";
|
|
@@ -135,9 +135,11 @@ class TooltipTriggerState {
|
|
|
135
135
|
#isPointerDown = box(false);
|
|
136
136
|
#hasPointerMoveOpened = $state(false);
|
|
137
137
|
#isDisabled = $derived.by(() => this.opts.disabled.current || this.root.disabled);
|
|
138
|
+
domContext;
|
|
138
139
|
constructor(opts, root) {
|
|
139
140
|
this.opts = opts;
|
|
140
141
|
this.root = root;
|
|
142
|
+
this.domContext = new DOMContext(opts.ref);
|
|
141
143
|
}
|
|
142
144
|
handlePointerUp = () => {
|
|
143
145
|
this.#isPointerDown.current = false;
|
|
@@ -151,7 +153,7 @@ class TooltipTriggerState {
|
|
|
151
153
|
if (this.#isDisabled)
|
|
152
154
|
return;
|
|
153
155
|
this.#isPointerDown.current = true;
|
|
154
|
-
|
|
156
|
+
this.domContext.getDocument().addEventListener("pointerup", () => {
|
|
155
157
|
this.handlePointerUp();
|
|
156
158
|
}, { once: true });
|
|
157
159
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { arrow, autoUpdate, flip, hide, limitShift, offset, shift, size, } from "@floating-ui/dom";
|
|
2
|
-
import { attachRef, box, cssToStyleObj, styleToString } from "svelte-toolbelt";
|
|
2
|
+
import { attachRef, box, cssToStyleObj, getWindow, styleToString, } from "svelte-toolbelt";
|
|
3
3
|
import { Context, ElementSize, watch } from "runed";
|
|
4
4
|
import { isNotNull } from "../../../internal/is.js";
|
|
5
5
|
import { useId } from "../../../internal/use-id.js";
|
|
@@ -195,7 +195,8 @@ class FloatingContentState {
|
|
|
195
195
|
watch(() => this.contentRef.current, (contentNode) => {
|
|
196
196
|
if (!contentNode)
|
|
197
197
|
return;
|
|
198
|
-
|
|
198
|
+
const win = getWindow(contentNode);
|
|
199
|
+
this.contentZIndex = win.getComputedStyle(contentNode).zIndex;
|
|
199
200
|
});
|
|
200
201
|
$effect(() => {
|
|
201
202
|
this.floating.floating.current = this.wrapperRef.current;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterSleep, afterTick, executeCallbacks } from "svelte-toolbelt";
|
|
1
|
+
import { afterSleep, afterTick, DOMContext, executeCallbacks } from "svelte-toolbelt";
|
|
2
2
|
import { Context, watch } from "runed";
|
|
3
3
|
import { on } from "svelte/events";
|
|
4
4
|
import { createFocusScopeAPI, createFocusScopeStack, removeLinks, } from "./focus-scope-stack.svelte.js";
|
|
@@ -21,6 +21,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
21
21
|
const focusScope = createFocusScopeAPI();
|
|
22
22
|
const ctx = FocusScopeContext.getOr({ ignoreCloseAutoFocus: false });
|
|
23
23
|
let lastFocusedElement = null;
|
|
24
|
+
const domContext = new DOMContext(ref);
|
|
24
25
|
function manageFocus(event) {
|
|
25
26
|
if (focusScope.paused || !ref.current || focusScope.isHandlingFocus)
|
|
26
27
|
return;
|
|
@@ -89,14 +90,16 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
89
90
|
* If the element was removed and focus is now outside the container,
|
|
90
91
|
* (e.g., browser moved it to body), refocus the container.
|
|
91
92
|
*/
|
|
92
|
-
if (elementWasRemoved &&
|
|
93
|
+
if (elementWasRemoved &&
|
|
94
|
+
ref.current &&
|
|
95
|
+
!ref.current.contains(domContext.getActiveElement())) {
|
|
93
96
|
focus(ref.current);
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
watch([() => ref.current, () => enabled.current], ([container, enabled]) => {
|
|
97
100
|
if (!container || !enabled)
|
|
98
101
|
return;
|
|
99
|
-
const removeEvents = executeCallbacks(on(
|
|
102
|
+
const removeEvents = executeCallbacks(on(domContext.getDocument(), "focusin", manageFocus), on(domContext.getDocument(), "focusout", manageFocus));
|
|
100
103
|
const mutationObserver = new MutationObserver(handleMutations);
|
|
101
104
|
mutationObserver.observe(container, {
|
|
102
105
|
childList: true,
|
|
@@ -111,7 +114,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
111
114
|
watch([() => forceMount.current, () => ref.current], ([forceMount, container]) => {
|
|
112
115
|
if (forceMount)
|
|
113
116
|
return;
|
|
114
|
-
const prevFocusedElement =
|
|
117
|
+
const prevFocusedElement = domContext.getActiveElement();
|
|
115
118
|
handleOpen(container, prevFocusedElement);
|
|
116
119
|
return () => {
|
|
117
120
|
if (!container)
|
|
@@ -122,7 +125,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
122
125
|
watch([() => forceMount.current, () => ref.current, () => enabled.current], ([forceMount, container]) => {
|
|
123
126
|
if (!forceMount)
|
|
124
127
|
return;
|
|
125
|
-
const prevFocusedElement =
|
|
128
|
+
const prevFocusedElement = domContext.getActiveElement();
|
|
126
129
|
handleOpen(container, prevFocusedElement);
|
|
127
130
|
return () => {
|
|
128
131
|
if (!container)
|
|
@@ -132,7 +135,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
132
135
|
});
|
|
133
136
|
function handleOpen(container, prevFocusedElement) {
|
|
134
137
|
if (!container)
|
|
135
|
-
container =
|
|
138
|
+
container = domContext.getElementById(id.current);
|
|
136
139
|
if (!container || !enabled.current)
|
|
137
140
|
return;
|
|
138
141
|
focusScopeStack.add(focusScope);
|
|
@@ -146,7 +149,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
146
149
|
return;
|
|
147
150
|
const result = focusFirst(removeLinks(getTabbableCandidates(container)), {
|
|
148
151
|
select: true,
|
|
149
|
-
});
|
|
152
|
+
}, () => domContext.getActiveElement());
|
|
150
153
|
if (!result)
|
|
151
154
|
focus(container);
|
|
152
155
|
});
|
|
@@ -159,7 +162,9 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
159
162
|
const shouldIgnore = ctx.ignoreCloseAutoFocus;
|
|
160
163
|
afterSleep(0, () => {
|
|
161
164
|
if (!destroyEvent.defaultPrevented && prevFocusedElement && !shouldIgnore) {
|
|
162
|
-
focus(isTabbable(prevFocusedElement)
|
|
165
|
+
focus(isTabbable(prevFocusedElement)
|
|
166
|
+
? prevFocusedElement
|
|
167
|
+
: domContext.getDocument().body, {
|
|
163
168
|
select: true,
|
|
164
169
|
});
|
|
165
170
|
}
|
|
@@ -174,7 +179,7 @@ export function useFocusScope({ id, loop, enabled, onOpenAutoFocus, onCloseAutoF
|
|
|
174
179
|
if (focusScope.paused)
|
|
175
180
|
return;
|
|
176
181
|
const isTabKey = e.key === kbd.TAB && !e.ctrlKey && !e.altKey && !e.metaKey;
|
|
177
|
-
const focusedElement =
|
|
182
|
+
const focusedElement = domContext.getActiveElement();
|
|
178
183
|
if (!(isTabKey && focusedElement))
|
|
179
184
|
return;
|
|
180
185
|
const container = ref.current;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DOMContext } from "svelte-toolbelt";
|
|
1
2
|
import type { TextSelectionLayerImplProps } from "./types.js";
|
|
2
3
|
import type { ReadableBoxedValues } from "../../../internal/box.svelte.js";
|
|
3
4
|
type TextSelectionLayerStateProps = ReadableBoxedValues<Required<Omit<TextSelectionLayerImplProps, "children" | "preventOverflowTextSelection" | "ref">> & {
|
|
@@ -6,6 +7,7 @@ type TextSelectionLayerStateProps = ReadableBoxedValues<Required<Omit<TextSelect
|
|
|
6
7
|
export declare class TextSelectionLayerState {
|
|
7
8
|
#private;
|
|
8
9
|
readonly opts: TextSelectionLayerStateProps;
|
|
10
|
+
readonly domContext: DOMContext;
|
|
9
11
|
constructor(opts: TextSelectionLayerStateProps);
|
|
10
12
|
}
|
|
11
13
|
export declare function useTextSelectionLayer(props: TextSelectionLayerStateProps): TextSelectionLayerState;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { composeHandlers, executeCallbacks } from "svelte-toolbelt";
|
|
1
|
+
import { DOMContext, composeHandlers, contains, executeCallbacks, } from "svelte-toolbelt";
|
|
2
2
|
import { watch } from "runed";
|
|
3
3
|
import { on } from "svelte/events";
|
|
4
4
|
import { noop } from "../../../internal/noop.js";
|
|
5
5
|
import { isHTMLElement } from "../../../internal/is.js";
|
|
6
|
-
import { isOrContainsTarget } from "../../../internal/elements.js";
|
|
7
6
|
globalThis.bitsTextSelectionLayers ??= new Map();
|
|
8
7
|
export class TextSelectionLayerState {
|
|
9
8
|
opts;
|
|
9
|
+
domContext;
|
|
10
10
|
#unsubSelectionLock = noop;
|
|
11
11
|
constructor(opts) {
|
|
12
12
|
this.opts = opts;
|
|
13
|
+
this.domContext = new DOMContext(opts.ref);
|
|
13
14
|
let unsubEvents = noop;
|
|
14
15
|
watch(() => this.opts.enabled.current, (isEnabled) => {
|
|
15
16
|
if (isEnabled) {
|
|
@@ -25,7 +26,7 @@ export class TextSelectionLayerState {
|
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
#addEventListeners() {
|
|
28
|
-
return executeCallbacks(on(
|
|
29
|
+
return executeCallbacks(on(this.domContext.getDocument(), "pointerdown", this.#pointerdown), on(this.domContext.getDocument(), "pointerup", composeHandlers(this.#resetSelectionLock, this.opts.onPointerUp.current)));
|
|
29
30
|
}
|
|
30
31
|
#pointerdown = (e) => {
|
|
31
32
|
const node = this.opts.ref.current;
|
|
@@ -37,12 +38,12 @@ export class TextSelectionLayerState {
|
|
|
37
38
|
* pointerdown occurred inside the node. You are still allowed to select text
|
|
38
39
|
* outside the node provided pointerdown occurs outside the node.
|
|
39
40
|
*/
|
|
40
|
-
if (!isHighestLayer(this) || !
|
|
41
|
+
if (!isHighestLayer(this) || !contains(node, target))
|
|
41
42
|
return;
|
|
42
43
|
this.opts.onPointerDown.current(e);
|
|
43
44
|
if (e.defaultPrevented)
|
|
44
45
|
return;
|
|
45
|
-
this.#unsubSelectionLock = preventTextSelectionOverflow(node);
|
|
46
|
+
this.#unsubSelectionLock = preventTextSelectionOverflow(node, this.domContext.getDocument().body);
|
|
46
47
|
};
|
|
47
48
|
#resetSelectionLock = () => {
|
|
48
49
|
this.#unsubSelectionLock();
|
|
@@ -53,8 +54,7 @@ export function useTextSelectionLayer(props) {
|
|
|
53
54
|
return new TextSelectionLayerState(props);
|
|
54
55
|
}
|
|
55
56
|
const getUserSelect = (node) => node.style.userSelect || node.style.webkitUserSelect;
|
|
56
|
-
function preventTextSelectionOverflow(node) {
|
|
57
|
-
const body = document.body;
|
|
57
|
+
function preventTextSelectionOverflow(node, body) {
|
|
58
58
|
const originalBodyUserSelect = getUserSelect(body);
|
|
59
59
|
const originalNodeUserSelect = getUserSelect(node);
|
|
60
60
|
setUserSelect(body, "none");
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { type WritableBox } from "svelte-toolbelt";
|
|
2
|
+
type BoxAutoResetOptions<T> = {
|
|
3
|
+
afterMs?: number;
|
|
4
|
+
onChange?: (value: T) => void;
|
|
5
|
+
getWindow: () => Window & typeof globalThis;
|
|
6
|
+
};
|
|
2
7
|
/**
|
|
3
8
|
* Creates a box which will be reset to the default value after some time.
|
|
4
9
|
*
|
|
5
10
|
* @param defaultValue The value which will be set.
|
|
6
11
|
* @param afterMs A zero-or-greater delay in milliseconds.
|
|
7
12
|
*/
|
|
8
|
-
export declare function boxAutoReset<T>(defaultValue: T,
|
|
13
|
+
export declare function boxAutoReset<T>(defaultValue: T, options: BoxAutoResetOptions<T>): WritableBox<T>;
|
|
14
|
+
export {};
|
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
import { box } from "svelte-toolbelt";
|
|
2
2
|
import { noop } from "./noop.js";
|
|
3
|
+
const defaultOptions = {
|
|
4
|
+
afterMs: 10000,
|
|
5
|
+
onChange: noop,
|
|
6
|
+
};
|
|
3
7
|
/**
|
|
4
8
|
* Creates a box which will be reset to the default value after some time.
|
|
5
9
|
*
|
|
6
10
|
* @param defaultValue The value which will be set.
|
|
7
11
|
* @param afterMs A zero-or-greater delay in milliseconds.
|
|
8
12
|
*/
|
|
9
|
-
export function boxAutoReset(defaultValue,
|
|
13
|
+
export function boxAutoReset(defaultValue, options) {
|
|
14
|
+
const { afterMs, onChange, getWindow } = { ...defaultOptions, ...options };
|
|
10
15
|
let timeout = null;
|
|
11
16
|
let value = $state(defaultValue);
|
|
12
17
|
function resetAfter() {
|
|
13
|
-
return
|
|
18
|
+
return getWindow().setTimeout(() => {
|
|
14
19
|
value = defaultValue;
|
|
15
|
-
onChange(defaultValue);
|
|
20
|
+
onChange?.(defaultValue);
|
|
16
21
|
}, afterMs);
|
|
17
22
|
}
|
|
18
23
|
$effect(() => {
|
|
19
24
|
return () => {
|
|
20
25
|
if (timeout)
|
|
21
|
-
clearTimeout(timeout);
|
|
26
|
+
getWindow().clearTimeout(timeout);
|
|
22
27
|
};
|
|
23
28
|
});
|
|
24
29
|
return box.with(() => value, (v) => {
|
|
25
30
|
value = v;
|
|
26
|
-
onChange(v);
|
|
31
|
+
onChange?.(v);
|
|
27
32
|
if (timeout)
|
|
28
|
-
clearTimeout(timeout);
|
|
33
|
+
getWindow().clearTimeout(timeout);
|
|
29
34
|
timeout = resetAfter();
|
|
30
35
|
});
|
|
31
36
|
}
|
|
@@ -2,6 +2,6 @@ export type Announcer = ReturnType<typeof getAnnouncer>;
|
|
|
2
2
|
/**
|
|
3
3
|
* Creates an announcer object that can be used to make `aria-live` announcements to screen readers.
|
|
4
4
|
*/
|
|
5
|
-
export declare function getAnnouncer(): {
|
|
5
|
+
export declare function getAnnouncer(doc: Document | null): {
|
|
6
6
|
announce: (value: string | null | number, kind?: "assertive" | "polite", timeout?: number) => NodeJS.Timeout | undefined;
|
|
7
7
|
};
|
|
@@ -5,40 +5,40 @@ import { isBrowser, isHTMLElement } from "../is.js";
|
|
|
5
5
|
* Within the date components, we use this to announce when the values of the individual segments
|
|
6
6
|
* change, as without it we get inconsistent behavior across screen readers.
|
|
7
7
|
*/
|
|
8
|
-
function initAnnouncer() {
|
|
9
|
-
if (!isBrowser)
|
|
8
|
+
function initAnnouncer(doc) {
|
|
9
|
+
if (!isBrowser || !doc)
|
|
10
10
|
return null;
|
|
11
|
-
let el =
|
|
12
|
-
if (!isHTMLElement(el)) {
|
|
13
|
-
const div = document.createElement("div");
|
|
14
|
-
div.style.cssText = srOnlyStylesString;
|
|
15
|
-
div.setAttribute("data-bits-announcer", "");
|
|
16
|
-
div.appendChild(createLog("assertive"));
|
|
17
|
-
div.appendChild(createLog("polite"));
|
|
18
|
-
el = div;
|
|
19
|
-
document.body.insertBefore(el, document.body.firstChild);
|
|
20
|
-
}
|
|
11
|
+
let el = doc.querySelector("[data-bits-announcer]");
|
|
21
12
|
/**
|
|
22
13
|
* Creates a log element for assertive or polite announcements.
|
|
23
14
|
*/
|
|
24
|
-
|
|
25
|
-
const log =
|
|
15
|
+
const createLog = (kind) => {
|
|
16
|
+
const log = doc.createElement("div");
|
|
26
17
|
log.role = "log";
|
|
27
18
|
log.ariaLive = kind;
|
|
28
19
|
log.setAttribute("aria-relevant", "additions");
|
|
29
20
|
return log;
|
|
21
|
+
};
|
|
22
|
+
if (!isHTMLElement(el)) {
|
|
23
|
+
const div = doc.createElement("div");
|
|
24
|
+
div.style.cssText = srOnlyStylesString;
|
|
25
|
+
div.setAttribute("data-bits-announcer", "");
|
|
26
|
+
div.appendChild(createLog("assertive"));
|
|
27
|
+
div.appendChild(createLog("polite"));
|
|
28
|
+
el = div;
|
|
29
|
+
doc.body.insertBefore(el, doc.body.firstChild);
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Retrieves the log element for assertive or polite announcements.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
const getLog = (kind) => {
|
|
35
35
|
if (!isHTMLElement(el))
|
|
36
36
|
return null;
|
|
37
37
|
const log = el.querySelector(`[aria-live="${kind}"]`);
|
|
38
38
|
if (!isHTMLElement(log))
|
|
39
39
|
return null;
|
|
40
40
|
return log;
|
|
41
|
-
}
|
|
41
|
+
};
|
|
42
42
|
return {
|
|
43
43
|
getLog,
|
|
44
44
|
};
|
|
@@ -46,16 +46,16 @@ function initAnnouncer() {
|
|
|
46
46
|
/**
|
|
47
47
|
* Creates an announcer object that can be used to make `aria-live` announcements to screen readers.
|
|
48
48
|
*/
|
|
49
|
-
export function getAnnouncer() {
|
|
50
|
-
const announcer = initAnnouncer();
|
|
49
|
+
export function getAnnouncer(doc) {
|
|
50
|
+
const announcer = initAnnouncer(doc);
|
|
51
51
|
/**
|
|
52
52
|
* Announces a message to screen readers using the specified kind of announcement.
|
|
53
53
|
*/
|
|
54
54
|
function announce(value, kind = "assertive", timeout = 7500) {
|
|
55
|
-
if (!announcer || !isBrowser)
|
|
55
|
+
if (!announcer || !isBrowser || !doc)
|
|
56
56
|
return;
|
|
57
57
|
const log = announcer.getLog(kind);
|
|
58
|
-
const content =
|
|
58
|
+
const content = doc.createElement("div");
|
|
59
59
|
if (typeof value === "number") {
|
|
60
60
|
value = value.toString();
|
|
61
61
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { endOfMonth, isSameDay, isSameMonth, startOfMonth, } from "@internationalized/date";
|
|
2
|
-
import { afterTick, styleToString } from "svelte-toolbelt";
|
|
2
|
+
import { afterTick, getDocument, styleToString, } from "svelte-toolbelt";
|
|
3
3
|
import { untrack } from "svelte";
|
|
4
4
|
import { getDaysInMonth, getLastFirstDayOfWeek, getNextLastDayOfWeek, hasTime, isAfter, isBefore, parseAnyDateValue, parseStringToDateValue, toDate, } from "./utils.js";
|
|
5
5
|
import { getDataDisabled, getDataInvalid, getDataReadonly } from "../attrs.js";
|
|
@@ -331,7 +331,8 @@ export function useMonthViewOptionsSync(props) {
|
|
|
331
331
|
* Returns a function that removes the heading element.
|
|
332
332
|
*/
|
|
333
333
|
export function createAccessibleHeading({ calendarNode, label, accessibleHeadingId, }) {
|
|
334
|
-
const
|
|
334
|
+
const doc = getDocument(calendarNode);
|
|
335
|
+
const div = doc.createElement("div");
|
|
335
336
|
div.style.cssText = styleToString({
|
|
336
337
|
border: "0px",
|
|
337
338
|
clip: "rect(0px, 0px, 0px, 0px)",
|
|
@@ -344,7 +345,7 @@ export function createAccessibleHeading({ calendarNode, label, accessibleHeading
|
|
|
344
345
|
whiteSpace: "nowrap",
|
|
345
346
|
width: "1px",
|
|
346
347
|
});
|
|
347
|
-
const h2 =
|
|
348
|
+
const h2 = doc.createElement("div");
|
|
348
349
|
h2.textContent = label;
|
|
349
350
|
h2.id = accessibleHeadingId;
|
|
350
351
|
h2.role = "heading";
|
|
@@ -352,7 +353,7 @@ export function createAccessibleHeading({ calendarNode, label, accessibleHeading
|
|
|
352
353
|
calendarNode.insertBefore(div, calendarNode.firstChild);
|
|
353
354
|
div.appendChild(h2);
|
|
354
355
|
return () => {
|
|
355
|
-
const h2 =
|
|
356
|
+
const h2 = doc.getElementById(accessibleHeadingId);
|
|
356
357
|
if (!h2)
|
|
357
358
|
return;
|
|
358
359
|
div.parentElement?.removeChild(div);
|
|
@@ -442,7 +443,8 @@ export function getCalendarElementProps({ fullCalendarLabel, id, isInvalid, disa
|
|
|
442
443
|
};
|
|
443
444
|
}
|
|
444
445
|
export function pickerOpenFocus(e) {
|
|
445
|
-
const
|
|
446
|
+
const doc = getDocument(e.target);
|
|
447
|
+
const nodeToFocus = doc.querySelector("[data-bits-day][data-focused]");
|
|
446
448
|
if (nodeToFocus) {
|
|
447
449
|
e.preventDefault();
|
|
448
450
|
nodeToFocus?.focus();
|
|
@@ -61,6 +61,12 @@ export declare function isAcceptableSegmentKey(key: string): boolean;
|
|
|
61
61
|
* @param fieldNode - The id of the date field associated with the segment
|
|
62
62
|
*/
|
|
63
63
|
export declare function isFirstSegment(id: string, fieldNode: HTMLElement | null): boolean;
|
|
64
|
+
type SetDescriptionProps = {
|
|
65
|
+
id: string;
|
|
66
|
+
formatter: Formatter;
|
|
67
|
+
value: DateValue;
|
|
68
|
+
doc: Document;
|
|
69
|
+
};
|
|
64
70
|
/**
|
|
65
71
|
* Creates or updates a description element for a date field
|
|
66
72
|
* which enables screen readers to read the date field's value.
|
|
@@ -69,12 +75,12 @@ export declare function isFirstSegment(id: string, fieldNode: HTMLElement | null
|
|
|
69
75
|
* so it can be associated via `aria-describedby` and read by
|
|
70
76
|
* screen readers as the user interacts with the date field.
|
|
71
77
|
*/
|
|
72
|
-
export declare function setDescription(
|
|
78
|
+
export declare function setDescription(props: SetDescriptionProps): void;
|
|
73
79
|
/**
|
|
74
80
|
* Removes the description element for the date field with
|
|
75
81
|
* the provided ID. This function should be called when the
|
|
76
82
|
* date field is unmounted.
|
|
77
83
|
*/
|
|
78
|
-
export declare function removeDescriptionElement(id: string): void;
|
|
84
|
+
export declare function removeDescriptionElement(id: string, doc: Document): void;
|
|
79
85
|
export declare function getDefaultHourCycle(locale: string): 12 | 24;
|
|
80
86
|
export {};
|
|
@@ -345,19 +345,20 @@ export function isFirstSegment(id, fieldNode) {
|
|
|
345
345
|
* so it can be associated via `aria-describedby` and read by
|
|
346
346
|
* screen readers as the user interacts with the date field.
|
|
347
347
|
*/
|
|
348
|
-
export function setDescription(
|
|
348
|
+
export function setDescription(props) {
|
|
349
|
+
const { id, formatter, value, doc } = props;
|
|
349
350
|
if (!isBrowser)
|
|
350
351
|
return;
|
|
351
352
|
const valueString = formatter.selectedDate(value);
|
|
352
|
-
const el =
|
|
353
|
+
const el = doc.getElementById(id);
|
|
353
354
|
if (!el) {
|
|
354
|
-
const div =
|
|
355
|
+
const div = doc.createElement("div");
|
|
355
356
|
div.style.cssText = styleToString({
|
|
356
357
|
display: "none",
|
|
357
358
|
});
|
|
358
359
|
div.id = id;
|
|
359
360
|
div.innerText = `Selected Date: ${valueString}`;
|
|
360
|
-
|
|
361
|
+
doc.body.appendChild(div);
|
|
361
362
|
}
|
|
362
363
|
else {
|
|
363
364
|
el.innerText = `Selected Date: ${valueString}`;
|
|
@@ -368,13 +369,13 @@ export function setDescription(id, formatter, value) {
|
|
|
368
369
|
* the provided ID. This function should be called when the
|
|
369
370
|
* date field is unmounted.
|
|
370
371
|
*/
|
|
371
|
-
export function removeDescriptionElement(id) {
|
|
372
|
+
export function removeDescriptionElement(id, doc) {
|
|
372
373
|
if (!isBrowser)
|
|
373
374
|
return;
|
|
374
|
-
const el =
|
|
375
|
+
const el = doc.getElementById(id);
|
|
375
376
|
if (!el)
|
|
376
377
|
return;
|
|
377
|
-
|
|
378
|
+
doc.body.removeChild(el);
|
|
378
379
|
}
|
|
379
380
|
export function getDefaultHourCycle(locale) {
|
|
380
381
|
const formatter = new Intl.DateTimeFormat(locale, { hour: "numeric" });
|
|
@@ -54,6 +54,12 @@ export declare function inferTimeGranularity(granularity: TimeGranularity | unde
|
|
|
54
54
|
* @param fieldNode - The id of the date field associated with the segment
|
|
55
55
|
*/
|
|
56
56
|
export declare function isFirstTimeSegment(id: string, fieldNode: HTMLElement | null): boolean;
|
|
57
|
+
type SetTimeDescriptionProps = {
|
|
58
|
+
id: string;
|
|
59
|
+
formatter: TimeFormatter;
|
|
60
|
+
value: TimeValue;
|
|
61
|
+
doc: Document;
|
|
62
|
+
};
|
|
57
63
|
/**
|
|
58
64
|
* Creates or updates a description element for a date field
|
|
59
65
|
* which enables screen readers to read the date field's value.
|
|
@@ -62,13 +68,13 @@ export declare function isFirstTimeSegment(id: string, fieldNode: HTMLElement |
|
|
|
62
68
|
* so it can be associated via `aria-describedby` and read by
|
|
63
69
|
* screen readers as the user interacts with the date field.
|
|
64
70
|
*/
|
|
65
|
-
export declare function setTimeDescription(
|
|
71
|
+
export declare function setTimeDescription(props: SetTimeDescriptionProps): void;
|
|
66
72
|
/**
|
|
67
73
|
* Removes the description element for the date field with
|
|
68
74
|
* the provided ID. This function should be called when the
|
|
69
75
|
* date field is unmounted.
|
|
70
76
|
*/
|
|
71
|
-
export declare function removeTimeDescriptionElement(id: string): void;
|
|
77
|
+
export declare function removeTimeDescriptionElement(id: string, doc: Document): void;
|
|
72
78
|
export declare function convertTimeValueToDateValue(time: TimeValue): CalendarDateTime | ZonedDateTime;
|
|
73
79
|
export declare function convertTimeValueToTime(time: TimeValue): Time;
|
|
74
80
|
export declare function isTimeBefore(timeToCompare: Time, referenceTime: Time): boolean;
|