vueless 0.0.725 → 0.0.727

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.725",
3
+ "version": "0.0.727",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue";
3
3
 
4
- import ULabel from "../ui.form-label/ULabel.vue";
5
4
  import UButton from "../ui.button/UButton.vue";
6
5
 
7
6
  import useUI from "../composables/useUI.ts";
@@ -65,78 +64,61 @@ const mutatedProps = computed(() => ({
65
64
  selected: isSelected,
66
65
  }));
67
66
 
68
- const { toggleLabelAttrs, optionsAttrs, toggleButtonInactiveAttrs, toggleButtonActiveAttrs } =
69
- useUI<Config>(defaultConfig, mutatedProps);
67
+ const { optionsAttrs, toggleButtonInactiveAttrs, toggleButtonActiveAttrs } = useUI<Config>(
68
+ defaultConfig,
69
+ mutatedProps,
70
+ );
70
71
  </script>
71
72
 
72
73
  <template>
73
- <ULabel
74
- :label="label"
75
- :description="description"
76
- :align="labelAlign"
77
- :disabled="disabled"
78
- :error="error"
79
- centred
80
- v-bind="toggleLabelAttrs"
81
- :data-test="dataTest"
82
- >
83
- <template #label>
84
- <!--
85
- @slot Use this to add custom content instead of the entire Toggle label.
86
- @binding {string} label
87
- -->
88
- <slot name="label" :label="label" />
89
- </template>
90
-
91
- <div v-bind="optionsAttrs">
92
- <UButton
93
- v-for="(option, index) in options"
94
- :key="option.value"
95
- :label="option.label"
96
- tabindex="0"
97
- color="gray"
98
- :size="size"
99
- :round="round"
100
- :block="block"
101
- :square="square"
102
- :disabled="disabled"
103
- v-bind="isSelected(option) ? toggleButtonActiveAttrs : toggleButtonInactiveAttrs"
104
- :data-test="`${dataTest}-option-${index}`"
105
- @click="onClickOption(option)"
106
- >
107
- <template #left="{ iconName }">
108
- <!--
74
+ <div v-bind="optionsAttrs">
75
+ <UButton
76
+ v-for="(option, index) in options"
77
+ :key="option.value"
78
+ :label="option.label"
79
+ tabindex="0"
80
+ color="gray"
81
+ :size="size"
82
+ :round="round"
83
+ :block="block"
84
+ :square="square"
85
+ :disabled="disabled"
86
+ v-bind="isSelected(option) ? toggleButtonActiveAttrs : toggleButtonInactiveAttrs"
87
+ :data-test="`${dataTest}-option-${index}`"
88
+ @click="onClickOption(option)"
89
+ >
90
+ <template #left="{ iconName }">
91
+ <!--
109
92
  @slot Use it to add something before the label.
110
93
  @binding {object} option
111
94
  @binding {string} icon-name
112
95
  @binding {number} index
113
96
  -->
114
- <slot name="left" :option="option" :icon-name="iconName" :index="index" />
115
- </template>
97
+ <slot name="left" :option="option" :icon-name="iconName" :index="index" />
98
+ </template>
116
99
 
117
- <template #default="{ label, iconName }">
118
- <!--
100
+ <template #default="{ label, iconName }">
101
+ <!--
119
102
  @slot Use it to add something instead of the toggle option label.
120
103
  @binding {object} option
121
104
  @binding {string} label
122
105
  @binding {string} icon-name
123
106
  @binding {number} index
124
107
  -->
125
- <slot name="option" :option="option" :label="label" :icon-name="iconName" :index="index">
126
- {{ option.label }}
127
- </slot>
128
- </template>
108
+ <slot name="option" :option="option" :label="label" :icon-name="iconName" :index="index">
109
+ {{ option.label }}
110
+ </slot>
111
+ </template>
129
112
 
130
- <template #right="{ iconName }">
131
- <!--
113
+ <template #right="{ iconName }">
114
+ <!--
132
115
  @slot Use it to add something after the label.
133
116
  @binding {object} option
134
117
  @binding {string} icon-name
135
118
  @binding {number} index
136
119
  -->
137
- <slot name="right" :option="option" :icon-name="iconName" :index="index" />
138
- </template>
139
- </UButton>
140
- </div>
141
- </ULabel>
120
+ <slot name="right" :option="option" :icon-name="iconName" :index="index" />
121
+ </template>
122
+ </UButton>
123
+ </div>
142
124
  </template>
