vueless 1.0.2-beta.10 → 1.0.2-beta.12
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/icons/storybook/description.svg +1 -0
- package/icons/storybook/schedule.svg +1 -0
- package/icons/storybook/straighten.svg +1 -0
- package/package.json +1 -1
- package/ui.dropdown-badge/config.ts +3 -12
- package/ui.dropdown-button/config.ts +4 -10
- package/ui.dropdown-link/config.ts +6 -12
- package/ui.dropdown-link/storybook/stories.ts +6 -2
- package/ui.form-input/storybook/stories.ts +35 -8
- package/ui.form-input-file/UInputFile.vue +3 -2
- package/ui.form-input-file/storybook/stories.ts +25 -9
- package/ui.form-input-number/storybook/stories.ts +38 -8
- package/ui.form-input-password/storybook/stories.ts +27 -6
- package/ui.form-input-rating/storybook/stories.ts +15 -3
- package/ui.form-input-search/storybook/stories.ts +39 -14
- package/ui.form-textarea/storybook/stories.ts +5 -10
- package/ui.text-block/UText.vue +4 -4
- package/ui.text-block/config.ts +22 -1
- package/ui.text-block/storybook/stories.ts +69 -34
- package/ui.text-block/tests/UText.test.ts +74 -11
- package/ui.text-block/types.ts +28 -2
- package/ui.text-header/UHeader.vue +1 -0
- package/ui.text-header/config.ts +18 -2
- package/ui.text-header/storybook/stories.ts +5 -1
- package/ui.text-header/tests/UHeader.test.ts +25 -1
- package/ui.text-header/types.ts +7 -1
- package/ui.text-notify/storybook/docs.mdx +15 -15
- package/utils/storybook.ts +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="M319-249.52h322v-62.63H319v62.63Zm0-170h322v-62.63H319v62.63Zm-96.85 345.5q-27.6 0-47.86-20.27-20.27-20.26-20.27-47.86v-675.7q0-27.7 20.27-48.03 20.26-20.34 47.86-20.34h361.48l222.59 222.59v521.48q0 27.6-20.34 47.86-20.33 20.27-48.03 20.27h-515.7Zm326.7-557.83v-186h-326.7v675.7h515.7v-489.7h-189Zm-326.7-186v186-186 675.7-675.7Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="m624.37-286.04 49.06-48.59-157.8-158.86v-198.55h-65.26v224.89l174 181.11ZM480.03-74.02q-83.46 0-157.51-31.95-74.05-31.94-129.32-87.21-55.28-55.26-87.23-129.3-31.95-74.03-31.95-157.49 0-83.46 32-157.54 32-74.07 87.2-129.27 55.2-55.2 129.25-87.32 74.05-32.12 157.53-32.12t157.53 32.12q74.05 32.12 129.25 87.32 55.2 55.2 87.32 129.25 32.12 74.05 32.12 157.53T854.1-322.47q-32.12 74.05-87.32 129.25-55.2 55.2-129.24 87.2t-157.51 32ZM480-480Zm-.12 337.85q139.16 0 238.57-99.17 99.4-99.16 99.4-238.56t-99.37-238.69Q619.11-817.85 480-817.85q-139.28 0-238.57 99.26-99.28 99.25-99.28 238.59 0 139.52 99.28 238.68 99.28 99.17 238.45 99.17Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="M142.63-235.46q-27.6 0-47.86-20.33-20.27-20.34-20.27-48.04v-352.34q0-26.7 20.27-47.54 20.26-20.83 47.86-20.83h674.74q27.6 0 47.86 20.83 20.27 20.84 20.27 47.54v352.34q0 27.7-20.27 48.04-20.26 20.33-47.86 20.33H142.63Zm0-68.37h674.74v-352.34H690V-480h-60v-176.17H510V-480h-60v-176.17H330V-480h-60v-176.17H142.63v352.34ZM270-480h60-60Zm180 0h60-60Zm180 0h60-60Zm-150 0Z"/></svg>
|
package/package.json
CHANGED
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
export default /*tw*/ {
|
|
2
|
-
wrapper:
|
|
3
|
-
base: "relative inline-block",
|
|
4
|
-
variants: {
|
|
5
|
-
disabled: {
|
|
6
|
-
true: "cursor-not-allowed",
|
|
7
|
-
},
|
|
8
|
-
},
|
|
9
|
-
},
|
|
2
|
+
wrapper: "relative inline-block h-max",
|
|
10
3
|
dropdownBadge: {
|
|
11
4
|
base: "{UBadge}",
|
|
12
5
|
variants: {
|
|
13
|
-
opened: {
|
|
14
|
-
true: "group",
|
|
15
|
-
},
|
|
16
6
|
disabled: {
|
|
17
7
|
true: "opacity-(--vl-disabled-opacity) pointer-events-none",
|
|
18
8
|
},
|
|
19
9
|
},
|
|
20
10
|
},
|
|
21
11
|
toggleIcon: {
|
|
22
|
-
base: "{UIcon} transition duration-300
|
|
12
|
+
base: "{UIcon} transition duration-300 -mr-0.5",
|
|
23
13
|
defaults: {
|
|
24
14
|
size: {
|
|
25
15
|
sm: "2xs",
|
|
@@ -27,6 +17,7 @@ export default /*tw*/ {
|
|
|
27
17
|
lg: "xs",
|
|
28
18
|
},
|
|
29
19
|
},
|
|
20
|
+
compoundVariants: [{ opened: true, class: "rotate-180" }],
|
|
30
21
|
},
|
|
31
22
|
listbox: {
|
|
32
23
|
base: "{UListbox} w-fit",
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
export default /*tw*/ {
|
|
2
|
-
wrapper: "relative inline-block",
|
|
3
|
-
dropdownButton: {
|
|
4
|
-
base: "{UButton} justify-between",
|
|
5
|
-
variants: {
|
|
6
|
-
opened: {
|
|
7
|
-
true: "group",
|
|
8
|
-
},
|
|
9
|
-
},
|
|
10
|
-
},
|
|
2
|
+
wrapper: "relative inline-block h-max",
|
|
3
|
+
dropdownButton: "{UButton} justify-between",
|
|
11
4
|
toggleIcon: {
|
|
12
|
-
base: "{UIcon} transition duration-300
|
|
5
|
+
base: "{UIcon} transition duration-300 -mr-1",
|
|
13
6
|
defaults: {
|
|
14
7
|
size: {
|
|
15
8
|
"2xs": "2xs",
|
|
@@ -20,6 +13,7 @@ export default /*tw*/ {
|
|
|
20
13
|
xl: "sm",
|
|
21
14
|
},
|
|
22
15
|
},
|
|
16
|
+
compoundVariants: [{ opened: true, class: "rotate-180" }],
|
|
23
17
|
},
|
|
24
18
|
listbox: {
|
|
25
19
|
base: "{UListbox} w-fit",
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
export default /*tw*/ {
|
|
2
|
-
wrapper:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`,
|
|
7
|
-
variants: {
|
|
8
|
-
opened: {
|
|
9
|
-
true: "group",
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
},
|
|
2
|
+
wrapper: `
|
|
3
|
+
inline-flex gap-0.5 relative items-center justify-between rounded h-max
|
|
4
|
+
focus-visible:outline focus-visible:outline-medium focus-visible:outline-offset-4 focus-visible:outline-{color}
|
|
5
|
+
`,
|
|
13
6
|
dropdownLink: "{ULink} focus-visible:outline-hidden",
|
|
14
7
|
toggleIcon: {
|
|
15
|
-
base: "{UIcon} block transition duration-300
|
|
8
|
+
base: "{UIcon} block transition duration-300",
|
|
16
9
|
defaults: {
|
|
17
10
|
size: {
|
|
18
11
|
sm: "2xs",
|
|
@@ -20,6 +13,7 @@ export default /*tw*/ {
|
|
|
20
13
|
lg: "sm",
|
|
21
14
|
},
|
|
22
15
|
},
|
|
16
|
+
compoundVariants: [{ opened: true, class: "rotate-180" }],
|
|
23
17
|
},
|
|
24
18
|
listbox: {
|
|
25
19
|
base: "{UListbox} w-fit",
|
|
@@ -162,8 +162,12 @@ export const DefaultSlot = DefaultTemplate.bind({});
|
|
|
162
162
|
DefaultSlot.args = {
|
|
163
163
|
toggleIcon: false,
|
|
164
164
|
slotTemplate: `
|
|
165
|
-
<template #default>
|
|
166
|
-
<UAvatar
|
|
165
|
+
<template #default="{ opened }">
|
|
166
|
+
<UAvatar
|
|
167
|
+
rounded="full"
|
|
168
|
+
src="https://avatar.iran.liara.run/public"
|
|
169
|
+
:class="{ 'outline-medium outline-primary': opened }"
|
|
170
|
+
/>
|
|
167
171
|
</template>
|
|
168
172
|
`,
|
|
169
173
|
};
|
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import UInput from "../../ui.form-input/UInput.vue";
|
|
10
10
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
11
|
-
import UButton from "../../ui.button/UButton.vue";
|
|
12
11
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
13
12
|
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
-
import
|
|
13
|
+
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
14
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
15
15
|
|
|
16
16
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
17
17
|
import type { Props } from "../types.ts";
|
|
18
|
+
import { ref } from "vue";
|
|
18
19
|
|
|
19
20
|
interface UInputArgs extends Props {
|
|
20
21
|
slotTemplate?: string;
|
|
@@ -177,23 +178,49 @@ export const IconProps: StoryFn<UInputArgs> = (args) => ({
|
|
|
177
178
|
});
|
|
178
179
|
|
|
179
180
|
export const Slots: StoryFn<UInputArgs> = (args) => ({
|
|
180
|
-
components: { UInput, URow,
|
|
181
|
+
components: { UInput, URow, UDropdownButton, UText },
|
|
181
182
|
setup() {
|
|
182
|
-
|
|
183
|
+
const countryCodes = [
|
|
184
|
+
{ label: "+33", id: "+33" },
|
|
185
|
+
{ label: "+44", id: "+44" },
|
|
186
|
+
{ label: "+49", id: "+49" },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const countryCode = ref("+33");
|
|
190
|
+
|
|
191
|
+
return { args, countryCode, countryCodes };
|
|
183
192
|
},
|
|
184
193
|
template: `
|
|
185
194
|
<URow>
|
|
186
|
-
<UInput
|
|
195
|
+
<UInput
|
|
196
|
+
label="Phone Number"
|
|
197
|
+
placeholder="Enter your phone number"
|
|
198
|
+
:config="{ leftSlot: 'pl-0' }"
|
|
199
|
+
>
|
|
187
200
|
<template #left>
|
|
188
|
-
<
|
|
201
|
+
<UDropdownButton
|
|
202
|
+
v-model="countryCode"
|
|
203
|
+
:options="countryCodes"
|
|
204
|
+
square
|
|
205
|
+
size="sm"
|
|
206
|
+
variant="ghost"
|
|
207
|
+
class="rounded-r-none h-[49px]"
|
|
208
|
+
/>
|
|
189
209
|
</template>
|
|
190
210
|
</UInput>
|
|
191
211
|
|
|
192
|
-
<UInput
|
|
212
|
+
<UInput label="Website" placeholder="Enter your website">
|
|
193
213
|
<template #right>
|
|
194
|
-
<
|
|
214
|
+
<UText label=".com" variant="lifted" />
|
|
195
215
|
</template>
|
|
196
216
|
</UInput>
|
|
197
217
|
</URow>
|
|
198
218
|
`,
|
|
199
219
|
});
|
|
220
|
+
Slots.parameters = {
|
|
221
|
+
docs: {
|
|
222
|
+
story: {
|
|
223
|
+
height: "250px",
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
};
|
|
@@ -333,15 +333,16 @@ const {
|
|
|
333
333
|
:removable="multiple && !disabled"
|
|
334
334
|
@remove="onClickRemoveItem"
|
|
335
335
|
>
|
|
336
|
-
<template #
|
|
336
|
+
<template #file="{ id, label, url, imageUrl, index }">
|
|
337
337
|
<!--
|
|
338
338
|
@slot Use it to add a file directly.
|
|
339
339
|
@binding {string | number} id
|
|
340
340
|
@binding {string} label
|
|
341
341
|
@binding {string} url
|
|
342
342
|
@binding {string} image-url
|
|
343
|
+
@binding {number} index
|
|
343
344
|
-->
|
|
344
|
-
<slot :id="id" :label="label" :url="url" :image-url="imageUrl" />
|
|
345
|
+
<slot :id="id" :label="label" :url="url" :image-url="imageUrl" :index="index" />
|
|
345
346
|
</template>
|
|
346
347
|
</UFiles>
|
|
347
348
|
|
|
@@ -10,6 +10,8 @@ import UInputFile from "../../ui.form-input-file/UInputFile.vue";
|
|
|
10
10
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
11
11
|
import UBadge from "../../ui.text-badge/UBadge.vue";
|
|
12
12
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
13
|
+
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
13
15
|
|
|
14
16
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
15
17
|
import type { Props } from "../types.ts";
|
|
@@ -84,7 +86,7 @@ MaxFileSize.args = {
|
|
|
84
86
|
|
|
85
87
|
export const AllowedFileTypes = DefaultTemplate.bind({});
|
|
86
88
|
AllowedFileTypes.args = {
|
|
87
|
-
allowedFileTypes: ["png", "jpeg"],
|
|
89
|
+
allowedFileTypes: [".png", ".jpeg"],
|
|
88
90
|
description: "Only png and jpeg formats are allowed.",
|
|
89
91
|
};
|
|
90
92
|
|
|
@@ -104,39 +106,53 @@ LabelSlot.args = {
|
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
export const Slots: StoryFn<UInputFileArgs> = (args) => ({
|
|
107
|
-
components: { UInputFile, UCol, UBadge, UIcon },
|
|
109
|
+
components: { UInputFile, UCol, UBadge, UIcon, URow, UText },
|
|
108
110
|
setup() {
|
|
109
111
|
return { args };
|
|
110
112
|
},
|
|
111
113
|
template: `
|
|
112
|
-
<UCol>
|
|
114
|
+
<UCol gap="xl">
|
|
113
115
|
<UInputFile
|
|
114
116
|
v-bind="args"
|
|
115
117
|
v-model="args.files"
|
|
116
|
-
label="Slot
|
|
118
|
+
label="Top Slot"
|
|
119
|
+
:allowedFileTypes="['.jpeg', '.png']"
|
|
120
|
+
:maxFileSize="2"
|
|
117
121
|
>
|
|
118
122
|
<template #top>
|
|
119
|
-
<
|
|
123
|
+
<URow align="center" gap="xs">
|
|
124
|
+
<UIcon name="info" size="sm" />
|
|
125
|
+
<UText variant="lifted">Recommended size: 400x400px, max 2MB</UText>
|
|
126
|
+
</URow>
|
|
120
127
|
</template>
|
|
121
128
|
</UInputFile>
|
|
122
129
|
|
|
123
130
|
<UInputFile
|
|
124
131
|
v-bind="args"
|
|
125
132
|
v-model="args.files"
|
|
126
|
-
label="Slot
|
|
133
|
+
label="Left Slot"
|
|
134
|
+
:allowedFileTypes="['.pdf', '.doc', '.docx']"
|
|
127
135
|
>
|
|
128
136
|
<template #left>
|
|
129
|
-
<
|
|
137
|
+
<URow align="center" gap="xs">
|
|
138
|
+
<UIcon name="description" size="sm" />
|
|
139
|
+
<UText label="PDF, DOC, DOCX" variant="lifted" size="xs" :wrap="false" />
|
|
140
|
+
</URow>
|
|
130
141
|
</template>
|
|
131
142
|
</UInputFile>
|
|
132
143
|
|
|
133
144
|
<UInputFile
|
|
134
145
|
v-bind="args"
|
|
135
146
|
v-model="args.files"
|
|
136
|
-
label="Slot
|
|
147
|
+
label="Bottom Slot"
|
|
148
|
+
multiple
|
|
149
|
+
:allowedFileTypes="['.png', '.jpeg']"
|
|
137
150
|
>
|
|
138
151
|
<template #bottom>
|
|
139
|
-
<
|
|
152
|
+
<URow align="center" gap="xs">
|
|
153
|
+
<UIcon name="schedule" size="sm" />
|
|
154
|
+
<UText label="Processing may take a few moments for multiple files" variant="lifted" />
|
|
155
|
+
</URow>
|
|
140
156
|
</template>
|
|
141
157
|
</UInputFile>
|
|
142
158
|
</UCol>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
1
2
|
import {
|
|
2
3
|
getArgs,
|
|
3
4
|
getArgTypes,
|
|
@@ -11,7 +12,8 @@ import UCol from "../../ui.container-col/UCol.vue";
|
|
|
11
12
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
12
13
|
import UButton from "../../ui.button/UButton.vue";
|
|
13
14
|
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
-
import
|
|
15
|
+
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
16
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
15
17
|
|
|
16
18
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
17
19
|
import type { Props } from "../types.ts";
|
|
@@ -185,23 +187,51 @@ export const IconProps: StoryFn<UInputNumberArgs> = (args) => ({
|
|
|
185
187
|
});
|
|
186
188
|
|
|
187
189
|
export const Slots: StoryFn<UInputNumberArgs> = (args) => ({
|
|
188
|
-
components: { UInputNumber, URow, UButton,
|
|
190
|
+
components: { UInputNumber, URow, UButton, UDropdownButton, UText },
|
|
189
191
|
setup() {
|
|
190
|
-
|
|
192
|
+
const currencies = [
|
|
193
|
+
{ label: "USD", id: "usd" },
|
|
194
|
+
{ label: "EUR", id: "eur" },
|
|
195
|
+
{ label: "UAH", id: "uah" },
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const currency = ref("usd");
|
|
199
|
+
|
|
200
|
+
return { args, currency, currencies };
|
|
191
201
|
},
|
|
192
202
|
template: `
|
|
193
|
-
<URow>
|
|
194
|
-
<UInputNumber
|
|
203
|
+
<URow class="gap-4">
|
|
204
|
+
<UInputNumber
|
|
205
|
+
label="Left slot"
|
|
206
|
+
placeholder="Enter discount amount"
|
|
207
|
+
:config="{ numberInput: { leftSlot: 'pl-0' } }"
|
|
208
|
+
>
|
|
195
209
|
<template #left>
|
|
196
|
-
<
|
|
210
|
+
<UDropdownButton
|
|
211
|
+
v-model="currency"
|
|
212
|
+
:options="currencies"
|
|
213
|
+
size="sm"
|
|
214
|
+
variant="ghost"
|
|
215
|
+
class="rounded-r-none h-[49px]"
|
|
216
|
+
/>
|
|
197
217
|
</template>
|
|
198
218
|
</UInputNumber>
|
|
199
219
|
|
|
200
|
-
<UInputNumber
|
|
220
|
+
<UInputNumber
|
|
221
|
+
label="Right slot"
|
|
222
|
+
placeholder="Enter your annual payment"
|
|
223
|
+
>
|
|
201
224
|
<template #right>
|
|
202
|
-
<
|
|
225
|
+
<UText label="%, per year" variant="lifted" size="sm" :wrap="false" />
|
|
203
226
|
</template>
|
|
204
227
|
</UInputNumber>
|
|
205
228
|
</URow>
|
|
206
229
|
`,
|
|
207
230
|
});
|
|
231
|
+
Slots.parameters = {
|
|
232
|
+
docs: {
|
|
233
|
+
story: {
|
|
234
|
+
height: "250px",
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
};
|
|
@@ -10,7 +10,7 @@ import UInputPassword from "../UInputPassword.vue";
|
|
|
10
10
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
11
11
|
import URow from "../../ui.container-row/URow.vue";
|
|
12
12
|
import UButton from "../../ui.button/UButton.vue";
|
|
13
|
-
import
|
|
13
|
+
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
14
14
|
|
|
15
15
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
16
16
|
import type { Props } from "../types.ts";
|
|
@@ -126,25 +126,39 @@ export const IconProps: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
129
|
-
components: { UInputPassword, URow, UButton,
|
|
129
|
+
components: { UInputPassword, URow, UButton, UDropdownButton },
|
|
130
130
|
setup() {
|
|
131
|
-
|
|
131
|
+
const wifiTypes = [
|
|
132
|
+
{ label: "WPA2", id: "wpa2" },
|
|
133
|
+
{ label: "WPA3", id: "wpa3" },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
return { args, wifiTypes };
|
|
132
137
|
},
|
|
133
138
|
template: `
|
|
134
|
-
<URow>
|
|
139
|
+
<URow align="stretch">
|
|
135
140
|
<UInputPassword
|
|
136
141
|
v-bind="args"
|
|
137
142
|
v-model="args.modelValue"
|
|
143
|
+
placeholder="Enter your password"
|
|
138
144
|
:config="{ passwordInput: { leftSlot: 'pl-0' } }"
|
|
139
145
|
>
|
|
140
146
|
<template #left>
|
|
141
|
-
<
|
|
147
|
+
<UDropdownButton
|
|
148
|
+
v-model="args.wifiType"
|
|
149
|
+
:options="wifiTypes"
|
|
150
|
+
label="Wifi type"
|
|
151
|
+
size="sm"
|
|
152
|
+
variant="ghost"
|
|
153
|
+
class="rounded-r-none h-[49px]"
|
|
154
|
+
/>
|
|
142
155
|
</template>
|
|
143
156
|
</UInputPassword>
|
|
144
157
|
|
|
145
158
|
<UInputPassword
|
|
146
159
|
v-bind="args"
|
|
147
160
|
v-model="args.modelValue"
|
|
161
|
+
placeholder="Enter your password"
|
|
148
162
|
:config="{ passwordInput: { rightSlot: 'pr-0' } }"
|
|
149
163
|
>
|
|
150
164
|
<template #right="{ visible, toggle }">
|
|
@@ -153,7 +167,7 @@ export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
153
167
|
color="neutral"
|
|
154
168
|
variant="ghost"
|
|
155
169
|
size="sm"
|
|
156
|
-
class="rounded-l-none h-
|
|
170
|
+
class="rounded-l-none h-[49px] min-w-[69px]"
|
|
157
171
|
@click="toggle"
|
|
158
172
|
/>
|
|
159
173
|
</template>
|
|
@@ -161,3 +175,10 @@ export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
161
175
|
</URow>
|
|
162
176
|
`,
|
|
163
177
|
});
|
|
178
|
+
Slots.parameters = {
|
|
179
|
+
docs: {
|
|
180
|
+
story: {
|
|
181
|
+
height: "200px",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
};
|
|
@@ -116,9 +116,17 @@ RatingIcons.parameters = {
|
|
|
116
116
|
|
|
117
117
|
export const CounterSlot = DefaultTemplate.bind({});
|
|
118
118
|
CounterSlot.args = {
|
|
119
|
+
counter: true,
|
|
120
|
+
stars: 5,
|
|
121
|
+
modelValue: 0,
|
|
119
122
|
slotTemplate: `
|
|
120
123
|
<template #counter="{ counter }">
|
|
121
|
-
<UBadge
|
|
124
|
+
<UBadge
|
|
125
|
+
:label="counter === 0
|
|
126
|
+
? 'No rating yet'
|
|
127
|
+
: counter + ' out of ' + args.stars + ' stars'"
|
|
128
|
+
:color="counter ? 'success' : 'warning'"
|
|
129
|
+
/>
|
|
122
130
|
</template>
|
|
123
131
|
`,
|
|
124
132
|
};
|
|
@@ -127,8 +135,12 @@ export const TotalSlot = DefaultTemplate.bind({});
|
|
|
127
135
|
TotalSlot.args = {
|
|
128
136
|
total: 250,
|
|
129
137
|
slotTemplate: `
|
|
130
|
-
<template #total="{total}">
|
|
131
|
-
<UBadge
|
|
138
|
+
<template #total="{ total }">
|
|
139
|
+
<UBadge
|
|
140
|
+
:label="total + ' reviews'"
|
|
141
|
+
color="info"
|
|
142
|
+
size="sm"
|
|
143
|
+
/>
|
|
132
144
|
</template>
|
|
133
145
|
`,
|
|
134
146
|
};
|
|
@@ -7,12 +7,11 @@ import {
|
|
|
7
7
|
} from "../../utils/storybook.ts";
|
|
8
8
|
|
|
9
9
|
import UInputSearch from "../../ui.form-input-search/UInputSearch.vue";
|
|
10
|
-
import UButton from "../../ui.button/UButton.vue";
|
|
11
10
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
12
11
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
13
12
|
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
13
|
+
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
14
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
16
15
|
|
|
17
16
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
18
17
|
import type { Props } from "../types.ts";
|
|
@@ -40,7 +39,7 @@ export default {
|
|
|
40
39
|
} as Meta;
|
|
41
40
|
|
|
42
41
|
const DefaultTemplate: StoryFn<UInputSearchArgs> = (args: UInputSearchArgs) => ({
|
|
43
|
-
components: { UInputSearch,
|
|
42
|
+
components: { UInputSearch, UIcon },
|
|
44
43
|
setup: () => ({ args, slots: getSlotNames(UInputSearch.__name) }),
|
|
45
44
|
template: `
|
|
46
45
|
<UInputSearch
|
|
@@ -136,23 +135,49 @@ export const IconProps: StoryFn<UInputSearchArgs> = (args) => ({
|
|
|
136
135
|
});
|
|
137
136
|
|
|
138
137
|
export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
|
|
139
|
-
components: { UInputSearch, URow,
|
|
138
|
+
components: { UInputSearch, UCol, URow, UIcon, UDropdownButton, UText },
|
|
140
139
|
setup() {
|
|
141
|
-
|
|
140
|
+
const aiVersions = [
|
|
141
|
+
{ label: "GPT-4o", id: "gpt-4o" },
|
|
142
|
+
{ label: "GPT-4o-mini", id: "gpt-4o-mini" },
|
|
143
|
+
{ label: "GPT-4", id: "gpt-4" },
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
return { args, aiVersions };
|
|
142
147
|
},
|
|
143
148
|
template: `
|
|
144
|
-
<
|
|
145
|
-
<UInputSearch
|
|
146
|
-
<template #
|
|
147
|
-
<
|
|
149
|
+
<UCol>
|
|
150
|
+
<UInputSearch placeholder="Search by rental district...">
|
|
151
|
+
<template #right>
|
|
152
|
+
<URow align="center" gap="xs">
|
|
153
|
+
<UIcon name="straighten" size="sm" />
|
|
154
|
+
<UText label="+2km" variant="muted" />
|
|
155
|
+
</URow>
|
|
148
156
|
</template>
|
|
149
157
|
</UInputSearch>
|
|
150
158
|
|
|
151
|
-
<UInputSearch
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
<UInputSearch
|
|
160
|
+
placeholder="Ask something..."
|
|
161
|
+
:config="{ searchInput: { leftSlot: 'pl-0' } }"
|
|
162
|
+
>
|
|
163
|
+
<template #left>
|
|
164
|
+
<UDropdownButton
|
|
165
|
+
v-model="args.aiVersion"
|
|
166
|
+
:options="aiVersions"
|
|
167
|
+
label="AI Version"
|
|
168
|
+
size="sm"
|
|
169
|
+
variant="ghost"
|
|
170
|
+
class="rounded-r-none"
|
|
171
|
+
/>
|
|
154
172
|
</template>
|
|
155
173
|
</UInputSearch>
|
|
156
|
-
</
|
|
174
|
+
</UCol>
|
|
157
175
|
`,
|
|
158
176
|
});
|
|
177
|
+
Slots.parameters = {
|
|
178
|
+
docs: {
|
|
179
|
+
story: {
|
|
180
|
+
height: "270px",
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ref } from "vue";
|
|
2
1
|
import {
|
|
3
2
|
getArgs,
|
|
4
3
|
getArgTypes,
|
|
@@ -11,8 +10,8 @@ import UTextarea from "../../ui.form-textarea/UTextarea.vue";
|
|
|
11
10
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
12
11
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
13
12
|
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
-
import UAvatar from "../../ui.image-avatar/UAvatar.vue";
|
|
15
13
|
import tooltip from "../../directives/tooltip/vTooltip.ts";
|
|
14
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
16
15
|
|
|
17
16
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
18
17
|
import type { Props } from "../types.ts";
|
|
@@ -123,18 +122,14 @@ NoAutocomplete.parameters = {
|
|
|
123
122
|
};
|
|
124
123
|
|
|
125
124
|
export const Slots: StoryFn<UTextareaArgs> = (args) => ({
|
|
126
|
-
components: { UTextarea, URow, UIcon,
|
|
125
|
+
components: { UTextarea, URow, UIcon, UText },
|
|
127
126
|
directives: { tooltip },
|
|
128
|
-
setup() {
|
|
129
|
-
const switchModel = ref(false);
|
|
130
|
-
|
|
131
|
-
return { args, switchModel };
|
|
132
|
-
},
|
|
127
|
+
setup: () => ({ args }),
|
|
133
128
|
template: `
|
|
134
129
|
<URow>
|
|
135
|
-
<UTextarea v-bind="args">
|
|
130
|
+
<UTextarea v-bind="args" v-model="args.modelValue" :max-length="300">
|
|
136
131
|
<template #left>
|
|
137
|
-
<
|
|
132
|
+
<UText :label="args.modelValue?.length + '/300'" variant="lifted" />
|
|
138
133
|
</template>
|
|
139
134
|
</UTextarea>
|
|
140
135
|
|
package/ui.text-block/UText.vue
CHANGED
|
@@ -3,7 +3,6 @@ import { useTemplateRef } from "vue";
|
|
|
3
3
|
|
|
4
4
|
import useUI from "../composables/useUI.ts";
|
|
5
5
|
import { getDefaults } from "../utils/ui.ts";
|
|
6
|
-
import { hasSlotContent } from "../utils/helper.ts";
|
|
7
6
|
|
|
8
7
|
import { COMPONENT_NAME } from "./constants.ts";
|
|
9
8
|
import defaultConfig from "./config.ts";
|
|
@@ -30,13 +29,14 @@ defineExpose({
|
|
|
30
29
|
* Get element / nested component attributes for each config token ✨
|
|
31
30
|
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
32
31
|
*/
|
|
33
|
-
const { getDataTest, wrapperAttrs,
|
|
32
|
+
const { getDataTest, wrapperAttrs, labelAttrs } = useUI<Config>(defaultConfig);
|
|
34
33
|
</script>
|
|
35
34
|
|
|
36
35
|
<template>
|
|
37
36
|
<div ref="wrapper" v-bind="wrapperAttrs" :data-test="getDataTest()">
|
|
38
37
|
<!-- @slot Use it to add something inside. -->
|
|
39
|
-
<
|
|
40
|
-
|
|
38
|
+
<slot>
|
|
39
|
+
<div v-bind="labelAttrs" v-text="label" />
|
|
40
|
+
</slot>
|
|
41
41
|
</div>
|
|
42
42
|
</template>
|
package/ui.text-block/config.ts
CHANGED
|
@@ -12,6 +12,15 @@ export default /*tw*/ {
|
|
|
12
12
|
[&_ul]:ml-2 [&_ol]:ml-2
|
|
13
13
|
`,
|
|
14
14
|
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: "text-{color}",
|
|
17
|
+
lifted: "text-{color}-lifted",
|
|
18
|
+
accented: "text-{color}-accented",
|
|
19
|
+
muted: "text-{color}/(--vl-disabled-opacity)",
|
|
20
|
+
},
|
|
21
|
+
color: {
|
|
22
|
+
inherit: "text-inherit",
|
|
23
|
+
},
|
|
15
24
|
size: {
|
|
16
25
|
xs: "text-tiny space-y-1",
|
|
17
26
|
sm: "text-small space-y-2",
|
|
@@ -26,12 +35,24 @@ export default /*tw*/ {
|
|
|
26
35
|
line: {
|
|
27
36
|
true: "leading-none",
|
|
28
37
|
},
|
|
38
|
+
wrap: {
|
|
39
|
+
false: "text-nowrap",
|
|
40
|
+
},
|
|
29
41
|
},
|
|
42
|
+
compoundVariants: [
|
|
43
|
+
{ color: "text", variant: "default", class: "text-default" },
|
|
44
|
+
{ color: "text", variant: "lifted", class: "text-lifted" },
|
|
45
|
+
{ color: "text", variant: "accented", class: "text-accented" },
|
|
46
|
+
{ color: "text", variant: "muted", class: "text-muted" },
|
|
47
|
+
],
|
|
30
48
|
},
|
|
31
|
-
|
|
49
|
+
label: "",
|
|
32
50
|
defaults: {
|
|
51
|
+
color: "text",
|
|
52
|
+
variant: "default",
|
|
33
53
|
size: "md",
|
|
34
54
|
align: "left",
|
|
35
55
|
line: false,
|
|
56
|
+
wrap: true,
|
|
36
57
|
},
|
|
37
58
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
trimText,
|
|
2
3
|
getArgs,
|
|
3
4
|
getArgTypes,
|
|
4
5
|
getSlotNames,
|
|
5
6
|
getSlotsFragment,
|
|
6
7
|
getDocsDescription,
|
|
7
|
-
getEnumVariantDescription,
|
|
8
8
|
} from "../../utils/storybook.ts";
|
|
9
9
|
|
|
10
10
|
import UText from "../../ui.text-block/UText.vue";
|
|
@@ -12,20 +12,23 @@ import URow from "../../ui.container-row/URow.vue";
|
|
|
12
12
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
13
13
|
import UBadge from "../../ui.text-badge/UBadge.vue";
|
|
14
14
|
|
|
15
|
-
import tooltip from "../../directives/tooltip/vTooltip.ts";
|
|
16
|
-
|
|
17
15
|
import type { Meta, StoryFn } from "@storybook/vue3";
|
|
18
16
|
import type { Props } from "../types.ts";
|
|
19
17
|
|
|
20
18
|
interface UTextArgs extends Props {
|
|
21
19
|
slotTemplate?: string;
|
|
22
|
-
enum: "size" | "align";
|
|
20
|
+
enum: "size" | "align" | "variant" | "color";
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
export default {
|
|
26
24
|
id: "4020",
|
|
27
25
|
title: "Text & Content / Text",
|
|
28
26
|
component: UText,
|
|
27
|
+
args: {
|
|
28
|
+
label: trimText(`
|
|
29
|
+
Easily customize your UI with flexible components.
|
|
30
|
+
`),
|
|
31
|
+
},
|
|
29
32
|
argTypes: {
|
|
30
33
|
...getArgTypes(UText.__name),
|
|
31
34
|
},
|
|
@@ -34,29 +37,20 @@ export default {
|
|
|
34
37
|
...getDocsDescription(UText.__name),
|
|
35
38
|
},
|
|
36
39
|
},
|
|
37
|
-
args: {},
|
|
38
40
|
} as Meta;
|
|
39
41
|
|
|
40
|
-
const defaultTemplate = `
|
|
41
|
-
<p>
|
|
42
|
-
<b>To proceed with your registration</b>, please enter your <u>email address</u> in the field below.
|
|
43
|
-
<i>A verification link</i> will be sent to your inbox shortly.
|
|
44
|
-
</p>
|
|
45
|
-
`;
|
|
46
|
-
|
|
47
42
|
const DefaultTemplate: StoryFn<UTextArgs> = (args: UTextArgs) => ({
|
|
48
43
|
components: { UText, URow, UBadge },
|
|
49
44
|
setup: () => ({ args, slots: getSlotNames(UText.__name) }),
|
|
50
45
|
template: `
|
|
51
46
|
<UText v-bind="args">
|
|
52
|
-
${args.slotTemplate || getSlotsFragment(
|
|
47
|
+
${args.slotTemplate || getSlotsFragment("")}
|
|
53
48
|
</UText>
|
|
54
49
|
`,
|
|
55
50
|
});
|
|
56
51
|
|
|
57
52
|
const EnumTemplate: StoryFn<UTextArgs> = (args: UTextArgs, { argTypes }) => ({
|
|
58
53
|
components: { UText, UCol },
|
|
59
|
-
directives: { tooltip },
|
|
60
54
|
setup: () => ({ args, argTypes, getArgs }),
|
|
61
55
|
template: `
|
|
62
56
|
<UCol>
|
|
@@ -64,11 +58,8 @@ const EnumTemplate: StoryFn<UTextArgs> = (args: UTextArgs, { argTypes }) => ({
|
|
|
64
58
|
v-for="option in argTypes?.[args.enum]?.options"
|
|
65
59
|
v-bind="getArgs(args, option)"
|
|
66
60
|
:key="option"
|
|
67
|
-
v-tooltip="option"
|
|
68
61
|
class="w-full"
|
|
69
|
-
|
|
70
|
-
${args.slotTemplate || defaultTemplate}
|
|
71
|
-
</UText>
|
|
62
|
+
/>
|
|
72
63
|
</UCol>
|
|
73
64
|
`,
|
|
74
65
|
});
|
|
@@ -78,23 +69,57 @@ Default.args = {};
|
|
|
78
69
|
|
|
79
70
|
export const Align = EnumTemplate.bind({});
|
|
80
71
|
Align.args = { enum: "align" };
|
|
81
|
-
Align.parameters = getEnumVariantDescription();
|
|
82
72
|
|
|
83
73
|
export const Sizes = EnumTemplate.bind({});
|
|
84
74
|
Sizes.args = { enum: "size" };
|
|
85
|
-
Sizes.parameters = getEnumVariantDescription();
|
|
86
75
|
|
|
87
|
-
export const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
76
|
+
export const Color = EnumTemplate.bind({});
|
|
77
|
+
Color.args = { enum: "color" };
|
|
78
|
+
|
|
79
|
+
export const Variant = EnumTemplate.bind({});
|
|
80
|
+
Variant.args = { enum: "variant" };
|
|
81
|
+
|
|
82
|
+
export const Line: StoryFn<UTextArgs> = (args: UTextArgs) => ({
|
|
83
|
+
components: { UText, UCol },
|
|
84
|
+
setup: () => ({ args }),
|
|
85
|
+
template: `
|
|
86
|
+
<UCol>
|
|
87
|
+
<UText>
|
|
88
|
+
<div class="rounded-medium border border-primary border-dashed w-fit">
|
|
89
|
+
Text with default library line height.
|
|
90
|
+
</div>
|
|
91
|
+
</UText>
|
|
92
|
+
|
|
93
|
+
<UText line>
|
|
94
|
+
<div class="rounded-medium border border-primary border-dashed w-fit">
|
|
95
|
+
Text with line height equal to its font size.
|
|
96
|
+
</div>
|
|
97
|
+
</UText>
|
|
98
|
+
</UCol>
|
|
99
|
+
`,
|
|
100
|
+
});
|
|
101
|
+
Line.args = {};
|
|
102
|
+
|
|
103
|
+
export const Wrap: StoryFn<UTextArgs> = (args: UTextArgs) => ({
|
|
104
|
+
components: { UText, UCol },
|
|
105
|
+
setup: () => ({ args }),
|
|
106
|
+
template: `
|
|
107
|
+
<UCol>
|
|
108
|
+
<UText>
|
|
109
|
+
<div class="rounded-medium border border-primary border-dashed w-32">
|
|
110
|
+
Text with wrapping enabled (default behavior).
|
|
111
|
+
</div>
|
|
112
|
+
</UText>
|
|
113
|
+
|
|
114
|
+
<UText :wrap="false">
|
|
115
|
+
<div class="rounded-medium border border-primary border-dashed w-32">
|
|
116
|
+
Text with wrapping disabled (text-nowrap).
|
|
117
|
+
</div>
|
|
118
|
+
</UText>
|
|
119
|
+
</UCol>
|
|
120
|
+
`,
|
|
121
|
+
});
|
|
122
|
+
Wrap.args = {};
|
|
98
123
|
|
|
99
124
|
export const Paragraphs = DefaultTemplate.bind({});
|
|
100
125
|
Paragraphs.args = {
|
|
@@ -141,11 +166,21 @@ List.args = {
|
|
|
141
166
|
`,
|
|
142
167
|
};
|
|
143
168
|
|
|
144
|
-
export const
|
|
145
|
-
|
|
169
|
+
export const FormattedText = DefaultTemplate.bind({});
|
|
170
|
+
FormattedText.args = {
|
|
171
|
+
slotTemplate: `
|
|
172
|
+
<p>
|
|
173
|
+
<b>To proceed with your registration</b>, please enter your <u>email address</u> in the field below.
|
|
174
|
+
<i>A verification link</i> will be sent to your inbox shortly.
|
|
175
|
+
</p>
|
|
176
|
+
`,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const NestedComponent = DefaultTemplate.bind({});
|
|
180
|
+
NestedComponent.args = {
|
|
146
181
|
slotTemplate: `
|
|
147
182
|
<p>To proceed with your registration, please enter your
|
|
148
|
-
<UBadge label="email address"
|
|
183
|
+
<UBadge label="email address" color="success" variant="subtle" /> in the field below.
|
|
149
184
|
A verification link will be sent to your inbox shortly.</p>
|
|
150
185
|
`,
|
|
151
186
|
};
|
|
@@ -47,6 +47,56 @@ describe("UText.vue", () => {
|
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
// Color prop
|
|
51
|
+
it("applies the correct color class", async () => {
|
|
52
|
+
const colors = [
|
|
53
|
+
"primary",
|
|
54
|
+
"secondary",
|
|
55
|
+
"error",
|
|
56
|
+
"warning",
|
|
57
|
+
"success",
|
|
58
|
+
"info",
|
|
59
|
+
"notice",
|
|
60
|
+
"neutral",
|
|
61
|
+
"grayscale",
|
|
62
|
+
"inherit",
|
|
63
|
+
"text",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
colors.forEach((color) => {
|
|
67
|
+
const component = mount(UText, {
|
|
68
|
+
props: {
|
|
69
|
+
color: color as Props["color"],
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
color === "text"
|
|
74
|
+
? expect(component.attributes("class")).toContain("text-default")
|
|
75
|
+
: expect(component.attributes("class")).toContain(color);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Variant prop
|
|
80
|
+
it("applies the correct variant class", async () => {
|
|
81
|
+
const variants = {
|
|
82
|
+
default: "text-primary",
|
|
83
|
+
lifted: "text-primary-lifted",
|
|
84
|
+
accented: "text-primary-accented",
|
|
85
|
+
muted: "text-primary/(--vl-disabled-opacity)",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
Object.entries(variants).forEach(([variant, classes]) => {
|
|
89
|
+
const component = mount(UText, {
|
|
90
|
+
props: {
|
|
91
|
+
variant: variant as Props["variant"],
|
|
92
|
+
color: "primary",
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(component.attributes("class")).toContain(classes);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
50
100
|
// Line prop
|
|
51
101
|
it("applies line class when line prop is true", () => {
|
|
52
102
|
const line = true;
|
|
@@ -60,17 +110,30 @@ describe("UText.vue", () => {
|
|
|
60
110
|
expect(component.attributes("class")).toContain("leading-none");
|
|
61
111
|
});
|
|
62
112
|
|
|
63
|
-
//
|
|
64
|
-
it("
|
|
65
|
-
const
|
|
113
|
+
// Wrap prop
|
|
114
|
+
it("applies text-nowrap class when wrap prop is false", () => {
|
|
115
|
+
const wrap = false;
|
|
116
|
+
|
|
117
|
+
const component = mount(UText, {
|
|
118
|
+
props: {
|
|
119
|
+
wrap,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(component.attributes("class")).toContain("text-nowrap");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Label prop
|
|
127
|
+
it("renders the correct label content", () => {
|
|
128
|
+
const label = "Text label";
|
|
66
129
|
|
|
67
130
|
const component = mount(UText, {
|
|
68
131
|
props: {
|
|
69
|
-
|
|
132
|
+
label,
|
|
70
133
|
},
|
|
71
134
|
});
|
|
72
135
|
|
|
73
|
-
expect(component.html()).toContain(
|
|
136
|
+
expect(component.html()).toContain(label);
|
|
74
137
|
});
|
|
75
138
|
|
|
76
139
|
// DataTest prop
|
|
@@ -88,7 +151,7 @@ describe("UText.vue", () => {
|
|
|
88
151
|
|
|
89
152
|
// Config prop overriding classes
|
|
90
153
|
it("applies custom classes from config prop", () => {
|
|
91
|
-
const customClasses = "
|
|
154
|
+
const customClasses = "font-bold";
|
|
92
155
|
|
|
93
156
|
const component = mount(UText, {
|
|
94
157
|
props: {
|
|
@@ -117,14 +180,14 @@ describe("UText.vue", () => {
|
|
|
117
180
|
expect(component.text()).toContain(slotContent);
|
|
118
181
|
});
|
|
119
182
|
|
|
120
|
-
// Default slot overrides
|
|
121
|
-
it("default slot overrides
|
|
122
|
-
const
|
|
183
|
+
// Default slot overrides label prop
|
|
184
|
+
it("default slot overrides label prop", () => {
|
|
185
|
+
const label = "Label Text";
|
|
123
186
|
const slotContent = "Custom Content";
|
|
124
187
|
|
|
125
188
|
const component = mount(UText, {
|
|
126
189
|
props: {
|
|
127
|
-
|
|
190
|
+
label,
|
|
128
191
|
},
|
|
129
192
|
slots: {
|
|
130
193
|
default: slotContent,
|
|
@@ -132,7 +195,7 @@ describe("UText.vue", () => {
|
|
|
132
195
|
});
|
|
133
196
|
|
|
134
197
|
expect(component.text()).toContain(slotContent);
|
|
135
|
-
expect(component.html()).not.toContain(
|
|
198
|
+
expect(component.html()).not.toContain(label);
|
|
136
199
|
});
|
|
137
200
|
});
|
|
138
201
|
|
package/ui.text-block/types.ts
CHANGED
|
@@ -5,9 +5,9 @@ export type Config = typeof defaultConfig;
|
|
|
5
5
|
|
|
6
6
|
export interface Props {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Text label.
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
label?: string;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Text size.
|
|
@@ -19,11 +19,37 @@ export interface Props {
|
|
|
19
19
|
*/
|
|
20
20
|
align?: "left" | "center" | "right";
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Text variant.
|
|
24
|
+
*/
|
|
25
|
+
variant?: "default" | "accented" | "lifted" | "muted";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Text color.
|
|
29
|
+
*/
|
|
30
|
+
color?:
|
|
31
|
+
| "primary"
|
|
32
|
+
| "secondary"
|
|
33
|
+
| "error"
|
|
34
|
+
| "warning"
|
|
35
|
+
| "success"
|
|
36
|
+
| "info"
|
|
37
|
+
| "notice"
|
|
38
|
+
| "neutral"
|
|
39
|
+
| "grayscale"
|
|
40
|
+
| "inherit"
|
|
41
|
+
| "text"; // the default design system text color
|
|
42
|
+
|
|
22
43
|
/**
|
|
23
44
|
* Removes text line height (useful for 1-line text).
|
|
24
45
|
*/
|
|
25
46
|
line?: boolean;
|
|
26
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Enables text wrapping.
|
|
50
|
+
*/
|
|
51
|
+
wrap?: boolean;
|
|
52
|
+
|
|
27
53
|
/**
|
|
28
54
|
* Component config object.
|
|
29
55
|
*/
|
|
@@ -35,6 +35,7 @@ const { getDataTest, headerAttrs } = useUI<Config>(defaultConfig);
|
|
|
35
35
|
|
|
36
36
|
<template>
|
|
37
37
|
<component :is="tag" ref="header" v-bind="headerAttrs" :data-test="getDataTest()">
|
|
38
|
+
<!-- @slot Use it to add html inside. -->
|
|
38
39
|
<slot>{{ label }}</slot>
|
|
39
40
|
</component>
|
|
40
41
|
</template>
|
package/ui.text-header/config.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
export default /*tw*/ {
|
|
2
2
|
header: {
|
|
3
|
-
base: "
|
|
3
|
+
base: "font-medium",
|
|
4
4
|
variants: {
|
|
5
|
+
variant: {
|
|
6
|
+
default: "text-{color}",
|
|
7
|
+
lifted: "text-{color}-lifted",
|
|
8
|
+
accented: "text-{color}-accented",
|
|
9
|
+
muted: "text-{color}/(--vl-disabled-opacity)",
|
|
10
|
+
},
|
|
11
|
+
color: {
|
|
12
|
+
inherit: "text-inherit",
|
|
13
|
+
},
|
|
5
14
|
size: {
|
|
6
15
|
xs: "text-lg",
|
|
7
16
|
sm: "text-xl",
|
|
@@ -14,9 +23,16 @@ export default /*tw*/ {
|
|
|
14
23
|
true: "!leading-none",
|
|
15
24
|
},
|
|
16
25
|
},
|
|
26
|
+
compoundVariants: [
|
|
27
|
+
{ color: "text", variant: "default", class: "text-default" },
|
|
28
|
+
{ color: "text", variant: "lifted", class: "text-lifted" },
|
|
29
|
+
{ color: "text", variant: "accented", class: "text-accented" },
|
|
30
|
+
{ color: "text", variant: "muted", class: "text-muted" },
|
|
31
|
+
],
|
|
17
32
|
},
|
|
18
33
|
defaults: {
|
|
19
|
-
color: "
|
|
34
|
+
color: "text",
|
|
35
|
+
variant: "default",
|
|
20
36
|
size: "md",
|
|
21
37
|
tag: "div",
|
|
22
38
|
line: true,
|
|
@@ -18,7 +18,7 @@ import type { Props } from "../types.ts";
|
|
|
18
18
|
|
|
19
19
|
interface UHeaderArgs extends Props {
|
|
20
20
|
slotTemplate?: string;
|
|
21
|
-
enum: "size" | "color";
|
|
21
|
+
enum: "size" | "color" | "variant";
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export default {
|
|
@@ -71,6 +71,10 @@ export const Sizes = EnumTemplate.bind({});
|
|
|
71
71
|
Sizes.args = { enum: "size" };
|
|
72
72
|
Sizes.parameters = getEnumVariantDescription();
|
|
73
73
|
|
|
74
|
+
export const Variants = EnumTemplate.bind({});
|
|
75
|
+
Variants.args = { enum: "variant" };
|
|
76
|
+
Variants.parameters = getEnumVariantDescription();
|
|
77
|
+
|
|
74
78
|
export const Colors = EnumTemplate.bind({});
|
|
75
79
|
Colors.args = { enum: "color" };
|
|
76
80
|
Colors.parameters = getEnumVariantDescription();
|
|
@@ -42,6 +42,7 @@ describe("UHeader.vue", () => {
|
|
|
42
42
|
"notice",
|
|
43
43
|
"neutral",
|
|
44
44
|
"grayscale",
|
|
45
|
+
"text",
|
|
45
46
|
];
|
|
46
47
|
|
|
47
48
|
colors.forEach((color) => {
|
|
@@ -51,7 +52,30 @@ describe("UHeader.vue", () => {
|
|
|
51
52
|
},
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
color === "text"
|
|
56
|
+
? expect(component.attributes("class")).toContain("text-default")
|
|
57
|
+
: expect(component.attributes("class")).toContain(color);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Variant prop
|
|
62
|
+
it("applies the correct variant class", async () => {
|
|
63
|
+
const variants = {
|
|
64
|
+
default: "text-primary",
|
|
65
|
+
lifted: "text-primary-lifted",
|
|
66
|
+
accented: "text-primary-accented",
|
|
67
|
+
muted: "text-primary/(--vl-disabled-opacity)",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
Object.entries(variants).forEach(([variant, classes]) => {
|
|
71
|
+
const component = mount(UHeader, {
|
|
72
|
+
props: {
|
|
73
|
+
variant: variant as Props["variant"],
|
|
74
|
+
color: "primary",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(component.attributes("class")).toContain(classes);
|
|
55
79
|
});
|
|
56
80
|
});
|
|
57
81
|
|
package/ui.text-header/types.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface Props {
|
|
|
14
14
|
*/
|
|
15
15
|
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Text variant.
|
|
19
|
+
*/
|
|
20
|
+
variant?: "default" | "accented" | "lifted" | "muted";
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Header color.
|
|
19
24
|
*/
|
|
@@ -26,7 +31,8 @@ export interface Props {
|
|
|
26
31
|
| "info"
|
|
27
32
|
| "notice"
|
|
28
33
|
| "neutral"
|
|
29
|
-
| "grayscale"
|
|
34
|
+
| "grayscale"
|
|
35
|
+
| "text"; // the default design system text color
|
|
30
36
|
|
|
31
37
|
/**
|
|
32
38
|
* Allows changing HTML tag.
|
|
@@ -21,18 +21,18 @@ Use `notifySuccess`, `notifyWarning`, `notifyError` shortcut methods to trigger
|
|
|
21
21
|
|
|
22
22
|
<Source code={`
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
notify,
|
|
25
|
+
notifySuccess,
|
|
26
|
+
notifyWarning,
|
|
27
|
+
notifyError,
|
|
28
28
|
} from "vueless";
|
|
29
29
|
|
|
30
30
|
notify({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
type: "success", // @values: success, warning, error
|
|
32
|
+
label: "Hurray!",
|
|
33
|
+
description: "The file successfully downloaded.",
|
|
34
|
+
duration: 1000, // in milliseconds
|
|
35
|
+
ignoreDuplicates: false // ignore notifications with the same 'description'
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
notifySuccess({ description: "The file successfully downloaded." });
|
|
@@ -52,18 +52,18 @@ notifySuccess({ description: "The file successfully downloaded." });
|
|
|
52
52
|
clearNotifications();
|
|
53
53
|
`} language="jsx" dark />
|
|
54
54
|
|
|
55
|
-
##
|
|
55
|
+
## Delayed notifications
|
|
56
56
|
You can create a delayed notification which you can trigger any time later.
|
|
57
57
|
|
|
58
58
|
<Source code={`
|
|
59
59
|
import { setDelayedNotify, getDelayedNotify } from "vueless";
|
|
60
60
|
|
|
61
61
|
setDelayedNotify({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
type: "success", // @values: success, warning, error
|
|
63
|
+
label: "Hurray!",
|
|
64
|
+
description: "The file successfully downloaded.",
|
|
65
|
+
duration: 1000, // in milliseconds
|
|
66
|
+
ignoreDuplicates: false // ignore notifications with the same 'description'
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// and somewhere below
|