@witchcraft/ui 0.0.1 → 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 +18 -28
- package/dist/module.d.mts +3 -1
- package/dist/module.d.ts +3 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +20 -11
- package/dist/runtime/assets/base.css +1 -1
- package/dist/runtime/assets/locales/en.json +2 -2
- package/dist/runtime/assets/tailwind.css +1 -1
- package/dist/runtime/assets/utils.css +1 -0
- package/dist/runtime/build/WitchcraftUiResolver.js +1 -1
- package/dist/runtime/components/Icon/Icon.vue +10 -5
- package/dist/runtime/components/LibButton/LibButton.vue +41 -46
- package/dist/runtime/components/LibCheckbox/LibCheckbox.vue +7 -3
- package/dist/runtime/components/LibColorInput/LibColorInput.vue +111 -36
- package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.d.ts +2 -0
- package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.js +26 -9
- package/dist/runtime/components/LibColorPicker/LibColorPicker.vue +242 -131
- package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.d.ts +2 -0
- package/dist/runtime/components/LibColorPicker/utils/safeConvertToHsva.js +18 -0
- package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.d.ts +2 -0
- package/dist/runtime/components/LibColorPicker/utils/safeConvertToRgba.js +17 -0
- package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.d.ts +2 -0
- package/dist/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.js +8 -0
- package/dist/runtime/components/LibColorPicker/utils/truncate.d.ts +1 -0
- package/dist/runtime/components/LibColorPicker/utils/truncate.js +5 -0
- package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.js +1 -1
- package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +11 -8
- package/dist/runtime/components/LibDatePicker/LibDatePicker.vue +4 -17
- package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +192 -131
- package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue +183 -115
- package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue +3 -3
- package/dist/runtime/components/LibDebug/LibDebug.vue +15 -5
- package/dist/runtime/components/LibDevOnly/LibDevOnly.vue +1 -3
- package/dist/runtime/components/LibFileInput/LibFileInput.vue +54 -28
- package/dist/runtime/components/{LibInput/LibInput.stories.d.ts → LibInputDeprecated/LibInputDeprecated.stories.d.ts} +6 -6
- package/dist/runtime/components/{LibInput/LibInput.stories.js → LibInputDeprecated/LibInputDeprecated.stories.js} +64 -19
- package/{src/runtime/components/LibInput/LibInput.vue → dist/runtime/components/LibInputDeprecated/LibInputDeprecated.vue} +40 -33
- package/dist/runtime/components/LibLabel/LibLabel.vue +2 -2
- package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.d.ts +1 -1
- package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.js +5 -4
- package/dist/runtime/components/LibMultiValues/LibMultiValues.vue +11 -12
- package/dist/runtime/components/LibNotifications/LibNotification.vue +19 -10
- package/dist/runtime/components/LibNotifications/LibNotifications.stories.js +2 -2
- package/dist/runtime/components/LibNotifications/LibNotifications.vue +20 -11
- package/dist/runtime/components/LibPagination/LibPagination.stories.js +2 -2
- package/dist/runtime/components/LibPagination/LibPagination.vue +19 -19
- package/dist/runtime/components/LibPalette/LibPalette.vue +3 -3
- package/dist/runtime/components/LibPopup/LibPopup.stories.js +2 -2
- package/dist/runtime/components/LibPopup/LibPopup.vue +30 -66
- package/dist/runtime/components/LibProgressBar/LibProgressBar.vue +3 -1
- package/dist/runtime/components/LibRecorder/LibRecorder.vue +2 -2
- package/dist/runtime/components/LibRoot/LibRoot.vue +14 -1
- package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.js +1 -1
- package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue +5 -7
- package/dist/runtime/components/LibSuggestions/LibSuggestions.vue +42 -25
- package/dist/runtime/components/LibTable/LibTable.vue +8 -8
- package/dist/runtime/components/Scrolling.stories.d.ts +6 -0
- package/dist/runtime/components/Scrolling.stories.js +44 -0
- package/dist/runtime/components/Template/NAME.vue +1 -1
- package/dist/runtime/components/TestControls/TestControls.vue +1 -1
- package/dist/runtime/components/index.d.ts +12 -11
- package/dist/runtime/components/index.js +12 -11
- package/dist/runtime/components/shared/props.d.ts +81 -16
- package/dist/runtime/components/shared/storyHelpers/playInput.js +5 -5
- package/dist/runtime/components/shared/storyHelpers/playSuggestions.js +15 -11
- package/dist/runtime/composables/index.d.ts +5 -0
- package/dist/runtime/composables/index.js +5 -0
- package/dist/runtime/composables/useDivideAttrs.js +1 -0
- package/dist/runtime/composables/useDragWithThreshold.d.ts +71 -0
- package/dist/runtime/composables/useDragWithThreshold.js +40 -0
- package/dist/runtime/composables/usePreHydrationValue.d.ts +12 -0
- package/dist/runtime/composables/usePreHydrationValue.js +15 -0
- package/dist/runtime/composables/useSetupI18n.d.ts +2 -0
- package/dist/runtime/composables/useSetupI18n.js +5 -1
- package/dist/runtime/composables/useSuggestions.d.ts +7 -5
- package/dist/runtime/composables/useSuggestions.js +94 -57
- package/dist/runtime/directives/vResizableCols.js +3 -1
- package/dist/runtime/helpers/NotificationHandler.d.ts +5 -0
- package/dist/runtime/helpers/index.d.ts +3 -1
- package/dist/runtime/helpers/index.js +3 -1
- package/dist/runtime/types/index.d.ts +6 -0
- package/dist/runtime/utils/notifyIfError.d.ts +14 -0
- package/dist/runtime/utils/notifyIfError.js +29 -0
- package/package.json +18 -20
- package/src/module.ts +31 -12
- package/src/runtime/assets/base.css +10 -1
- package/src/runtime/assets/locales/en.json +2 -2
- package/src/runtime/assets/tailwind.css +1 -1
- package/src/runtime/assets/{style.css → utils.css} +86 -4
- package/src/runtime/build/WitchcraftUiResolver.ts +1 -1
- package/src/runtime/components/Icon/Icon.vue +10 -5
- package/src/runtime/components/LibButton/LibButton.vue +41 -46
- package/src/runtime/components/LibCheckbox/LibCheckbox.vue +7 -3
- package/src/runtime/components/LibColorInput/LibColorInput.vue +111 -36
- package/src/runtime/components/LibColorPicker/LibColorPicker.stories.ts +25 -4
- package/src/runtime/components/LibColorPicker/LibColorPicker.vue +242 -131
- package/src/runtime/components/LibColorPicker/utils/safeConvertToHsva.ts +25 -0
- package/src/runtime/components/LibColorPicker/utils/safeConvertToRgba.ts +23 -0
- package/src/runtime/components/LibColorPicker/utils/toLowPrecisionRgbaString.ts +13 -0
- package/src/runtime/components/LibColorPicker/utils/truncate.ts +6 -0
- package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.ts +1 -1
- package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +11 -8
- package/src/runtime/components/LibDatePicker/LibDatePicker.vue +4 -17
- package/src/runtime/components/LibDatePicker/LibRangeDatePicker.vue +192 -131
- package/src/runtime/components/LibDatePicker/LibSingleDatePicker.vue +183 -115
- package/src/runtime/components/LibDatePicker/LibTimeZonePicker.vue +3 -3
- package/src/runtime/components/LibDebug/LibDebug.vue +15 -5
- package/src/runtime/components/LibDevOnly/LibDevOnly.vue +1 -3
- package/src/runtime/components/LibFileInput/LibFileInput.vue +54 -28
- package/src/runtime/components/{LibInput/LibInput.stories.ts → LibInputDeprecated/LibInputDeprecated.stories.ts} +64 -19
- package/{dist/runtime/components/LibInput/LibInput.vue → src/runtime/components/LibInputDeprecated/LibInputDeprecated.vue} +40 -33
- package/src/runtime/components/LibLabel/LibLabel.vue +2 -2
- package/src/runtime/components/LibMultiValues/LibMultiValues.stories.ts +5 -4
- package/src/runtime/components/LibMultiValues/LibMultiValues.vue +11 -12
- package/src/runtime/components/LibNotifications/LibNotification.vue +19 -10
- package/src/runtime/components/LibNotifications/LibNotifications.stories.ts +2 -2
- package/src/runtime/components/LibNotifications/LibNotifications.vue +20 -11
- package/src/runtime/components/LibPagination/LibPagination.stories.ts +2 -2
- package/src/runtime/components/LibPagination/LibPagination.vue +19 -19
- package/src/runtime/components/LibPalette/LibPalette.vue +3 -3
- package/src/runtime/components/LibPopup/LibPopup.stories.ts +2 -2
- package/src/runtime/components/LibPopup/LibPopup.vue +30 -66
- package/src/runtime/components/LibProgressBar/LibProgressBar.vue +3 -1
- package/src/runtime/components/LibRecorder/LibRecorder.vue +2 -2
- package/src/runtime/components/LibRoot/LibRoot.vue +14 -1
- package/src/runtime/components/LibSimpleInput/LibSimpleInput.stories.ts +1 -1
- package/src/runtime/components/LibSimpleInput/LibSimpleInput.vue +5 -7
- package/src/runtime/components/LibSuggestions/LibSuggestions.vue +42 -25
- package/src/runtime/components/LibTable/LibTable.vue +8 -8
- package/src/runtime/components/Scrolling.stories.ts +58 -0
- package/src/runtime/components/Template/NAME.vue +1 -1
- package/src/runtime/components/TestControls/TestControls.vue +1 -1
- package/src/runtime/components/index.ts +12 -12
- package/src/runtime/components/shared/props.ts +82 -19
- package/src/runtime/components/shared/storyHelpers/playInput.ts +6 -5
- package/src/runtime/components/shared/storyHelpers/playSuggestions.ts +25 -11
- package/src/runtime/composables/index.ts +5 -0
- package/src/runtime/composables/useDarkMode.ts +2 -2
- package/src/runtime/composables/useDivideAttrs.ts +1 -0
- package/src/runtime/composables/useDragWithThreshold.ts +108 -0
- package/src/runtime/composables/usePreHydrationValue.ts +30 -0
- package/src/runtime/composables/useSetupI18n.ts +8 -2
- package/src/runtime/composables/useSuggestions.ts +92 -45
- package/src/runtime/directives/vResizableCols.ts +3 -1
- package/src/runtime/helpers/NotificationHandler.ts +5 -0
- package/src/runtime/helpers/index.ts +3 -1
- package/src/runtime/types/index.ts +5 -0
- package/src/runtime/utils/notifyIfError.ts +45 -0
- package/dist/runtime/assets/style.css +0 -1
- package/dist/runtime/helpers/addValue.d.ts +0 -1
- package/dist/runtime/helpers/addValue.js +0 -8
- package/src/runtime/helpers/addValue.ts +0 -10
- /package/dist/runtime/components/{reset.stories.d.ts → Reset.stories.d.ts} +0 -0
- /package/dist/runtime/components/{reset.stories.js → Reset.stories.js} +0 -0
- /package/src/runtime/components/{reset.stories.ts → Reset.stories.ts} +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { Point } from "../types/index.js.js";
|
|
3
|
+
/**
|
|
4
|
+
* A composable for dragging items only after a certain threshold of movement has been reached.
|
|
5
|
+
*
|
|
6
|
+
* What to do with the actual pointer coordinates is up to you.
|
|
7
|
+
*
|
|
8
|
+
* The is compatible with the `useScrollNearContainerEdges` composable as well.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const {
|
|
12
|
+
* pointerCoords,
|
|
13
|
+
* passedDragThreshold,
|
|
14
|
+
* startDragThresholdCheck,
|
|
15
|
+
* endDragThresholdCheck,
|
|
16
|
+
* checkDragThreshold,
|
|
17
|
+
* } = useDragWithThreshold({ threshold: ref(5) })
|
|
18
|
+
*
|
|
19
|
+
* function grabPointerDown(e: PointerEvent): void {
|
|
20
|
+
* startDragThresholdCheck(e)
|
|
21
|
+
* e.preventDefault()
|
|
22
|
+
*
|
|
23
|
+
* document.addEventListener("pointermove", grabPointerMove)
|
|
24
|
+
* document.addEventListener("pointerup", grabPointerUp)
|
|
25
|
+
* document.addEventListener("keyup", escapeDrag)
|
|
26
|
+
* }
|
|
27
|
+
* function grabPointerMove(e: PointerEvent): void {
|
|
28
|
+
* e.preventDefault()
|
|
29
|
+
* checkDragThreshold(e)
|
|
30
|
+
* if (passedDragThreshold.value) {
|
|
31
|
+
*
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* function grabPointerUp(e: PointerEvent): void {
|
|
35
|
+
* if (passedDragThreshold.value) {
|
|
36
|
+
* // drag
|
|
37
|
+
* } else {
|
|
38
|
+
* // handleAsClick(e)
|
|
39
|
+
* }
|
|
40
|
+
* stopDrag()
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* function stopDrag(): void {
|
|
44
|
+
* endDragThresholdCheck()
|
|
45
|
+
* document.removeEventListener("keyup", escapeDrag)
|
|
46
|
+
* document.removeEventListener("pointermove", grabPointerMove)
|
|
47
|
+
* document.removeEventListener("pointerup", grabPointerUp)
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* function escapeDrag(e: KeyboardEvent): void {
|
|
51
|
+
* if (e.code === "Escape") stopDrag()
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const useDragWithThreshold: ({ initialOffset, pointerCoords, threshold, }?: {
|
|
56
|
+
initialOffset?: Ref<Point | undefined>;
|
|
57
|
+
pointerCoords?: Ref<Point | undefined>;
|
|
58
|
+
threshold?: Ref<number>;
|
|
59
|
+
}) => {
|
|
60
|
+
initialOffset: Ref<Point | undefined, Point | undefined>;
|
|
61
|
+
pointerCoords: Ref<Point | undefined, Point | undefined>;
|
|
62
|
+
threshold: Ref<number, number>;
|
|
63
|
+
passedDragThreshold: Ref<boolean, boolean>;
|
|
64
|
+
getEventCoords: (e: {
|
|
65
|
+
clientX: number;
|
|
66
|
+
clientY: number;
|
|
67
|
+
}) => Point;
|
|
68
|
+
checkDragThreshold: (e: PointerEvent) => void;
|
|
69
|
+
startDragThresholdCheck: (e: PointerEvent) => void;
|
|
70
|
+
endDragThresholdCheck: () => void;
|
|
71
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export const useDragWithThreshold = ({
|
|
3
|
+
initialOffset = ref({ x: 0, y: 0 }),
|
|
4
|
+
pointerCoords = ref({ x: 0, y: 0 }),
|
|
5
|
+
threshold = ref(10)
|
|
6
|
+
} = {}) => {
|
|
7
|
+
const passedDragThreshold = ref(false);
|
|
8
|
+
function getDistance(p1, p2) {
|
|
9
|
+
const xDiff = p2.x - p1.x;
|
|
10
|
+
const yDiff = p2.y - p1.y;
|
|
11
|
+
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
|
12
|
+
}
|
|
13
|
+
function getEventCoords(e) {
|
|
14
|
+
return { x: e.clientX, y: e.clientY };
|
|
15
|
+
}
|
|
16
|
+
function checkDragThreshold(e) {
|
|
17
|
+
pointerCoords.value = getEventCoords(e);
|
|
18
|
+
passedDragThreshold.value ||= initialOffset.value !== void 0 && getDistance(initialOffset.value, pointerCoords.value) >= threshold.value;
|
|
19
|
+
}
|
|
20
|
+
function startDragThresholdCheck(e) {
|
|
21
|
+
passedDragThreshold.value = false;
|
|
22
|
+
initialOffset.value = getEventCoords(e);
|
|
23
|
+
pointerCoords.value = getEventCoords(e);
|
|
24
|
+
}
|
|
25
|
+
function endDragThresholdCheck() {
|
|
26
|
+
passedDragThreshold.value = false;
|
|
27
|
+
initialOffset.value = void 0;
|
|
28
|
+
pointerCoords.value = void 0;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
initialOffset,
|
|
32
|
+
pointerCoords,
|
|
33
|
+
threshold,
|
|
34
|
+
passedDragThreshold,
|
|
35
|
+
getEventCoords,
|
|
36
|
+
checkDragThreshold,
|
|
37
|
+
startDragThresholdCheck,
|
|
38
|
+
endDragThresholdCheck
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* This takes the value of the element at the given id (onBeforeMount, i.e. pre-hydration if available), and sets the given ref to that value onMounted.
|
|
5
|
+
*
|
|
6
|
+
* This should be compatible with both nuxt and vue.
|
|
7
|
+
*
|
|
8
|
+
* Nore that while it's generic, you will need to provide a transform function to convert the string to the desired type.
|
|
9
|
+
*
|
|
10
|
+
* @experimental
|
|
11
|
+
*/
|
|
12
|
+
export declare function usePreHydrationValue<T>(id: string, refVal: Ref<T>, transform?: (val: string) => T): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { onBeforeMount, onMounted } from "vue";
|
|
2
|
+
export function usePreHydrationValue(id, refVal, transform = (val) => val) {
|
|
3
|
+
let temp = "";
|
|
4
|
+
onBeforeMount(() => {
|
|
5
|
+
const el = document.getElementById(id);
|
|
6
|
+
if (el?.value) {
|
|
7
|
+
temp = el.value;
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
onMounted(() => {
|
|
11
|
+
if (temp) {
|
|
12
|
+
refVal.value = transform(temp);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -6,6 +6,8 @@ export type TranslationFunction = (key: string, replacements?: Record<string, an
|
|
|
6
6
|
* Should be called only once. You can choose to await it or not (see the `useDummyMessageSetWhileLoading` option).
|
|
7
7
|
*
|
|
8
8
|
* A default function is available, see {@link defaultTranslationFunction}.
|
|
9
|
+
*
|
|
10
|
+
* To avoid hydration errors, on the server, the message loading is awaited.
|
|
9
11
|
*/
|
|
10
12
|
export declare function useSetupI18n({ locale, useBuiltinTranslations, useDummyMessageSetWhileLoading, translationFunction, }: {
|
|
11
13
|
locale: Ref<string>;
|
|
@@ -40,7 +40,11 @@ export async function useSetupI18n({
|
|
|
40
40
|
messages.value = newMessages;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
if (import.meta.server) {
|
|
44
|
+
await loadMessageSet(locale.value);
|
|
45
|
+
} else {
|
|
46
|
+
void loadMessageSet(locale.value);
|
|
47
|
+
}
|
|
44
48
|
watch(locale, async () => {
|
|
45
49
|
void loadMessageSet(locale.value);
|
|
46
50
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AnyFunction } from "@alanscodelog/utils";
|
|
1
2
|
import { type Ref } from "vue";
|
|
2
3
|
import { type SuggestionsEmits, type SuggestionsOptions } from "../components/shared/props.js.js";
|
|
3
4
|
/**
|
|
@@ -5,7 +6,7 @@ import { type SuggestionsEmits, type SuggestionsOptions } from "../components/sh
|
|
|
5
6
|
*
|
|
6
7
|
* Note that while object suggestions are supported, the `suggestionLabel` prop is required and $inputModel and $modelValue will still be string values (as returned by the suggestionLabel function).
|
|
7
8
|
*/
|
|
8
|
-
export declare function useSuggestions<TSuggestion>($inputValue: Ref<string>, $modelValue: Ref<string>, emit: SuggestionsEmits, opts: SuggestionsOptions<TSuggestion>, debug?: boolean): {
|
|
9
|
+
export declare function useSuggestions<TSuggestion, TMultivalue extends boolean = false>($inputValue: Ref<string>, $modelValue: Ref<TMultivalue extends true ? string[] : string>, $open: Ref<boolean>, emit: SuggestionsEmits, opts: SuggestionsOptions<TSuggestion>, debug?: boolean): {
|
|
9
10
|
list: import("vue").ComputedRef<any[] | undefined>;
|
|
10
11
|
filtered: import("vue").ComputedRef<any[] | undefined>;
|
|
11
12
|
active: Ref<number, number>;
|
|
@@ -16,11 +17,11 @@ export declare function useSuggestions<TSuggestion>($inputValue: Ref<string>, $m
|
|
|
16
17
|
hasValidSuggestion: import("vue").ComputedRef<boolean>;
|
|
17
18
|
openable: import("vue").ComputedRef<boolean | undefined>;
|
|
18
19
|
getLabel: (item: any) => string;
|
|
19
|
-
|
|
20
|
+
$open: Ref<boolean, boolean>;
|
|
20
21
|
open: () => void;
|
|
21
22
|
close: () => void;
|
|
22
|
-
enterSelected: () => void;
|
|
23
|
-
|
|
23
|
+
enterSelected: (doClose?: boolean) => void;
|
|
24
|
+
enterIndex: (num: number, doClose?: boolean) => void;
|
|
24
25
|
toggle: () => void;
|
|
25
26
|
cancel: () => void;
|
|
26
27
|
select: (num: number) => void;
|
|
@@ -29,10 +30,11 @@ export declare function useSuggestions<TSuggestion>($inputValue: Ref<string>, $m
|
|
|
29
30
|
first: () => void;
|
|
30
31
|
last: () => void;
|
|
31
32
|
};
|
|
32
|
-
export declare function useSuggestionsInputAria(id: Ref<string>,
|
|
33
|
+
export declare function useSuggestionsInputAria(id: Ref<string>, $open: Ref<boolean>, activeSuggestion: Ref<number>, suggestions: Ref<any | undefined>): import("vue").ComputedRef<{
|
|
33
34
|
"aria-autocomplete": "both" | undefined;
|
|
34
35
|
"aria-controls": string | undefined;
|
|
35
36
|
role: string | undefined;
|
|
36
37
|
"aria-expanded": boolean | undefined;
|
|
37
38
|
"aria-activedescendant": string | undefined;
|
|
38
39
|
}>;
|
|
40
|
+
export declare function suggestionLabelGuard<TFunction extends AnyFunction>(item: any, suggestionLabeler: TFunction | undefined): asserts suggestionLabeler is TFunction;
|
|
@@ -1,33 +1,42 @@
|
|
|
1
1
|
import { isBlank } from "@alanscodelog/utils/isBlank.js";
|
|
2
2
|
import { isObject } from "@alanscodelog/utils/isObject.js";
|
|
3
|
+
import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn.js";
|
|
4
|
+
import { removeIfIn } from "@alanscodelog/utils/removeIfIn.js";
|
|
3
5
|
import { computed, ref, toRaw, watch } from "vue";
|
|
4
|
-
export function useSuggestions($inputValue, $modelValue, emit, opts, debug = false) {
|
|
6
|
+
export function useSuggestions($inputValue, $modelValue, $open, emit, opts, debug = false) {
|
|
5
7
|
if (typeof opts.suggestions?.[0] === "object" && !opts.suggestionLabel && !opts.suggestionsFilter) {
|
|
6
8
|
throw new Error("`suggestionLabel` or `suggestionsFilter` must be passed if suggestions are objects.");
|
|
7
9
|
}
|
|
8
|
-
const isOpen = ref(false);
|
|
9
10
|
const activeSuggestion = ref(-1);
|
|
10
|
-
watch(isOpen, (val) => {
|
|
11
|
-
emit("update:isOpen", val);
|
|
12
|
-
});
|
|
13
11
|
watch(activeSuggestion, (val) => {
|
|
14
12
|
emit("update:activeSuggestion", val);
|
|
15
13
|
});
|
|
16
|
-
|
|
14
|
+
if (opts.suggestions) {
|
|
15
|
+
for (const suggestion of opts.suggestions) {
|
|
16
|
+
suggestionLabelGuard(suggestion, opts.suggestionLabel);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const getSuggestionLabel = (item) => {
|
|
20
|
+
suggestionLabelGuard(item, opts.suggestionLabel);
|
|
17
21
|
if (isObject(item)) {
|
|
18
|
-
|
|
19
|
-
return opts.suggestionLabel(item);
|
|
20
|
-
}
|
|
21
|
-
throw new Error("`suggestionLabel` must be passed if suggestions are objects.");
|
|
22
|
+
return opts.suggestionLabel(item);
|
|
22
23
|
}
|
|
23
24
|
return item;
|
|
24
25
|
};
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
const defaultSuggestionsFilter = (input, items) => input === "" ? [...items] : items.filter((item) => {
|
|
27
|
+
if (Array.isArray($modelValue.value)) {
|
|
28
|
+
if ($modelValue.value.includes(getSuggestionLabel(item))) return true;
|
|
29
|
+
}
|
|
30
|
+
return getSuggestionLabel(item).toLowerCase().includes(input.toLowerCase());
|
|
31
|
+
});
|
|
27
32
|
const suggestionsFilter = computed(() => opts.suggestionsFilter ?? defaultSuggestionsFilter);
|
|
28
33
|
const suggestionsList = computed(() => {
|
|
29
34
|
if (opts.suggestions) {
|
|
30
|
-
const
|
|
35
|
+
const suggestions = [...opts.suggestions];
|
|
36
|
+
if (Array.isArray($modelValue.value) && !opts.showSelectedValues) {
|
|
37
|
+
pushIfNotIn(suggestions, $modelValue.value);
|
|
38
|
+
}
|
|
39
|
+
const res = suggestionsFilter.value($inputValue.value, suggestions);
|
|
31
40
|
return res;
|
|
32
41
|
}
|
|
33
42
|
return void 0;
|
|
@@ -43,19 +52,30 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
43
52
|
if (opts.suggestions) {
|
|
44
53
|
const res = suggestionAvailable.value ? suggestionsList.value : opts.suggestions;
|
|
45
54
|
if (opts.restrictToSuggestions && !isValidSuggestion.value) return res;
|
|
46
|
-
if (opts.preventDuplicateValues && opts.values) {
|
|
47
|
-
return res.filter((suggestion) => !opts.values.includes(suggestionKey(suggestion)));
|
|
48
|
-
}
|
|
49
55
|
return res;
|
|
50
56
|
}
|
|
51
57
|
return void 0;
|
|
52
58
|
});
|
|
53
|
-
|
|
59
|
+
function setValue(val) {
|
|
60
|
+
if (Array.isArray($modelValue.value)) {
|
|
61
|
+
if ($modelValue.value.includes(val)) {
|
|
62
|
+
removeIfIn($modelValue.value, val);
|
|
63
|
+
return true;
|
|
64
|
+
} else {
|
|
65
|
+
pushIfNotIn($modelValue.value, [val]);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
$modelValue.value = val;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
function closeSuggestions() {
|
|
73
|
+
if (!opts.canClose) return;
|
|
54
74
|
if (debug) console.log("closeSuggestions");
|
|
55
|
-
|
|
75
|
+
$open.value = false;
|
|
56
76
|
activeSuggestion.value = -1;
|
|
57
|
-
}
|
|
58
|
-
|
|
77
|
+
}
|
|
78
|
+
function openSuggestions() {
|
|
59
79
|
if (debug) console.log("openSuggestions", { openable: openable.value });
|
|
60
80
|
if (!openable.value) return;
|
|
61
81
|
if (activeSuggestion.value === -1) {
|
|
@@ -65,20 +85,22 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
65
85
|
activeSuggestion.value = 0;
|
|
66
86
|
}
|
|
67
87
|
}
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
function enterSuggestion(num) {
|
|
88
|
+
$open.value = true;
|
|
89
|
+
}
|
|
90
|
+
function enterSuggestion(num, doClose = true) {
|
|
71
91
|
if (num < -1 || num > (filteredSuggestions.value?.length ?? 0)) return;
|
|
72
92
|
if (debug) console.log("enterSuggestion", num);
|
|
73
93
|
if (filteredSuggestions.value === void 0) return;
|
|
74
94
|
const suggestion = filteredSuggestions.value[num];
|
|
75
|
-
const val =
|
|
76
|
-
|
|
77
|
-
$inputValue.value = getSuggestionLabel(suggestion);
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
const val = getSuggestionLabel(suggestion);
|
|
96
|
+
const wasRemoved = setValue(val);
|
|
97
|
+
$inputValue.value = Array.isArray($modelValue.value) ? "" : getSuggestionLabel(suggestion);
|
|
98
|
+
if (doClose) {
|
|
99
|
+
closeSuggestions();
|
|
100
|
+
}
|
|
101
|
+
emit("submit", val, toRaw(suggestion), wasRemoved);
|
|
80
102
|
}
|
|
81
|
-
|
|
103
|
+
function enterSelected(doClose = true) {
|
|
82
104
|
if (activeSuggestion.value === -1) {
|
|
83
105
|
if (!opts.restrictToSuggestions) {
|
|
84
106
|
if (debug) console.log("enterSelected, unrestricted, emitting submit");
|
|
@@ -89,9 +111,9 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
89
111
|
return;
|
|
90
112
|
}
|
|
91
113
|
if (debug) console.log("enterSelected");
|
|
92
|
-
enterSuggestion(activeSuggestion.value);
|
|
93
|
-
}
|
|
94
|
-
|
|
114
|
+
enterSuggestion(activeSuggestion.value, doClose);
|
|
115
|
+
}
|
|
116
|
+
function selectSuggestion(num) {
|
|
95
117
|
if (debug) console.log("selectSuggestion", num);
|
|
96
118
|
if (num >= -1) {
|
|
97
119
|
activeSuggestion.value = num;
|
|
@@ -99,37 +121,41 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
99
121
|
if (num === Infinity && (filteredSuggestions.value?.length ?? 0) > 0) {
|
|
100
122
|
activeSuggestion.value = filteredSuggestions.value.length - 1;
|
|
101
123
|
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
124
|
+
}
|
|
125
|
+
function toggleSuggestions() {
|
|
126
|
+
$open.value ? closeSuggestions() : openSuggestions();
|
|
127
|
+
}
|
|
128
|
+
function prevSuggestion() {
|
|
107
129
|
if (!filteredSuggestions.value) return;
|
|
108
130
|
if (activeSuggestion.value > 0) {
|
|
109
131
|
activeSuggestion.value--;
|
|
110
132
|
} else if (filteredSuggestions.value) {
|
|
111
133
|
activeSuggestion.value = filteredSuggestions.value.length - 1;
|
|
112
134
|
}
|
|
113
|
-
}
|
|
114
|
-
|
|
135
|
+
}
|
|
136
|
+
function nextSuggestion() {
|
|
115
137
|
if (!filteredSuggestions.value) return;
|
|
116
138
|
if (activeSuggestion.value >= filteredSuggestions.value.length - 1) {
|
|
117
139
|
activeSuggestion.value = 0;
|
|
118
140
|
} else {
|
|
119
141
|
activeSuggestion.value++;
|
|
120
142
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
143
|
+
}
|
|
144
|
+
function firstSuggestion() {
|
|
123
145
|
selectSuggestion(0);
|
|
124
|
-
}
|
|
125
|
-
|
|
146
|
+
}
|
|
147
|
+
function lastSuggestion() {
|
|
126
148
|
selectSuggestion(Infinity);
|
|
127
|
-
}
|
|
128
|
-
|
|
149
|
+
}
|
|
150
|
+
function cancel() {
|
|
151
|
+
if (Array.isArray($modelValue.value)) {
|
|
152
|
+
$inputValue.value = "";
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
129
155
|
if (debug) console.log("cancel");
|
|
130
156
|
$inputValue.value = getSuggestionLabel($modelValue.value);
|
|
131
157
|
closeSuggestions();
|
|
132
|
-
}
|
|
158
|
+
}
|
|
133
159
|
watch(() => opts.canOpen, (val) => {
|
|
134
160
|
if (!val) {
|
|
135
161
|
if (debug) console.log("canOpen changed to false, closing suggestions");
|
|
@@ -149,10 +175,14 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
149
175
|
}
|
|
150
176
|
});
|
|
151
177
|
watch($modelValue, () => {
|
|
152
|
-
|
|
178
|
+
if (Array.isArray($modelValue.value)) {
|
|
179
|
+
$inputValue.value = "";
|
|
180
|
+
} else {
|
|
181
|
+
$inputValue.value = getSuggestionLabel($modelValue.value);
|
|
182
|
+
}
|
|
153
183
|
if (debug) console.log("modelValue changed");
|
|
154
184
|
});
|
|
155
|
-
|
|
185
|
+
function defaultSuggestionSelector(suggestions, input) {
|
|
156
186
|
if (input.length === 0) return 0;
|
|
157
187
|
let longestMatch;
|
|
158
188
|
let ii = -1;
|
|
@@ -168,17 +198,17 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
168
198
|
}
|
|
169
199
|
}
|
|
170
200
|
return ii;
|
|
171
|
-
}
|
|
201
|
+
}
|
|
172
202
|
watch($inputValue, () => {
|
|
173
203
|
if (debug) console.log("input changed:", $inputValue.value, "modelValue:", $modelValue.value);
|
|
174
|
-
if (getSuggestionLabel($modelValue.value) === $inputValue.value) return;
|
|
204
|
+
if (!Array.isArray($modelValue.value) && getSuggestionLabel($modelValue.value) === $inputValue.value) return;
|
|
175
205
|
if (suggestionAvailable.value) {
|
|
176
206
|
if (debug) console.log("input changed, suggestion available, opening suggestions");
|
|
177
207
|
openSuggestions();
|
|
178
208
|
}
|
|
179
|
-
if (!opts.restrictToSuggestions) {
|
|
209
|
+
if (!opts.restrictToSuggestions && !Array.isArray($modelValue.value)) {
|
|
180
210
|
if (debug) console.log("input changed, unrestricted, setting modelValue");
|
|
181
|
-
$
|
|
211
|
+
setValue($inputValue.value);
|
|
182
212
|
}
|
|
183
213
|
if (exactlyMatchingSuggestion.value && suggestionsList.value) {
|
|
184
214
|
if (debug) console.log("input changed, exactly matching, setting activeSuggestion");
|
|
@@ -200,11 +230,11 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
200
230
|
hasValidSuggestion: isValidSuggestion,
|
|
201
231
|
openable,
|
|
202
232
|
getLabel: getSuggestionLabel,
|
|
203
|
-
|
|
233
|
+
$open,
|
|
204
234
|
open: openSuggestions,
|
|
205
235
|
close: closeSuggestions,
|
|
206
236
|
enterSelected,
|
|
207
|
-
enterSuggestion,
|
|
237
|
+
enterIndex: enterSuggestion,
|
|
208
238
|
toggle: toggleSuggestions,
|
|
209
239
|
cancel,
|
|
210
240
|
select: selectSuggestion,
|
|
@@ -214,13 +244,20 @@ export function useSuggestions($inputValue, $modelValue, emit, opts, debug = fal
|
|
|
214
244
|
last: lastSuggestion
|
|
215
245
|
};
|
|
216
246
|
}
|
|
217
|
-
export function useSuggestionsInputAria(id,
|
|
247
|
+
export function useSuggestionsInputAria(id, $open, activeSuggestion, suggestions) {
|
|
218
248
|
const ariaInputProps = computed(() => ({
|
|
219
249
|
"aria-autocomplete": suggestions !== void 0 ? "both" : void 0,
|
|
220
250
|
"aria-controls": suggestions !== void 0 ? `suggestions-${id.value}` : void 0,
|
|
221
251
|
role: suggestions ? "combobox" : void 0,
|
|
222
|
-
"aria-expanded": suggestions !== void 0 ?
|
|
223
|
-
"aria-activedescendant":
|
|
252
|
+
"aria-expanded": suggestions !== void 0 ? $open.value : void 0,
|
|
253
|
+
"aria-activedescendant": $open.value ? `suggestion-${id.value}-${activeSuggestion.value}` : void 0
|
|
224
254
|
}));
|
|
225
255
|
return ariaInputProps;
|
|
226
256
|
}
|
|
257
|
+
export function suggestionLabelGuard(item, suggestionLabeler) {
|
|
258
|
+
if (isObject(item)) {
|
|
259
|
+
if (!suggestionLabeler) {
|
|
260
|
+
throw new Error("`suggestionLabel` must be passed if suggestions are objects.");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -213,7 +213,9 @@ const positionGrips = (el) => {
|
|
|
213
213
|
const $el = getElInfo(el);
|
|
214
214
|
for (const grip of $el.grips.keys()) {
|
|
215
215
|
const col = $el.grips.get(grip);
|
|
216
|
-
const
|
|
216
|
+
const colEls = getColEls(el)[col];
|
|
217
|
+
if (!colEls) unreachable();
|
|
218
|
+
const colBox = getBox(colEls);
|
|
217
219
|
const gripBox = getBox(grip);
|
|
218
220
|
grip.style.left = `${xPos + colBox.width - gripBox.width / 2}px`;
|
|
219
221
|
xPos += colBox.width;
|
|
@@ -28,11 +28,16 @@ export type RawNotificationEntry<TOptions extends string[] = ["Ok", "Cancel"], T
|
|
|
28
28
|
message: string;
|
|
29
29
|
title?: string;
|
|
30
30
|
code?: string;
|
|
31
|
+
/** @default ["Ok", "Cancel"] */
|
|
31
32
|
options?: TOptions;
|
|
33
|
+
/** @default false */
|
|
32
34
|
requiresAction?: boolean;
|
|
33
35
|
cancellable?: TCancellable;
|
|
36
|
+
/** @default "Ok" */
|
|
34
37
|
default?: TOptions[number];
|
|
38
|
+
/** @default [] */
|
|
35
39
|
dangerous?: TOptions[number][];
|
|
40
|
+
/** @default false if cancellable, otherwise the default timeout */
|
|
36
41
|
timeout?: number | boolean;
|
|
37
42
|
icon?: string;
|
|
38
43
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export { addValue } from "./addValue.js.js";
|
|
2
1
|
export { base64ToImg } from "./base64ToImg.js.js";
|
|
3
2
|
export { copy } from "./copy.js.js";
|
|
3
|
+
export { createNoonUtcDate } from "./createNoonUtcDate.js.js";
|
|
4
4
|
export { createRecorderHandler } from "./storybook.js.js";
|
|
5
|
+
export { defaultTranslationFunction } from "./defaultTranslationFunction.js.js";
|
|
6
|
+
export { getTimeZoneList } from "./getTimeZoneList.js.js";
|
|
5
7
|
export { hasModifiers } from "./hasModifiers.js.js";
|
|
6
8
|
export { NotificationHandler } from "./NotificationHandler.js.js";
|
|
7
9
|
export { readFile } from "./readFile.js.js";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export { addValue } from "./addValue.js";
|
|
2
1
|
export { base64ToImg } from "./base64ToImg.js";
|
|
3
2
|
export { copy } from "./copy.js";
|
|
3
|
+
export { createNoonUtcDate } from "./createNoonUtcDate.js";
|
|
4
4
|
export { createRecorderHandler } from "./storybook.js";
|
|
5
|
+
export { defaultTranslationFunction } from "./defaultTranslationFunction.js";
|
|
6
|
+
export { getTimeZoneList } from "./getTimeZoneList.js";
|
|
5
7
|
export { hasModifiers } from "./hasModifiers.js";
|
|
6
8
|
export { NotificationHandler } from "./NotificationHandler.js";
|
|
7
9
|
export { readFile } from "./readFile.js";
|
|
@@ -53,18 +53,24 @@ export type FileInputError = ErrorW<{
|
|
|
53
53
|
isValidMimeType: boolean;
|
|
54
54
|
isValidExtension: boolean;
|
|
55
55
|
}>;
|
|
56
|
+
/** h 0-360+ (deg), s 0-100%, v 0-100%, a 0-1 */
|
|
56
57
|
export type HsvaColor = {
|
|
57
58
|
h: number;
|
|
58
59
|
s: number;
|
|
59
60
|
v: number;
|
|
60
61
|
a?: number;
|
|
61
62
|
};
|
|
63
|
+
/** rgb 0-255, 0-1 for alpha */
|
|
62
64
|
export type RgbaColor = {
|
|
63
65
|
r: number;
|
|
64
66
|
g: number;
|
|
65
67
|
b: number;
|
|
66
68
|
a?: number;
|
|
67
69
|
};
|
|
70
|
+
export type Point = {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
};
|
|
68
74
|
export type ScrollNearContainerEdgesOptions = {
|
|
69
75
|
containerEl: Ref<HTMLElement | null>;
|
|
70
76
|
/** Margin inside contianer that allows scrolling. 10 by default. */
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifies the user if the given value is an error. Useful for making non-critical errors don't go unnoticed.
|
|
3
|
+
*
|
|
4
|
+
* Integrates with {@link TypedError} from `@alanscodelog/utils` to display the error code as well if it has one.
|
|
5
|
+
*
|
|
6
|
+
* If the value is not an error, it is returned.
|
|
7
|
+
*/
|
|
8
|
+
export declare function notifyIfError<T>(err: T, { logger, ns, force, }?: {
|
|
9
|
+
logger?: {
|
|
10
|
+
debug: (...args: any[]) => void;
|
|
11
|
+
};
|
|
12
|
+
ns?: string;
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}): T;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TypedError } from "@alanscodelog/utils/TypedError.js";
|
|
2
|
+
import { useNotificationHandler } from "../composables/useNotificationHandler.js";
|
|
3
|
+
export function notifyIfError(err, {
|
|
4
|
+
logger,
|
|
5
|
+
ns,
|
|
6
|
+
force = false
|
|
7
|
+
} = {}) {
|
|
8
|
+
if (force || err instanceof Error) {
|
|
9
|
+
const errMessage = {
|
|
10
|
+
message: err instanceof Error ? err.message : `Unknown error ${err}`,
|
|
11
|
+
code: err instanceof TypedError ? err.code : void 0
|
|
12
|
+
};
|
|
13
|
+
if (logger) {
|
|
14
|
+
logger.debug({
|
|
15
|
+
ns,
|
|
16
|
+
...errMessage,
|
|
17
|
+
stack: err instanceof Error ? err.stack : void 0
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
void useNotificationHandler().notify({
|
|
21
|
+
title: "Error",
|
|
22
|
+
...errMessage,
|
|
23
|
+
options: ["Ok"],
|
|
24
|
+
cancellable: "Ok",
|
|
25
|
+
timeout: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return err;
|
|
29
|
+
}
|