@@ -1,22 +1,4 @@
1
1
  export default /*tw*/ {
2
- toggleLabel: {
3
- base: "{ULabel} flex-wrap",
4
- variants: {
5
- block: {
6
- true: "w-full",
7
- },
8
- },
9
- defaults: {
10
- size: {
11
- "2xs": "sm",
12
- xs: "sm",
13
- sm: "md",
14
- md: "md",
15
- lg: "lg",
16
- xl: "lg",
17
- },
18
- },
19
- },
20
2
  options: {
21
3
  base: "flex",
22
4
  variants: {
@@ -33,7 +15,7 @@ export default /*tw*/ {
33
15
  false: "flex-nowrap gap-1 p-1 w-fit border rounded-dynamic border-gray-300",
34
16
  },
35
17
  disabled: {
36
- true: "pointer-events-none",
18
+ true: "pointer-events-none bg-gray-100",
37
19
  },
38
20
  block: {
39
21
  true: "w-full flex-nowrap",
@@ -78,7 +60,6 @@ export default /*tw*/ {
78
60
  },
79
61
  },
80
62
  defaults: {
81
- labelAlign: "top",
82
63
  size: "md",
83
64
  split: false,
84
65
  block: false,
@@ -1,4 +1,4 @@
1
- import { ref, computed } from "vue";
1
+ import { ref } from "vue";
2
2
  import {
3
3
  getArgTypes,
4
4
  getSlotNames,
@@ -24,7 +24,6 @@ export default {
24
24
  title: "Buttons & Links / Toggle",
25
25
  component: UToggle,
26
26
  args: {
27
- label: "Please choose a role",
28
27
  modelValue: "11",
29
28
  options: [
30
29
  { value: "11", label: "Admin" },
@@ -47,22 +46,11 @@ const DefaultTemplate: StoryFn<UToggleArgs> = (args: UToggleArgs) => ({
47
46
  components: { UToggle, UIcon, UBadge },
48
47
  setup() {
49
48
  const slots = getSlotNames(UToggle.__name);
50
- const errorMessage = computed(() => {
51
- if (args.name === "error" && Array.isArray(args.modelValue)) {
52
- return args.modelValue.length === 0 ? "Please select at least one option" : "";
53
- }
54
49
 
55
- return "";
56
- });
57
-
58
- return { args, slots, errorMessage };
50
+ return { args, slots };
59
51
  },
60
52
  template: `
61
- <UToggle
62
- v-bind="args"
63
- v-model="args.modelValue"
64
- :error="errorMessage"
65
- >
53
+ <UToggle v-bind="args" v-model="args.modelValue">
66
54
  ${args.slotTemplate || getSlotsFragment("")}
67
55
  </UToggle>
68
56
  `,
@@ -87,7 +75,6 @@ const EnumVariantTemplate: StoryFn<UToggleArgs> = (args: UToggleArgs, { argTypes
87
75
  v-bind="args"
88
76
  v-model="values[option]"
89
77
  :[args.enum]="option"
90
- :label="option"
91
78
  :options="[
92
79
  { value: option + 1, label: option },
93
80
  { value: option + 2, label: option },
@@ -106,7 +93,6 @@ Default.args = {
106
93
  export const Disabled = DefaultTemplate.bind({});
107
94
  Disabled.args = {
108
95
  name: "Disabled",
109
- label: "You can disable the whole toggle or specific items",
110
96
  options: [
111
97
  { value: "11", label: "Admin" },
112
98
  { value: "12", label: "Editor", disabled: true },
@@ -115,9 +101,6 @@ Disabled.args = {
115
101
  ],
116
102
  };
117
103
 
118
- export const Description = DefaultTemplate.bind({});
119
- Description.args = { name: "Description", description: "You can also add description" };
120
-
121
104
  export const Sizes = EnumVariantTemplate.bind({});
122
105
  Sizes.args = { name: "sizeTemplate", enum: "size" };
123
106
 
@@ -126,15 +109,6 @@ Multiple.args = {
126
109
  name: "multiple",
127
110
  multiple: true,
128
111
  modelValue: [],
129
- label: "You can choose more than one option",
130
- };
131
-
132
- export const Error = DefaultTemplate.bind({});
133
- Error.args = {
134
- name: "error",
135
- multiple: true,
136
- modelValue: [],
137
- label: "If no option is selected, error message is displayed",
138
112
  };
139
113
 
140
114
  export const Block = DefaultTemplate.bind({});
@@ -150,7 +124,6 @@ export const Square = DefaultTemplate.bind({});
150
124
  Square.args = {
151
125
  name: "square",
152
126
  square: true,
153
- label: "Square prop is useful when icons are present",
154
127
  options: [
155
128
  { value: "11", label: "star" },
156
129
  { value: "12", label: "add" },
@@ -166,7 +139,6 @@ Square.args = {
166
139
  export const DefaultSlot = DefaultTemplate.bind({});
167
140
  DefaultSlot.args = {
168
141
  name: "defaultSlot",
169
- label: "Please select an operation to proceed",
170
142
  options: [
171
143
  { value: "1", label: "Download", rightIcon: "download", color: "green" },
172
144
  { value: "2", label: "Edit", rightIcon: "edit_note", color: "orange" },
@@ -195,13 +167,13 @@ export const Slots: StoryFn<UToggleArgs> = (args) => ({
195
167
  <URow no-mobile>
196
168
  <UToggle v-bind="args" v-model="leftModel" name="leftSlot">
197
169
  <template #left="{ index }">
198
- <UIcon v-if="index === 0" name="settings" />
170
+ <UIcon size="sm" color="inherit" v-if="index === 0" name="settings" />
199
171
  </template>
200
172
  </UToggle>
201
173
 
202
174
  <UToggle v-bind="args" v-model="rightModel" name="rightSlot">
203
175
  <template #right="{ index }">
204
- <UIcon v-if="index === 2" name="person" />
176
+ <UIcon size="sm" color="inherit" v-if="index === 2" name="person" />
205
177
  </template>
206
178
  </UToggle>
207
179
  </URow>
@@ -30,26 +30,6 @@ export interface Props {
30
30
  */
31
31
  size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl";
32
32
 
33
- /**
34
- * Error message.
35
- */
36
- error?: string;
37
-
38
- /**
39
- * Label placement.
40
- */
41
- labelAlign?: "top" | "topWithDesc" | "left" | "right";
42
-
43
- /**
44
- * Toggle label.
45
- */
46
- label?: string;
47
-
48
- /**
49
- * Toggle description.
50
- */
51
- description?: string;
52
-
53
33
  /**
54
34
  * Toggle name.
55
35
  */
@@ -22,8 +22,8 @@ export default /*tw*/ {
22
22
  base: "{UDropdownList} w-fit",
23
23
  variants: {
24
24
  yPosition: {
25
- top: "bottom-5 mb-5",
26
- bottom: "top-3 mt-5",
25
+ top: "bottom-5 mb-3",
26
+ bottom: "top-5 mt-3",
27
27
  },
28
28
  xPosition: {
29
29
  left: "left-0",
@@ -25,8 +25,8 @@ export default /*tw*/ {
25
25
  base: "{UDropdownList} w-fit",
26
26
  variants: {
27
27
  yPosition: {
28
- top: "bottom-full mb-1",
29
- bottom: "top-full mt-1",
28
+ top: "bottom-full mb-1.5",
29
+ bottom: "top-full mt-1.5",
30
30
  },
31
31
  xPosition: {
32
32
  left: "left-0",
@@ -34,8 +34,8 @@ export default /*tw*/ {
34
34
  base: "{UDropdownList} w-fit",
35
35
  variants: {
36
36
  yPosition: {
37
- top: "bottom-3 mb-3",
38
- bottom: "top-3 mt-3",
37
+ top: "bottom-3 mb-2.5",
38
+ bottom: "top-3 mt-2.5",
39
39
  },
40
40
  xPosition: {
41
41
  left: "left-0",
@@ -14,12 +14,12 @@ export default /*tw*/ {
14
14
  },
15
15
  },
16
16
  },
17
- dateInRange: "bg-brand/5 !text-brand-900 hover:bg-brand/15 rounded-none",
17
+ dateInRange: "bg-brand-600/10 !text-brand-900 hover:bg-brand/15 rounded-none",
18
18
  edgeDateInRange: "",
19
19
  firstDateInRange: "rounded-r-none",
20
20
  lastDateInRange: "rounded-l-none",
21
21
  anotherMonthDate: "text-gray-400",
22
- activeDate: "bg-brand-100",
22
+ activeDate: "bg-brand-600/10",
23
23
  selectedDate: "",
24
24
  currentDate: "border-2 border-brand-600",
25
25
  day: "{UButton} size-9 w-full",
@@ -29,8 +29,8 @@ export default /*tw*/ {
29
29
  rightIcon: "{UIcon} {>inputIcon}",
30
30
  passwordIcon: "{UIcon} {>inputIcon}",
31
31
  passwordIconWrapper: "flex items-center justify-end whitespace-nowrap pr-2.5 gap-1 rounded-inherit rounded-l-none",
32
- leftSlot: "pl-3 flex items-center",
33
- rightSlot: "pr-3 flex items-center",
32
+ leftSlot: "pl-2.5 flex items-center",
33
+ rightSlot: "pr-2.5 flex items-center",
34
34
  input: {
35
35
  base: `
36
36
  block w-full py-2 px-3 font-normal !leading-none text-gray-900 bg-transparent
@@ -25,7 +25,7 @@ export default /*tw*/ {
25
25
  descriptionTop: "{UText} text-gray-700 mb-2",
26
26
  descriptionBottom: "{UText} text-gray-700 mt-2",
27
27
  content: {
28
- base: "p-3 gap-3 flex justify-between items-start relative w-full rounded-dynamic bg-brand-50",
28
+ base: "p-3 gap-3 flex justify-between items-start relative w-full rounded-dynamic bg-brand-600/5",
29
29
  variants: {
30
30
  multiple: {
31
31
  false: "items-center",
@@ -6,7 +6,6 @@ import { hasSlotContent } from "../utils/helper.ts";
6
6
  import { getDefaults } from "../utils/ui.ts";
7
7
 
8
8
  import UIcon from "../ui.image-icon/UIcon.vue";
9
- import ULabel from "../ui.form-label/ULabel.vue";
10
9
 
11
10
  import { COMPONENT_NAME } from "./constants.ts";
12
11
  import defaultConfig from "./config.ts";
@@ -60,65 +59,46 @@ function onMouseHover(overStar: number | null = null) {
60
59
  * Get element / nested component attributes for each config token ✨
61
60
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
62
61
  */
63
- const { config, inputLabelAttrs, containerAttrs, counterAttrs, totalAttrs, starsAttrs, starAttrs } =
62
+ const { config, containerAttrs, counterAttrs, totalAttrs, starsAttrs, starAttrs } =
64
63
  useUI<Config>(defaultConfig);
65
64
  </script>
66
65
 
67
66
  <template>
68
- <ULabel
69
- :label="label"
70
- :error="error"
71
- :size="size"
72
- :align="labelAlign"
73
- :description="description"
74
- :disabled="disabled"
75
- v-bind="inputLabelAttrs"
76
- :data-test="dataTest"
77
- >
78
- <template #label>
67
+ <div v-bind="containerAttrs">
68
+ <div v-if="counter || hasSlotContent($slots['counter'])" v-bind="counterAttrs">
79
69
  <!--
80
- @slot Use this to add custom content instead of the label.
81
- @binding {string} label
82
- -->
83
- <slot name="label" :label="label" />
84
- </template>
85
-
86
- <div v-bind="containerAttrs">
87
- <div v-if="counter || hasSlotContent($slots['counter'])" v-bind="counterAttrs">
88
- <!--
89
70
  @slot Use it to customise counter.
90
71
  @binding {number} counter
91
72
  @binding {number} total
92
73
  -->
93
- <slot name="counter" :counter="counterValue" :total="total">
94
- {{ counterValue }}
95
- </slot>
96
- </div>
97
-
98
- <div v-bind="starsAttrs">
99
- <UIcon
100
- v-for="star in stars"
101
- :key="star"
102
- internal
103
- :color="error ? 'red' : 'brand'"
104
- :interactive="selectable"
105
- :name="starIcon(star)"
106
- v-bind="starAttrs"
107
- :data-test="`${dataTest}-rating-star-${star}`"
108
- @click="onClickStar(star)"
109
- @mouseleave="onMouseHover()"
110
- @mouseover="onMouseHover(star)"
111
- />
112
- </div>
113
-
114
- <div v-if="total || hasSlotContent($slots['total'])" v-bind="totalAttrs">
115
- <!--
74
+ <slot name="counter" :counter="counterValue" :total="total">
75
+ {{ counterValue }}
76
+ </slot>
77
+ </div>
78
+
79
+ <div v-bind="starsAttrs">
80
+ <UIcon
81
+ v-for="star in stars"
82
+ :key="star"
83
+ internal
84
+ :color="error ? 'red' : 'brand'"
85
+ :interactive="selectable"
86
+ :name="starIcon(star)"
87
+ v-bind="starAttrs"
88
+ :data-test="`${dataTest}-rating-star-${star}`"
89
+ @click="onClickStar(star)"
90
+ @mouseleave="onMouseHover()"
91
+ @mouseover="onMouseHover(star)"
92
+ />
93
+ </div>
94
+
95
+ <div v-if="total || hasSlotContent($slots['total'])" v-bind="totalAttrs">
96
+ <!--
116
97
  @slot Use it to customise total.
117
98
  @binding {number} counter
118
99
  @binding {number} total
119
100
  -->
120
- <slot name="total" :counter="counter" :total="total">({{ total }})</slot>
121
- </div>
101
+ <slot name="total" :counter="counter" :total="total">({{ total }})</slot>
122
102
  </div>
123
- </ULabel>
103
+ </div>
124
104
  </template>
@@ -1,5 +1,4 @@
1
1
  export default /*tw*/ {
2
- inputLabel: "{ULabel}",
3
2
  container: {
4
3
  base: "flex items-center text-gray-500 !leading-none",
5
4
  variants: {
@@ -45,7 +44,6 @@ export default /*tw*/ {
45
44
  },
46
45
  },
47
46
  defaults: {
48
- labelAlign: "top",
49
47
  size: "md",
50
48
  stars: 5,
51
49
  counter: false,
@@ -1,4 +1,3 @@
1
- import { computed } from "vue";
2
1
  import {
3
2
  getArgTypes,
4
3
  getSlotNames,
@@ -15,7 +14,7 @@ import type { Props } from "../types.ts";
15
14
 
16
15
  interface UInputRatingArgs extends Props {
17
16
  slotTemplate?: string;
18
- enum: "size" | "labelAlign";
17
+ enum: "size";
19
18
  }
20
19
 
21
20
  export default {
@@ -40,16 +39,11 @@ const DefaultTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs) => (
40
39
  components: { UInputRating, UBadge },
41
40
  setup() {
42
41
  const slots = getSlotNames(UInputRating.__name);
43
- const errorMessage = computed(() => (!args.modelValue ? args.error : ""));
44
42
 
45
- return { args, slots, errorMessage };
43
+ return { args, slots };
46
44
  },
47
45
  template: `
48
- <UInputRating
49
- v-bind="args"
50
- v-model="args.modelValue"
51
- :error="errorMessage"
52
- >
46
+ <UInputRating v-bind="args" v-model="args.modelValue">
53
47
  ${args.slotTemplate || getSlotsFragment("")}
54
48
  </UInputRating>
55
49
  `,
@@ -81,25 +75,12 @@ const EnumVariantTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs,
81
75
  export const Default = DefaultTemplate.bind({});
82
76
  Default.args = {};
83
77
 
84
- export const Description = DefaultTemplate.bind({});
85
- Description.args = { description: "Your review helps us improve our services." };
86
-
87
- export const Error = DefaultTemplate.bind({});
88
- Error.args = {
89
- selectable: true,
90
- modelValue: 0,
91
- error: "Please select a rating before submitting your review.",
92
- };
93
-
94
78
  export const Disabled = DefaultTemplate.bind({});
95
79
  Disabled.args = { disabled: true };
96
80
 
97
81
  export const Sizes = EnumVariantTemplate.bind({});
98
82
  Sizes.args = { enum: "size" };
99
83
 
100
- export const LabelPlacement = EnumVariantTemplate.bind({});
101
- LabelPlacement.args = { enum: "labelAlign" };
102
-
103
84
  export const StarAmount = DefaultTemplate.bind({});
104
85
  StarAmount.args = { stars: 7 };
105
86
  StarAmount.parameters = {
@@ -5,21 +5,11 @@ import type { ComponentConfig } from "../types.ts";
5
5
  export type Config = typeof defaultConfig;
6
6
 
7
7
  export interface Props {
8
- /**
9
- * Rating label.
10
- */
11
- label?: string;
12
-
13
8
  /**
14
9
  * Rating value.
15
10
  */
16
11
  modelValue: number;
17
12
 
18
- /**
19
- * Rating description.
20
- */
21
- description?: string;
22
-
23
13
  /**
24
14
  * Rating number of stars.
25
15
  */
@@ -30,16 +20,6 @@ export interface Props {
30
20
  */
31
21
  size?: "sm" | "md" | "lg";
32
22
 
33
- /**
34
- * Rating label placement.
35
- */
36
- labelAlign?: "top" | "topWithDesc" | "left" | "right";
37
-
38
- /**
39
- * Rating error message.
40
- */
41
- error?: string;
42
-
43
23
  /**
44
24
  * Disable the input.
45
25
  */
@@ -81,10 +81,10 @@ export default /*tw*/ {
81
81
  },
82
82
  leftIcon: "{UIcon} {>selectIcon}",
83
83
  rightIcon: "{UIcon} {>selectIcon}",
84
- leftSlot: "{>toggle} pl-3",
85
- rightSlot: "{>toggle} pr-3",
84
+ leftSlot: "{>toggle} pl-2.5",
85
+ rightSlot: "{>toggle} pr-2.5",
86
86
  beforeToggle: "{>toggle} cursor-auto",
87
- afterToggle: "{>toggle} mr-3 items-start pt-3 cursor-auto",
87
+ afterToggle: "{>toggle} mr-2.5 items-start pt-3 cursor-auto",
88
88
  toggle: {
89
89
  base: "flex items-center",
90
90
  compoundVariants: [
@@ -1,8 +1,8 @@
1
1
  export default /*tw*/ {
2
2
  textareaLabel: "{ULabel}",
3
3
  slot: "flex items-center justify-center whitespace-nowrap gap-1 rounded-dynamic",
4
- leftSlot: "{>slot} pl-3 rounded-r-none",
5
- rightSlot: "{>slot} pr-3 rounded-l-none",
4
+ leftSlot: "{>slot} pl-2.5 rounded-r-none",
5
+ rightSlot: "{>slot} pr-2.5 rounded-l-none",
6
6
  wrapper: {
7
7
  base: `
8
8
  flex bg-white transition w-full
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, onBeforeUnmount, watch, ref, onMounted, onUnmounted } from "vue";
2
+ import { computed, watch, ref } from "vue";
3
3
 
4
4
  import useUI from "../composables/useUI.ts";
5
5
  import { getDefaults } from "../utils/ui.ts";
@@ -7,7 +7,7 @@ import { getDefaults } from "../utils/ui.ts";
7
7
  import { clamp, queue, getRequestWithoutQuery } from "./utilLoaderProgress.ts";
8
8
  import { useLoaderProgress } from "./useLoaderProgress.ts";
9
9
 
10
- import { COMPONENT_NAME, MAXIMUM, SPEED, INFINITY_LOADING } from "./constants.ts";
10
+ import { COMPONENT_NAME, MAXIMUM, SPEED } from "./constants.ts";
11
11
  import defaultConfig from "./config.ts";
12
12
 
13
13
  import type { Props, Config } from "./types.ts";
@@ -25,16 +25,9 @@ const progress = ref(0);
25
25
  const opacity = ref(1);
26
26
  const status = ref<number | null>(null);
27
27
 
28
- const {
29
- requestQueue,
30
- removeRequestUrl,
31
- isLoading,
32
- loaderProgressOff,
33
- loaderProgressOn,
34
- addRequestUrl,
35
- } = useLoaderProgress();
28
+ const { requestQueue } = useLoaderProgress();
36
29
 
37
- const isStarted = computed(() => {
30
+ const isLoading = computed(() => {
38
31
  return typeof status.value === "number";
39
32
  });
40
33
 
@@ -45,68 +38,37 @@ const barStyle = computed(() => {
45
38
  };
46
39
  });
47
40
 
48
- const resourceNamesArray = computed(() => {
49
- return Array.isArray(props.resources)
50
- ? props.resources.map(getRequestWithoutQuery)
51
- : [getRequestWithoutQuery(props.resources)];
52
- });
53
-
54
- const isPropsLoading = computed(
55
- () => requestQueue.value.includes(INFINITY_LOADING) && props.loading,
56
- );
57
-
58
- watch(() => requestQueue.value.length, onChangeRequestsQueue);
59
-
60
- onMounted(() => {
61
- window.addEventListener("loaderProgressOn", setLoaderOnHandler as EventListener);
62
- window.addEventListener("loaderProgressOff", setLoaderOffHandler as EventListener);
63
-
64
- if (props.resources) {
65
- onChangeRequestsQueue();
41
+ const resourceSubscriptions = computed(() => {
42
+ if (Array.isArray(props.resources)) {
43
+ return props.resources.map(getRequestWithoutQuery);
66
44
  }
67
- });
68
45
 
69
- onBeforeUnmount(() => {
70
- removeRequestUrl(resourceNamesArray.value);
46
+ return [getRequestWithoutQuery(props.resources)];
71
47
  });
72
48
 
73
- onUnmounted(() => {
74
- window.removeEventListener("loaderProgressOn", setLoaderOnHandler as EventListener);
75
- window.removeEventListener("loaderProgressOff", setLoaderOffHandler as EventListener);
49
+ const isActiveRequests = computed(() => {
50
+ const isAnyRequestActive = props.resources === "any" && requestQueue.value.length;
51
+ const isSubscribedRequestsActive = resourceSubscriptions.value.some((resource) =>
52
+ requestQueue.value.includes(resource),
53
+ );
54
+
55
+ return isAnyRequestActive || isSubscribedRequestsActive;
76
56
  });
77
57
 
58
+ watch(() => requestQueue, onChangeRequestsQueue, { immediate: true, deep: true });
59
+
78
60
  watch(
79
61
  () => props.loading,
80
- () => {
81
- if (props.loading) {
82
- addRequestUrl(INFINITY_LOADING);
83
- isLoading.value = true;
84
- } else {
85
- removeRequestUrl(INFINITY_LOADING);
86
- }
87
- },
88
- { immediate: true },
62
+ () => (props.loading ? start() : stop()),
89
63
  );
90
64
 
91
- function setLoaderOnHandler(event: CustomEvent<{ resource: string }>) {
92
- loaderProgressOn(event.detail.resource);
93
- }
94
-
95
- function setLoaderOffHandler(event: CustomEvent<{ resource: string }>) {
96
- loaderProgressOff(event.detail.resource);
97
- }
98
-
99
65
  function onChangeRequestsQueue() {
100
- const isActiveRequests =
101
- isPropsLoading.value ||
102
- resourceNamesArray.value.some((resource: string) => {
103
- return requestQueue.value.includes(resource);
104
- });
66
+ if (props.loading !== undefined) return;
105
67
 
106
- if (isActiveRequests && !isStarted.value && isLoading.value) {
68
+ if (isActiveRequests.value && !isLoading.value) {
107
69
  start();
108
- } else if (!isActiveRequests && isStarted.value && show.value) {
109
- done();
70
+ } else if (!isActiveRequests.value && isLoading.value && show.value) {
71
+ stop();
110
72
  }
111
73
  }
112
74
 
@@ -125,8 +87,9 @@ function afterEnter() {
125
87
  }
126
88
 
127
89
  function work() {
90
+ // TODO: Use requestAnimationFrame for animations instead of setTimeout for better performance and smoothness.
128
91
  setTimeout(() => {
129
- if (!isStarted.value) {
92
+ if (!isLoading.value) {
130
93
  return;
131
94
  }
132
95
 
@@ -152,7 +115,7 @@ function start() {
152
115
  function set(amount: number) {
153
116
  let currentProgress;
154
117
 
155
- if (isStarted.value) {
118
+ if (isLoading.value) {
156
119
  currentProgress = amount < progress.value ? clamp(amount, 0, 100) : clamp(amount, 0.8, 100);
157
120
  } else {
158
121
  currentProgress = 0;
@@ -200,10 +163,30 @@ function increase(amount?: number) {
200
163
  set(clamp(currentProgress + (amount || 0), 0, MAXIMUM));
201
164
  }
202
165
 
203
- function done() {
166
+ function stop() {
204
167
  set(100);
205
168
  }
206
169
 
170
+ defineExpose({
171
+ /**
172
+ * Start loading animation.
173
+ * @property {Function}
174
+ */
175
+ start,
176
+
177
+ /**
178
+ * Stop loading animation.
179
+ * @property {Function}
180
+ */
181
+ stop,
182
+
183
+ /**
184
+ * Loading state.
185
+ * @property {Boolean}
186
+ */
187
+ isLoading,
188
+ });
189
+
207
190
  /**
208
191
  * Get element / nested component attributes for each config token ✨
209
192
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
@@ -20,6 +20,6 @@ export default /*tw*/ {
20
20
  defaults: {
21
21
  color: "brand",
22
22
  size: "md",
23
- loading: false,
23
+ loading: undefined,
24
24
  },
25
25
  };
@@ -6,4 +6,3 @@ export const COMPONENT_NAME = "ULoaderProgress";
6
6
 
7
7
  export const MAXIMUM = 99;
8
8
  export const SPEED = 150;
9
- export const INFINITY_LOADING = "Infinity";
@@ -21,33 +21,21 @@ The loader uses queue of resources and will be shown until at least one item is
21
21
  import { useLoaderProgress } from "vueless";
22
22
 
23
23
  const {
24
- isLoading,
25
24
  loaderProgressOn,
26
25
  loaderProgressOff,
27
26
  requestQueue,
28
- addRequestUrl
29
- removeRequestUrl
30
27
  } = useLoaderProgress();
31
28
 
32
- // get loader state
33
- console.log(isLoading.value);
34
-
35
29
  // show loader (add resource into queue)
36
30
  loaderProgressOn("/transactions");
31
+ loaderProgressOff(["/transactions", "/products"]);
37
32
 
38
33
  // hide loader (remove resource from queue)
39
34
  loaderProgressOff("/transactions");
35
+ loaderProgressOff(["/transactions", "/products"]);
40
36
 
41
- // get current resource array
37
+ // get current global resource queue
42
38
  console.log(requestQueue.value);
43
-
44
- // add resource into loader queue
45
- addRequestUrl("/transactions");
46
- addRequestUrl(["/transactions", "/products"]);
47
-
48
- // remove resource from loader queue
49
- removeRequestUrl("/transactions");
50
- removeRequestUrl(["/transactions", "/products"]);
51
39
  `} language="jsx" dark />
52
40
 
53
41
  ## Using loader outside Vue components
@@ -97,7 +97,7 @@ const EnumVariantTemplate: StoryFn<ULoaderProgressArgs> = (
97
97
  });
98
98
 
99
99
  export const Default = DefaultTemplate.bind({});
100
- Default.args = { loading: false };
100
+ Default.args = {};
101
101
 
102
102
  export const Color = EnumVariantTemplate.bind({});
103
103
  Color.args = { enum: "color" };
@@ -33,7 +33,7 @@ export interface Props {
33
33
  /**
34
34
  * API resource names (endpoint URIs).
35
35
  */
36
- resources?: string | string[];
36
+ resources?: string | string[] | "any" | ["any"];
37
37
 
38
38
  /**
39
39
  * Progress size.
@@ -1,4 +1,4 @@
1
- import { inject, readonly, ref } from "vue";
1
+ import { inject, onBeforeUnmount, readonly, ref } from "vue";
2
2
 
3
3
  import type { Ref } from "vue";
4
4
 
@@ -7,38 +7,14 @@ import { getRequestWithoutQuery } from "./utilLoaderProgress.ts";
7
7
  export const LoaderProgressSymbol = Symbol.for("vueless:loader-progress");
8
8
 
9
9
  type LoaderProgress = {
10
- isLoading: Ref<boolean>;
11
10
  requestQueue: Readonly<Ref<readonly string[]>>;
12
11
  loaderProgressOn: (url: string | string[]) => void;
13
12
  loaderProgressOff: (url: string | string[]) => void;
14
- addRequestUrl: (url: string | string[]) => void;
15
- removeRequestUrl: (url: string | string[]) => void;
16
13
  };
17
14
 
18
- const isLoading = ref(false);
19
15
  const requestQueue = ref<string[]>([]);
20
- const requestTimeout = ref<number | undefined>(undefined);
21
16
 
22
17
  function loaderProgressOn(url: string | string[]): void {
23
- addRequestUrl(url);
24
- isLoading.value = true;
25
-
26
- if (requestTimeout.value !== undefined) {
27
- clearTimeout(requestTimeout.value);
28
- }
29
- }
30
-
31
- function loaderProgressOff(url: string | string[]): void {
32
- removeRequestUrl(url);
33
-
34
- requestTimeout.value = window.setTimeout(() => {
35
- if (!requestQueue.value.length) {
36
- isLoading.value = false;
37
- }
38
- }, 50);
39
- }
40
-
41
- function addRequestUrl(url: string | string[]): void {
42
18
  if (Array.isArray(url)) {
43
19
  requestQueue.value.push(...url.map(getRequestWithoutQuery));
44
20
  } else {
@@ -46,28 +22,41 @@ function addRequestUrl(url: string | string[]): void {
46
22
  }
47
23
  }
48
24
 
49
- function removeRequestUrl(url: string | string[]): void {
25
+ function loaderProgressOff(url: string | string[]): void {
50
26
  if (Array.isArray(url)) {
51
- url.map(getRequestWithoutQuery).forEach(removeRequestUrl);
27
+ url.map(getRequestWithoutQuery).forEach(loaderProgressOff);
52
28
  } else {
53
29
  requestQueue.value = requestQueue.value.filter((item) => item !== getRequestWithoutQuery(url));
54
30
  }
55
31
  }
56
32
 
33
+ function setLoaderOnHandler(event: CustomEvent<{ resource: string }>) {
34
+ loaderProgressOn(event.detail.resource);
35
+ }
36
+
37
+ function setLoaderOffHandler(event: CustomEvent<{ resource: string }>) {
38
+ loaderProgressOff(event.detail.resource);
39
+ }
40
+
57
41
  export function createLoaderProgress(): LoaderProgress {
58
42
  return {
59
- isLoading,
60
43
  requestQueue: readonly(requestQueue),
61
44
  loaderProgressOn,
62
45
  loaderProgressOff,
63
- addRequestUrl,
64
- removeRequestUrl,
65
46
  };
66
47
  }
67
48
 
68
49
  export function useLoaderProgress(): LoaderProgress {
69
50
  const loaderProgress = inject<LoaderProgress>(LoaderProgressSymbol);
70
51
 
52
+ window.addEventListener("loaderProgressOn", setLoaderOnHandler as EventListener);
53
+ window.addEventListener("loaderProgressOff", setLoaderOffHandler as EventListener);
54
+
55
+ onBeforeUnmount(() => {
56
+ window.removeEventListener("loaderProgressOn", setLoaderOnHandler as EventListener);
57
+ window.removeEventListener("loaderProgressOff", setLoaderOffHandler as EventListener);
58
+ });
59
+
71
60
  if (!loaderProgress) {
72
61
  throw new Error(
73
62
  "LoaderProgress not provided. Ensure you are using `provide` with `LoaderProgressSymbol`.",