vueless 1.4.12-beta.8 → 1.5.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 +1 -1
- package/index.d.ts +128 -0
- package/index.ts +128 -0
- package/package.json +10 -9
- package/ui.button/config.ts +1 -1
- package/ui.button-link/config.ts +1 -1
- package/ui.button-toggle/UToggle.vue +6 -4
- package/ui.button-toggle/storybook/stories.ts +4 -1
- package/ui.button-toggle/tests/UToggle.test-d.ts +48 -0
- package/ui.button-toggle/tests/UToggleTypes.vue +55 -0
- package/ui.button-toggle/types.ts +18 -6
- package/ui.container-accordion/types.ts +5 -2
- package/ui.container-collapsible/config.ts +1 -1
- package/ui.data-list/UDataList.vue +48 -17
- package/ui.data-list/storybook/stories.ts +4 -1
- package/ui.data-list/tests/UDataList.test-d.ts +76 -0
- package/ui.data-list/tests/UDataList.test.ts +50 -1
- package/ui.data-list/tests/UDataListTypes.vue +110 -0
- package/ui.data-list/types.ts +37 -5
- package/ui.data-table/UTable.vue +68 -10
- package/ui.data-table/UTableRow.vue +4 -3
- package/ui.data-table/config.ts +1 -1
- package/ui.data-table/storybook/stories.ts +4 -1
- package/ui.data-table/tests/UTable.test-d.ts +179 -0
- package/ui.data-table/tests/UTable.test.ts +1 -1
- package/ui.data-table/tests/UTableEmptyColumnsTypes.vue +27 -0
- package/ui.data-table/tests/UTableTypes.vue +193 -0
- package/ui.data-table/types.ts +91 -8
- package/ui.data-table/utilTable.ts +1 -1
- package/ui.dropdown/UDropdown.vue +19 -8
- package/ui.dropdown/storybook/stories.ts +4 -1
- package/ui.dropdown/tests/UDropdown.test-d.ts +61 -0
- package/ui.dropdown/tests/UDropdown.test.ts +8 -1
- package/ui.dropdown/tests/UDropdownTypes.vue +111 -0
- package/ui.dropdown/types.ts +27 -3
- package/ui.dropdown-badge/UDropdownBadge.vue +9 -4
- package/ui.dropdown-badge/storybook/stories.ts +6 -1
- package/ui.dropdown-badge/tests/UDropdownBadge.test-d.ts +55 -0
- package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +8 -1
- package/ui.dropdown-badge/tests/UDropdownBadgeTypes.vue +107 -0
- package/ui.dropdown-badge/types.ts +17 -3
- package/ui.dropdown-button/UDropdownButton.vue +9 -4
- package/ui.dropdown-button/storybook/stories.ts +6 -1
- package/ui.dropdown-button/tests/UDropdownButton.test-d.ts +57 -0
- package/ui.dropdown-button/tests/UDropdownButton.test.ts +8 -1
- package/ui.dropdown-button/tests/UDropdownButtonTypes.vue +107 -0
- package/ui.dropdown-button/types.ts +17 -3
- package/ui.dropdown-link/UDropdownLink.vue +9 -4
- package/ui.dropdown-link/config.ts +1 -1
- package/ui.dropdown-link/storybook/stories.ts +4 -1
- package/ui.dropdown-link/tests/UDropdownLink.test-d.ts +55 -0
- package/ui.dropdown-link/tests/UDropdownLink.test.ts +8 -1
- package/ui.dropdown-link/tests/UDropdownLinkTypes.vue +107 -0
- package/ui.dropdown-link/types.ts +17 -3
- package/ui.form-calendar/config.ts +1 -1
- package/ui.form-checkbox/config.ts +1 -1
- package/ui.form-checkbox/tests/UCheckbox.test.ts +4 -4
- package/ui.form-checkbox/types.ts +4 -2
- package/ui.form-checkbox-group/UCheckboxGroup.vue +8 -2
- package/ui.form-date-picker/UDatePicker.vue +11 -1
- package/ui.form-date-picker/tests/UDatePicker.test.ts +47 -0
- package/ui.form-input/config.ts +1 -1
- package/ui.form-input-file/config.ts +1 -1
- package/ui.form-listbox/UListbox.vue +29 -12
- package/ui.form-listbox/config.ts +1 -1
- package/ui.form-listbox/storybook/stories.ts +4 -1
- package/ui.form-listbox/tests/UListbox.test-d.ts +63 -0
- package/ui.form-listbox/tests/UListboxTypes.vue +107 -0
- package/ui.form-listbox/types.ts +34 -2
- package/ui.form-radio/config.ts +1 -1
- package/ui.form-radio-group/URadioGroup.vue +15 -3
- package/ui.form-radio-group/config.ts +8 -3
- package/ui.form-radio-group/types.ts +2 -3
- package/ui.form-select/USelect.vue +49 -17
- package/ui.form-select/config.ts +4 -4
- package/ui.form-select/storybook/stories.ts +4 -1
- package/ui.form-select/tests/USelect.test-d.ts +76 -0
- package/ui.form-select/tests/USelect.test.ts +65 -1
- package/ui.form-select/tests/USelectTypes.vue +113 -0
- package/ui.form-select/types.ts +55 -4
- package/ui.form-switch/USwitch.vue +13 -12
- package/ui.form-switch/config.ts +2 -1
- package/ui.form-switch/tests/USwitch.test.ts +151 -0
- package/ui.form-textarea/config.ts +1 -1
- package/ui.image-avatar/types.ts +5 -2
- package/ui.image-avatar-group/types.ts +5 -2
- package/ui.image-icon/UIcon.vue +3 -2
- package/ui.image-icon/types.ts +2 -2
- package/ui.navigation-breadcrumbs/types.ts +5 -2
- package/ui.navigation-tabs/UTabs.vue +6 -4
- package/ui.navigation-tabs/storybook/stories.ts +4 -1
- package/ui.navigation-tabs/tests/UTabs.test-d.ts +50 -0
- package/ui.navigation-tabs/tests/UTabsTypes.vue +65 -0
- package/ui.navigation-tabs/types.ts +25 -3
- package/ui.skeleton-text/constants.ts +1 -1
- package/ui.text-badge/config.ts +1 -1
- package/utils/node/loaderIcon.js +1 -1
- package/v.tooltip/vTooltip.ts +1 -1
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import defaultConfig from "./config";
|
|
2
|
-
import type { ComponentConfig } from "../types";
|
|
2
|
+
import type { ComponentConfig, UnknownObject } from "../types";
|
|
3
3
|
import type { Props as ULinkProps } from "../ui.button-link/types";
|
|
4
4
|
|
|
5
5
|
export type Config = typeof defaultConfig;
|
|
6
6
|
|
|
7
|
-
export interface
|
|
7
|
+
export interface BaseBreadcrumb extends ULinkProps {
|
|
8
8
|
icon?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/* Any object shape is a valid breadcrumb, only reserved breadcrumb keys are type checked. */
|
|
12
|
+
export type UBreadcrumb = BaseBreadcrumb & (object | UnknownObject);
|
|
13
|
+
|
|
11
14
|
export interface Props {
|
|
12
15
|
/**
|
|
13
16
|
* Array of links.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
1
|
+
<script setup lang="ts" generic="TItem extends UTabsOption">
|
|
2
2
|
import { ref, computed, provide, onMounted, onUnmounted, useTemplateRef } from "vue";
|
|
3
3
|
|
|
4
4
|
import { useUI } from "../composables/useUI";
|
|
@@ -10,16 +10,18 @@ import UButton from "../ui.button/UButton.vue";
|
|
|
10
10
|
import { COMPONENT_NAME, SCROLL_OFFSET, DRAG_THRESHOLD, DRAG_CLICK_SUPPRESS_MS } from "./constants";
|
|
11
11
|
import defaultConfig from "./config";
|
|
12
12
|
|
|
13
|
-
import type { Props, Config } from "./types";
|
|
13
|
+
import type { Props, Config, UTabsOption, UTabsSlots } from "./types";
|
|
14
14
|
|
|
15
15
|
defineOptions({ inheritAttrs: false });
|
|
16
16
|
|
|
17
|
-
const props = withDefaults(defineProps<Props
|
|
18
|
-
...getDefaults<Props
|
|
17
|
+
const props = withDefaults(defineProps<Props<TItem>>(), {
|
|
18
|
+
...getDefaults<Props<TItem>, Config>(defaultConfig, COMPONENT_NAME),
|
|
19
19
|
modelValue: "",
|
|
20
20
|
options: () => [],
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
defineSlots<UTabsSlots<TItem>>();
|
|
24
|
+
|
|
23
25
|
const emit = defineEmits([
|
|
24
26
|
/**
|
|
25
27
|
* Triggers when the selected tab changes.
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getDocsDescription,
|
|
7
7
|
} from "../../utils/storybook";
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import UTabsComponent from "../../ui.navigation-tabs/UTabs.vue";
|
|
10
10
|
import URow from "../../ui.container-row/URow.vue";
|
|
11
11
|
import ULabel from "../../ui.form-label/ULabel.vue";
|
|
12
12
|
import UTab from "../../ui.navigation-tab/UTab.vue";
|
|
@@ -20,6 +20,9 @@ import johnDoe from "../../ui.navigation-tab/storybook/assets/john-doe.png";
|
|
|
20
20
|
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
21
21
|
import type { Props } from "../types";
|
|
22
22
|
|
|
23
|
+
/* Generic SFCs are typed as a function, which drops the compiler-generated `__name`. */
|
|
24
|
+
const UTabs = UTabsComponent as typeof UTabsComponent & { __name: string };
|
|
25
|
+
|
|
23
26
|
interface UTabsArgs extends Props {
|
|
24
27
|
slotTemplate?: string;
|
|
25
28
|
enum: "size";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expectTypeOf } from "vitest";
|
|
2
|
+
|
|
3
|
+
import type { Props, UTabsOption, UTabsSlots } from "../types";
|
|
4
|
+
|
|
5
|
+
interface CountedTab {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
count: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type LabelProps = Parameters<NonNullable<UTabsSlots<CountedTab>["label"]>>[0];
|
|
12
|
+
|
|
13
|
+
describe("UTabs types", () => {
|
|
14
|
+
it("infers the caller's option shape in a slot", () => {
|
|
15
|
+
expectTypeOf<LabelProps["item"]>().toEqualTypeOf<CountedTab>();
|
|
16
|
+
expectTypeOf<LabelProps["item"]["count"]>().toEqualTypeOf<number>();
|
|
17
|
+
expectTypeOf<LabelProps["index"]>().toEqualTypeOf<number>();
|
|
18
|
+
expectTypeOf<LabelProps["label"]>().toEqualTypeOf<string>();
|
|
19
|
+
expectTypeOf<LabelProps["active"]>().toEqualTypeOf<boolean>();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("promises the caller's members — options reach slots untransformed", () => {
|
|
23
|
+
/* Unlike UTable, `UTabs` iterates `props.options` directly: no flattening, filtering
|
|
24
|
+
* or synthetic entries, so the members are guaranteed rather than optional. */
|
|
25
|
+
expectTypeOf<LabelProps["item"]["label"]>().toEqualTypeOf<string>();
|
|
26
|
+
expectTypeOf<undefined>().not.toExtend<LabelProps["item"]["count"]>();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("does not expose undeclared option properties to a slot", () => {
|
|
30
|
+
type HasIndexSignature = string extends keyof LabelProps["item"] ? true : false;
|
|
31
|
+
|
|
32
|
+
expectTypeOf<HasIndexSignature>().toEqualTypeOf<false>();
|
|
33
|
+
expectTypeOf<keyof LabelProps["item"]>().not.toEqualTypeOf<string>();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("types the remaining slots", () => {
|
|
37
|
+
type Left = Parameters<NonNullable<UTabsSlots<CountedTab>["left"]>>[0];
|
|
38
|
+
type Right = Parameters<NonNullable<UTabsSlots<CountedTab>["right"]>>[0];
|
|
39
|
+
type Prev = Parameters<NonNullable<UTabsSlots["prev"]>>[0];
|
|
40
|
+
|
|
41
|
+
expectTypeOf<Left["item"]>().toEqualTypeOf<CountedTab>();
|
|
42
|
+
expectTypeOf<Right["item"]>().toEqualTypeOf<CountedTab>();
|
|
43
|
+
expectTypeOf<Prev["iconName"]>().toEqualTypeOf<string>();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("resolves Props with no type argument", () => {
|
|
47
|
+
expectTypeOf<Props>().toEqualTypeOf<Props<UTabsOption>>();
|
|
48
|
+
expectTypeOf<Props<CountedTab>["options"]>().toEqualTypeOf<CountedTab[] | undefined>();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!-- Type-level fixture: checked by `npm run ts:check`, never mounted.
|
|
2
|
+
Proves slot props infer from the `options` prop at a real call site. -->
|
|
3
|
+
<template>
|
|
4
|
+
<!-- `UTabs` iterates `options` verbatim, so the caller's shape is guaranteed, not optional. -->
|
|
5
|
+
<UTabs :options="options">
|
|
6
|
+
<template #left="{ item, index, active, iconName }">
|
|
7
|
+
{{ expectString(item.label) }}
|
|
8
|
+
{{ expectString(item.badge) }}
|
|
9
|
+
{{ expectNumber(index) }}
|
|
10
|
+
{{ expectBoolean(active) }}
|
|
11
|
+
{{ expectOptionalString(iconName) }}
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<template #label="{ item, index, label, active }">
|
|
15
|
+
{{ expectString(item.badge) }}
|
|
16
|
+
{{ expectString(label) }}
|
|
17
|
+
{{ expectNumber(index) }}
|
|
18
|
+
{{ expectBoolean(active) }}
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<template #right="{ item }">
|
|
22
|
+
<!-- @vue-expect-error `nonExistent` is not a key of the inferred option. -->
|
|
23
|
+
{{ item.nonExistent }}
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<template #prev="{ iconName }">{{ expectString(iconName) }}</template>
|
|
27
|
+
<template #next="{ iconName }">{{ expectString(iconName) }}</template>
|
|
28
|
+
</UTabs>
|
|
29
|
+
|
|
30
|
+
<!-- Options from a non-literal source still expose the declared option shape. -->
|
|
31
|
+
<UTabs :options="typedOptions">
|
|
32
|
+
<template #label="{ item }">{{ expectNumber(item.count) }}</template>
|
|
33
|
+
</UTabs>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
import { ref } from "vue";
|
|
38
|
+
|
|
39
|
+
import UTabs from "../UTabs.vue";
|
|
40
|
+
|
|
41
|
+
interface CountedTab {
|
|
42
|
+
value: string;
|
|
43
|
+
label: string;
|
|
44
|
+
count: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const options = [{ value: "1", label: "Dashboard", badge: "new" }];
|
|
48
|
+
const typedOptions = ref<CountedTab[]>([]);
|
|
49
|
+
|
|
50
|
+
function expectString(value: string) {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function expectNumber(value: number) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function expectBoolean(value: boolean) {
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function expectOptionalString(value: string | undefined) {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
@@ -8,13 +8,35 @@ export type SetUTabsSelectedItem = (
|
|
|
8
8
|
value: string | number | boolean | UnknownArray | UnknownObject,
|
|
9
9
|
) => void;
|
|
10
10
|
|
|
11
|
-
export interface
|
|
11
|
+
export interface BaseTabOption {
|
|
12
12
|
value: string | number;
|
|
13
13
|
label: string;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/* Any object shape is a valid option, only reserved option keys are type checked. */
|
|
18
|
+
export type UTabsOption = BaseTabOption & (object | UnknownObject);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Slots receive each option verbatim — `UTabs` iterates `props.options` without
|
|
22
|
+
* filtering, flattening or synthesising entries, so the caller's shape is guaranteed.
|
|
23
|
+
*/
|
|
24
|
+
export interface UTabsSlots<TItem extends UTabsOption = UTabsOption> {
|
|
25
|
+
default?: () => unknown;
|
|
26
|
+
prev?: (props: { iconName: string }) => unknown;
|
|
27
|
+
next?: (props: { iconName: string }) => unknown;
|
|
28
|
+
left?: (props: { item: TItem; index: number; active: boolean; iconName?: string }) => unknown;
|
|
29
|
+
label?: (props: {
|
|
30
|
+
item: TItem;
|
|
31
|
+
index: number;
|
|
32
|
+
label: string;
|
|
33
|
+
active: boolean;
|
|
34
|
+
iconName?: string;
|
|
35
|
+
}) => unknown;
|
|
36
|
+
right?: (props: { item: TItem; index: number; active: boolean; iconName?: string }) => unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Props<TItem extends UTabsOption = UTabsOption> {
|
|
18
40
|
/**
|
|
19
41
|
* Selected tab value.
|
|
20
42
|
*/
|
|
@@ -23,7 +45,7 @@ export interface Props {
|
|
|
23
45
|
/**
|
|
24
46
|
* Tab options.
|
|
25
47
|
*/
|
|
26
|
-
options?:
|
|
48
|
+
options?: TItem[];
|
|
27
49
|
|
|
28
50
|
/**
|
|
29
51
|
* Tabs size.
|
package/ui.text-badge/config.ts
CHANGED
|
@@ -2,7 +2,7 @@ export default /*tw*/ {
|
|
|
2
2
|
badge: {
|
|
3
3
|
base: `
|
|
4
4
|
inline-flex items-center justify-between py-1
|
|
5
|
-
border border-solid rounded-medium leading-none! outline-solid outline-transparent
|
|
5
|
+
border border-solid rounded-medium leading-none! outline-solid outline-medium outline-transparent
|
|
6
6
|
`,
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
package/utils/node/loaderIcon.js
CHANGED
|
@@ -227,7 +227,7 @@ async function findAndCopyIcons(files, library, vuelessConfig, debug) {
|
|
|
227
227
|
const iconPropsPattern = `\\b\\w*(name|icon)\\w*\\s*=\\s*(['"])(.*?)\\2`;
|
|
228
228
|
const uComponentIconNamePattern =
|
|
229
229
|
// eslint-disable-next-line vue/max-len
|
|
230
|
-
/<UIcon\b[^>]*?\b(name)\s*[:=]\s*(['"])(.*?)\2[^>]*?>|<(?!UIcon\b)(
|
|
230
|
+
/<UIcon\b[^>]*?\b(name)\s*[:=]\s*(['"])(.*?)\2[^>]*?>|<(?!UIcon\b)([A-Z]\w+)\b[^>]*?\b(\w*icon\w*)\s*[:=]\s*(['"])(.*?)\6[^>]*?>/;
|
|
231
231
|
|
|
232
232
|
const uComponentIconNameArray = fileContents.match(new RegExp(uComponentIconNamePattern, "g"));
|
|
233
233
|
|
package/v.tooltip/vTooltip.ts
CHANGED
|
@@ -64,7 +64,7 @@ function setUpTippy(el: HTMLElement, props: string | TippyProps) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
if (typeof props !== "string" && props.content && String(props.content).length) {
|
|
67
|
+
if (typeof props !== "string" && props && props.content && String(props.content).length) {
|
|
68
68
|
tippy(el, merge({}, settings, props || {}));
|
|
69
69
|
}
|
|
70
70
|
}
|