ropav 0.13.0 → 0.14.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/components/accordion/index.d.ts +1 -0
- package/dist/components/autocomplete/index.d.ts +1 -0
- package/dist/components/calendar/index.d.ts +1 -0
- package/dist/components/checkbox-group/index.d.ts +1 -0
- package/dist/components/color-slider/index.d.ts +1 -0
- package/dist/components/combo-box/index.d.ts +2 -0
- package/dist/components/date-field/index.d.ts +1 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/use-date-range-picker.js +1 -1
- package/dist/components/disclosure-group/index.d.ts +1 -0
- package/dist/components/dropdown/dropdown-menu.js +7 -3
- package/dist/components/dropdown/dropdown-root.js +1 -1
- package/dist/components/dropdown/dropdown-submenu-trigger.js +1 -1
- package/dist/components/dropdown/dropdown.context.d.ts +1 -1
- package/dist/components/dropdown/index.d.ts +1 -0
- package/dist/components/field-error/field-error-root.js +1 -1
- package/dist/components/form/form-root.js +1 -1
- package/dist/components/number-field/index.d.ts +1 -0
- package/dist/components/overlay/overlay-popover.js +1 -1
- package/dist/components/radio-group/index.d.ts +1 -0
- package/dist/components/range-calendar/index.d.ts +1 -0
- package/dist/components/segmented-control/index.d.ts +1 -0
- package/dist/components/select/index.d.ts +1 -0
- package/dist/components/slider/index.d.ts +1 -0
- package/dist/components/table/table-drag-handle.js +1 -1
- package/dist/components/tabs/index.d.ts +1 -0
- package/dist/components/tabs/tabs-root.js +5 -0
- package/dist/components/tabs/tabs.types.d.ts +8 -0
- package/dist/components/time-field/index.d.ts +2 -0
- package/dist/components/toggle-button/index.d.ts +1 -0
- package/dist/components/toggle-button-group/index.d.ts +1 -0
- package/dist/components/tooltip/index.d.ts +1 -0
- package/dist/composables/drag-manager.js +1 -1
- package/dist/composables/drag-modality.js +2 -1
- package/dist/composables/focus-scope-registry.d.ts +19 -0
- package/dist/composables/focus-scope-registry.js +38 -0
- package/dist/composables/form-context.d.ts +25 -0
- package/dist/composables/form-context.js +17 -0
- package/dist/composables/index.d.ts +0 -24
- package/dist/composables/interaction-modality.d.ts +42 -0
- package/dist/composables/interaction-modality.js +91 -0
- package/dist/composables/use-calendar-cell.js +1 -1
- package/dist/composables/use-checkbox-group-state.js +2 -1
- package/dist/composables/use-combo-box-state.d.ts +2 -1
- package/dist/composables/use-combo-box-state.js +3 -2
- package/dist/composables/use-css-variable.js +1 -5
- package/dist/composables/use-default-locale.d.ts +9 -0
- package/dist/composables/use-default-locale.js +39 -0
- package/dist/composables/use-dismissable.js +1 -1
- package/dist/composables/use-display-names.js +1 -1
- package/dist/composables/use-drag.js +1 -1
- package/dist/composables/use-droppable-collection.js +1 -1
- package/dist/composables/use-focus-scope.d.ts +0 -12
- package/dist/composables/use-focus-scope.js +4 -31
- package/dist/composables/use-form-validation-state.d.ts +1 -60
- package/dist/composables/use-form-validation-state.js +3 -134
- package/dist/composables/use-form-validation.d.ts +2 -1
- package/dist/composables/use-form-validation.js +2 -2
- package/dist/composables/use-interaction-states.d.ts +0 -36
- package/dist/composables/use-interaction-states.js +8 -91
- package/dist/composables/use-locale.d.ts +0 -14
- package/dist/composables/use-locale.js +4 -37
- package/dist/composables/use-localized-string-dictionary.d.ts +10 -0
- package/dist/composables/use-localized-string-dictionary.js +26 -0
- package/dist/composables/use-localized-string-formatter.d.ts +0 -9
- package/dist/composables/use-localized-string-formatter.js +3 -24
- package/dist/composables/use-long-press.d.ts +0 -2
- package/dist/composables/use-long-press.js +2 -2
- package/dist/composables/use-menu-trigger-state.d.ts +64 -0
- package/dist/composables/use-menu-trigger-state.js +90 -0
- package/dist/composables/use-menu-trigger.d.ts +2 -1
- package/dist/composables/use-modal-overlay.d.ts +2 -1
- package/dist/composables/use-modal-overlay.js +3 -2
- package/dist/composables/use-overlay-trigger-state.d.ts +0 -62
- package/dist/composables/use-overlay-trigger-state.js +2 -87
- package/dist/composables/use-page-size.d.ts +14 -0
- package/dist/composables/use-page-size.js +42 -0
- package/dist/composables/use-press.d.ts +0 -20
- package/dist/composables/use-press.js +3 -26
- package/dist/composables/use-range-calendar.js +1 -1
- package/dist/composables/use-select-state.d.ts +1 -1
- package/dist/composables/use-select-state.js +1 -1
- package/dist/composables/use-slider.js +1 -1
- package/dist/composables/use-toast-region.js +2 -1
- package/dist/composables/use-tooltip-trigger-state.js +1 -15
- package/dist/composables/use-tooltip-trigger.js +1 -1
- package/dist/composables/use-viewport-size.d.ts +0 -13
- package/dist/composables/use-viewport-size.js +1 -38
- package/dist/composables/validation-result.d.ts +37 -0
- package/dist/composables/validation-result.js +122 -0
- package/dist/composables/virtual-event.d.ts +20 -0
- package/dist/composables/virtual-event.js +28 -0
- package/dist/index.js +11 -35
- package/dist/ropav.min.css +1 -1
- package/dist/utils/date-validation.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -1,65 +1,6 @@
|
|
|
1
|
+
import { getFocusRingModality, retainInteractionModality } from "./interaction-modality.js";
|
|
1
2
|
import { computed, onScopeDispose, shallowRef, toValue } from "vue";
|
|
2
3
|
//#region src/composables/use-interaction-states.ts
|
|
3
|
-
var modality = shallowRef("keyboard");
|
|
4
|
-
/**
|
|
5
|
-
* The same answer, kept out of the reactive graph.
|
|
6
|
-
*
|
|
7
|
-
* These are two different questions and they need two different answers. "Should a focus ring be
|
|
8
|
-
* painted" may only change when something is pressed or a key is struck — a bare mouse move must
|
|
9
|
-
* not erase a ring that is already there. "How is the user driving the page right now" has to
|
|
10
|
-
* follow the pointer as it moves, because a tooltip decides whether to open on hover by asking it.
|
|
11
|
-
* Answering the second reactively would make every mouse move re-render every focusable thing on
|
|
12
|
-
* the page and drop the ring while doing it, which is why React Aria keeps the same split.
|
|
13
|
-
*/
|
|
14
|
-
var latestModality = "keyboard";
|
|
15
|
-
/** Number of live consumers, so the document listeners are attached exactly once. */
|
|
16
|
-
var consumerCount = 0;
|
|
17
|
-
var onGlobalKeydown = (event) => {
|
|
18
|
-
if (event.metaKey || event.altKey || event.ctrlKey) return;
|
|
19
|
-
latestModality = "keyboard";
|
|
20
|
-
modality.value = "keyboard";
|
|
21
|
-
};
|
|
22
|
-
var onGlobalPointerdown = () => {
|
|
23
|
-
latestModality = "pointer";
|
|
24
|
-
modality.value = "pointer";
|
|
25
|
-
};
|
|
26
|
-
/** Tracked without touching the reactive ref, for the reason above. */
|
|
27
|
-
var onGlobalPointerMoved = () => {
|
|
28
|
-
latestModality = "pointer";
|
|
29
|
-
};
|
|
30
|
-
/** Attach the shared modality listeners, returning the release for this consumer. */
|
|
31
|
-
var retainModalityListeners = () => {
|
|
32
|
-
if (typeof document === "undefined") return () => {};
|
|
33
|
-
if (++consumerCount === 1) {
|
|
34
|
-
document.addEventListener("keydown", onGlobalKeydown, true);
|
|
35
|
-
document.addEventListener("pointerdown", onGlobalPointerdown, true);
|
|
36
|
-
document.addEventListener("pointermove", onGlobalPointerMoved, true);
|
|
37
|
-
document.addEventListener("pointerup", onGlobalPointerMoved, true);
|
|
38
|
-
}
|
|
39
|
-
return () => {
|
|
40
|
-
if (--consumerCount === 0) {
|
|
41
|
-
document.removeEventListener("keydown", onGlobalKeydown, true);
|
|
42
|
-
document.removeEventListener("pointerdown", onGlobalPointerdown, true);
|
|
43
|
-
document.removeEventListener("pointermove", onGlobalPointerMoved, true);
|
|
44
|
-
document.removeEventListener("pointerup", onGlobalPointerMoved, true);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Keep the shared modality listeners attached, returning the release.
|
|
50
|
-
*
|
|
51
|
-
* For a component that has to ask how the user is driving the page without otherwise taking part
|
|
52
|
-
* in the interaction lifecycle — a tooltip trigger, which opens on hover only for a real pointer.
|
|
53
|
-
* Without this the answer would be whatever it was when the last interactive component unmounted.
|
|
54
|
-
*/
|
|
55
|
-
var retainInteractionModality = () => retainModalityListeners();
|
|
56
|
-
/**
|
|
57
|
-
* How the user is driving the page right now.
|
|
58
|
-
*
|
|
59
|
-
* Ported from React Aria's `getInteractionModality`. Not reactive on purpose — read it inside an
|
|
60
|
-
* event handler, where the answer is the one that matters.
|
|
61
|
-
*/
|
|
62
|
-
var getInteractionModality = () => latestModality;
|
|
63
4
|
/**
|
|
64
5
|
* The same answer as a reactive ref, for text that is rendered rather than read in a handler.
|
|
65
6
|
*
|
|
@@ -73,33 +14,9 @@ var getInteractionModality = () => latestModality;
|
|
|
73
14
|
* interaction lifecycle.
|
|
74
15
|
*/
|
|
75
16
|
var useInteractionModality = () => {
|
|
76
|
-
const release =
|
|
17
|
+
const release = retainInteractionModality();
|
|
77
18
|
onScopeDispose(release);
|
|
78
|
-
return computed(() =>
|
|
79
|
-
};
|
|
80
|
-
/**
|
|
81
|
-
* Whether focus arriving right now is the kind that came from a keyboard.
|
|
82
|
-
*
|
|
83
|
-
* Ported from React Aria's `isFocusVisible`, and reads the same answer React Aria reads — the one
|
|
84
|
-
* that follows the pointer as it moves. Read it inside a handler: a tooltip asks it on focus to
|
|
85
|
-
* tell tabbing to a button apart from clicking it, and only the pointer-following answer knows the
|
|
86
|
-
* user had already reached for the mouse.
|
|
87
|
-
*
|
|
88
|
-
* Not the answer that decides whether a ring is painted. That one lives on `useInteractionStates`
|
|
89
|
-
* and deliberately ignores a bare mouse move, so moving the pointer cannot erase a ring that is
|
|
90
|
-
* already there. The two can disagree, and that is the point.
|
|
91
|
-
*/
|
|
92
|
-
var isFocusVisible = () => latestModality === "keyboard";
|
|
93
|
-
/**
|
|
94
|
-
* Declare how the last interaction reached the page.
|
|
95
|
-
*
|
|
96
|
-
* Ported from React Aria's `setInteractionModality`. A component that moves focus itself —
|
|
97
|
-
* a slider label handing focus to its first thumb — has to say that the move came from the
|
|
98
|
-
* keyboard, or the ring it just earned would not be painted.
|
|
99
|
-
*/
|
|
100
|
-
var setInteractionModality = (next) => {
|
|
101
|
-
latestModality = next;
|
|
102
|
-
modality.value = next;
|
|
19
|
+
return computed(() => getFocusRingModality());
|
|
103
20
|
};
|
|
104
21
|
/**
|
|
105
22
|
* Track hover, press and focus so a component can publish them as `data-*`
|
|
@@ -161,13 +78,13 @@ var useInteractionStates = (options = {}) => {
|
|
|
161
78
|
focused.value = false;
|
|
162
79
|
endPress();
|
|
163
80
|
};
|
|
164
|
-
const releaseModalityListeners =
|
|
81
|
+
const releaseModalityListeners = retainInteractionModality();
|
|
165
82
|
onScopeDispose(() => {
|
|
166
83
|
releaseModalityListeners();
|
|
167
84
|
releasePress?.();
|
|
168
85
|
}, true);
|
|
169
86
|
return {
|
|
170
|
-
isFocusVisible: computed(() => focused.value && !isDisabled.value &&
|
|
87
|
+
isFocusVisible: computed(() => focused.value && !isDisabled.value && getFocusRingModality() === "keyboard"),
|
|
171
88
|
isFocused: computed(() => focused.value && !isDisabled.value),
|
|
172
89
|
isHovered: computed(() => hovered.value && !isInert.value),
|
|
173
90
|
isPressed: computed(() => pressed.value && !isInert.value),
|
|
@@ -209,16 +126,16 @@ var useFocusWithin = (options = {}) => {
|
|
|
209
126
|
if (currentTarget instanceof Node && relatedTarget instanceof Node && currentTarget.contains(relatedTarget)) return;
|
|
210
127
|
focusWithin.value = false;
|
|
211
128
|
};
|
|
212
|
-
const releaseModalityListeners =
|
|
129
|
+
const releaseModalityListeners = retainInteractionModality();
|
|
213
130
|
onScopeDispose(() => {
|
|
214
131
|
releaseModalityListeners();
|
|
215
132
|
}, true);
|
|
216
133
|
return {
|
|
217
|
-
isFocusVisible: computed(() => focusWithin.value && !isDisabled.value &&
|
|
134
|
+
isFocusVisible: computed(() => focusWithin.value && !isDisabled.value && getFocusRingModality() === "keyboard"),
|
|
218
135
|
isFocusWithin: computed(() => focusWithin.value && !isDisabled.value),
|
|
219
136
|
onFocusin,
|
|
220
137
|
onFocusout
|
|
221
138
|
};
|
|
222
139
|
};
|
|
223
140
|
//#endregion
|
|
224
|
-
export {
|
|
141
|
+
export { useFocusWithin, useInteractionModality, useInteractionStates };
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import type { Locale } from "../utils/locale.js";
|
|
2
2
|
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
3
|
-
/**
|
|
4
|
-
* The locale an ancestor has chosen, or `null` when nobody has and the browser's own should win.
|
|
5
|
-
*
|
|
6
|
-
* Loose, because most of the tree runs without a provider and reading the browser setting is the
|
|
7
|
-
* right answer there — not an error.
|
|
8
|
-
*/
|
|
9
|
-
export declare const useLocaleContext: () => ComputedRef<Locale> | null, provideLocaleContext: (value: ComputedRef<Locale> | null) => ComputedRef<Locale> | null;
|
|
10
|
-
/**
|
|
11
|
-
* The locale the browser is set to, following the user changing it.
|
|
12
|
-
*
|
|
13
|
-
* Ported from React Aria's `packages/react-aria/src/i18n/useDefaultLocale.ts` (react-aria 3.51.0),
|
|
14
|
-
* minus its server branch — this package does not render on the server yet.
|
|
15
|
-
*/
|
|
16
|
-
export declare const useDefaultLocale: () => ComputedRef<Locale>;
|
|
17
3
|
/**
|
|
18
4
|
* The locale that applies here: the nearest ancestor's choice, or the browser's when there is none.
|
|
19
5
|
*
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createContext } from "../utils/create-context.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { isRTL } from "../utils/locale.js";
|
|
3
|
+
import { useDefaultLocale } from "./use-default-locale.js";
|
|
4
|
+
import { computed, toValue } from "vue";
|
|
4
5
|
//#region src/composables/use-locale.ts
|
|
5
6
|
/**
|
|
6
7
|
* The locale an ancestor has chosen, or `null` when nobody has and the browser's own should win.
|
|
@@ -14,40 +15,6 @@ var [useLocaleContext, provideLocaleContext] = createContext({
|
|
|
14
15
|
strict: false
|
|
15
16
|
});
|
|
16
17
|
/**
|
|
17
|
-
* The browser's locale, shared by every consumer and updated when the user changes it.
|
|
18
|
-
*
|
|
19
|
-
* Module-level rather than per-component: `languagechange` is a window event and the answer is the
|
|
20
|
-
* same for the whole page, so one listener serves all of them. Held behind a reference count so a
|
|
21
|
-
* page with no locale-aware component on it carries no listener.
|
|
22
|
-
*/
|
|
23
|
-
var browserLocale = shallowRef(getDefaultLocale());
|
|
24
|
-
var consumerCount = 0;
|
|
25
|
-
var onLanguageChange = () => {
|
|
26
|
-
browserLocale.value = getDefaultLocale();
|
|
27
|
-
};
|
|
28
|
-
/** Attach the shared `languagechange` listener, returning the release for this consumer. */
|
|
29
|
-
var retainBrowserLocale = () => {
|
|
30
|
-
/* c8 ignore next */
|
|
31
|
-
if (typeof window === "undefined") return () => {};
|
|
32
|
-
if (++consumerCount === 1) {
|
|
33
|
-
onLanguageChange();
|
|
34
|
-
window.addEventListener("languagechange", onLanguageChange);
|
|
35
|
-
}
|
|
36
|
-
return () => {
|
|
37
|
-
if (--consumerCount === 0) window.removeEventListener("languagechange", onLanguageChange);
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* The locale the browser is set to, following the user changing it.
|
|
42
|
-
*
|
|
43
|
-
* Ported from React Aria's `packages/react-aria/src/i18n/useDefaultLocale.ts` (react-aria 3.51.0),
|
|
44
|
-
* minus its server branch — this package does not render on the server yet.
|
|
45
|
-
*/
|
|
46
|
-
var useDefaultLocale = () => {
|
|
47
|
-
onScopeDispose(retainBrowserLocale());
|
|
48
|
-
return computed(() => browserLocale.value);
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
18
|
* The locale that applies here: the nearest ancestor's choice, or the browser's when there is none.
|
|
52
19
|
*
|
|
53
20
|
* Ported from React Aria's `packages/react-aria/src/i18n/I18nProvider.tsx` (react-aria 3.51.0).
|
|
@@ -77,4 +44,4 @@ var provideLocale = (locale) => {
|
|
|
77
44
|
return resolved;
|
|
78
45
|
};
|
|
79
46
|
//#endregion
|
|
80
|
-
export { provideLocale,
|
|
47
|
+
export { provideLocale, useLocale };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LocalizedString, LocalizedStrings } from "@internationalized/string";
|
|
2
|
+
import { LocalizedStringDictionary } from "@internationalized/string";
|
|
3
|
+
/**
|
|
4
|
+
* The dictionary for a table of localized strings.
|
|
5
|
+
*
|
|
6
|
+
* Ported from React Aria's `packages/react-aria/src/i18n/useLocalizedStringFormatter.ts`
|
|
7
|
+
* (react-aria 3.51.0), without its global-dictionary lookup: that exists so a server can hand
|
|
8
|
+
* pre-loaded strings to the client, which this package does not do yet.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useLocalizedStringDictionary: <K extends string = string, T extends LocalizedString = string>(strings: LocalizedStrings<K, T>) => LocalizedStringDictionary<K, T>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LocalizedStringDictionary } from "@internationalized/string";
|
|
2
|
+
//#region src/composables/use-localized-string-dictionary.ts
|
|
3
|
+
/**
|
|
4
|
+
* Dictionaries built so far, keyed by the table each was built from.
|
|
5
|
+
*
|
|
6
|
+
* A dictionary is derived entirely from its strings and is read-only afterwards, so every consumer
|
|
7
|
+
* of the same table can share one. Weak, so a table that goes out of scope takes its dictionary
|
|
8
|
+
* with it.
|
|
9
|
+
*/
|
|
10
|
+
var dictionaries = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
/**
|
|
12
|
+
* The dictionary for a table of localized strings.
|
|
13
|
+
*
|
|
14
|
+
* Ported from React Aria's `packages/react-aria/src/i18n/useLocalizedStringFormatter.ts`
|
|
15
|
+
* (react-aria 3.51.0), without its global-dictionary lookup: that exists so a server can hand
|
|
16
|
+
* pre-loaded strings to the client, which this package does not do yet.
|
|
17
|
+
*/
|
|
18
|
+
var useLocalizedStringDictionary = (strings) => {
|
|
19
|
+
const cached = dictionaries.get(strings);
|
|
20
|
+
if (cached) return cached;
|
|
21
|
+
const dictionary = new LocalizedStringDictionary(strings);
|
|
22
|
+
dictionaries.set(strings, dictionary);
|
|
23
|
+
return dictionary;
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { useLocalizedStringDictionary };
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import type { LocalizedStringFormatter as Formatter, LocalizedString, LocalizedStrings } from "@internationalized/string";
|
|
2
2
|
import type { ComputedRef } from "vue";
|
|
3
|
-
import { LocalizedStringDictionary } from "@internationalized/string";
|
|
4
|
-
/**
|
|
5
|
-
* The dictionary for a table of localized strings.
|
|
6
|
-
*
|
|
7
|
-
* Ported from React Aria's `packages/react-aria/src/i18n/useLocalizedStringFormatter.ts`
|
|
8
|
-
* (react-aria 3.51.0), without its global-dictionary lookup: that exists so a server can hand
|
|
9
|
-
* pre-loaded strings to the client, which this package does not do yet.
|
|
10
|
-
*/
|
|
11
|
-
export declare const useLocalizedStringDictionary: <K extends string = string, T extends LocalizedString = string>(strings: LocalizedStrings<K, T>) => LocalizedStringDictionary<K, T>;
|
|
12
3
|
/**
|
|
13
4
|
* A formatter reading a table of localized strings in the locale that applies here.
|
|
14
5
|
*
|
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
import { useLocale } from "./use-locale.js";
|
|
2
|
+
import { useLocalizedStringDictionary } from "./use-localized-string-dictionary.js";
|
|
2
3
|
import { computed } from "vue";
|
|
3
|
-
import {
|
|
4
|
+
import { LocalizedStringFormatter } from "@internationalized/string";
|
|
4
5
|
//#region src/composables/use-localized-string-formatter.ts
|
|
5
6
|
/**
|
|
6
|
-
* Dictionaries built so far, keyed by the table each was built from.
|
|
7
|
-
*
|
|
8
|
-
* A dictionary is derived entirely from its strings and is read-only afterwards, so every consumer
|
|
9
|
-
* of the same table can share one. Weak, so a table that goes out of scope takes its dictionary
|
|
10
|
-
* with it.
|
|
11
|
-
*/
|
|
12
|
-
var dictionaries = /* @__PURE__ */ new WeakMap();
|
|
13
|
-
/**
|
|
14
|
-
* The dictionary for a table of localized strings.
|
|
15
|
-
*
|
|
16
|
-
* Ported from React Aria's `packages/react-aria/src/i18n/useLocalizedStringFormatter.ts`
|
|
17
|
-
* (react-aria 3.51.0), without its global-dictionary lookup: that exists so a server can hand
|
|
18
|
-
* pre-loaded strings to the client, which this package does not do yet.
|
|
19
|
-
*/
|
|
20
|
-
var useLocalizedStringDictionary = (strings) => {
|
|
21
|
-
const cached = dictionaries.get(strings);
|
|
22
|
-
if (cached) return cached;
|
|
23
|
-
const dictionary = new LocalizedStringDictionary(strings);
|
|
24
|
-
dictionaries.set(strings, dictionary);
|
|
25
|
-
return dictionary;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
7
|
* A formatter reading a table of localized strings in the locale that applies here.
|
|
29
8
|
*
|
|
30
9
|
* Handed back as a computed rather than a formatter, so a consumer keeps up when the locale
|
|
@@ -36,4 +15,4 @@ var useLocalizedStringFormatter = (strings) => {
|
|
|
36
15
|
return computed(() => new LocalizedStringFormatter(locale.value.locale, dictionary));
|
|
37
16
|
};
|
|
38
17
|
//#endregion
|
|
39
|
-
export {
|
|
18
|
+
export { useLocalizedStringFormatter };
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { PressEvent, UsePressHandlers } from "./use-press.js";
|
|
2
2
|
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
3
|
-
/** How long the press has to be held before it counts as a long press. */
|
|
4
|
-
export declare const LONG_PRESS_THRESHOLD_MS = 500;
|
|
5
3
|
export interface LongPressEvent extends Omit<PressEvent, "type"> {
|
|
6
4
|
type: "longpressstart" | "longpressend" | "longpress";
|
|
7
5
|
}
|
|
@@ -69,7 +69,7 @@ var useLongPress = (options = {}) => {
|
|
|
69
69
|
if (event.pointerType === "touch") addGlobalListener(event.target, "contextmenu", (menu) => menu.preventDefault(), { once: true });
|
|
70
70
|
options.onLongPress?.(toLongPressEvent(event, "longpress"));
|
|
71
71
|
timeout = void 0;
|
|
72
|
-
}, toValue(options.threshold) ??
|
|
72
|
+
}, toValue(options.threshold) ?? LONG_PRESS_THRESHOLD_MS);
|
|
73
73
|
addGlobalListener(window, "pointerup", () => {
|
|
74
74
|
setTimeout(() => removeAllGlobalListeners(), 100);
|
|
75
75
|
}, { once: true });
|
|
@@ -82,4 +82,4 @@ var useLongPress = (options = {}) => {
|
|
|
82
82
|
};
|
|
83
83
|
};
|
|
84
84
|
//#endregion
|
|
85
|
-
export {
|
|
85
|
+
export { useLongPress };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CollectionKey } from "./use-collection.js";
|
|
2
|
+
import type { FocusStrategy, OverlayTriggerState, UseOverlayTriggerStateOptions } from "./use-overlay-trigger-state.js";
|
|
3
|
+
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
4
|
+
export interface MenuTriggerState extends OverlayTriggerState {
|
|
5
|
+
/** Which item the menu focuses when it opens, or `null` to focus the menu itself. */
|
|
6
|
+
focusStrategy: ComputedRef<FocusStrategy | null>;
|
|
7
|
+
open: (focusStrategy?: FocusStrategy | null) => void;
|
|
8
|
+
toggle: (focusStrategy?: FocusStrategy | null) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface RootMenuTriggerState extends MenuTriggerState {
|
|
11
|
+
/**
|
|
12
|
+
* The open submenu trigger keys, one per level: index 0 is the submenu opened from the root
|
|
13
|
+
* menu, index 1 the submenu opened from that one, and so on.
|
|
14
|
+
*/
|
|
15
|
+
expandedKeysStack: ComputedRef<CollectionKey[]>;
|
|
16
|
+
openSubmenu: (triggerKey: CollectionKey, level: number) => void;
|
|
17
|
+
closeSubmenu: (triggerKey: CollectionKey, level: number) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Open state for a menu trigger, ported from React Stately's `useMenuTriggerState`.
|
|
21
|
+
*
|
|
22
|
+
* Adds two things to a plain overlay trigger. A focus strategy, because how a menu was opened
|
|
23
|
+
* decides where focus lands: ArrowUp opens it focused on the last item, a mouse click opens it
|
|
24
|
+
* focused on the menu itself. And the stack of open submenus, held here on the root rather
|
|
25
|
+
* than in each submenu, which is what makes "only one path through the menu tree is open at a
|
|
26
|
+
* time" true by construction — opening a submenu at level N truncates the stack to N.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const state = useMenuTriggerState({isOpen: () => props.isOpen});
|
|
31
|
+
* state.open("last"); // ArrowUp on the trigger
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare const useMenuTriggerState: (options?: UseOverlayTriggerStateOptions) => RootMenuTriggerState;
|
|
35
|
+
export interface SubmenuTriggerState extends OverlayTriggerState {
|
|
36
|
+
focusStrategy: ComputedRef<FocusStrategy | null>;
|
|
37
|
+
open: (focusStrategy?: FocusStrategy | null) => void;
|
|
38
|
+
toggle: (focusStrategy?: FocusStrategy | null) => void;
|
|
39
|
+
/** Closes every menu in the tree, which is what selecting an item does. */
|
|
40
|
+
closeAll: () => void;
|
|
41
|
+
/** How deep this submenu sits, counting the root menu as level 0. */
|
|
42
|
+
submenuLevel: number;
|
|
43
|
+
}
|
|
44
|
+
export interface UseSubmenuTriggerStateOptions {
|
|
45
|
+
/**
|
|
46
|
+
* The key of the item that opens this submenu.
|
|
47
|
+
*
|
|
48
|
+
* Read reactively, because the item is declared beside the submenu rather than around it: the
|
|
49
|
+
* submenu exists before it has been told which item it belongs to, and is closed until it has.
|
|
50
|
+
*/
|
|
51
|
+
triggerKey: MaybeRefOrGetter<CollectionKey | null>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Open state for one submenu, ported from React Stately's `useSubmenuTriggerState`.
|
|
55
|
+
*
|
|
56
|
+
* Holds no open flag of its own — it derives one by asking the root whether its own trigger
|
|
57
|
+
* key is the one expanded at its level. That is what makes opening a sibling submenu close
|
|
58
|
+
* this one with no coordination between the two.
|
|
59
|
+
*
|
|
60
|
+
* The level is captured once, when the submenu trigger is created, and deliberately not
|
|
61
|
+
* recomputed: it describes where this trigger sits in the tree, which does not change, whereas
|
|
62
|
+
* the stack it was read from changes constantly.
|
|
63
|
+
*/
|
|
64
|
+
export declare const useSubmenuTriggerState: (options: UseSubmenuTriggerStateOptions, root: RootMenuTriggerState) => SubmenuTriggerState;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useOverlayTriggerState } from "./use-overlay-trigger-state.js";
|
|
2
|
+
import { computed, shallowRef, toValue } from "vue";
|
|
3
|
+
//#region src/composables/use-menu-trigger-state.ts
|
|
4
|
+
/**
|
|
5
|
+
* Open state for a menu trigger, ported from React Stately's `useMenuTriggerState`.
|
|
6
|
+
*
|
|
7
|
+
* Adds two things to a plain overlay trigger. A focus strategy, because how a menu was opened
|
|
8
|
+
* decides where focus lands: ArrowUp opens it focused on the last item, a mouse click opens it
|
|
9
|
+
* focused on the menu itself. And the stack of open submenus, held here on the root rather
|
|
10
|
+
* than in each submenu, which is what makes "only one path through the menu tree is open at a
|
|
11
|
+
* time" true by construction — opening a submenu at level N truncates the stack to N.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const state = useMenuTriggerState({isOpen: () => props.isOpen});
|
|
16
|
+
* state.open("last"); // ArrowUp on the trigger
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
var useMenuTriggerState = (options = {}) => {
|
|
20
|
+
const overlay = useOverlayTriggerState(options);
|
|
21
|
+
const focusStrategy = shallowRef(null);
|
|
22
|
+
const expandedKeys = shallowRef([]);
|
|
23
|
+
return {
|
|
24
|
+
close: () => {
|
|
25
|
+
expandedKeys.value = [];
|
|
26
|
+
overlay.close();
|
|
27
|
+
},
|
|
28
|
+
closeSubmenu: (triggerKey, level) => {
|
|
29
|
+
if (expandedKeys.value[level] === triggerKey) expandedKeys.value = expandedKeys.value.slice(0, level);
|
|
30
|
+
},
|
|
31
|
+
expandedKeysStack: computed(() => expandedKeys.value),
|
|
32
|
+
focusStrategy: computed(() => focusStrategy.value),
|
|
33
|
+
isOpen: overlay.isOpen,
|
|
34
|
+
open: (strategy = null) => {
|
|
35
|
+
focusStrategy.value = strategy;
|
|
36
|
+
overlay.open();
|
|
37
|
+
},
|
|
38
|
+
openSubmenu: (triggerKey, level) => {
|
|
39
|
+
if (level > expandedKeys.value.length) return;
|
|
40
|
+
expandedKeys.value = [...expandedKeys.value.slice(0, level), triggerKey];
|
|
41
|
+
},
|
|
42
|
+
setOpen: overlay.setOpen,
|
|
43
|
+
toggle: (strategy = null) => {
|
|
44
|
+
focusStrategy.value = strategy;
|
|
45
|
+
overlay.toggle();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Open state for one submenu, ported from React Stately's `useSubmenuTriggerState`.
|
|
51
|
+
*
|
|
52
|
+
* Holds no open flag of its own — it derives one by asking the root whether its own trigger
|
|
53
|
+
* key is the one expanded at its level. That is what makes opening a sibling submenu close
|
|
54
|
+
* this one with no coordination between the two.
|
|
55
|
+
*
|
|
56
|
+
* The level is captured once, when the submenu trigger is created, and deliberately not
|
|
57
|
+
* recomputed: it describes where this trigger sits in the tree, which does not change, whereas
|
|
58
|
+
* the stack it was read from changes constantly.
|
|
59
|
+
*/
|
|
60
|
+
var useSubmenuTriggerState = (options, root) => {
|
|
61
|
+
const submenuLevel = root.expandedKeysStack.value.length;
|
|
62
|
+
const focusStrategy = shallowRef(null);
|
|
63
|
+
const triggerKey = computed(() => toValue(options.triggerKey));
|
|
64
|
+
const isOpen = computed(() => triggerKey.value !== null && root.expandedKeysStack.value[submenuLevel] === triggerKey.value);
|
|
65
|
+
const close = () => {
|
|
66
|
+
focusStrategy.value = null;
|
|
67
|
+
if (triggerKey.value === null) return;
|
|
68
|
+
root.closeSubmenu(triggerKey.value, submenuLevel);
|
|
69
|
+
};
|
|
70
|
+
const open = (strategy = null) => {
|
|
71
|
+
focusStrategy.value = strategy;
|
|
72
|
+
if (triggerKey.value === null) return;
|
|
73
|
+
root.openSubmenu(triggerKey.value, submenuLevel);
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
close,
|
|
77
|
+
closeAll: root.close,
|
|
78
|
+
focusStrategy: computed(() => focusStrategy.value),
|
|
79
|
+
isOpen,
|
|
80
|
+
open,
|
|
81
|
+
setOpen: (next) => next ? open() : close(),
|
|
82
|
+
submenuLevel,
|
|
83
|
+
toggle: (strategy = null) => {
|
|
84
|
+
if (isOpen.value) close();
|
|
85
|
+
else open(strategy);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
export { useMenuTriggerState, useSubmenuTriggerState };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PressResponder } from "./press-responder.js";
|
|
2
|
+
import type { MenuTriggerState } from "./use-menu-trigger-state.js";
|
|
2
3
|
import type { OverlayType } from "./use-overlay-trigger.js";
|
|
3
|
-
import type { FocusStrategy
|
|
4
|
+
import type { FocusStrategy } from "./use-overlay-trigger-state.js";
|
|
4
5
|
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
5
6
|
/** How the menu is opened. */
|
|
6
7
|
export type MenuTriggerType = "press" | "longPress";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { PageSize
|
|
1
|
+
import type { PageSize } from "./use-page-size.js";
|
|
2
|
+
import type { ViewportSize } from "./use-viewport-size.js";
|
|
2
3
|
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
3
4
|
export interface UseModalOverlayOptions {
|
|
4
5
|
/**
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ariaHideOutside } from "../utils/aria-hide-outside.js";
|
|
2
|
-
import { useFocusScope } from "./use-focus-scope.js";
|
|
3
2
|
import { useDismissable } from "./use-dismissable.js";
|
|
3
|
+
import { useFocusScope } from "./use-focus-scope.js";
|
|
4
|
+
import { usePageSize } from "./use-page-size.js";
|
|
4
5
|
import { usePreventScroll } from "./use-prevent-scroll.js";
|
|
5
|
-
import {
|
|
6
|
+
import { useViewportSize } from "./use-viewport-size.js";
|
|
6
7
|
import { computed, toValue, watch } from "vue";
|
|
7
8
|
//#region src/composables/use-modal-overlay.ts
|
|
8
9
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CollectionKey } from "./use-collection.js";
|
|
2
1
|
import type { ComputedRef, MaybeRefOrGetter } from "vue";
|
|
3
2
|
/** Which end of a collection receives focus when an overlay opens. */
|
|
4
3
|
export type FocusStrategy = "first" | "last";
|
|
@@ -26,64 +25,3 @@ export interface OverlayTriggerState {
|
|
|
26
25
|
* ```
|
|
27
26
|
*/
|
|
28
27
|
export declare const useOverlayTriggerState: (options?: UseOverlayTriggerStateOptions) => OverlayTriggerState;
|
|
29
|
-
export interface MenuTriggerState extends OverlayTriggerState {
|
|
30
|
-
/** Which item the menu focuses when it opens, or `null` to focus the menu itself. */
|
|
31
|
-
focusStrategy: ComputedRef<FocusStrategy | null>;
|
|
32
|
-
open: (focusStrategy?: FocusStrategy | null) => void;
|
|
33
|
-
toggle: (focusStrategy?: FocusStrategy | null) => void;
|
|
34
|
-
}
|
|
35
|
-
export interface RootMenuTriggerState extends MenuTriggerState {
|
|
36
|
-
/**
|
|
37
|
-
* The open submenu trigger keys, one per level: index 0 is the submenu opened from the root
|
|
38
|
-
* menu, index 1 the submenu opened from that one, and so on.
|
|
39
|
-
*/
|
|
40
|
-
expandedKeysStack: ComputedRef<CollectionKey[]>;
|
|
41
|
-
openSubmenu: (triggerKey: CollectionKey, level: number) => void;
|
|
42
|
-
closeSubmenu: (triggerKey: CollectionKey, level: number) => void;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Open state for a menu trigger, ported from React Stately's `useMenuTriggerState`.
|
|
46
|
-
*
|
|
47
|
-
* Adds two things to a plain overlay trigger. A focus strategy, because how a menu was opened
|
|
48
|
-
* decides where focus lands: ArrowUp opens it focused on the last item, a mouse click opens it
|
|
49
|
-
* focused on the menu itself. And the stack of open submenus, held here on the root rather
|
|
50
|
-
* than in each submenu, which is what makes "only one path through the menu tree is open at a
|
|
51
|
-
* time" true by construction — opening a submenu at level N truncates the stack to N.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* const state = useMenuTriggerState({isOpen: () => props.isOpen});
|
|
56
|
-
* state.open("last"); // ArrowUp on the trigger
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
export declare const useMenuTriggerState: (options?: UseOverlayTriggerStateOptions) => RootMenuTriggerState;
|
|
60
|
-
export interface SubmenuTriggerState extends OverlayTriggerState {
|
|
61
|
-
focusStrategy: ComputedRef<FocusStrategy | null>;
|
|
62
|
-
open: (focusStrategy?: FocusStrategy | null) => void;
|
|
63
|
-
toggle: (focusStrategy?: FocusStrategy | null) => void;
|
|
64
|
-
/** Closes every menu in the tree, which is what selecting an item does. */
|
|
65
|
-
closeAll: () => void;
|
|
66
|
-
/** How deep this submenu sits, counting the root menu as level 0. */
|
|
67
|
-
submenuLevel: number;
|
|
68
|
-
}
|
|
69
|
-
export interface UseSubmenuTriggerStateOptions {
|
|
70
|
-
/**
|
|
71
|
-
* The key of the item that opens this submenu.
|
|
72
|
-
*
|
|
73
|
-
* Read reactively, because the item is declared beside the submenu rather than around it: the
|
|
74
|
-
* submenu exists before it has been told which item it belongs to, and is closed until it has.
|
|
75
|
-
*/
|
|
76
|
-
triggerKey: MaybeRefOrGetter<CollectionKey | null>;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Open state for one submenu, ported from React Stately's `useSubmenuTriggerState`.
|
|
80
|
-
*
|
|
81
|
-
* Holds no open flag of its own — it derives one by asking the root whether its own trigger
|
|
82
|
-
* key is the one expanded at its level. That is what makes opening a sibling submenu close
|
|
83
|
-
* this one with no coordination between the two.
|
|
84
|
-
*
|
|
85
|
-
* The level is captured once, when the submenu trigger is created, and deliberately not
|
|
86
|
-
* recomputed: it describes where this trigger sits in the tree, which does not change, whereas
|
|
87
|
-
* the stack it was read from changes constantly.
|
|
88
|
-
*/
|
|
89
|
-
export declare const useSubmenuTriggerState: (options: UseSubmenuTriggerStateOptions, root: RootMenuTriggerState) => SubmenuTriggerState;
|