@sit-onyx/headless 0.1.0-alpha.7 → 0.1.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 +2 -8
- package/dist/composables/comboBox/SelectOnlyCombobox.d.vue.ts +299 -0
- package/dist/composables/comboBox/TestCombobox.ct.d.ts +1 -0
- package/dist/composables/comboBox/TestCombobox.d.vue.ts +299 -0
- package/dist/composables/comboBox/createComboBox.d.ts +370 -0
- package/dist/composables/comboBox/createComboBox.testing.d.ts +10 -0
- package/dist/composables/helpers/useDismissible.d.ts +10 -0
- package/dist/composables/helpers/useGlobalListener.d.ts +10 -0
- package/dist/composables/helpers/useGlobalListener.spec.d.ts +1 -0
- package/dist/composables/helpers/useOutsideClick.d.ts +26 -0
- package/dist/composables/helpers/useOutsideClick.spec.d.ts +1 -0
- package/dist/composables/helpers/useTypeAhead.d.ts +11 -0
- package/dist/composables/helpers/useTypeAhead.spec.d.ts +1 -0
- package/dist/composables/listbox/TestListbox.ct.d.ts +1 -0
- package/dist/composables/listbox/TestListbox.d.vue.ts +2 -0
- package/dist/composables/listbox/createListbox.d.ts +102 -0
- package/dist/composables/listbox/createListbox.testing.d.ts +24 -0
- package/dist/composables/menuButton/TestMenuButton.ct.d.ts +1 -0
- package/dist/composables/menuButton/TestMenuButton.d.vue.ts +2 -0
- package/dist/composables/menuButton/createMenuButton.d.ts +78 -0
- package/dist/composables/menuButton/createMenuButton.testing.d.ts +24 -0
- package/dist/composables/navigationMenu/TestMenu.ct.d.ts +1 -0
- package/dist/composables/navigationMenu/TestMenu.d.vue.ts +2 -0
- package/dist/composables/navigationMenu/createMenu.d.ts +21 -0
- package/dist/composables/navigationMenu/createMenu.testing.d.ts +16 -0
- package/dist/composables/tabs/TestTabs.ct.d.ts +1 -0
- package/dist/composables/tabs/TestTabs.d.vue.ts +2 -0
- package/dist/composables/tabs/createTabs.d.ts +48 -0
- package/dist/composables/tabs/createTabs.testing.d.ts +13 -0
- package/dist/composables/tooltip/createToggletip.d.ts +36 -0
- package/dist/composables/tooltip/createTooltip.d.ts +42 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +780 -0
- package/dist/playwright.d.ts +5 -0
- package/dist/playwright.js +369 -0
- package/dist/utils/builder.d.ts +85 -0
- package/dist/utils/keyboard.d.ts +17 -0
- package/dist/utils/keyboard.spec.d.ts +1 -0
- package/dist/utils/math.d.ts +6 -0
- package/dist/utils/math.spec.d.ts +1 -0
- package/dist/utils/object.d.ts +5 -0
- package/dist/utils/object.spec.d.ts +1 -0
- package/dist/utils/timer.d.ts +10 -0
- package/dist/utils/types.d.ts +25 -0
- package/dist/utils/vitest.d.ts +12 -0
- package/package.json +24 -8
- package/src/composables/comboBox/TestCombobox.ct.tsx +0 -13
- package/src/composables/comboBox/TestCombobox.vue +0 -76
- package/src/composables/comboBox/createComboBox.ct.ts +0 -72
- package/src/composables/comboBox/createComboBox.ts +0 -163
- package/src/composables/listbox/TestListbox.ct.tsx +0 -17
- package/src/composables/listbox/TestListbox.vue +0 -91
- package/src/composables/listbox/createListbox.ct.ts +0 -141
- package/src/composables/listbox/createListbox.ts +0 -209
- package/src/composables/tooltip/createTooltip.ts +0 -150
- package/src/composables/typeAhead.spec.ts +0 -29
- package/src/composables/typeAhead.ts +0 -26
- package/src/index.ts +0 -4
- package/src/playwright.ts +0 -2
- package/src/utils/builder.ts +0 -37
- package/src/utils/id.ts +0 -14
- package/src/utils/keyboard.spec.ts +0 -18
- package/src/utils/keyboard.ts +0 -328
- package/src/utils/timer.ts +0 -15
- package/src/utils/types.ts +0 -8
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { computed, ref, unref, watchEffect, type MaybeRef, type Ref } from "vue";
|
|
2
|
-
import { createId } from "../..";
|
|
3
|
-
import { createBuilder, type HeadlessElementAttributes } from "../../utils/builder";
|
|
4
|
-
import { useTypeAhead } from "../typeAhead";
|
|
5
|
-
|
|
6
|
-
export type ListboxValue = string | number | boolean;
|
|
7
|
-
|
|
8
|
-
export type ListboxModelValue<
|
|
9
|
-
TValue extends ListboxValue = ListboxValue,
|
|
10
|
-
TMultiple extends boolean = false,
|
|
11
|
-
> = TMultiple extends true ? TValue[] : TValue;
|
|
12
|
-
|
|
13
|
-
export type CreateListboxOptions<
|
|
14
|
-
TValue extends ListboxValue = ListboxValue,
|
|
15
|
-
TMultiple extends boolean = false,
|
|
16
|
-
> = {
|
|
17
|
-
/**
|
|
18
|
-
* Aria label for the listbox.
|
|
19
|
-
*/
|
|
20
|
-
label: MaybeRef<string>;
|
|
21
|
-
/**
|
|
22
|
-
* Value of currently selected option.
|
|
23
|
-
*/
|
|
24
|
-
selectedOption: Ref<ListboxModelValue<TValue, TMultiple> | undefined>;
|
|
25
|
-
/**
|
|
26
|
-
* Value of currently (visually) active option.
|
|
27
|
-
*/
|
|
28
|
-
activeOption: Ref<TValue | undefined>;
|
|
29
|
-
/**
|
|
30
|
-
* Wether the listbox is controlled from the outside, e.g. by a combobox.
|
|
31
|
-
* This disables keyboard events and makes the listbox not focusable.
|
|
32
|
-
*/
|
|
33
|
-
controlled?: boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Whether the listbox is multiselect.
|
|
36
|
-
*/
|
|
37
|
-
multiple?: MaybeRef<TMultiple | undefined>;
|
|
38
|
-
/**
|
|
39
|
-
* Hook when an option is selected.
|
|
40
|
-
*/
|
|
41
|
-
onSelect?: (value: TValue) => void;
|
|
42
|
-
/**
|
|
43
|
-
* Hook when the first option should be activated.
|
|
44
|
-
*/
|
|
45
|
-
onActivateFirst?: () => void;
|
|
46
|
-
/**
|
|
47
|
-
* Hook when the last option should be activated.
|
|
48
|
-
*/
|
|
49
|
-
onActivateLast?: () => void;
|
|
50
|
-
/**
|
|
51
|
-
* Hook when the next option should be activated.
|
|
52
|
-
*/
|
|
53
|
-
onActivateNext?: (currentValue: TValue) => void;
|
|
54
|
-
/**
|
|
55
|
-
* Hook when the previous option should be activated.
|
|
56
|
-
*/
|
|
57
|
-
onActivatePrevious?: (currentValue: TValue) => void;
|
|
58
|
-
/**
|
|
59
|
-
* Hook when the first option starting with the given label should be activated.
|
|
60
|
-
*/
|
|
61
|
-
onTypeAhead?: (label: string) => void;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Composable for creating a accessibility-conform listbox.
|
|
66
|
-
* For supported keyboard shortcuts, see: https://www.w3.org/WAI/ARIA/apg/patterns/listbox/examples/listbox-scrollable/
|
|
67
|
-
*/
|
|
68
|
-
export const createListbox = createBuilder(
|
|
69
|
-
<TValue extends ListboxValue = ListboxValue, TMultiple extends boolean = false>(
|
|
70
|
-
options: CreateListboxOptions<TValue, TMultiple>,
|
|
71
|
-
) => {
|
|
72
|
-
const isMultiselect = computed(() => unref(options.multiple) ?? false);
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Map for option IDs. key = option value, key = ID for the HTML element
|
|
76
|
-
*/
|
|
77
|
-
const descendantKeyIdMap = new Map<TValue, string>();
|
|
78
|
-
|
|
79
|
-
const getOptionId = (value: TValue) => {
|
|
80
|
-
if (!descendantKeyIdMap.has(value)) {
|
|
81
|
-
descendantKeyIdMap.set(value, createId("listbox-option"));
|
|
82
|
-
}
|
|
83
|
-
return descendantKeyIdMap.get(value)!;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Whether the listbox element is focused.
|
|
88
|
-
*/
|
|
89
|
-
const isFocused = ref(false);
|
|
90
|
-
|
|
91
|
-
// scroll currently active option into view if needed
|
|
92
|
-
watchEffect(() => {
|
|
93
|
-
if (options.activeOption.value == undefined || (!isFocused.value && !options.controlled))
|
|
94
|
-
return;
|
|
95
|
-
const id = getOptionId(options.activeOption.value);
|
|
96
|
-
document.getElementById(id)?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const typeAhead = useTypeAhead((inputString) => options.onTypeAhead?.(inputString));
|
|
100
|
-
|
|
101
|
-
const handleKeydown = (event: KeyboardEvent) => {
|
|
102
|
-
switch (event.key) {
|
|
103
|
-
case " ":
|
|
104
|
-
event.preventDefault();
|
|
105
|
-
if (options.activeOption.value != undefined) {
|
|
106
|
-
// TODO: don't call onSelect if the option is disabled
|
|
107
|
-
options.onSelect?.(options.activeOption.value);
|
|
108
|
-
}
|
|
109
|
-
break;
|
|
110
|
-
|
|
111
|
-
case "ArrowUp":
|
|
112
|
-
event.preventDefault();
|
|
113
|
-
// if no option is active yet, activate the last option
|
|
114
|
-
if (options.activeOption.value == undefined) {
|
|
115
|
-
options.onActivateLast?.();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
options.onActivatePrevious?.(options.activeOption.value);
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case "ArrowDown":
|
|
123
|
-
event.preventDefault();
|
|
124
|
-
// if no option is active yet, activate the first option
|
|
125
|
-
if (options.activeOption.value == undefined) {
|
|
126
|
-
options.onActivateFirst?.();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
options.onActivateNext?.(options.activeOption.value);
|
|
131
|
-
break;
|
|
132
|
-
|
|
133
|
-
case "Home":
|
|
134
|
-
event.preventDefault();
|
|
135
|
-
options.onActivateFirst?.();
|
|
136
|
-
break;
|
|
137
|
-
|
|
138
|
-
case "End":
|
|
139
|
-
event.preventDefault();
|
|
140
|
-
options.onActivateLast?.();
|
|
141
|
-
break;
|
|
142
|
-
|
|
143
|
-
default:
|
|
144
|
-
// if printable characters are pressed, the first option/text starting with the typed characters should be active
|
|
145
|
-
typeAhead(event);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const listbox = computed<HeadlessElementAttributes>(() =>
|
|
150
|
-
options.controlled
|
|
151
|
-
? {
|
|
152
|
-
role: "listbox",
|
|
153
|
-
"aria-multiselectable": isMultiselect.value,
|
|
154
|
-
"aria-label": unref(options.label),
|
|
155
|
-
tabindex: "-1",
|
|
156
|
-
}
|
|
157
|
-
: {
|
|
158
|
-
role: "listbox",
|
|
159
|
-
"aria-multiselectable": isMultiselect.value,
|
|
160
|
-
"aria-label": unref(options.label),
|
|
161
|
-
tabindex: "0",
|
|
162
|
-
"aria-activedescendant":
|
|
163
|
-
options.activeOption.value != undefined
|
|
164
|
-
? getOptionId(options.activeOption.value)
|
|
165
|
-
: undefined,
|
|
166
|
-
onFocus: () => (isFocused.value = true),
|
|
167
|
-
onBlur: () => (isFocused.value = false),
|
|
168
|
-
onKeydown: handleKeydown,
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
elements: {
|
|
174
|
-
listbox,
|
|
175
|
-
group: computed(() => {
|
|
176
|
-
return (options: { label: string }) => ({
|
|
177
|
-
role: "group",
|
|
178
|
-
"aria-label": options.label,
|
|
179
|
-
});
|
|
180
|
-
}),
|
|
181
|
-
option: computed(() => {
|
|
182
|
-
return (data: {
|
|
183
|
-
label: string;
|
|
184
|
-
value: TValue;
|
|
185
|
-
selected?: boolean;
|
|
186
|
-
disabled?: boolean;
|
|
187
|
-
}) => {
|
|
188
|
-
const isSelected = data.selected ?? false;
|
|
189
|
-
return {
|
|
190
|
-
id: getOptionId(data.value),
|
|
191
|
-
role: "option",
|
|
192
|
-
"aria-label": data.label,
|
|
193
|
-
"aria-checked": isMultiselect.value ? isSelected : undefined,
|
|
194
|
-
"aria-selected": !isMultiselect.value ? isSelected : undefined,
|
|
195
|
-
"aria-disabled": data.disabled,
|
|
196
|
-
onClick: () => !data.disabled && options.onSelect?.(data.value),
|
|
197
|
-
} as const;
|
|
198
|
-
};
|
|
199
|
-
}),
|
|
200
|
-
},
|
|
201
|
-
state: {
|
|
202
|
-
isFocused,
|
|
203
|
-
},
|
|
204
|
-
internals: {
|
|
205
|
-
getOptionId,
|
|
206
|
-
},
|
|
207
|
-
};
|
|
208
|
-
},
|
|
209
|
-
);
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computed,
|
|
3
|
-
onBeforeMount,
|
|
4
|
-
onBeforeUnmount,
|
|
5
|
-
ref,
|
|
6
|
-
unref,
|
|
7
|
-
watchEffect,
|
|
8
|
-
type MaybeRef,
|
|
9
|
-
} from "vue";
|
|
10
|
-
import { createId } from "../..";
|
|
11
|
-
import { createBuilder } from "../../utils/builder";
|
|
12
|
-
|
|
13
|
-
export type CreateTooltipOptions = {
|
|
14
|
-
open: MaybeRef<TooltipOpen>;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type TooltipOpen =
|
|
18
|
-
| TooltipTrigger
|
|
19
|
-
| boolean
|
|
20
|
-
| {
|
|
21
|
-
type: "hover";
|
|
22
|
-
/**
|
|
23
|
-
* Number of milliseconds to use as debounce when showing/hiding the tooltip
|
|
24
|
-
*/
|
|
25
|
-
debounce: number;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const TOOLTIP_TRIGGERS = ["hover", "click"] as const;
|
|
29
|
-
export type TooltipTrigger = (typeof TOOLTIP_TRIGGERS)[number];
|
|
30
|
-
|
|
31
|
-
export const createTooltip = createBuilder((options: CreateTooltipOptions) => {
|
|
32
|
-
const tooltipId = createId("tooltip");
|
|
33
|
-
const _isVisible = ref(false);
|
|
34
|
-
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
35
|
-
|
|
36
|
-
const debounce = computed(() => {
|
|
37
|
-
const open = unref(options.open);
|
|
38
|
-
if (typeof open !== "object") return 200;
|
|
39
|
-
return open.debounce;
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const openType = computed(() => {
|
|
43
|
-
const open = unref(options.open);
|
|
44
|
-
if (typeof open !== "object") return open;
|
|
45
|
-
return open.type;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Debounced visible state that will only be toggled after a given timeout.
|
|
50
|
-
*/
|
|
51
|
-
const debouncedVisible = computed({
|
|
52
|
-
get: () => _isVisible.value,
|
|
53
|
-
set: (newValue) => {
|
|
54
|
-
clearTimeout(timeout);
|
|
55
|
-
timeout = setTimeout(() => {
|
|
56
|
-
_isVisible.value = newValue;
|
|
57
|
-
}, debounce.value);
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Whether the tooltip should be currently visible.
|
|
63
|
-
* If openMode is set as boolean it will prefer it over the hover/click state.
|
|
64
|
-
*/
|
|
65
|
-
const isVisible = computed(() => {
|
|
66
|
-
if (typeof openType.value === "boolean") return openType.value;
|
|
67
|
-
return debouncedVisible.value;
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Toggles the tooltip if element is clicked.
|
|
72
|
-
*/
|
|
73
|
-
const handleClick = () => {
|
|
74
|
-
_isVisible.value = !_isVisible.value;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const hoverEvents = computed(() => {
|
|
78
|
-
if (openType.value !== "hover") return;
|
|
79
|
-
return {
|
|
80
|
-
onMouseover: () => (debouncedVisible.value = true),
|
|
81
|
-
onMouseout: () => (debouncedVisible.value = false),
|
|
82
|
-
onFocusin: () => (_isVisible.value = true),
|
|
83
|
-
onFocusout: () => (_isVisible.value = false),
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Closes the tooltip if Escape is pressed.
|
|
89
|
-
*/
|
|
90
|
-
const handleDocumentKeydown = (event: KeyboardEvent) => {
|
|
91
|
-
if (event.key !== "Escape") return;
|
|
92
|
-
_isVisible.value = false;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Document click handle that closes then tooltip when clicked outside.
|
|
97
|
-
* Should only be called when trigger is "click".
|
|
98
|
-
*/
|
|
99
|
-
const handleDocumentClick = (event: MouseEvent) => {
|
|
100
|
-
const tooltipParent = document.getElementById(tooltipId)?.parentElement;
|
|
101
|
-
if (!tooltipParent || !(event.target instanceof Node)) return;
|
|
102
|
-
|
|
103
|
-
const isOutsideClick = !tooltipParent.contains(event.target);
|
|
104
|
-
if (isOutsideClick) _isVisible.value = false;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// add global document event listeners only on/before mounted to also work in server side rendering
|
|
108
|
-
onBeforeMount(() => {
|
|
109
|
-
document.addEventListener("keydown", handleDocumentKeydown);
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Registers keydown and click handlers when trigger is "click" to close
|
|
113
|
-
* the tooltip.
|
|
114
|
-
*/
|
|
115
|
-
watchEffect(() => {
|
|
116
|
-
if (openType.value === "click") {
|
|
117
|
-
document.addEventListener("click", handleDocumentClick);
|
|
118
|
-
} else {
|
|
119
|
-
document.removeEventListener("click", handleDocumentClick);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Clean up global event listeners to prevent dangling events.
|
|
126
|
-
*/
|
|
127
|
-
onBeforeUnmount(() => {
|
|
128
|
-
document.addEventListener("keydown", handleDocumentKeydown);
|
|
129
|
-
document.addEventListener("click", handleDocumentClick);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return {
|
|
133
|
-
elements: {
|
|
134
|
-
trigger: computed(() => ({
|
|
135
|
-
"aria-describedby": tooltipId,
|
|
136
|
-
onClick: openType.value === "click" ? handleClick : undefined,
|
|
137
|
-
...hoverEvents.value,
|
|
138
|
-
})),
|
|
139
|
-
tooltip: computed(() => ({
|
|
140
|
-
role: "tooltip",
|
|
141
|
-
id: tooltipId,
|
|
142
|
-
tabindex: "-1",
|
|
143
|
-
...hoverEvents.value,
|
|
144
|
-
})),
|
|
145
|
-
},
|
|
146
|
-
state: {
|
|
147
|
-
isVisible,
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { beforeAll, expect, test, vi } from "vitest";
|
|
2
|
-
import { useTypeAhead } from "./typeAhead";
|
|
3
|
-
|
|
4
|
-
beforeAll(() => {
|
|
5
|
-
vi.useFakeTimers();
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
test("useTypeAhead", () => {
|
|
9
|
-
const spy = vi.fn();
|
|
10
|
-
const typeAhead = useTypeAhead(spy);
|
|
11
|
-
|
|
12
|
-
typeAhead({ key: "a" });
|
|
13
|
-
expect(spy).toHaveBeenCalledOnce();
|
|
14
|
-
expect(spy).toHaveBeenLastCalledWith("a");
|
|
15
|
-
|
|
16
|
-
typeAhead({ key: "Alt" });
|
|
17
|
-
expect(spy).toHaveBeenCalledOnce();
|
|
18
|
-
expect(spy).toHaveBeenLastCalledWith("a");
|
|
19
|
-
|
|
20
|
-
typeAhead({ key: "b" });
|
|
21
|
-
expect(spy).toHaveBeenCalledTimes(2);
|
|
22
|
-
expect(spy).toHaveBeenLastCalledWith("ab");
|
|
23
|
-
|
|
24
|
-
vi.runAllTimers();
|
|
25
|
-
|
|
26
|
-
typeAhead({ key: "c" });
|
|
27
|
-
expect(spy).toBeCalledTimes(3);
|
|
28
|
-
expect(spy).toHaveBeenLastCalledWith("c");
|
|
29
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { isPrintableCharacter } from "../utils/keyboard";
|
|
2
|
-
import { debounce } from "../utils/timer";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Enhances typeAhead to combine multiple inputs in quick succession and filter out non-printable characters.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* const typeAhead = useTypeAhead((inputString) => console.log("Typed string:", inputString));
|
|
10
|
-
* // ...
|
|
11
|
-
* addEventListener("keydown", typeAhead);
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export const useTypeAhead = (callback: (input: string) => void, timeout = 500) => {
|
|
15
|
-
let inputString = "";
|
|
16
|
-
const debouncedReset = debounce(() => (inputString = ""), timeout);
|
|
17
|
-
|
|
18
|
-
return (event: Pick<KeyboardEvent, "key">) => {
|
|
19
|
-
if (!isPrintableCharacter(event.key)) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
debouncedReset();
|
|
23
|
-
inputString = `${inputString}${event.key}`;
|
|
24
|
-
callback(inputString);
|
|
25
|
-
};
|
|
26
|
-
};
|
package/src/index.ts
DELETED
package/src/playwright.ts
DELETED
package/src/utils/builder.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { ComputedRef, HtmlHTMLAttributes, Ref } from "vue";
|
|
2
|
-
import type { IfDefined } from "./types";
|
|
3
|
-
|
|
4
|
-
export type IteratedHeadlessElementFunc<T extends Record<string, unknown>> = (
|
|
5
|
-
opts: T,
|
|
6
|
-
) => HtmlHTMLAttributes;
|
|
7
|
-
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
-
export type HeadlessElementAttributes = HtmlHTMLAttributes | IteratedHeadlessElementFunc<any>;
|
|
10
|
-
|
|
11
|
-
export type HeadlessElements = Record<
|
|
12
|
-
string,
|
|
13
|
-
HeadlessElementAttributes | ComputedRef<HeadlessElementAttributes>
|
|
14
|
-
>;
|
|
15
|
-
|
|
16
|
-
export type HeadlessState = Record<string, Ref>;
|
|
17
|
-
|
|
18
|
-
export type HeadlessComposable<
|
|
19
|
-
Elements extends HeadlessElements,
|
|
20
|
-
State extends HeadlessState | undefined = undefined,
|
|
21
|
-
Internals extends object | undefined = undefined,
|
|
22
|
-
> = {
|
|
23
|
-
elements: Elements;
|
|
24
|
-
} & IfDefined<"internals", Internals> &
|
|
25
|
-
IfDefined<"state", State>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* We use this identity function to ensure the correct typings of the headless composables
|
|
29
|
-
*/
|
|
30
|
-
export const createBuilder = <
|
|
31
|
-
P,
|
|
32
|
-
Elements extends HeadlessElements,
|
|
33
|
-
State extends HeadlessState | undefined = undefined,
|
|
34
|
-
Internals extends object | undefined = undefined,
|
|
35
|
-
>(
|
|
36
|
-
builder: (props: P) => HeadlessComposable<Elements, State, Internals>,
|
|
37
|
-
) => builder;
|
package/src/utils/id.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns a unique global id string
|
|
3
|
-
*/
|
|
4
|
-
// ⚠️ we make use of an IIFE to encapsulate the globalCounter so it can never accidentally be used somewhere else.
|
|
5
|
-
const nextId = (() => {
|
|
6
|
-
let globalCounter = 1;
|
|
7
|
-
return () => globalCounter++;
|
|
8
|
-
})();
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Creates a globally unique string using a counter.
|
|
12
|
-
* The given name is the prefix.
|
|
13
|
-
*/
|
|
14
|
-
export const createId = (name: string) => `${name}-${nextId()}`;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
2
|
-
import { isPrintableCharacter } from "./keyboard";
|
|
3
|
-
|
|
4
|
-
test.each([
|
|
5
|
-
// ARRANGE
|
|
6
|
-
{ key: "a", expected: true },
|
|
7
|
-
{ key: "🎉", expected: true },
|
|
8
|
-
{ key: "あ", expected: true },
|
|
9
|
-
{ key: " ", expected: true },
|
|
10
|
-
{ key: "Meta", expected: false },
|
|
11
|
-
{ key: "Fn", expected: false },
|
|
12
|
-
])("should return $expected for key $key", ({ key, expected }) => {
|
|
13
|
-
// ACT
|
|
14
|
-
const result = isPrintableCharacter(key);
|
|
15
|
-
|
|
16
|
-
// ASSERT
|
|
17
|
-
expect(result).toBe(expected);
|
|
18
|
-
});
|