vueless 1.4.12-beta.9 → 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.
Files changed (97) hide show
  1. package/README.md +1 -1
  2. package/index.d.ts +128 -0
  3. package/index.ts +128 -0
  4. package/package.json +10 -9
  5. package/ui.button/config.ts +1 -1
  6. package/ui.button-link/config.ts +1 -1
  7. package/ui.button-toggle/UToggle.vue +6 -4
  8. package/ui.button-toggle/storybook/stories.ts +4 -1
  9. package/ui.button-toggle/tests/UToggle.test-d.ts +48 -0
  10. package/ui.button-toggle/tests/UToggleTypes.vue +55 -0
  11. package/ui.button-toggle/types.ts +18 -6
  12. package/ui.container-accordion/types.ts +5 -2
  13. package/ui.container-collapsible/config.ts +1 -1
  14. package/ui.data-list/UDataList.vue +48 -17
  15. package/ui.data-list/storybook/stories.ts +4 -1
  16. package/ui.data-list/tests/UDataList.test-d.ts +76 -0
  17. package/ui.data-list/tests/UDataList.test.ts +50 -1
  18. package/ui.data-list/tests/UDataListTypes.vue +110 -0
  19. package/ui.data-list/types.ts +37 -5
  20. package/ui.data-table/UTable.vue +68 -10
  21. package/ui.data-table/config.ts +1 -1
  22. package/ui.data-table/storybook/stories.ts +4 -1
  23. package/ui.data-table/tests/UTable.test-d.ts +179 -0
  24. package/ui.data-table/tests/UTable.test.ts +1 -1
  25. package/ui.data-table/tests/UTableEmptyColumnsTypes.vue +27 -0
  26. package/ui.data-table/tests/UTableTypes.vue +193 -0
  27. package/ui.data-table/types.ts +91 -8
  28. package/ui.data-table/utilTable.ts +1 -1
  29. package/ui.dropdown/UDropdown.vue +19 -8
  30. package/ui.dropdown/storybook/stories.ts +4 -1
  31. package/ui.dropdown/tests/UDropdown.test-d.ts +61 -0
  32. package/ui.dropdown/tests/UDropdown.test.ts +8 -1
  33. package/ui.dropdown/tests/UDropdownTypes.vue +111 -0
  34. package/ui.dropdown/types.ts +27 -3
  35. package/ui.dropdown-badge/UDropdownBadge.vue +9 -4
  36. package/ui.dropdown-badge/storybook/stories.ts +6 -1
  37. package/ui.dropdown-badge/tests/UDropdownBadge.test-d.ts +55 -0
  38. package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +8 -1
  39. package/ui.dropdown-badge/tests/UDropdownBadgeTypes.vue +107 -0
  40. package/ui.dropdown-badge/types.ts +17 -3
  41. package/ui.dropdown-button/UDropdownButton.vue +9 -4
  42. package/ui.dropdown-button/storybook/stories.ts +6 -1
  43. package/ui.dropdown-button/tests/UDropdownButton.test-d.ts +57 -0
  44. package/ui.dropdown-button/tests/UDropdownButton.test.ts +8 -1
  45. package/ui.dropdown-button/tests/UDropdownButtonTypes.vue +107 -0
  46. package/ui.dropdown-button/types.ts +17 -3
  47. package/ui.dropdown-link/UDropdownLink.vue +9 -4
  48. package/ui.dropdown-link/config.ts +1 -1
  49. package/ui.dropdown-link/storybook/stories.ts +4 -1
  50. package/ui.dropdown-link/tests/UDropdownLink.test-d.ts +55 -0
  51. package/ui.dropdown-link/tests/UDropdownLink.test.ts +8 -1
  52. package/ui.dropdown-link/tests/UDropdownLinkTypes.vue +107 -0
  53. package/ui.dropdown-link/types.ts +17 -3
  54. package/ui.form-calendar/config.ts +1 -1
  55. package/ui.form-checkbox/config.ts +1 -1
  56. package/ui.form-checkbox/tests/UCheckbox.test.ts +4 -4
  57. package/ui.form-checkbox/types.ts +4 -2
  58. package/ui.form-checkbox-group/UCheckboxGroup.vue +8 -2
  59. package/ui.form-date-picker/UDatePicker.vue +11 -1
  60. package/ui.form-date-picker/tests/UDatePicker.test.ts +47 -0
  61. package/ui.form-input/config.ts +1 -1
  62. package/ui.form-input-file/config.ts +1 -1
  63. package/ui.form-listbox/UListbox.vue +29 -12
  64. package/ui.form-listbox/config.ts +1 -1
  65. package/ui.form-listbox/storybook/stories.ts +4 -1
  66. package/ui.form-listbox/tests/UListbox.test-d.ts +63 -0
  67. package/ui.form-listbox/tests/UListboxTypes.vue +107 -0
  68. package/ui.form-listbox/types.ts +34 -2
  69. package/ui.form-radio/config.ts +1 -1
  70. package/ui.form-radio-group/URadioGroup.vue +15 -3
  71. package/ui.form-radio-group/config.ts +8 -3
  72. package/ui.form-radio-group/types.ts +2 -3
  73. package/ui.form-select/USelect.vue +49 -17
  74. package/ui.form-select/config.ts +4 -4
  75. package/ui.form-select/storybook/stories.ts +4 -1
  76. package/ui.form-select/tests/USelect.test-d.ts +76 -0
  77. package/ui.form-select/tests/USelect.test.ts +65 -1
  78. package/ui.form-select/tests/USelectTypes.vue +113 -0
  79. package/ui.form-select/types.ts +55 -4
  80. package/ui.form-switch/USwitch.vue +13 -12
  81. package/ui.form-switch/config.ts +2 -1
  82. package/ui.form-switch/tests/USwitch.test.ts +151 -0
  83. package/ui.form-textarea/config.ts +1 -1
  84. package/ui.image-avatar/types.ts +5 -2
  85. package/ui.image-avatar-group/types.ts +5 -2
  86. package/ui.image-icon/UIcon.vue +3 -2
  87. package/ui.image-icon/types.ts +2 -2
  88. package/ui.navigation-breadcrumbs/types.ts +5 -2
  89. package/ui.navigation-tabs/UTabs.vue +6 -4
  90. package/ui.navigation-tabs/storybook/stories.ts +4 -1
  91. package/ui.navigation-tabs/tests/UTabs.test-d.ts +50 -0
  92. package/ui.navigation-tabs/tests/UTabsTypes.vue +65 -0
  93. package/ui.navigation-tabs/types.ts +25 -3
  94. package/ui.skeleton-text/constants.ts +1 -1
  95. package/ui.text-badge/config.ts +1 -1
  96. package/utils/node/loaderIcon.js +1 -1
  97. 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 UBreadcrumb extends ULinkProps {
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, Config>(defaultConfig, COMPONENT_NAME),
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 UTabs from "../../ui.navigation-tabs/UTabs.vue";
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 UTabsOption {
11
+ export interface BaseTabOption {
12
12
  value: string | number;
13
13
  label: string;
14
14
  disabled?: boolean;
15
15
  }
16
16
 
17
- export interface Props {
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?: UTabsOption[];
48
+ options?: TItem[];
27
49
 
28
50
  /**
29
51
  * Tabs size.
@@ -2,4 +2,4 @@
2
2
  This const is needed to prevent the issue in script setup:
3
3
  `defineProps` is referencing locally declared variables. (vue/valid-define-props)
4
4
  */
5
- export const COMPONENT_NAME = "USkeletonInput";
5
+ export const COMPONENT_NAME = "USkeletonText";
@@ -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: {
@@ -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)(U\w+)\b[^>]*?\b(\w*icon\w*)\s*[:=]\s*(['"])(.*?)\6[^>]*?>/;
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
 
@@ -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
  }