@witchcraft/ui 0.0.1
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 +236 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +34 -0
- package/dist/module.d.ts +34 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +124 -0
- package/dist/runtime/assets/base.css +1 -0
- package/dist/runtime/assets/locales/en.json +33 -0
- package/dist/runtime/assets/style.css +1 -0
- package/dist/runtime/assets/tailwind.css +1 -0
- package/dist/runtime/assets/theme.css +1 -0
- package/dist/runtime/build/WitchcraftUiResolver.d.ts +5 -0
- package/dist/runtime/build/WitchcraftUiResolver.js +17 -0
- package/dist/runtime/build/generateTheme.d.ts +1 -0
- package/dist/runtime/build/generateTheme.js +14 -0
- package/dist/runtime/build/unpluginIconViteOptions.d.ts +2 -0
- package/dist/runtime/build/unpluginIconViteOptions.js +10 -0
- package/dist/runtime/components/Aria/Aria.vue +18 -0
- package/dist/runtime/components/Focus.stories.d.ts +11 -0
- package/dist/runtime/components/Focus.stories.js +53 -0
- package/dist/runtime/components/Icon/Icon.vue +39 -0
- package/dist/runtime/components/LibButton/LibButton.stories.d.ts +12 -0
- package/dist/runtime/components/LibButton/LibButton.stories.js +94 -0
- package/dist/runtime/components/LibButton/LibButton.vue +247 -0
- package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.d.ts +14 -0
- package/dist/runtime/components/LibCheckbox/LibCheckbox.stories.js +29 -0
- package/dist/runtime/components/LibCheckbox/LibCheckbox.vue +132 -0
- package/dist/runtime/components/LibColorInput/LibColorInput.stories.d.ts +7 -0
- package/dist/runtime/components/LibColorInput/LibColorInput.stories.js +58 -0
- package/dist/runtime/components/LibColorInput/LibColorInput.vue +125 -0
- package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.d.ts +7 -0
- package/dist/runtime/components/LibColorPicker/LibColorPicker.stories.js +51 -0
- package/dist/runtime/components/LibColorPicker/LibColorPicker.vue +448 -0
- package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.d.ts +7 -0
- package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.js +36 -0
- package/dist/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +95 -0
- package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.d.ts +11 -0
- package/dist/runtime/components/LibDatePicker/LibDatePicker.stories.js +98 -0
- package/dist/runtime/components/LibDatePicker/LibDatePicker.vue +67 -0
- package/dist/runtime/components/LibDatePicker/LibRangeDatePicker.vue +370 -0
- package/dist/runtime/components/LibDatePicker/LibSingleDatePicker.vue +314 -0
- package/dist/runtime/components/LibDatePicker/LibTimeZonePicker.vue +27 -0
- package/dist/runtime/components/LibDatePicker/helpers.d.ts +25 -0
- package/dist/runtime/components/LibDatePicker/helpers.js +28 -0
- package/dist/runtime/components/LibDebug/LibDebug.stories.d.ts +9 -0
- package/dist/runtime/components/LibDebug/LibDebug.stories.js +46 -0
- package/dist/runtime/components/LibDebug/LibDebug.vue +91 -0
- package/dist/runtime/components/LibDevOnly/LibDevOnly.vue +53 -0
- package/dist/runtime/components/LibFileInput/LibFileInput.stories.d.ts +10 -0
- package/dist/runtime/components/LibFileInput/LibFileInput.stories.js +63 -0
- package/dist/runtime/components/LibFileInput/LibFileInput.vue +273 -0
- package/dist/runtime/components/LibInput/LibInput.stories.d.ts +33 -0
- package/dist/runtime/components/LibInput/LibInput.stories.js +339 -0
- package/dist/runtime/components/LibInput/LibInput.vue +372 -0
- package/dist/runtime/components/LibLabel/LibLabel.stories.d.ts +6 -0
- package/dist/runtime/components/LibLabel/LibLabel.stories.js +25 -0
- package/dist/runtime/components/LibLabel/LibLabel.vue +66 -0
- package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.d.ts +23 -0
- package/dist/runtime/components/LibMultiValues/LibMultiValues.stories.js +60 -0
- package/dist/runtime/components/LibMultiValues/LibMultiValues.vue +127 -0
- package/dist/runtime/components/LibNotifications/LibNotification.stories.d.ts +15 -0
- package/dist/runtime/components/LibNotifications/LibNotification.stories.js +126 -0
- package/dist/runtime/components/LibNotifications/LibNotification.vue +121 -0
- package/dist/runtime/components/LibNotifications/LibNotifications.stories.d.ts +6 -0
- package/dist/runtime/components/LibNotifications/LibNotifications.stories.js +109 -0
- package/dist/runtime/components/LibNotifications/LibNotifications.vue +133 -0
- package/dist/runtime/components/LibPagination/LibPagination.stories.d.ts +6 -0
- package/dist/runtime/components/LibPagination/LibPagination.stories.js +40 -0
- package/dist/runtime/components/LibPagination/LibPagination.vue +261 -0
- package/dist/runtime/components/LibPalette/LibPalette.stories.d.ts +6 -0
- package/dist/runtime/components/LibPalette/LibPalette.stories.js +20 -0
- package/dist/runtime/components/LibPalette/LibPalette.vue +49 -0
- package/dist/runtime/components/LibPopup/LibPopup.stories.d.ts +14 -0
- package/dist/runtime/components/LibPopup/LibPopup.stories.js +147 -0
- package/dist/runtime/components/LibPopup/LibPopup.vue +441 -0
- package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.d.ts +10 -0
- package/dist/runtime/components/LibProgressBar/LibProgressBar.stories.js +81 -0
- package/dist/runtime/components/LibProgressBar/LibProgressBar.vue +192 -0
- package/dist/runtime/components/LibRecorder/LibRecorder.stories.d.ts +19 -0
- package/dist/runtime/components/LibRecorder/LibRecorder.stories.js +63 -0
- package/dist/runtime/components/LibRecorder/LibRecorder.vue +243 -0
- package/dist/runtime/components/LibRoot/LibRoot.vue +126 -0
- package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.d.ts +26 -0
- package/dist/runtime/components/LibSimpleInput/LibSimpleInput.stories.js +78 -0
- package/dist/runtime/components/LibSimpleInput/LibSimpleInput.vue +148 -0
- package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.d.ts +27 -0
- package/dist/runtime/components/LibSuggestions/LibSuggestions.stories.js +112 -0
- package/dist/runtime/components/LibSuggestions/LibSuggestions.vue +198 -0
- package/dist/runtime/components/LibTable/LibTable.stories.d.ts +16 -0
- package/dist/runtime/components/LibTable/LibTable.stories.js +156 -0
- package/dist/runtime/components/LibTable/LibTable.vue +177 -0
- package/dist/runtime/components/Template/NAME.vue +49 -0
- package/dist/runtime/components/Template/TemplateStory.d.ts +7 -0
- package/dist/runtime/components/Template/TemplateStory.js +22 -0
- package/dist/runtime/components/TestControls/TestControls.vue +19 -0
- package/dist/runtime/components/index.d.ts +19 -0
- package/dist/runtime/components/index.js +19 -0
- package/dist/runtime/components/reset.stories.d.ts +5 -0
- package/dist/runtime/components/reset.stories.js +19 -0
- package/dist/runtime/components/shared/props.d.ts +135 -0
- package/dist/runtime/components/shared/props.js +14 -0
- package/dist/runtime/components/shared/storyHelpers/playInput.d.ts +8 -0
- package/dist/runtime/components/shared/storyHelpers/playInput.js +26 -0
- package/dist/runtime/components/shared/storyHelpers/playSuggestions.d.ts +12 -0
- package/dist/runtime/components/shared/storyHelpers/playSuggestions.js +83 -0
- package/dist/runtime/composables/index.d.ts +11 -0
- package/dist/runtime/composables/index.js +11 -0
- package/dist/runtime/composables/useAccesibilityOutline.d.ts +41 -0
- package/dist/runtime/composables/useAccesibilityOutline.js +58 -0
- package/dist/runtime/composables/useAriaLabel.d.ts +6 -0
- package/dist/runtime/composables/useAriaLabel.js +15 -0
- package/dist/runtime/composables/useDarkMode.d.ts +38 -0
- package/dist/runtime/composables/useDarkMode.js +79 -0
- package/dist/runtime/composables/useDivideAttrs.d.ts +27 -0
- package/dist/runtime/composables/useDivideAttrs.js +26 -0
- package/dist/runtime/composables/useGlobalResizeObserver.d.ts +3 -0
- package/dist/runtime/composables/useGlobalResizeObserver.js +28 -0
- package/dist/runtime/composables/useInjectedDarkMode.d.ts +2 -0
- package/dist/runtime/composables/useInjectedDarkMode.js +13 -0
- package/dist/runtime/composables/useInjectedI18n.d.ts +2 -0
- package/dist/runtime/composables/useInjectedI18n.js +7 -0
- package/dist/runtime/composables/useInjectedLocale.d.ts +2 -0
- package/dist/runtime/composables/useInjectedLocale.js +21 -0
- package/dist/runtime/composables/useNotificationHandler.d.ts +4 -0
- package/dist/runtime/composables/useNotificationHandler.js +21 -0
- package/dist/runtime/composables/useScrollNearContainerEdges.d.ts +68 -0
- package/dist/runtime/composables/useScrollNearContainerEdges.js +116 -0
- package/dist/runtime/composables/useScrollNearContainerEdges.stories.d.ts +7 -0
- package/dist/runtime/composables/useScrollNearContainerEdges.stories.js +85 -0
- package/dist/runtime/composables/useSetupDarkMode.d.ts +12 -0
- package/dist/runtime/composables/useSetupDarkMode.js +4 -0
- package/dist/runtime/composables/useSetupI18n.d.ts +20 -0
- package/dist/runtime/composables/useSetupI18n.js +50 -0
- package/dist/runtime/composables/useSetupLocale.d.ts +9 -0
- package/dist/runtime/composables/useSetupLocale.js +21 -0
- package/dist/runtime/composables/useShowDevOnlyKey.d.ts +7 -0
- package/dist/runtime/composables/useShowDevOnlyKey.js +20 -0
- package/dist/runtime/composables/useSuggestions.d.ts +38 -0
- package/dist/runtime/composables/useSuggestions.js +226 -0
- package/dist/runtime/directives/index.d.ts +4 -0
- package/dist/runtime/directives/index.js +4 -0
- package/dist/runtime/directives/vDetectFlex.d.ts +2 -0
- package/dist/runtime/directives/vDetectFlex.js +109 -0
- package/dist/runtime/directives/vExtractRootEl.d.ts +22 -0
- package/dist/runtime/directives/vExtractRootEl.js +13 -0
- package/dist/runtime/directives/vResizableCols.d.ts +60 -0
- package/dist/runtime/directives/vResizableCols.js +252 -0
- package/dist/runtime/directives/vResizeObserver.d.ts +2 -0
- package/dist/runtime/directives/vResizeObserver.js +34 -0
- package/dist/runtime/globalResizeObserver.d.ts +5 -0
- package/dist/runtime/globalResizeObserver.js +5 -0
- package/dist/runtime/helpers/NotificationHandler.d.ts +48 -0
- package/dist/runtime/helpers/NotificationHandler.js +162 -0
- package/dist/runtime/helpers/addValue.d.ts +1 -0
- package/dist/runtime/helpers/addValue.js +8 -0
- package/dist/runtime/helpers/base64ToImg.d.ts +1 -0
- package/dist/runtime/helpers/base64ToImg.js +11 -0
- package/dist/runtime/helpers/copy.d.ts +1 -0
- package/dist/runtime/helpers/copy.js +10 -0
- package/dist/runtime/helpers/createNoonUtcDate.d.ts +7 -0
- package/dist/runtime/helpers/createNoonUtcDate.js +14 -0
- package/dist/runtime/helpers/defaultTranslationFunction.d.ts +16 -0
- package/dist/runtime/helpers/defaultTranslationFunction.js +14 -0
- package/dist/runtime/helpers/getTimeZoneList.d.ts +1 -0
- package/dist/runtime/helpers/getTimeZoneList.js +3 -0
- package/dist/runtime/helpers/hasModifiers.d.ts +1 -0
- package/dist/runtime/helpers/hasModifiers.js +1 -0
- package/dist/runtime/helpers/index.d.ts +8 -0
- package/dist/runtime/helpers/index.js +8 -0
- package/dist/runtime/helpers/readFile.d.ts +1 -0
- package/dist/runtime/helpers/readFile.js +13 -0
- package/dist/runtime/helpers/resizeObserverWrapper.d.ts +8 -0
- package/dist/runtime/helpers/resizeObserverWrapper.js +37 -0
- package/dist/runtime/helpers/storybook.d.ts +7 -0
- package/dist/runtime/helpers/storybook.js +42 -0
- package/dist/runtime/main.lib.d.ts +26 -0
- package/dist/runtime/main.lib.js +8 -0
- package/dist/runtime/nuxt/plugins/vue-plugin.d.ts +2 -0
- package/dist/runtime/nuxt/plugins/vue-plugin.js +12 -0
- package/dist/runtime/tailwind/index.d.ts +1 -0
- package/dist/runtime/tailwind/index.js +1 -0
- package/dist/runtime/tailwind/themeConvertionOpts.d.ts +2 -0
- package/dist/runtime/tailwind/themeConvertionOpts.js +12 -0
- package/dist/runtime/theme.d.ts +2 -0
- package/dist/runtime/theme.js +2 -0
- package/dist/runtime/types/index.d.ts +119 -0
- package/dist/runtime/types/index.js +0 -0
- package/dist/runtime/utils/twMerge.d.ts +10 -0
- package/dist/runtime/utils/twMerge.js +10 -0
- package/dist/runtime/vue/VueComponentsPlugin.d.ts +2 -0
- package/dist/runtime/vue/VueComponentsPlugin.js +10 -0
- package/dist/runtime/vue/registerComponents.d.ts +19 -0
- package/dist/runtime/vue/registerComponents.js +10 -0
- package/dist/runtime/vue/registerDirectives.d.ts +3 -0
- package/dist/runtime/vue/registerDirectives.js +9 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +7 -0
- package/package.json +207 -0
- package/src/module.ts +176 -0
- package/src/runtime/assets/base.css +67 -0
- package/src/runtime/assets/locales/en.json +33 -0
- package/src/runtime/assets/style.css +144 -0
- package/src/runtime/assets/tailwind.css +5 -0
- package/src/runtime/assets/theme.css +65 -0
- package/src/runtime/build/WitchcraftUiResolver.ts +27 -0
- package/src/runtime/build/generateTheme.ts +16 -0
- package/src/runtime/build/unpluginIconViteOptions.ts +11 -0
- package/src/runtime/components/Aria/Aria.vue +27 -0
- package/src/runtime/components/Focus.stories.ts +67 -0
- package/src/runtime/components/Icon/Icon.vue +39 -0
- package/src/runtime/components/LibButton/LibButton.stories.ts +107 -0
- package/src/runtime/components/LibButton/LibButton.vue +247 -0
- package/src/runtime/components/LibCheckbox/LibCheckbox.stories.ts +41 -0
- package/src/runtime/components/LibCheckbox/LibCheckbox.vue +132 -0
- package/src/runtime/components/LibColorInput/LibColorInput.stories.ts +69 -0
- package/src/runtime/components/LibColorInput/LibColorInput.vue +125 -0
- package/src/runtime/components/LibColorPicker/LibColorPicker.stories.ts +60 -0
- package/src/runtime/components/LibColorPicker/LibColorPicker.vue +448 -0
- package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.stories.ts +51 -0
- package/src/runtime/components/LibDarkModeSwitcher/LibDarkModeSwitcher.vue +95 -0
- package/src/runtime/components/LibDatePicker/LibDatePicker.stories.ts +114 -0
- package/src/runtime/components/LibDatePicker/LibDatePicker.vue +67 -0
- package/src/runtime/components/LibDatePicker/LibRangeDatePicker.vue +370 -0
- package/src/runtime/components/LibDatePicker/LibSingleDatePicker.vue +314 -0
- package/src/runtime/components/LibDatePicker/LibTimeZonePicker.vue +27 -0
- package/src/runtime/components/LibDatePicker/helpers.ts +55 -0
- package/src/runtime/components/LibDebug/LibDebug.stories.ts +58 -0
- package/src/runtime/components/LibDebug/LibDebug.vue +91 -0
- package/src/runtime/components/LibDevOnly/LibDevOnly.vue +53 -0
- package/src/runtime/components/LibFileInput/LibFileInput.stories.ts +79 -0
- package/src/runtime/components/LibFileInput/LibFileInput.vue +273 -0
- package/src/runtime/components/LibInput/LibInput.stories.ts +367 -0
- package/src/runtime/components/LibInput/LibInput.vue +372 -0
- package/src/runtime/components/LibLabel/LibLabel.stories.ts +37 -0
- package/src/runtime/components/LibLabel/LibLabel.vue +66 -0
- package/src/runtime/components/LibMultiValues/LibMultiValues.stories.ts +83 -0
- package/src/runtime/components/LibMultiValues/LibMultiValues.vue +127 -0
- package/src/runtime/components/LibNotifications/LibNotification.stories.ts +142 -0
- package/src/runtime/components/LibNotifications/LibNotification.vue +121 -0
- package/src/runtime/components/LibNotifications/LibNotifications.stories.ts +124 -0
- package/src/runtime/components/LibNotifications/LibNotifications.vue +133 -0
- package/src/runtime/components/LibPagination/LibPagination.stories.ts +53 -0
- package/src/runtime/components/LibPagination/LibPagination.vue +261 -0
- package/src/runtime/components/LibPalette/LibPalette.stories.ts +32 -0
- package/src/runtime/components/LibPalette/LibPalette.vue +49 -0
- package/src/runtime/components/LibPopup/LibPopup.stories.ts +157 -0
- package/src/runtime/components/LibPopup/LibPopup.vue +441 -0
- package/src/runtime/components/LibProgressBar/LibProgressBar.stories.ts +94 -0
- package/src/runtime/components/LibProgressBar/LibProgressBar.vue +192 -0
- package/src/runtime/components/LibRecorder/LibRecorder.stories.ts +81 -0
- package/src/runtime/components/LibRecorder/LibRecorder.vue +243 -0
- package/src/runtime/components/LibRoot/LibRoot.vue +126 -0
- package/src/runtime/components/LibSimpleInput/LibSimpleInput.stories.ts +98 -0
- package/src/runtime/components/LibSimpleInput/LibSimpleInput.vue +148 -0
- package/src/runtime/components/LibSuggestions/LibSuggestions.stories.ts +137 -0
- package/src/runtime/components/LibSuggestions/LibSuggestions.vue +198 -0
- package/src/runtime/components/LibTable/LibTable.stories.ts +170 -0
- package/src/runtime/components/LibTable/LibTable.vue +177 -0
- package/src/runtime/components/Template/NAME.vue +49 -0
- package/src/runtime/components/Template/TemplateStory.ts +38 -0
- package/src/runtime/components/TestControls/TestControls.vue +19 -0
- package/src/runtime/components/index.ts +22 -0
- package/src/runtime/components/reset.stories.ts +32 -0
- package/src/runtime/components/shared/props.ts +142 -0
- package/src/runtime/components/shared/storyHelpers/playInput.ts +35 -0
- package/src/runtime/components/shared/storyHelpers/playSuggestions.ts +105 -0
- package/src/runtime/composables/index.ts +13 -0
- package/src/runtime/composables/useAccesibilityOutline.ts +104 -0
- package/src/runtime/composables/useAriaLabel.ts +23 -0
- package/src/runtime/composables/useDarkMode.ts +146 -0
- package/src/runtime/composables/useDivideAttrs.ts +52 -0
- package/src/runtime/composables/useGlobalResizeObserver.ts +33 -0
- package/src/runtime/composables/useInjectedDarkMode.ts +15 -0
- package/src/runtime/composables/useInjectedI18n.ts +10 -0
- package/src/runtime/composables/useInjectedLocale.ts +24 -0
- package/src/runtime/composables/useNotificationHandler.ts +32 -0
- package/src/runtime/composables/useScrollNearContainerEdges.stories.ts +93 -0
- package/src/runtime/composables/useScrollNearContainerEdges.ts +205 -0
- package/src/runtime/composables/useSetupDarkMode.ts +14 -0
- package/src/runtime/composables/useSetupI18n.ts +77 -0
- package/src/runtime/composables/useSetupLocale.ts +32 -0
- package/src/runtime/composables/useShowDevOnlyKey.ts +28 -0
- package/src/runtime/composables/useSuggestions.ts +297 -0
- package/src/runtime/directives/index.ts +6 -0
- package/src/runtime/directives/vDetectFlex.ts +159 -0
- package/src/runtime/directives/vExtractRootEl.ts +38 -0
- package/src/runtime/directives/vResizableCols.ts +378 -0
- package/src/runtime/directives/vResizeObserver.ts +45 -0
- package/src/runtime/globalResizeObserver.ts +12 -0
- package/src/runtime/helpers/NotificationHandler.ts +227 -0
- package/src/runtime/helpers/addValue.ts +10 -0
- package/src/runtime/helpers/base64ToImg.ts +14 -0
- package/src/runtime/helpers/copy.ts +11 -0
- package/src/runtime/helpers/createNoonUtcDate.ts +21 -0
- package/src/runtime/helpers/defaultTranslationFunction.ts +33 -0
- package/src/runtime/helpers/getTimeZoneList.ts +4 -0
- package/src/runtime/helpers/hasModifiers.ts +1 -0
- package/src/runtime/helpers/index.ts +10 -0
- package/src/runtime/helpers/readFile.ts +22 -0
- package/src/runtime/helpers/resizeObserverWrapper.ts +45 -0
- package/src/runtime/helpers/storybook.ts +52 -0
- package/src/runtime/main.lib.ts +31 -0
- package/src/runtime/nuxt/plugins/vue-plugin.ts +19 -0
- package/src/runtime/tailwind/index.ts +3 -0
- package/src/runtime/tailwind/themeConvertionOpts.ts +15 -0
- package/src/runtime/theme.ts +5 -0
- package/src/runtime/types/index.ts +116 -0
- package/src/runtime/utils/twMerge.ts +13 -0
- package/src/runtime/vue/VueComponentsPlugin.ts +16 -0
- package/src/runtime/vue/registerComponents.ts +31 -0
- package/src/runtime/vue/registerDirectives.ts +12 -0
- package/types/components.d.ts +27 -0
- package/types/global.d.ts +16 -0
- package/types/index.d.ts +5 -0
- package/types/vite.d.ts +2 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { expect, userEvent, within } from "@storybook/test";
|
|
2
|
+
export const playBasicVModel = async ({ canvasElement, args }) => {
|
|
3
|
+
const canvas = within(canvasElement);
|
|
4
|
+
const input = canvas.getByLabelText(args.label ?? "", { selector: "input" });
|
|
5
|
+
await userEvent.type(input, "A");
|
|
6
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("A");
|
|
7
|
+
await userEvent.clear(input);
|
|
8
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("");
|
|
9
|
+
};
|
|
10
|
+
export const playMultipleValues = async ({ canvasElement, args }) => {
|
|
11
|
+
const initialValues = [...args.values ?? []];
|
|
12
|
+
const canvas = within(canvasElement);
|
|
13
|
+
const input = canvas.getByLabelText(args.label ?? "", { selector: "input" });
|
|
14
|
+
await userEvent.type(input, "A");
|
|
15
|
+
await expect(canvas.getByTestId("model-value")).toHaveTextContent("A");
|
|
16
|
+
await userEvent.keyboard("{Enter}");
|
|
17
|
+
await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A"].join(", "));
|
|
18
|
+
await expect(canvas.getByTestId("model-value")).toBeEmptyDOMElement();
|
|
19
|
+
await userEvent.type(input, "B");
|
|
20
|
+
await expect(canvas.getByTestId("model-value")).toHaveTextContent("B");
|
|
21
|
+
await userEvent.keyboard("{Enter}");
|
|
22
|
+
await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A", "B"].join(", "));
|
|
23
|
+
await userEvent.clear(input);
|
|
24
|
+
await userEvent.keyboard("{Enter}");
|
|
25
|
+
await expect(canvas.getByTestId("values").textContent).toBe([...initialValues, "A", "B"].join(", "));
|
|
26
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const playBasicSelect: ({ canvasElement, args }: {
|
|
2
|
+
canvasElement: HTMLElement;
|
|
3
|
+
args: any;
|
|
4
|
+
}) => Promise<void>;
|
|
5
|
+
export declare const playBasicKeyboardSelect: ({ canvasElement, args }: {
|
|
6
|
+
canvasElement: HTMLElement;
|
|
7
|
+
args: any;
|
|
8
|
+
}) => Promise<void>;
|
|
9
|
+
export declare const playBasicClickSelect: ({ canvasElement, args }: {
|
|
10
|
+
canvasElement: HTMLElement;
|
|
11
|
+
args: any;
|
|
12
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { expect, userEvent, within } from "@storybook/test";
|
|
2
|
+
export const playBasicSelect = async ({ canvasElement, args }) => {
|
|
3
|
+
const canvas = within(canvasElement);
|
|
4
|
+
const input = canvas.getByLabelText(args.label ?? "", { selector: "input" });
|
|
5
|
+
await userEvent.type(input, "A");
|
|
6
|
+
await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
7
|
+
await userEvent.type(input, "BCDE");
|
|
8
|
+
await expect(canvas.queryByRole("option", { name: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", selected: true })).toBeInTheDocument();
|
|
9
|
+
await userEvent.keyboard("{Enter}");
|
|
10
|
+
if (args.values !== void 0) {
|
|
11
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("");
|
|
12
|
+
await expect(canvas.queryByRole("listbox")).not.toBeNull();
|
|
13
|
+
} else {
|
|
14
|
+
await expect(canvas.getByTestId("model-value")).toHaveTextContent("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
15
|
+
await expect(canvas.queryByRole("listbox")).toBeNull();
|
|
16
|
+
}
|
|
17
|
+
await userEvent.keyboard("{Backspace}");
|
|
18
|
+
await expect(canvas.queryByRole("option", { selected: true })).toBeInTheDocument();
|
|
19
|
+
await userEvent.clear(input);
|
|
20
|
+
await userEvent.type(input, "unmatched");
|
|
21
|
+
if (!args.suggestionsFilter) {
|
|
22
|
+
await expect(canvas.queryAllByRole("option", { selected: true })).toEqual([]);
|
|
23
|
+
}
|
|
24
|
+
await userEvent.clear(input);
|
|
25
|
+
await expect(await canvas.findByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
26
|
+
if (args.restrictToSuggestions && args.values === void 0) {
|
|
27
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
|
28
|
+
}
|
|
29
|
+
await userEvent.keyboard("{Enter}");
|
|
30
|
+
if (args.values === void 0) {
|
|
31
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("A");
|
|
32
|
+
}
|
|
33
|
+
await userEvent.clear(input);
|
|
34
|
+
await userEvent.keyboard("AB{Escape}");
|
|
35
|
+
await expect(canvas.queryByRole("listbox")).toBeNull();
|
|
36
|
+
if (args.values === void 0) {
|
|
37
|
+
if (args.restrictToSuggestions) {
|
|
38
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("A");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
export const playBasicKeyboardSelect = async ({ canvasElement, args }) => {
|
|
43
|
+
const canvas = within(canvasElement);
|
|
44
|
+
const input = canvas.getByLabelText(args.label ?? "", { selector: "input" });
|
|
45
|
+
await userEvent.clear(input);
|
|
46
|
+
await userEvent.type(input, "A");
|
|
47
|
+
await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
48
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
49
|
+
await expect(canvas.queryByRole("option", { name: "AB", selected: true })).toBeInTheDocument();
|
|
50
|
+
await userEvent.keyboard("{ArrowUp}");
|
|
51
|
+
await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
52
|
+
await userEvent.keyboard("{ArrowUp}");
|
|
53
|
+
await expect(canvas.queryByRole("option", { name: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", selected: true })).toBeInTheDocument();
|
|
54
|
+
await userEvent.keyboard("{ArrowDown}");
|
|
55
|
+
await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
56
|
+
await userEvent.keyboard("{PageDown}");
|
|
57
|
+
await expect(canvas.queryByRole("option", { name: "ABCDEFGHIJKLMNOPQRSTUVWXYZ", selected: true })).toBeInTheDocument();
|
|
58
|
+
await userEvent.keyboard("{PageUp}");
|
|
59
|
+
await expect(canvas.queryByRole("option", { name: "A", selected: true })).toBeInTheDocument();
|
|
60
|
+
const testOpen = async (key) => {
|
|
61
|
+
await userEvent.keyboard("{Escape}");
|
|
62
|
+
await expect(canvas.queryByRole("listbox")).toBeNull();
|
|
63
|
+
await userEvent.keyboard(`{${key}}`);
|
|
64
|
+
await expect(canvas.queryByRole("listbox")).toBeInTheDocument();
|
|
65
|
+
};
|
|
66
|
+
await testOpen("ArrowDown");
|
|
67
|
+
await testOpen("ArrowUp");
|
|
68
|
+
await testOpen("PageDown");
|
|
69
|
+
await testOpen("PageUp");
|
|
70
|
+
};
|
|
71
|
+
export const playBasicClickSelect = async ({ canvasElement, args }) => {
|
|
72
|
+
const canvas = within(canvasElement);
|
|
73
|
+
const input = canvas.getByLabelText(args.label ?? "", { selector: "input" });
|
|
74
|
+
await userEvent.clear(input);
|
|
75
|
+
await userEvent.type(input, "A");
|
|
76
|
+
await userEvent.click(canvas.getByRole("option", { name: "AB" }));
|
|
77
|
+
if (args.values === void 0) {
|
|
78
|
+
await expect(canvas.getByTestId("model-value").textContent).toBe("AB");
|
|
79
|
+
await expect(canvas.queryByRole("listbox")).toBeNull();
|
|
80
|
+
} else {
|
|
81
|
+
await expect(canvas.getByTestId("values")).toHaveTextContent(/AB$/);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { useAccesibilityOutline } from "./useAccesibilityOutline.js.js";
|
|
2
|
+
export { useAriaLabel } from "./useAriaLabel.js.js";
|
|
3
|
+
export { useDarkMode } from "./useDarkMode.js.js";
|
|
4
|
+
export { useDivideAttrs } from "./useDivideAttrs.js.js";
|
|
5
|
+
export { useGlobalResizeObserver } from "./useGlobalResizeObserver.js.js";
|
|
6
|
+
export { useInjectedDarkMode } from "./useInjectedDarkMode.js.js";
|
|
7
|
+
export { useNotificationHandler } from "./useNotificationHandler.js.js";
|
|
8
|
+
export { useScrollNearContainerEdges } from "./useScrollNearContainerEdges.js.js";
|
|
9
|
+
export { useSetupDarkMode } from "./useSetupDarkMode.js.js";
|
|
10
|
+
export { useShowDevOnlyKey } from "./useShowDevOnlyKey.js.js";
|
|
11
|
+
export { useSuggestions } from "./useSuggestions.js.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { useAccesibilityOutline } from "./useAccesibilityOutline.js";
|
|
2
|
+
export { useAriaLabel } from "./useAriaLabel.js";
|
|
3
|
+
export { useDarkMode } from "./useDarkMode.js";
|
|
4
|
+
export { useDivideAttrs } from "./useDivideAttrs.js";
|
|
5
|
+
export { useGlobalResizeObserver } from "./useGlobalResizeObserver.js";
|
|
6
|
+
export { useInjectedDarkMode } from "./useInjectedDarkMode.js";
|
|
7
|
+
export { useNotificationHandler } from "./useNotificationHandler.js";
|
|
8
|
+
export { useScrollNearContainerEdges } from "./useScrollNearContainerEdges.js";
|
|
9
|
+
export { useSetupDarkMode } from "./useSetupDarkMode.js";
|
|
10
|
+
export { useShowDevOnlyKey } from "./useShowDevOnlyKey.js";
|
|
11
|
+
export { useSuggestions } from "./useSuggestions.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a reactive ref that indicates whether the user presses a key *and* produced a change in focus. Used for only providing a `:focus` outline when the user is navigating via the keyboard.
|
|
4
|
+
*
|
|
5
|
+
* Can be optionally passed a reactive ref to enable/disabled the calculation (e.g. based on a user setting). This is returned back by the function in case you want to just use the control variable it auto creates. Event listeners are automatically attached/detached when this is changed.
|
|
6
|
+
*
|
|
7
|
+
* In setup:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const el = ref(null)
|
|
10
|
+
*
|
|
11
|
+
* const {outline, control: outlineControl} = useAccesibilityOutline(el)
|
|
12
|
+
* outlineControl.value = userSettings.outlineOnlyOnNavigation
|
|
13
|
+
* // or
|
|
14
|
+
* useAccesibilityOutline(el, userSettings.outlineOnlyOnNavigation)
|
|
15
|
+
*
|
|
16
|
+
* const classes = computed(() =>( {
|
|
17
|
+
* // if setting is false => always outline
|
|
18
|
+
* outline: !userSettings.outlineOnlyOnNavigation || outline.value
|
|
19
|
+
* }))
|
|
20
|
+
*
|
|
21
|
+
* return {
|
|
22
|
+
* classes,
|
|
23
|
+
* el
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* In the root app component. Note it requires the element have a tabindex.
|
|
28
|
+
* ```html
|
|
29
|
+
* <div id="app" :class="classes" ref="el" tabindex="-1"></div>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* In style of any subcomponents we can now target `:focus` only when the user is navigating with the keyboard.
|
|
33
|
+
*
|
|
34
|
+
* ```css
|
|
35
|
+
* .outline button:focus {
|
|
36
|
+
* ....
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
export declare function useAccesibilityOutline(target: Ref<HTMLElement | null>, enable?: Ref<boolean>): Record<"outline" | "control", Ref<boolean>>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { castType } from "@alanscodelog/utils/castType.js";
|
|
2
|
+
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
3
|
+
export function useAccesibilityOutline(target, enable = ref(true)) {
|
|
4
|
+
const outline = ref(false);
|
|
5
|
+
const awaitingFocus = ref(false);
|
|
6
|
+
const keydown = (_e) => {
|
|
7
|
+
awaitingFocus.value = true;
|
|
8
|
+
setTimeout(() => {
|
|
9
|
+
awaitingFocus.value = false;
|
|
10
|
+
}, 0);
|
|
11
|
+
};
|
|
12
|
+
const mousedown = (_e) => {
|
|
13
|
+
awaitingFocus.value = false;
|
|
14
|
+
outline.value = false;
|
|
15
|
+
};
|
|
16
|
+
const focusin = (_e) => {
|
|
17
|
+
if (awaitingFocus.value && enable.value) {
|
|
18
|
+
outline.value = true;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
let canAttach = false;
|
|
22
|
+
const attach = () => {
|
|
23
|
+
if (!canAttach) return;
|
|
24
|
+
castType(target.value);
|
|
25
|
+
if (!target.value) return;
|
|
26
|
+
target.value.addEventListener("focusin", focusin);
|
|
27
|
+
target.value.addEventListener("keydown", keydown);
|
|
28
|
+
target.value.addEventListener("mousedown", mousedown);
|
|
29
|
+
};
|
|
30
|
+
const detach = () => {
|
|
31
|
+
if (target.value) {
|
|
32
|
+
castType(target.value);
|
|
33
|
+
target.value.removeEventListener("focusin", focusin);
|
|
34
|
+
target.value.removeEventListener("keydown", keydown);
|
|
35
|
+
target.value.removeEventListener("mousedown", mousedown);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
watch(enable, (newVal, oldVal) => {
|
|
39
|
+
if (newVal === oldVal) return;
|
|
40
|
+
if (newVal) {
|
|
41
|
+
attach();
|
|
42
|
+
} else {
|
|
43
|
+
outline.value = false;
|
|
44
|
+
detach();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
onMounted(() => {
|
|
48
|
+
canAttach = true;
|
|
49
|
+
if (enable.value) {
|
|
50
|
+
attach();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
onBeforeUnmount(() => {
|
|
54
|
+
canAttach = false;
|
|
55
|
+
detach();
|
|
56
|
+
});
|
|
57
|
+
return { outline, control: enable };
|
|
58
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ComputedRef } from "vue";
|
|
2
|
+
/** Returns a computed ref that creates aria-label and aria-labelledby with the correct id for labelledby. */
|
|
3
|
+
export declare const useAriaLabel: (props: {
|
|
4
|
+
id?: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
}, fallbackId?: string) => ComputedRef<Record<string, string | undefined>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { computed, onMounted, ref } from "vue";
|
|
2
|
+
export const useAriaLabel = (props, fallbackId) => {
|
|
3
|
+
const id = computed(() => props.id ?? fallbackId);
|
|
4
|
+
const labelExists = ref(false);
|
|
5
|
+
const aria = computed(() => ({
|
|
6
|
+
"aria-label": labelExists.value ? void 0 : props.label,
|
|
7
|
+
"aria-labelledby": labelExists.value ? `label-${id.value}` : void 0
|
|
8
|
+
}));
|
|
9
|
+
onMounted(() => {
|
|
10
|
+
if (id.value && document.querySelector(`#label-${id.value}`)) {
|
|
11
|
+
labelExists.value = true;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return aria;
|
|
15
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type InjectionKey, type Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use `useSetupDarkMode` instead.
|
|
4
|
+
*
|
|
5
|
+
* A composable for setting up dark mode that automatically takes care of saving the user's preference.
|
|
6
|
+
*
|
|
7
|
+
* See the returned utilities for more details.
|
|
8
|
+
*
|
|
9
|
+
* It also provides injection keys with a ref so it can be accessed in deep nested components if needed. Alternatively you can use `useInjectedDarkMode` for ease of use.
|
|
10
|
+
*
|
|
11
|
+
* Note that this should only be called once at the root of the app.
|
|
12
|
+
*/
|
|
13
|
+
export declare const useDarkMode: ({ useLocalStorage, darkModeOrder, isClientSide }?: DarkModeOptions) => DarkModeState & DarkModeCommands;
|
|
14
|
+
export declare const defaultDarkModeOrder: readonly ["system", "dark", "light"];
|
|
15
|
+
export declare const isDarkModeInjectionKey: InjectionKey<Ref<boolean, boolean>>;
|
|
16
|
+
export declare const manualDarkModeInjectionKey: InjectionKey<Ref<boolean | undefined, boolean | undefined>>;
|
|
17
|
+
export declare const darkModeCommandsInjectionKey: InjectionKey<DarkModeCommands>;
|
|
18
|
+
export declare const darkModeStateInjectionKey: InjectionKey<DarkModeState>;
|
|
19
|
+
export type DarkModeOptions = {
|
|
20
|
+
useLocalStorage?: boolean | string;
|
|
21
|
+
darkModeOrder?: readonly ("system" | "dark" | "light")[];
|
|
22
|
+
/** True by default, should be passed import.meta.client if using nuxt, or false when running server side. */
|
|
23
|
+
isClientSide?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export interface DarkModeCommands {
|
|
26
|
+
setDarkMode: (value: "dark" | "light" | "system") => void;
|
|
27
|
+
cycleDarkMode: () => void;
|
|
28
|
+
}
|
|
29
|
+
export interface DarkModeState {
|
|
30
|
+
/** Whether the dark mode should be enabled or not */
|
|
31
|
+
darkMode: Ref<boolean>;
|
|
32
|
+
/** The current state of the darkMode but as a string (dark, light, system) */
|
|
33
|
+
darkModeState: Ref<"dark" | "light" | "system">;
|
|
34
|
+
/** The value of the manuably controllable dark mode. You can set this to true/false or undefined to allow the systemDarkMode to take priority. Alternatively use setDarkMode instead. */
|
|
35
|
+
manualDarkMode: Ref<boolean | undefined>;
|
|
36
|
+
/** The value of the system dark mode. This is automatically set depending on the user's `prefer-color-scheme` and should not be set directly. */
|
|
37
|
+
systemDarkMode: Ref<boolean>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { computed, onMounted, provide, ref, watch } from "vue";
|
|
2
|
+
const defaultLocalStorageKey = "prefersColorSchemeDark";
|
|
3
|
+
const defaultOrder = ["system", "dark", "light"];
|
|
4
|
+
const injectionKey = Symbol("isDarkMode");
|
|
5
|
+
const manualInjectionKey = Symbol("manualDarkMode");
|
|
6
|
+
const commandsInjectionKey = Symbol("darkModeCommands");
|
|
7
|
+
const stateInjectionKey = Symbol("darkModeState");
|
|
8
|
+
export const useDarkMode = ({
|
|
9
|
+
useLocalStorage = true,
|
|
10
|
+
darkModeOrder = defaultOrder,
|
|
11
|
+
/** True by default, should be passed import.meta.client if using nuxt, or false when running server side. */
|
|
12
|
+
isClientSide = true
|
|
13
|
+
} = {}) => {
|
|
14
|
+
const systemDarkMode = ref(false);
|
|
15
|
+
const manualDarkMode = ref(void 0);
|
|
16
|
+
if (useLocalStorage && isClientSide) {
|
|
17
|
+
watch(manualDarkMode, () => {
|
|
18
|
+
localStorage.setItem(defaultLocalStorageKey, manualDarkMode.value ? "true" : "false");
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const darkMode = computed(() => manualDarkMode.value ?? systemDarkMode.value);
|
|
22
|
+
const darkModeState = computed(
|
|
23
|
+
() => manualDarkMode.value === void 0 ? "system" : manualDarkMode.value ? "dark" : "light"
|
|
24
|
+
);
|
|
25
|
+
function setDarkMode(value) {
|
|
26
|
+
manualDarkMode.value = value === "dark" ? true : value === "light" ? false : void 0;
|
|
27
|
+
}
|
|
28
|
+
function cycleDarkMode() {
|
|
29
|
+
const index = darkModeOrder.indexOf(darkModeState.value);
|
|
30
|
+
if (index === 2) {
|
|
31
|
+
setDarkMode(darkModeOrder[0]);
|
|
32
|
+
} else {
|
|
33
|
+
setDarkMode(darkModeOrder[index + 1]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
onMounted(() => {
|
|
37
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches }) => {
|
|
38
|
+
if (matches) {
|
|
39
|
+
systemDarkMode.value = true;
|
|
40
|
+
} else {
|
|
41
|
+
systemDarkMode.value = false;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (useLocalStorage !== false) {
|
|
45
|
+
const key = typeof useLocalStorage === "string" ? useLocalStorage : defaultLocalStorageKey;
|
|
46
|
+
const value = localStorage.getItem(key);
|
|
47
|
+
if (value === "true") {
|
|
48
|
+
manualDarkMode.value = true;
|
|
49
|
+
} else if (value === "false") {
|
|
50
|
+
manualDarkMode.value = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
provide(injectionKey, darkMode);
|
|
55
|
+
provide(manualInjectionKey, manualDarkMode);
|
|
56
|
+
provide(stateInjectionKey, {
|
|
57
|
+
darkMode,
|
|
58
|
+
darkModeState,
|
|
59
|
+
manualDarkMode,
|
|
60
|
+
systemDarkMode
|
|
61
|
+
});
|
|
62
|
+
provide(commandsInjectionKey, {
|
|
63
|
+
setDarkMode,
|
|
64
|
+
cycleDarkMode
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
darkMode,
|
|
68
|
+
darkModeState,
|
|
69
|
+
setDarkMode,
|
|
70
|
+
cycleDarkMode,
|
|
71
|
+
manualDarkMode,
|
|
72
|
+
systemDarkMode
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
export const defaultDarkModeOrder = defaultOrder;
|
|
76
|
+
export const isDarkModeInjectionKey = injectionKey;
|
|
77
|
+
export const manualDarkModeInjectionKey = manualInjectionKey;
|
|
78
|
+
export const darkModeCommandsInjectionKey = commandsInjectionKey;
|
|
79
|
+
export const darkModeStateInjectionKey = stateInjectionKey;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
/**
|
|
3
|
+
* Allows users to more easily pass attributes to different parts of a component. Suppose a component has an input and a wrapper and you want most attributes to go to the input, but any that start with `wrapper` to go to the wrapper.
|
|
4
|
+
*
|
|
5
|
+
* You can do:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const extraAttrs = useDivideAttrs(["wrapper"] as const)
|
|
9
|
+
* ```
|
|
10
|
+
* This will correctly remove the start of the key. So if the user passes:
|
|
11
|
+
* ```vue
|
|
12
|
+
* <your-component regular-attr wrapper-attr wrapperAttrs/>
|
|
13
|
+
* ```
|
|
14
|
+
* You will get a ref like:
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* extraAttrs.value = {
|
|
18
|
+
* attrs: { regular-attr: true },
|
|
19
|
+
* wrapperAttrs: { attr: true, Attrs: true },
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* Additionally attributes are properly reactive, tough this is probably a bit expensive given they weren't meant to be reactive.
|
|
23
|
+
*
|
|
24
|
+
* Note that if using multi-word prefixes, like `inner-wrapper`, they will appear as the key `inner-wrapperAttrs` internally, but users will be able to correctly pass `inner-wrapper-prop`. Aditionally these can be passed down to deeper components, allowing easy wrapping.
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const useDivideAttrs: <T extends readonly string[]>(divisionKeys: T) => Ref<Record<`${T[number]}Attrs` | "attrs", Record<string, any>>>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { keys } from "@alanscodelog/utils/keys.js";
|
|
2
|
+
import { computed, useAttrs } from "vue";
|
|
3
|
+
export const useDivideAttrs = (divisionKeys) => computed(() => {
|
|
4
|
+
const attrs = useAttrs();
|
|
5
|
+
const res = { attrs: {} };
|
|
6
|
+
const unseen = keys(attrs);
|
|
7
|
+
for (const key of divisionKeys) {
|
|
8
|
+
res[`${key}Attrs`] = {};
|
|
9
|
+
for (let i = 0; i < unseen.length; i++) {
|
|
10
|
+
const attrKey = unseen[i];
|
|
11
|
+
if (attrKey.startsWith(`${key}-`)) {
|
|
12
|
+
res[`${key}Attrs`][attrKey.slice(key.length + 1)] = attrs[attrKey];
|
|
13
|
+
unseen.splice(i, 1);
|
|
14
|
+
i--;
|
|
15
|
+
} else if (attrKey.startsWith(key)) {
|
|
16
|
+
res[`${key}Attrs`][attrKey.slice(key.length)] = attrs[attrKey];
|
|
17
|
+
unseen.splice(i, 1);
|
|
18
|
+
i--;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (const attrKey of unseen) {
|
|
23
|
+
res.attrs[attrKey] = attrs[attrKey];
|
|
24
|
+
}
|
|
25
|
+
return res;
|
|
26
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, watch } from "vue";
|
|
2
|
+
import { globalResizeObserver } from "../globalResizeObserver.js";
|
|
3
|
+
export const useGlobalResizeObserver = (el, cb) => {
|
|
4
|
+
let wasMounted = false;
|
|
5
|
+
if (el.value) {
|
|
6
|
+
wasMounted = true;
|
|
7
|
+
globalResizeObserver.observe(el.value, cb);
|
|
8
|
+
}
|
|
9
|
+
onMounted(() => {
|
|
10
|
+
watch(el, (_newval, oldval) => {
|
|
11
|
+
if (el.value) {
|
|
12
|
+
globalResizeObserver.observe(el.value, cb);
|
|
13
|
+
} else {
|
|
14
|
+
if (oldval) {
|
|
15
|
+
globalResizeObserver.unobserve(oldval, cb);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (el.value && !wasMounted) {
|
|
20
|
+
globalResizeObserver.observe(el.value, cb);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
onBeforeUnmount(() => {
|
|
24
|
+
if (el.value) {
|
|
25
|
+
globalResizeObserver.unobserve(el.value, cb);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { inject } from "vue";
|
|
2
|
+
import { darkModeCommandsInjectionKey, darkModeStateInjectionKey } from "./useDarkMode.js";
|
|
3
|
+
export function useInjectedDarkMode() {
|
|
4
|
+
const darkModeState = inject(darkModeStateInjectionKey);
|
|
5
|
+
const darkModeCommands = inject(darkModeCommandsInjectionKey);
|
|
6
|
+
if (!darkModeState || !darkModeCommands) {
|
|
7
|
+
throw new Error("useInjectedDarkMode could not find provided state. Did you use useSetupDarkMode?");
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
...darkModeState,
|
|
11
|
+
...darkModeCommands
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { inject } from "vue";
|
|
2
|
+
import { i18nInjectionKey } from "./useSetupI18n.js";
|
|
3
|
+
export function useInjectedI18n() {
|
|
4
|
+
const val = inject(i18nInjectionKey, void 0);
|
|
5
|
+
if (val === void 0) throw new Error("witchcraft/ui: i18n is not injected. See useSetupI18n.");
|
|
6
|
+
return val;
|
|
7
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { inject } from "vue";
|
|
2
|
+
import { languageLocaleInjectionKey, timeLocaleInjectionKey } from "./useSetupLocale.js";
|
|
3
|
+
export function useInjectedLocale() {
|
|
4
|
+
const timeLocale = inject(timeLocaleInjectionKey);
|
|
5
|
+
const languageLocale = inject(languageLocaleInjectionKey);
|
|
6
|
+
if (timeLocale === void 0 || languageLocale === void 0) {
|
|
7
|
+
throw new Error("useInjectedLocale could not find provided state. Did you use useSetupLocale?");
|
|
8
|
+
}
|
|
9
|
+
function setLanguageLocale(value) {
|
|
10
|
+
languageLocale.value = value;
|
|
11
|
+
}
|
|
12
|
+
function setTimeLocale(value) {
|
|
13
|
+
timeLocale.value = value;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
timeLocale,
|
|
17
|
+
languageLocale,
|
|
18
|
+
setLanguageLocale,
|
|
19
|
+
setTimeLocale
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NotificationHandler } from "../helpers/NotificationHandler.js.js";
|
|
2
|
+
export declare const useNotificationHandler: (handler?: NotificationHandler,
|
|
3
|
+
/** True by default, should be passed import.meta.client if using nuxt, or false when running server side. */
|
|
4
|
+
isClientSide?: boolean) => NotificationHandler;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
let first = true;
|
|
2
|
+
const configuration = {
|
|
3
|
+
notificationHandler: void 0,
|
|
4
|
+
isClientSide: void 0
|
|
5
|
+
};
|
|
6
|
+
export const useNotificationHandler = (handler, isClientSide) => {
|
|
7
|
+
const clientSide = isClientSide ?? configuration.isClientSide;
|
|
8
|
+
if (!clientSide) return void 0;
|
|
9
|
+
if (first) {
|
|
10
|
+
first = false;
|
|
11
|
+
configuration.isClientSide = clientSide ?? true;
|
|
12
|
+
if (handler) {
|
|
13
|
+
configuration.notificationHandler = handler;
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error("You must set the notification handler to use at least once before using it.");
|
|
16
|
+
}
|
|
17
|
+
} else if (handler || isClientSide) {
|
|
18
|
+
console.warn("You can only configure useNotificationHandler once. (Note that there might be false positive during HMR).");
|
|
19
|
+
}
|
|
20
|
+
return configuration.notificationHandler;
|
|
21
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { ScrollNearContainerEdgesOptions } from "../types/index.js.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a function `scrollContainer` that allows scrolling a container manually when the coordinates are near it's edges.
|
|
5
|
+
* Supports scrolling faster the closer one is to the edge, and configuing an inner and outer margin.
|
|
6
|
+
*
|
|
7
|
+
* It can be used in any *move event.
|
|
8
|
+
*
|
|
9
|
+
* By default it sets a setInterval timer to continue scrolling even when the user does not move.
|
|
10
|
+
* An `endScroll` function is provided which should be called on the *up event to cleanup the timer and variables properly (there is also the individual `clearScrollInterval` and `resetCanScroll` functions.
|
|
11
|
+
*
|
|
12
|
+
* It also provides an `isScrolling` ref and a `scrollIndicator` reactive object for knowing which direction the user is scrolling in to be able to style the element. They can be force cleared with the `resetCanScroll` function.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const {
|
|
16
|
+
* scrollEdges,
|
|
17
|
+
* isScrolling,
|
|
18
|
+
* scrollIndicator,
|
|
19
|
+
* endScroll,
|
|
20
|
+
* } = useScrollNearContainerEdges({
|
|
21
|
+
* containerEl,
|
|
22
|
+
* scrollMargin,
|
|
23
|
+
* outerScrollMargin,
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* const onPointerMove = (e: PointerEvent): void => {
|
|
27
|
+
* scrollEdges(e.clientX, e.clientY)
|
|
28
|
+
* if (isScrolling.value) {
|
|
29
|
+
* e.preventDefault()
|
|
30
|
+
* return
|
|
31
|
+
* }
|
|
32
|
+
* //...
|
|
33
|
+
* }
|
|
34
|
+
* const onPointerUp = (_e: PointerEvent): void => {
|
|
35
|
+
* endScroll()
|
|
36
|
+
* //...
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
* Styling example with tailwind and tailwind-merge:
|
|
40
|
+
*
|
|
41
|
+
* ```vue
|
|
42
|
+
* <div
|
|
43
|
+
* :class="twMerge(
|
|
44
|
+
* isScrolling && `relative after:content-[''] after:absolute after:inset after:border-2 after:border-transparent`,
|
|
45
|
+
* scrollIndicator.right && `after:border-r-accent-500/60`,
|
|
46
|
+
* scrollIndicator.down && `after:border-b-accent-500/60`,
|
|
47
|
+
* scrollIndicator.left && `after:border-l-accent-500/60`,
|
|
48
|
+
* scrollIndicator.up && `after:border-t-accent-500/60`,
|
|
49
|
+
* )"
|
|
50
|
+
* >
|
|
51
|
+
* <div class="overflow-auto" ref="containerEl" >
|
|
52
|
+
* </div>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const useScrollNearContainerEdges: ({ containerEl, scrollMargin, outerScrollMargin, fastPixelMultiplier, fastPixelAmount, useTimer, timerInterval, }: ScrollNearContainerEdgesOptions) => {
|
|
56
|
+
scrollEdges: (clientX: number, clientY: number, overrideUseTimer?: boolean) => void;
|
|
57
|
+
/** Reactive. */
|
|
58
|
+
scrollIndicator: {
|
|
59
|
+
left: boolean;
|
|
60
|
+
right: boolean;
|
|
61
|
+
down: boolean;
|
|
62
|
+
up: boolean;
|
|
63
|
+
};
|
|
64
|
+
resetScrollIndicator: () => void;
|
|
65
|
+
clearScrollInterval: () => void;
|
|
66
|
+
isScrolling: Ref<boolean>;
|
|
67
|
+
endScroll: () => void;
|
|
68
|
+
};
|