vueless 0.0.702 → 0.0.703

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.702",
3
+ "version": "0.0.703",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -287,7 +287,7 @@ const {
287
287
  @binding {string} icon-name
288
288
  -->
289
289
  <slot name="left" :icon-name="leftIcon">
290
- <UIcon v-if="leftIcon" internal color="inherit" :name="leftIcon" v-bind="leftIconAttrs" />
290
+ <UIcon v-if="leftIcon" internal color="gray" :name="leftIcon" v-bind="leftIconAttrs" />
291
291
  </slot>
292
292
  </span>
293
293
 
@@ -332,13 +332,7 @@ const {
332
332
  @binding {string} icon-name
333
333
  -->
334
334
  <slot name="right" :icon-name="rightIcon">
335
- <UIcon
336
- v-if="rightIcon"
337
- internal
338
- color="inherit"
339
- :name="rightIcon"
340
- v-bind="rightIconAttrs"
341
- />
335
+ <UIcon v-if="rightIcon" internal color="gray" :name="rightIcon" v-bind="rightIconAttrs" />
342
336
  </slot>
343
337
  </div>
344
338
  </label>
@@ -1,3 +1,4 @@
1
+ import { computed } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -25,7 +26,8 @@ export default {
25
26
  title: "Form Inputs & Controls / Input",
26
27
  component: UInput,
27
28
  args: {
28
- label: "Label",
29
+ label: "Full Name",
30
+ modelValue: "Satoshi Nakamoto",
29
31
  },
30
32
  argTypes: {
31
33
  ...getArgTypes(UInput.__name),
@@ -41,13 +43,19 @@ const DefaultTemplate: StoryFn<UInputArgs> = (args: UInputArgs) => ({
41
43
  components: { UInput, UIcon },
42
44
  setup() {
43
45
  const slots = getSlotNames(UInput.__name);
46
+ const errorMessage = computed(() =>
47
+ args.modelValue === "" && args.error !== ""
48
+ ? "This field is required. Please enter a value."
49
+ : "",
50
+ );
44
51
 
45
- return { args, slots };
52
+ return { args, slots, errorMessage };
46
53
  },
47
54
  template: `
48
55
  <UInput
49
56
  v-bind="args"
50
57
  v-model="args.modelValue"
58
+ :error="errorMessage"
51
59
  class="max-w-96"
52
60
  >
53
61
  ${args.slotTemplate || getSlotsFragment("")}
@@ -61,15 +69,15 @@ const EnumVariantTemplate: StoryFn<UInputArgs> = (args: UInputArgs, { argTypes }
61
69
  function getDescription(option: string) {
62
70
  switch (option) {
63
71
  case "string":
64
- return "Only letters are allowed";
72
+ return "Only letters are allowed.";
65
73
  case "number":
66
- return "Numbers are allowed (including decimals)";
74
+ return "Numbers are allowed (including decimals).";
67
75
  case "integer":
68
- return "Only integers are allowed";
76
+ return "Only integers are allowed.";
69
77
  case "stringAndNumber":
70
- return "Letters and numbers are allowed";
78
+ return "Letters and numbers are allowed.";
71
79
  case "symbol":
72
- return "Special characters are allowed";
80
+ return "Special characters are allowed.";
73
81
  default:
74
82
  return "";
75
83
  }
@@ -96,18 +104,18 @@ const EnumVariantTemplate: StoryFn<UInputArgs> = (args: UInputArgs, { argTypes }
96
104
  :key="index"
97
105
  v-bind="args"
98
106
  :[args.enum]="option"
99
- :label="option"
100
- :description="getDescription(option)"
107
+ :placeholder="args.enum === 'validationRule' ? '' : option"
108
+ :label="args.enum === 'validationRule' ? option : 'Full Name'"
109
+ :description="args.enum === 'validationRule' ? getDescription(option) : ''"
101
110
  class="max-w-96"
102
111
  />
103
112
 
104
113
  <UInput
105
114
  v-if="args.enum === 'validationRule'"
106
- v-bind="args"
107
- validation-rule="^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$"
115
+ validation-rule="^#([a-fA-F0-9]{0,6}|[a-fA-F0-9]{0,8})$"
108
116
  label="Custom RegExp"
109
- description="Enter a valid hex color code (e.g., #FF5733)"
110
- labelAlign="topWithDesc"
117
+ description="Enter a valid hex color code (e.g., #FF5733)."
118
+ label-align="topWithDesc"
111
119
  placeholder="#FF5733"
112
120
  class="max-w-96"
113
121
  />
@@ -119,31 +127,39 @@ export const Default = DefaultTemplate.bind({});
119
127
  Default.args = {};
120
128
 
121
129
  export const Placeholder = DefaultTemplate.bind({});
122
- Placeholder.args = { placeholder: "Type something here..." };
130
+ Placeholder.args = { modelValue: "", placeholder: "Type something here...", error: "" };
123
131
 
124
132
  export const Description = DefaultTemplate.bind({});
125
133
  Description.args = { description: "Provide additional details if necessary." };
126
134
 
127
135
  export const Error = DefaultTemplate.bind({});
128
- Error.args = { error: "This field is required. Please enter a value." };
136
+ Error.args = { modelValue: "" };
129
137
 
130
138
  export const Readonly = DefaultTemplate.bind({});
131
- Readonly.args = { readonly: true, modelValue: "Pre-filled content that cannot be changed" };
139
+ Readonly.args = {
140
+ readonly: true,
141
+ label: "Readonly data",
142
+ modelValue: "Pre-filled content that cannot be changed",
143
+ };
132
144
 
133
145
  export const Disabled = DefaultTemplate.bind({});
134
146
  Disabled.args = { disabled: true };
135
147
 
136
148
  export const TypePassword = DefaultTemplate.bind({});
137
- TypePassword.args = { type: "password" };
149
+ TypePassword.args = { label: "Password", modelValue: "donotforgetyourpassword", type: "password" };
138
150
 
139
151
  export const LabelPlacement = EnumVariantTemplate.bind({});
140
- LabelPlacement.args = { enum: "labelAlign" };
152
+ LabelPlacement.args = { enum: "labelAlign", label: "Full Name", modelValue: "" };
141
153
 
142
154
  export const Sizes = EnumVariantTemplate.bind({});
143
- Sizes.args = { enum: "size" };
155
+ Sizes.args = { enum: "size", modelValue: "" };
144
156
 
145
157
  export const ValidationRules = EnumVariantTemplate.bind({});
146
- ValidationRules.args = { enum: "validationRule", labelAlign: "topWithDesc" };
158
+ ValidationRules.args = {
159
+ enum: "validationRule",
160
+ labelAlign: "topWithDesc",
161
+ modelValue: "",
162
+ };
147
163
  ValidationRules.parameters = {
148
164
  docs: {
149
165
  description: {
@@ -161,13 +177,11 @@ export const IconProps: StoryFn<UInputArgs> = (args) => ({
161
177
  template: `
162
178
  <URow>
163
179
  <UInput
164
- v-bind="args"
165
180
  left-icon="feedback"
166
181
  label="Your opinion"
167
182
  placeholder="Share your feedback with us"
168
183
  />
169
184
  <UInput
170
- v-bind="args"
171
185
  right-icon="person"
172
186
  label="Username"
173
187
  placeholder="Enter your username"
@@ -18,7 +18,7 @@ export interface Props {
18
18
  /**
19
19
  * Label placement.
20
20
  */
21
- labelAlign?: "top" | "topInside" | "topWithDesc" | "left" | "right";
21
+ labelAlign?: "topInside" | "top" | "topWithDesc" | "left" | "right";
22
22
 
23
23
  /**
24
24
  * Input placeholder.
@@ -118,6 +118,7 @@ const { moneyInputAttrs } = useUI<Config>(defaultConfig);
118
118
  :label-align="labelAlign"
119
119
  :placeholder="placeholder"
120
120
  :description="description"
121
+ :readonly="readonly"
121
122
  :error="error"
122
123
  :disabled="disabled"
123
124
  inputmode="decimal"
@@ -130,16 +131,16 @@ const { moneyInputAttrs } = useUI<Config>(defaultConfig);
130
131
  @input="onInput"
131
132
  >
132
133
  <template #left>
133
- <!--
134
- @slot Use it to add something left.
134
+ <!--
135
+ @slot Use it to add something left.
135
136
  @binding {string} icon-name
136
137
  -->
137
138
  <slot name="left" :icon-name="leftIcon" />
138
139
  </template>
139
140
 
140
141
  <template #right>
141
- <!--
142
- @slot Use it to add something right.
142
+ <!--
143
+ @slot Use it to add something right.
143
144
  @binding {string} icon-name
144
145
  -->
145
146
  <slot name="right" :icon-name="leftIcon" />
@@ -1,3 +1,4 @@
1
+ import { computed } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -9,6 +10,8 @@ import UInputMoney from "../../ui.form-input-money/UInputMoney.vue";
9
10
  import UCol from "../../ui.container-col/UCol.vue";
10
11
  import UIcon from "../../ui.image-icon/UIcon.vue";
11
12
  import UButton from "../../ui.button/UButton.vue";
13
+ import URow from "../../ui.container-row/URow.vue";
14
+ import UAvatar from "../../ui.image-avatar/UAvatar.vue";
12
15
 
13
16
  import type { Meta, StoryFn } from "@storybook/vue3";
14
17
  import type { Props } from "../types.ts";
@@ -23,7 +26,8 @@ export default {
23
26
  title: "Form Inputs & Controls / Input Money",
24
27
  component: UInputMoney,
25
28
  args: {
26
- label: "Label",
29
+ label: "Expected amount",
30
+ modelValue: 245000.42,
27
31
  },
28
32
  argTypes: {
29
33
  ...getArgTypes(UInputMoney.__name),
@@ -39,13 +43,16 @@ const DefaultTemplate: StoryFn<UInputMoneyArgs> = (args: UInputMoneyArgs) => ({
39
43
  components: { UInputMoney, UIcon, UButton },
40
44
  setup() {
41
45
  const slots = getSlotNames(UInputMoney.__name);
46
+ const errorMessage = computed(() => (Number(args.modelValue) > 0 ? "" : args.error));
42
47
 
43
- return { args, slots };
48
+ return { args, slots, errorMessage };
44
49
  },
45
50
  template: `
46
51
  <UInputMoney
47
52
  v-bind="args"
48
53
  v-model="args.modelValue"
54
+ :error="errorMessage"
55
+ class="max-w-96"
49
56
  >
50
57
  ${args.slotTemplate || getSlotsFragment("")}
51
58
  </UInputMoney>
@@ -55,89 +62,159 @@ const DefaultTemplate: StoryFn<UInputMoneyArgs> = (args: UInputMoneyArgs) => ({
55
62
  const EnumVariantTemplate: StoryFn<UInputMoneyArgs> = (args: UInputMoneyArgs, { argTypes }) => ({
56
63
  components: { UInputMoney, UCol },
57
64
  setup() {
65
+ let filteredOptions = argTypes?.[args.enum]?.options;
66
+
67
+ if (args.enum === "labelAlign") {
68
+ filteredOptions = argTypes?.[args.enum]?.options?.filter(
69
+ (item) => item !== "right" && item !== "topWithDesc",
70
+ );
71
+ }
72
+
58
73
  return {
59
74
  args,
60
- options: argTypes?.[args.enum]?.options,
75
+ filteredOptions,
61
76
  };
62
77
  },
63
78
  template: `
64
79
  <UCol>
65
80
  <UInputMoney
66
- v-for="(option, index) in options"
81
+ v-for="(option, index) in filteredOptions"
67
82
  :key="index"
68
- v-bind="args"
69
83
  :[args.enum]="option"
70
- :label="option"
84
+ label="Expected amount"
85
+ :placeholder="option"
86
+ class="max-w-96"
71
87
  />
72
88
  </UCol>
73
89
  `,
74
90
  });
75
91
 
76
92
  export const Default = DefaultTemplate.bind({});
77
- Default.args = { modelValue: 245000.42 };
93
+ Default.args = {};
78
94
 
79
- export const Sizes = EnumVariantTemplate.bind({});
80
- Sizes.args = { enum: "size" };
95
+ export const Placeholder = DefaultTemplate.bind({});
96
+ Placeholder.args = { placeholder: "Enter amount in USD" };
81
97
 
82
- export const Symbol = DefaultTemplate.bind({});
83
- Symbol.args = { symbol: "" };
98
+ export const Description = DefaultTemplate.bind({});
99
+ Description.args = { description: "Please enter the transaction amount." };
84
100
 
85
- export const LabelAlign = EnumVariantTemplate.bind({});
86
- LabelAlign.args = { enum: "labelAlign" };
101
+ export const Error = DefaultTemplate.bind({});
102
+ Error.args = {
103
+ modelValue: -245000.42,
104
+ error: "Invalid amount. Please enter a positive number with up to two decimal places.",
105
+ };
87
106
 
88
- export const Placeholder = DefaultTemplate.bind({});
89
- Placeholder.args = { placeholder: "Placeholder" };
107
+ export const ReadOnly = DefaultTemplate.bind({});
108
+ ReadOnly.args = { readonly: true };
90
109
 
91
- export const Error = DefaultTemplate.bind({});
92
- Error.args = { error: "Some error." };
110
+ export const Disabled = DefaultTemplate.bind({});
111
+ Disabled.args = { disabled: true };
93
112
 
94
- export const Description = DefaultTemplate.bind({});
95
- Description.args = { description: "Some description." };
113
+ export const Symbol = DefaultTemplate.bind({});
114
+ Symbol.args = { symbol: "" };
115
+
116
+ export const Sizes = EnumVariantTemplate.bind({});
117
+ Sizes.args = { enum: "size" };
96
118
 
97
- export const MinFractionDigits = DefaultTemplate.bind({});
98
- MinFractionDigits.args = { minFractionDigits: 2, maxFractionDigits: 4 };
119
+ export const LabelAlign = EnumVariantTemplate.bind({});
120
+ LabelAlign.args = { enum: "labelAlign" };
99
121
 
100
- export const MaxFractionDigits = DefaultTemplate.bind({});
101
- MaxFractionDigits.args = { maxFractionDigits: 4 };
122
+ export const LimitFractionDigits = DefaultTemplate.bind({});
123
+ LimitFractionDigits.args = {
124
+ minFractionDigits: 4,
125
+ maxFractionDigits: 6,
126
+ description: "You can enter from 4 to 6 decimal places.",
127
+ };
128
+ LimitFractionDigits.parameters = {
129
+ docs: {
130
+ description: {
131
+ story:
132
+ // eslint-disable-next-line vue/max-len
133
+ "`minFractionDigits` and `maxFractionDigits` props determine the minimum/maximum number of digits to display after the decimal separator.",
134
+ },
135
+ },
136
+ };
102
137
 
103
138
  export const DecimalSeparator = DefaultTemplate.bind({});
104
139
  DecimalSeparator.args = { decimalSeparator: "." };
140
+ DecimalSeparator.parameters = {
141
+ docs: {
142
+ description: {
143
+ story: "A symbol used to separate the integer part from the fractional part of a number.",
144
+ },
145
+ },
146
+ };
105
147
 
106
148
  export const ThousandsSeparator = DefaultTemplate.bind({});
107
149
  ThousandsSeparator.args = { thousandsSeparator: "-" };
150
+ ThousandsSeparator.parameters = {
151
+ docs: {
152
+ description: {
153
+ story: "A symbol used to separate the thousand parts of a number.",
154
+ },
155
+ },
156
+ };
108
157
 
109
158
  export const PositiveOnly = DefaultTemplate.bind({});
110
159
  PositiveOnly.args = { positiveOnly: true };
160
+ PositiveOnly.parameters = {
161
+ docs: {
162
+ description: {
163
+ story: "Allow only positive values.",
164
+ },
165
+ },
166
+ };
111
167
 
112
168
  export const Prefix = DefaultTemplate.bind({});
113
169
  Prefix.args = { prefix: "+" };
170
+ Prefix.parameters = {
171
+ docs: {
172
+ description: {
173
+ story: "Prefix to display before input value.",
174
+ },
175
+ },
176
+ };
114
177
 
115
- export const ReadOnly = DefaultTemplate.bind({});
116
- ReadOnly.args = { readonly: true };
117
-
118
- export const Disabled = DefaultTemplate.bind({});
119
- Disabled.args = { disabled: true };
120
-
121
- export const LeftIcon = DefaultTemplate.bind({});
122
- LeftIcon.args = { leftIcon: "star" };
123
-
124
- export const RightIcon = DefaultTemplate.bind({});
125
- RightIcon.args = { rightIcon: "star" };
126
-
127
- export const LeftSlot = DefaultTemplate.bind({});
128
- LeftSlot.args = {
129
- slotTemplate: `
130
- <template #left>
131
- <UButton variant="thirdary" filled square label="Filter" class="rounded-r-none h-full" />
132
- </template>
178
+ export const IconProps: StoryFn<UInputMoneyArgs> = (args) => ({
179
+ components: { UInputMoney, URow },
180
+ setup() {
181
+ return { args };
182
+ },
183
+ template: `
184
+ <URow>
185
+ <UInputMoney
186
+ left-icon="payments"
187
+ label="Annual payment"
188
+ placeholder="Enter your annual payment"
189
+ />
190
+ <UInputMoney
191
+ right-icon="currency_exchange"
192
+ label="Total amount"
193
+ symbol="£"
194
+ placeholder="Enter the amount you want to exchange"
195
+ />
196
+ </URow>
133
197
  `,
134
- };
198
+ });
135
199
 
136
- export const RightSlot = DefaultTemplate.bind({});
137
- RightSlot.args = {
138
- slotTemplate: `
139
- <template #right>
140
- <UButton variant="thirdary" filled square label="Filter" class="rounded-l-none" />
141
- </template>
200
+ export const Slots: StoryFn<UInputMoneyArgs> = (args) => ({
201
+ components: { UInputMoney, URow, UButton, UAvatar },
202
+ setup() {
203
+ return { args };
204
+ },
205
+ template: `
206
+ <URow no-mobile>
207
+ <UInputMoney v-bind="args">
208
+ <template #left>
209
+ <UAvatar />
210
+ </template>
211
+ </UInputMoney>
212
+
213
+ <UInputMoney v-bind="args" :config="{ moneyInput: { rightSlot: 'pr-0' } }">
214
+ <template #right>
215
+ <UButton label="Calculate" size="sm" class="rounded-l-none h-full" />
216
+ </template>
217
+ </UInputMoney>
218
+ </URow>
142
219
  `,
143
- };
220
+ });
@@ -7,13 +7,14 @@ import {
7
7
 
8
8
  import UInputNumber from "../../ui.form-input-number/UInputNumber.vue";
9
9
  import UCol from "../../ui.container-col/UCol.vue";
10
+ import UBadge from "../../ui.text-badge/UBadge.vue";
10
11
 
11
12
  import type { Meta, StoryFn } from "@storybook/vue3";
12
13
  import type { UInputNumberProps } from "../types.ts";
13
14
 
14
15
  interface UInputNumberArgs extends UInputNumberProps {
15
16
  slotTemplate?: string;
16
- enum: "size";
17
+ enum: "size" | "labelAlign";
17
18
  }
18
19
 
19
20
  export default {
@@ -22,6 +23,7 @@ export default {
22
23
  component: UInputNumber,
23
24
  args: {
24
25
  modelValue: 1,
26
+ label: "Choose the number of items",
25
27
  },
26
28
  argTypes: {
27
29
  ...getArgTypes(UInputNumber.__name),
@@ -34,7 +36,7 @@ export default {
34
36
  } as Meta;
35
37
 
36
38
  const DefaultTemplate: StoryFn<UInputNumberArgs> = (args: UInputNumberArgs) => ({
37
- components: { UInputNumber },
39
+ components: { UInputNumber, UBadge },
38
40
  setup() {
39
41
  const slots = getSlotNames(UInputNumber.__name);
40
42
 
@@ -55,24 +57,14 @@ const EnumVariantTemplate: StoryFn<UInputNumberArgs> = (args: UInputNumberArgs,
55
57
  options: argTypes?.[args.enum]?.options,
56
58
  };
57
59
  },
58
- data() {
59
- return {
60
- sizeValues: [
61
- { count: 1, label: "sm" },
62
- { count: 1, label: "md" },
63
- { count: 1, label: "lg" },
64
- ],
65
- };
66
- },
67
60
  template: `
68
61
  <UCol gap="xl">
69
62
  <UInputNumber
70
63
  v-for="(option, index) in options"
71
64
  :key="index"
72
65
  v-bind="args"
73
- v-model="sizeValues[index].count"
66
+ :label="option"
74
67
  :[args.enum]="option"
75
- :label="sizeValues[index].label"
76
68
  />
77
69
  </UCol>
78
70
  `,
@@ -81,15 +73,19 @@ const EnumVariantTemplate: StoryFn<UInputNumberArgs> = (args: UInputNumberArgs,
81
73
  export const Default = DefaultTemplate.bind({});
82
74
  Default.args = { step: 1, min: 1, max: 100 };
83
75
 
84
- export const Label = DefaultTemplate.bind({});
85
- Label.args = {
86
- modelValue: 1,
87
- step: 1,
88
- min: 1,
89
- max: 100,
90
- label: "Year",
76
+ export const Description = DefaultTemplate.bind({});
77
+ Description.args = {
78
+ label: "Choose your storage capacity (in GB)",
79
+ description: "Storage capacity may not exceed 40 GB.",
80
+ max: 40,
91
81
  };
92
82
 
83
+ export const Error = DefaultTemplate.bind({});
84
+ Error.args = { modelValue: NaN, error: "Passed value has incorrect format." };
85
+
86
+ export const Disabled = DefaultTemplate.bind({});
87
+ Disabled.args = { disabled: true };
88
+
93
89
  export const Sizes = EnumVariantTemplate.bind({});
94
90
  Sizes.args = {
95
91
  enum: "size",
@@ -99,14 +95,24 @@ Sizes.args = {
99
95
  max: 100,
100
96
  };
101
97
 
98
+ export const LabelPlacement = EnumVariantTemplate.bind({});
99
+ LabelPlacement.args = { enum: "labelAlign", description: "You may configure step value." };
100
+
102
101
  export const ValueLimit = DefaultTemplate.bind({});
103
102
  ValueLimit.args = {
104
- modelValue: 1,
103
+ modelValue: 7,
105
104
  step: 1,
106
105
  min: 5,
107
106
  max: 10,
108
107
  label: "Min is 5 | Max is 10",
109
108
  };
109
+ ValueLimit.parameters = {
110
+ docs: {
111
+ description: {
112
+ story: "To set the minimum and maximum values, use the `min` and `max` props.",
113
+ },
114
+ },
115
+ };
110
116
 
111
117
  export const Step = DefaultTemplate.bind({});
112
118
  Step.args = {
@@ -116,3 +122,19 @@ Step.args = {
116
122
  max: 100,
117
123
  label: "Step is 5",
118
124
  };
125
+ Step.parameters = {
126
+ docs: {
127
+ description: {
128
+ story: "`step` prop determines the increment/decrement value.",
129
+ },
130
+ },
131
+ };
132
+
133
+ export const SlotLabel = DefaultTemplate.bind({});
134
+ SlotLabel.args = {
135
+ slotTemplate: `
136
+ <template #label="{ label }">
137
+ <UBadge :label="label" />
138
+ </template>
139
+ `,
140
+ };
@@ -71,6 +71,7 @@ const { config, inputLabelAttrs, containerAttrs, counterAttrs, totalAttrs, stars
71
71
  :size="size"
72
72
  :align="labelAlign"
73
73
  :description="description"
74
+ :disabled="disabled"
74
75
  v-bind="inputLabelAttrs"
75
76
  :data-test="dataTest"
76
77
  >
@@ -13,6 +13,11 @@ export default /*tw*/ {
13
13
  stars: "flex",
14
14
  star: {
15
15
  base: "{UIcon}",
16
+ variants: {
17
+ disabled: {
18
+ true: "text-gray-400 pointer-events-none",
19
+ },
20
+ },
16
21
  defaults: {
17
22
  size: {
18
23
  sm: "xs",
@@ -1,3 +1,4 @@
1
+ import { computed } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -6,14 +7,15 @@ import {
6
7
  } from "../../utils/storybook.ts";
7
8
 
8
9
  import UInputRating from "../../ui.form-input-rating/UInputRating.vue";
9
- import URow from "../../ui.container-row/URow.vue";
10
+ import UCol from "../../ui.container-col/UCol.vue";
11
+ import UBadge from "../../ui.text-badge/UBadge.vue";
10
12
 
11
13
  import type { Meta, StoryFn } from "@storybook/vue3";
12
14
  import type { Props } from "../types.ts";
13
15
 
14
16
  interface UInputRatingArgs extends Props {
15
17
  slotTemplate?: string;
16
- enum: "size";
18
+ enum: "size" | "labelAlign";
17
19
  }
18
20
 
19
21
  export default {
@@ -22,7 +24,7 @@ export default {
22
24
  component: UInputRating,
23
25
  args: {
24
26
  modelValue: 2,
25
- label: "Label",
27
+ label: "Rate your experience: ",
26
28
  },
27
29
  argTypes: {
28
30
  ...getArgTypes(UInputRating.__name),
@@ -35,16 +37,20 @@ export default {
35
37
  } as Meta;
36
38
 
37
39
  const DefaultTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs) => ({
38
- components: { UInputRating },
40
+ components: { UInputRating, UBadge },
39
41
  setup() {
40
42
  const slots = getSlotNames(UInputRating.__name);
43
+ const errorMessage = computed(() =>
44
+ !args.modelValue && args.error !== "" ? "Your review helps us improve our services." : "",
45
+ );
41
46
 
42
- return { args, slots };
47
+ return { args, slots, errorMessage };
43
48
  },
44
49
  template: `
45
50
  <UInputRating
46
51
  v-bind="args"
47
52
  v-model="args.modelValue"
53
+ :error="errorMessage"
48
54
  >
49
55
  ${args.slotTemplate || getSlotsFragment("")}
50
56
  </UInputRating>
@@ -52,7 +58,7 @@ const DefaultTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs) => (
52
58
  });
53
59
 
54
60
  const EnumVariantTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs, { argTypes }) => ({
55
- components: { UInputRating, URow },
61
+ components: { UInputRating, UCol },
56
62
  setup() {
57
63
  return {
58
64
  args,
@@ -60,47 +66,84 @@ const EnumVariantTemplate: StoryFn<UInputRatingArgs> = (args: UInputRatingArgs,
60
66
  };
61
67
  },
62
68
  template: `
63
- <URow gap="2xl">
69
+ <UCol>
64
70
  <UInputRating
65
71
  v-for="(option, index) in options"
66
72
  :key="index"
67
73
  v-bind="args"
68
74
  v-model="args.modelValue"
69
75
  :[args.enum]="option"
76
+ :description="option"
77
+ modelValue="2"
70
78
  />
71
- </URow>
79
+ </UCol>
72
80
  `,
73
81
  });
74
82
 
75
83
  export const Default = DefaultTemplate.bind({});
76
84
  Default.args = {};
77
85
 
86
+ export const Description = DefaultTemplate.bind({});
87
+ Description.args = { description: "Your review helps us improve our services." };
88
+
89
+ export const Error = DefaultTemplate.bind({});
90
+ Error.args = { selectable: true, modelValue: NaN };
91
+
92
+ export const Disabled = DefaultTemplate.bind({});
93
+ Disabled.args = { disabled: true };
94
+
78
95
  export const Sizes = EnumVariantTemplate.bind({});
79
96
  Sizes.args = { enum: "size" };
80
97
 
98
+ export const LabelPlacement = EnumVariantTemplate.bind({});
99
+ LabelPlacement.args = { enum: "labelAlign" };
100
+
81
101
  export const StarAmount = DefaultTemplate.bind({});
82
- StarAmount.args = { modelValue: 4, stars: 7 };
102
+ StarAmount.args = { stars: 7 };
103
+ StarAmount.parameters = {
104
+ docs: {
105
+ description: {
106
+ story: "You can set the amount of stars to display via the `stars` prop.",
107
+ },
108
+ },
109
+ };
83
110
 
84
111
  export const Selectable = DefaultTemplate.bind({});
85
112
  Selectable.args = { selectable: true };
86
-
87
- export const Description = DefaultTemplate.bind({});
88
- Description.args = { description: "Some description" };
89
-
90
- export const Error = DefaultTemplate.bind({});
91
- Error.args = { error: "Some error" };
113
+ Selectable.parameters = {
114
+ docs: {
115
+ description: {
116
+ story: "If you want to allow users to select a rating, set the `selectable` prop to `true`.",
117
+ },
118
+ },
119
+ };
92
120
 
93
121
  export const WithCounter = DefaultTemplate.bind({});
94
- WithCounter.args = { counter: true };
122
+ WithCounter.args = { counter: true, selectable: true };
123
+ WithCounter.parameters = {
124
+ docs: {
125
+ description: {
126
+ story:
127
+ "You can display a counter of the current rating by setting the `counter` prop to `true`.",
128
+ },
129
+ },
130
+ };
95
131
 
96
132
  export const WithTotal = DefaultTemplate.bind({});
97
133
  WithTotal.args = { total: 250 };
134
+ WithTotal.parameters = {
135
+ docs: {
136
+ description: {
137
+ story: "You can display the total amount of reviews by setting the `total` prop to a number.",
138
+ },
139
+ },
140
+ };
98
141
 
99
142
  export const SlotCounter = DefaultTemplate.bind({});
100
143
  SlotCounter.args = {
101
144
  slotTemplate: `
102
- <template #counter="{counter}">
103
- Rating: {{counter}}
145
+ <template #counter="{ counter }">
146
+ <UBadge :label="'Current rating is: ' + String(counter)" color="green" />
104
147
  </template>
105
148
  `,
106
149
  };
@@ -110,7 +153,7 @@ SlotTotal.args = {
110
153
  total: 250,
111
154
  slotTemplate: `
112
155
  <template #total="{total}">
113
- ({{total}}) reviews
156
+ <UBadge :label="'Total reviews: ' + String(total)" color="green" />
114
157
  </template>
115
158
  `,
116
159
  };
@@ -5,11 +5,21 @@ 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
+
8
13
  /**
9
14
  * Rating value.
10
15
  */
11
16
  modelValue: number;
12
17
 
18
+ /**
19
+ * Rating description.
20
+ */
21
+ description?: string;
22
+
13
23
  /**
14
24
  * Rating number of stars.
15
25
  */
@@ -20,11 +30,6 @@ export interface Props {
20
30
  */
21
31
  size?: "sm" | "md" | "lg";
22
32
 
23
- /**
24
- * Rating label.
25
- */
26
- label?: string;
27
-
28
33
  /**
29
34
  * Rating label placement.
30
35
  */
@@ -36,9 +41,9 @@ export interface Props {
36
41
  error?: string;
37
42
 
38
43
  /**
39
- * Rating description.
44
+ * Disable the input.
40
45
  */
41
- description?: string;
46
+ disabled?: boolean;
42
47
 
43
48
  /**
44
49
  * Rating total.
@@ -168,6 +168,7 @@ const {
168
168
  v-if="!searchButtonLabel"
169
169
  internal
170
170
  interactive
171
+ color="gray"
171
172
  :name="rightIcon || config.defaults.searchIcon"
172
173
  v-bind="searchIconAttrs"
173
174
  :data-test="`${dataTest}-search-icon`"
@@ -4,18 +4,13 @@ export default /*tw*/ {
4
4
  base: "{UInput} {>searchInput}",
5
5
  rightSlot: "pr-0",
6
6
  },
7
- clearIcon: {
7
+ inputIcon: {
8
8
  base: "{UIcon}",
9
- defaults: {
10
- size: {
11
- sm: "xs",
12
- md: "sm",
13
- lg: "md",
9
+ variants: {
10
+ disabled: {
11
+ true: "text-gray-400 pointer-events-none",
14
12
  },
15
13
  },
16
- },
17
- searchIcon: {
18
- base: "{UIcon}",
19
14
  defaults: {
20
15
  size: {
21
16
  sm: "xs",
@@ -24,13 +19,15 @@ export default /*tw*/ {
24
19
  },
25
20
  },
26
21
  },
22
+ clearIcon: "{>inputIcon}",
23
+ searchIcon: "{>inputIcon}",
27
24
  searchButton: {
28
25
  base: "{UButton} rounded-l-none ml-1 outline outline-1 outline-gray-900",
29
26
  defaults: {
30
27
  size: {
31
28
  sm: "xs",
32
- md: "md",
33
- lg: "lg",
29
+ md: "sm",
30
+ lg: "md",
34
31
  },
35
32
  },
36
33
  },
@@ -1,3 +1,4 @@
1
+ import { computed } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -8,14 +9,17 @@ import {
8
9
  import UInputSearch from "../../ui.form-input-search/UInputSearch.vue";
9
10
  import UButton from "../../ui.button/UButton.vue";
10
11
  import UIcon from "../../ui.image-icon/UIcon.vue";
12
+ import UCol from "../../ui.container-col/UCol.vue";
11
13
  import URow from "../../ui.container-row/URow.vue";
14
+ import UAvatar from "../../ui.image-avatar/UAvatar.vue";
15
+ import UBadge from "../../ui.text-badge/UBadge.vue";
12
16
 
13
17
  import type { Meta, StoryFn } from "@storybook/vue3";
14
18
  import type { Props } from "../types.ts";
15
19
 
16
20
  interface UInputSearchArgs extends Props {
17
21
  slotTemplate?: string;
18
- enum: "size";
22
+ enum: "size" | "labelAlign";
19
23
  }
20
24
 
21
25
  export default {
@@ -25,6 +29,9 @@ export default {
25
29
  argTypes: {
26
30
  ...getArgTypes(UInputSearch.__name),
27
31
  },
32
+ args: {
33
+ modelValue: "Which UI library is the best?",
34
+ },
28
35
  parameters: {
29
36
  docs: {
30
37
  ...getDocsDescription(UInputSearch.__name),
@@ -36,70 +43,142 @@ const DefaultTemplate: StoryFn<UInputSearchArgs> = (args: UInputSearchArgs) => (
36
43
  components: { UInputSearch, UButton, UIcon },
37
44
  setup() {
38
45
  const slots = getSlotNames(UInputSearch.__name);
46
+ const errorMessage = computed(() =>
47
+ args.modelValue === "" && args.error !== ""
48
+ ? "This field is required. Please enter a value."
49
+ : "",
50
+ );
39
51
 
40
- return { args, slots };
52
+ return { args, slots, errorMessage };
41
53
  },
42
54
  template: `
43
- <UInputSearch v-bind="args" v-model="args.modelValue">
55
+ <UInputSearch
56
+ v-bind="args"
57
+ v-model="args.modelValue"
58
+ :error="errorMessage"
59
+ class="max-w-96"
60
+ >
44
61
  ${args.slotTemplate || getSlotsFragment("")}
45
62
  </UInputSearch>
46
63
  `,
47
64
  });
48
65
 
49
66
  const EnumVariantTemplate: StoryFn<UInputSearchArgs> = (args: UInputSearchArgs, { argTypes }) => ({
50
- components: { UInputSearch, URow },
67
+ components: { UInputSearch, UCol },
51
68
  setup() {
69
+ let filteredOptions = argTypes?.[args.enum]?.options;
70
+
71
+ if (args.enum === "labelAlign") {
72
+ filteredOptions = argTypes?.[args.enum]?.options?.filter(
73
+ (item) => item !== "right" && item !== "topWithDesc",
74
+ );
75
+ }
76
+
52
77
  return {
53
78
  args,
54
- options: argTypes?.[args.enum]?.options,
79
+ filteredOptions,
55
80
  };
56
81
  },
57
82
  template: `
58
- <URow>
83
+ <UCol>
59
84
  <UInputSearch
60
- v-for="(option, index) in options"
85
+ v-for="(option, index) in filteredOptions"
61
86
  :key="index"
62
87
  v-bind="args"
63
88
  v-model="args.modelValue"
64
89
  :[args.enum]="option"
90
+ :placeholder="option"
91
+ class="max-w-96"
65
92
  >
66
93
  </UInputSearch>
67
- </URow>
94
+ </UCol>
68
95
  `,
69
96
  });
70
97
 
71
98
  export const Default = DefaultTemplate.bind({});
72
99
  Default.args = {};
73
100
 
74
- export const SearchButton = DefaultTemplate.bind({});
75
- SearchButton.args = { searchButtonLabel: "Search" };
101
+ export const Label = DefaultTemplate.bind({});
102
+ Label.args = { label: "Search for product or brand" };
76
103
 
77
- export const MinLength = DefaultTemplate.bind({});
78
- MinLength.args = { minLength: 4 };
104
+ export const Placeholder = DefaultTemplate.bind({});
105
+ Placeholder.args = { modelValue: "", placeholder: "Type to search...", error: "" };
106
+
107
+ export const Description = DefaultTemplate.bind({});
108
+ Description.args = { description: "Search for additional details." };
109
+
110
+ export const Error = DefaultTemplate.bind({});
111
+ Error.args = { modelValue: "" };
112
+
113
+ export const Disabled = DefaultTemplate.bind({});
114
+ Disabled.args = { disabled: true };
79
115
 
80
116
  export const Sizes = EnumVariantTemplate.bind({});
81
- Sizes.args = { enum: "size" };
117
+ Sizes.args = { enum: "size", modelValue: "" };
82
118
 
83
- export const LeftIcon = DefaultTemplate.bind({});
84
- LeftIcon.args = { leftIcon: "star" };
119
+ export const LabelPlacement = EnumVariantTemplate.bind({});
120
+ LabelPlacement.args = { enum: "labelAlign", modelValue: "", label: "Search for product or brand" };
85
121
 
86
- export const RightIcon = DefaultTemplate.bind({});
87
- RightIcon.args = { rightIcon: "star" };
122
+ export const SearchButton = DefaultTemplate.bind({});
123
+ SearchButton.args = { searchButtonLabel: "Search" };
124
+ SearchButton.parameters = {
125
+ docs: {
126
+ description: {
127
+ story:
128
+ // eslint-disable-next-line vue/max-len
129
+ "`searchButtonLabel` prop shows a button with a passed label instead of the default search icon. When clicked, it triggers the search event.",
130
+ },
131
+ },
132
+ };
88
133
 
89
- export const LeftSlot = DefaultTemplate.bind({});
90
- LeftSlot.args = {
91
- slotTemplate: `
92
- <template #left>
93
- <UIcon name="star" />
94
- </template>
95
- `,
134
+ export const MinLength = DefaultTemplate.bind({});
135
+ MinLength.args = { minLength: 4 };
136
+ MinLength.parameters = {
137
+ docs: {
138
+ description: {
139
+ story:
140
+ "Determines minimum character length for search. If not met, search event is not fired.",
141
+ },
142
+ },
96
143
  };
97
144
 
98
- export const RightSlot = DefaultTemplate.bind({});
99
- RightSlot.args = {
100
- slotTemplate: `
101
- <template #right>
102
- <UIcon name="star" />
103
- </template>
145
+ export const IconProps: StoryFn<UInputSearchArgs> = (args) => ({
146
+ components: { UInputSearch, URow },
147
+ setup() {
148
+ return { args };
149
+ },
150
+ template: `
151
+ <URow>
152
+ <UInputSearch
153
+ left-icon="travel_explore"
154
+ placeholder="Search for a travel destination"
155
+ />
156
+ <UInputSearch
157
+ right-icon="person_search"
158
+ placeholder="Search for a person"
159
+ />
160
+ </URow>
104
161
  `,
105
- };
162
+ });
163
+
164
+ export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
165
+ components: { UInputSearch, URow, UButton, UAvatar, UBadge },
166
+ setup() {
167
+ return { args };
168
+ },
169
+ template: `
170
+ <URow no-mobile>
171
+ <UInputSearch v-bind="args" :config="{ searchInput: { leftSlot: 'pl-0' } }">
172
+ <template #left>
173
+ <UAvatar size="sm" />
174
+ </template>
175
+ </UInputSearch>
176
+
177
+ <UInputSearch v-bind="args" :config="{ searchInput: { rightSlot: 'pr-0' } }">
178
+ <template #right>
179
+ <UBadge label="Search" size="sm" color="green" />
180
+ </template>
181
+ </UInputSearch>
182
+ </URow>
183
+ `,
184
+ });
@@ -5,6 +5,11 @@ import type { ComponentConfig } from "../types.ts";
5
5
  export type Config = typeof defaultConfig;
6
6
 
7
7
  export interface Props {
8
+ /**
9
+ * Input label.
10
+ */
11
+ label?: string;
12
+
8
13
  /**
9
14
  * Search input value.
10
15
  */
@@ -23,12 +28,7 @@ export interface Props {
23
28
  /**
24
29
  * Label placement.
25
30
  */
26
- labelAlign?: "top" | "topInside" | "topWithDesc" | "left" | "right";
27
-
28
- /**
29
- * Input label.
30
- */
31
- label?: string;
31
+ labelAlign?: "topInside" | "top" | "topWithDesc" | "left" | "right";
32
32
 
33
33
  /**
34
34
  * Input description.