vueless 1.3.5-beta.1 → 1.3.5-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/components.d.ts +2 -1
- package/components.ts +2 -1
- package/composables/useBreakpoint.ts +97 -42
- package/constants.d.ts +2 -1
- package/constants.js +2 -1
- package/index.d.ts +1 -1
- package/index.ts +1 -1
- package/package.json +2 -2
- package/types.ts +1 -1
- package/ui.container-col/config.ts +8 -0
- package/ui.container-col/tests/UCol.test.ts +26 -0
- package/ui.container-col/types.ts +10 -0
- package/{ui.text-empty → ui.container-empty}/storybook/stories.ts +7 -4
- package/ui.container-placeholder/UPlaceholder.vue +50 -0
- package/ui.container-placeholder/config.ts +40 -0
- package/ui.container-placeholder/constants.ts +5 -0
- package/ui.container-placeholder/storybook/docs.mdx +15 -0
- package/ui.container-placeholder/storybook/stories.ts +126 -0
- package/ui.container-placeholder/tests/UPlaceholder.test.ts +173 -0
- package/ui.container-placeholder/types.ts +56 -0
- package/ui.container-row/config.ts +8 -0
- package/ui.container-row/tests/URow.test.ts +26 -0
- package/ui.container-row/types.ts +10 -0
- package/ui.data-list/UDataList.vue +1 -1
- package/ui.data-list/tests/UDataList.test.ts +1 -1
- package/ui.data-table/UTable.vue +1 -1
- package/ui.data-table/tests/UTable.test.ts +1 -1
- /package/{ui.text-empty → ui.container-empty}/UEmpty.vue +0 -0
- /package/{ui.text-empty → ui.container-empty}/config.ts +0 -0
- /package/{ui.text-empty → ui.container-empty}/constants.ts +0 -0
- /package/{ui.text-empty → ui.container-empty}/storybook/assets/empty-inbox.png +0 -0
- /package/{ui.text-empty → ui.container-empty}/storybook/docs.mdx +0 -0
- /package/{ui.text-empty → ui.container-empty}/tests/UEmpty.test.ts +0 -0
- /package/{ui.text-empty → ui.container-empty}/types.ts +0 -0
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ A UI library with Open Architecture for Vue.js 3 and Nuxt.js 3 / 4, powered by [
|
|
|
28
28
|
- 🖼️ Inline SVG icons
|
|
29
29
|
- 🪄 Auto component imports (as you use them)
|
|
30
30
|
- 🧿 Uncompiled source in npm for better DX
|
|
31
|
-
- 🧪️
|
|
31
|
+
- 🧪️ 1400+ unit tests ensuring consistent logic
|
|
32
32
|
- 🛡️ Full TypeScript support with type safety
|
|
33
33
|
|
|
34
34
|
## Built-In Storybook
|
package/components.d.ts
CHANGED
|
@@ -36,7 +36,6 @@ export { default as UNotify } from "./ui.text-notify/UNotify.vue";
|
|
|
36
36
|
export { default as UNumber } from "./ui.text-number/UNumber.vue";
|
|
37
37
|
export { default as UFile } from "./ui.text-file/UFile.vue";
|
|
38
38
|
export { default as UFiles } from "./ui.text-files/UFiles.vue";
|
|
39
|
-
export { default as UEmpty } from "./ui.text-empty/UEmpty.vue";
|
|
40
39
|
export { default as UBadge } from "./ui.text-badge/UBadge.vue";
|
|
41
40
|
/* Containers */
|
|
42
41
|
export { default as UDivider } from "./ui.container-divider/UDivider.vue";
|
|
@@ -46,6 +45,8 @@ export { default as UGroup } from "./ui.container-group/UGroup.vue";
|
|
|
46
45
|
export { default as UGroups } from "./ui.container-groups/UGroups.vue";
|
|
47
46
|
export { default as UAccordion } from "./ui.container-accordion/UAccordion.vue";
|
|
48
47
|
export { default as UAccordionItem } from "./ui.container-accordion-item/UAccordionItem.vue";
|
|
48
|
+
export { default as UEmpty } from "./ui.container-empty/UEmpty.vue";
|
|
49
|
+
export { default as UPlaceholder } from "./ui.container-placeholder/UPlaceholder.vue";
|
|
49
50
|
export { default as UCard } from "./ui.container-card/UCard.vue";
|
|
50
51
|
export { default as UModal } from "./ui.container-modal/UModal.vue";
|
|
51
52
|
export { default as UModalConfirm } from "./ui.container-modal-confirm/UModalConfirm.vue";
|
package/components.ts
CHANGED
|
@@ -36,7 +36,6 @@ export { default as UNotify } from "./ui.text-notify/UNotify.vue";
|
|
|
36
36
|
export { default as UNumber } from "./ui.text-number/UNumber.vue";
|
|
37
37
|
export { default as UFile } from "./ui.text-file/UFile.vue";
|
|
38
38
|
export { default as UFiles } from "./ui.text-files/UFiles.vue";
|
|
39
|
-
export { default as UEmpty } from "./ui.text-empty/UEmpty.vue";
|
|
40
39
|
export { default as UBadge } from "./ui.text-badge/UBadge.vue";
|
|
41
40
|
/* Containers */
|
|
42
41
|
export { default as UDivider } from "./ui.container-divider/UDivider.vue";
|
|
@@ -46,6 +45,8 @@ export { default as UGroup } from "./ui.container-group/UGroup.vue";
|
|
|
46
45
|
export { default as UGroups } from "./ui.container-groups/UGroups.vue";
|
|
47
46
|
export { default as UAccordion } from "./ui.container-accordion/UAccordion.vue";
|
|
48
47
|
export { default as UAccordionItem } from "./ui.container-accordion-item/UAccordionItem.vue";
|
|
48
|
+
export { default as UEmpty } from "./ui.container-empty/UEmpty.vue";
|
|
49
|
+
export { default as UPlaceholder } from "./ui.container-placeholder/UPlaceholder.vue";
|
|
49
50
|
export { default as UCard } from "./ui.container-card/UCard.vue";
|
|
50
51
|
export { default as UModal } from "./ui.container-modal/UModal.vue";
|
|
51
52
|
export { default as UModalConfirm } from "./ui.container-modal-confirm/UModalConfirm.vue";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { onMounted, ref, watch, computed, onBeforeUnmount } from "vue";
|
|
2
2
|
import { isSSR } from "../utils/helper";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type ResponsiveConfig<T> = Partial<Record<BreakpointName, T>>;
|
|
5
5
|
|
|
6
6
|
enum BreakpointName {
|
|
7
7
|
Xs = "xs",
|
|
@@ -21,12 +21,16 @@ enum BreakpointWidth {
|
|
|
21
21
|
"2xl" = 1536,
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
let isInitialized = false;
|
|
25
|
+
let animationId: number | undefined;
|
|
26
|
+
|
|
27
|
+
const windowWidth = ref(0);
|
|
28
|
+
const currentBreakpoint = ref(BreakpointName.Xs);
|
|
29
|
+
const BREAKPOINT_KEYS = Object.keys(BreakpointName) as (keyof typeof BreakpointName)[];
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
const currentBreakpoint: Ref<BreakpointName> = ref(BreakpointName.Xs);
|
|
31
|
+
watch(windowWidth, setBreakpoint, { immediate: true });
|
|
29
32
|
|
|
33
|
+
export function useBreakpoint() {
|
|
30
34
|
const isPhone = computed(() => {
|
|
31
35
|
return currentBreakpoint.value === BreakpointName.Xs;
|
|
32
36
|
});
|
|
@@ -63,50 +67,14 @@ export function useBreakpoint() {
|
|
|
63
67
|
return isDesktop.value || isLargeDesktop.value;
|
|
64
68
|
});
|
|
65
69
|
|
|
66
|
-
watch(windowWidth, setBreakpoint, { immediate: true });
|
|
67
|
-
|
|
68
70
|
onMounted(() => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
windowWidth.value = window.innerWidth;
|
|
72
|
-
|
|
73
|
-
window.addEventListener("resize", resizeListener, { passive: true });
|
|
71
|
+
initBreakpointListener();
|
|
74
72
|
});
|
|
75
73
|
|
|
76
74
|
onBeforeUnmount(() => {
|
|
77
|
-
if (isSSR) return;
|
|
78
|
-
|
|
79
75
|
window.removeEventListener("resize", resizeListener);
|
|
80
76
|
});
|
|
81
77
|
|
|
82
|
-
function resizeListener() {
|
|
83
|
-
if (isSSR) return;
|
|
84
|
-
|
|
85
|
-
if (animationId) {
|
|
86
|
-
window.cancelAnimationFrame(animationId);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
animationId = window.requestAnimationFrame(() => {
|
|
90
|
-
windowWidth.value = window.innerWidth;
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function setBreakpoint(newWindowWidth: number) {
|
|
95
|
-
if (newWindowWidth === undefined) return;
|
|
96
|
-
|
|
97
|
-
const breakpoints = [
|
|
98
|
-
{ width: BreakpointWidth["2xl"], name: BreakpointName["2xl"] },
|
|
99
|
-
{ width: BreakpointWidth.Xl, name: BreakpointName.Xl },
|
|
100
|
-
{ width: BreakpointWidth.Lg, name: BreakpointName.Lg },
|
|
101
|
-
{ width: BreakpointWidth.Md, name: BreakpointName.Md },
|
|
102
|
-
{ width: BreakpointWidth.Sm, name: BreakpointName.Sm },
|
|
103
|
-
];
|
|
104
|
-
|
|
105
|
-
currentBreakpoint.value =
|
|
106
|
-
breakpoints.find((breakpoint) => newWindowWidth >= breakpoint.width)?.name ||
|
|
107
|
-
BreakpointName.Xs;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
78
|
return {
|
|
111
79
|
isPhone,
|
|
112
80
|
isLargePhone,
|
|
@@ -120,3 +88,90 @@ export function useBreakpoint() {
|
|
|
120
88
|
breakpoint: currentBreakpoint,
|
|
121
89
|
};
|
|
122
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Shorthand function that can be used directly in templates.
|
|
94
|
+
* Returns the appropriate value based on the current breakpoint.
|
|
95
|
+
* Vue will track the reactive dependency and re-render when the breakpoint changes.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```vue
|
|
99
|
+
* <template>
|
|
100
|
+
* <UButton :size="r({ sm: 'sm', md: 'md' })">Click me</UButton>
|
|
101
|
+
* </template>
|
|
102
|
+
*
|
|
103
|
+
* <script setup>
|
|
104
|
+
* import { r } from "vueless";
|
|
105
|
+
* </script>
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function r<T>(config: ResponsiveConfig<T>): T {
|
|
109
|
+
initBreakpointListener();
|
|
110
|
+
|
|
111
|
+
const definedKeys = BREAKPOINT_KEYS.filter((key) => BreakpointName[key] in config);
|
|
112
|
+
|
|
113
|
+
if (!definedKeys.length) {
|
|
114
|
+
return undefined as T;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const currentIndex = BREAKPOINT_KEYS.findIndex(
|
|
118
|
+
(key) => BreakpointName[key] === currentBreakpoint.value,
|
|
119
|
+
);
|
|
120
|
+
const smallestDefinedIndex = BREAKPOINT_KEYS.indexOf(definedKeys[0]);
|
|
121
|
+
const largestDefinedIndex = BREAKPOINT_KEYS.indexOf(definedKeys[definedKeys.length - 1]);
|
|
122
|
+
|
|
123
|
+
if (currentIndex <= smallestDefinedIndex) {
|
|
124
|
+
return config[BreakpointName[definedKeys[0]]] as T;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (currentIndex >= largestDefinedIndex) {
|
|
128
|
+
return config[BreakpointName[definedKeys[definedKeys.length - 1]]] as T;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (let i = currentIndex; i >= 0; i--) {
|
|
132
|
+
const bp = BreakpointName[BREAKPOINT_KEYS[i]];
|
|
133
|
+
|
|
134
|
+
if (bp in config) {
|
|
135
|
+
return config[bp] as T;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return config[BreakpointName[definedKeys[0]]] as T;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function setBreakpoint(newWindowWidth: number) {
|
|
143
|
+
if (newWindowWidth === undefined) return;
|
|
144
|
+
|
|
145
|
+
for (let i = BREAKPOINT_KEYS.length - 1; i >= 0; i--) {
|
|
146
|
+
const key = BREAKPOINT_KEYS[i];
|
|
147
|
+
|
|
148
|
+
if (newWindowWidth >= BreakpointWidth[key]) {
|
|
149
|
+
currentBreakpoint.value = BreakpointName[key];
|
|
150
|
+
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
currentBreakpoint.value = BreakpointName.Xs;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resizeListener() {
|
|
159
|
+
if (isSSR) return;
|
|
160
|
+
|
|
161
|
+
if (animationId) {
|
|
162
|
+
window.cancelAnimationFrame(animationId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
animationId = window.requestAnimationFrame(() => {
|
|
166
|
+
windowWidth.value = window.innerWidth;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function initBreakpointListener() {
|
|
171
|
+
if (isInitialized || isSSR) return;
|
|
172
|
+
|
|
173
|
+
isInitialized = true;
|
|
174
|
+
windowWidth.value = window.innerWidth;
|
|
175
|
+
|
|
176
|
+
window.addEventListener("resize", resizeListener, { passive: true });
|
|
177
|
+
}
|
package/constants.d.ts
CHANGED
|
@@ -176,7 +176,6 @@ export namespace COMPONENTS {
|
|
|
176
176
|
let UNumber: string;
|
|
177
177
|
let UFile: string;
|
|
178
178
|
let UFiles: string;
|
|
179
|
-
let UEmpty: string;
|
|
180
179
|
let UBadge: string;
|
|
181
180
|
let UDivider: string;
|
|
182
181
|
let UCol: string;
|
|
@@ -185,6 +184,8 @@ export namespace COMPONENTS {
|
|
|
185
184
|
let UGroups: string;
|
|
186
185
|
let UAccordion: string;
|
|
187
186
|
let UAccordionItem: string;
|
|
187
|
+
let UEmpty: string;
|
|
188
|
+
let UPlaceholder: string;
|
|
188
189
|
let UCard: string;
|
|
189
190
|
let UModal: string;
|
|
190
191
|
let UModalConfirm: string;
|
package/constants.js
CHANGED
|
@@ -284,7 +284,6 @@ export const COMPONENTS = {
|
|
|
284
284
|
UNumber: "ui.text-number",
|
|
285
285
|
UFile: "ui.text-file",
|
|
286
286
|
UFiles: "ui.text-files",
|
|
287
|
-
UEmpty: "ui.text-empty",
|
|
288
287
|
UBadge: "ui.text-badge",
|
|
289
288
|
|
|
290
289
|
/* Containers */
|
|
@@ -295,6 +294,8 @@ export const COMPONENTS = {
|
|
|
295
294
|
UGroups: "ui.container-groups",
|
|
296
295
|
UAccordion: "ui.container-accordion",
|
|
297
296
|
UAccordionItem: "ui.container-accordion-item",
|
|
297
|
+
UEmpty: "ui.container-empty",
|
|
298
|
+
UPlaceholder: "ui.container-placeholder",
|
|
298
299
|
UCard: "ui.container-card",
|
|
299
300
|
UModal: "ui.container-modal",
|
|
300
301
|
UModalConfirm: "ui.container-modal-confirm",
|
package/index.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export { useLocale } from "./composables/useLocale";
|
|
|
29
29
|
export { useUI } from "./composables/useUI";
|
|
30
30
|
export { useDarkMode } from "./composables/useDarkMode";
|
|
31
31
|
export { useRequestQueue } from "./composables/useRequestQueue";
|
|
32
|
-
export { useBreakpoint } from "./composables/useBreakpoint";
|
|
32
|
+
export { useBreakpoint, r } from "./composables/useBreakpoint";
|
|
33
33
|
export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
|
|
34
34
|
export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
|
|
35
35
|
export { useMutationObserver } from "./composables/useMutationObserver";
|
package/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ export { useLocale } from "./composables/useLocale";
|
|
|
35
35
|
export { useUI } from "./composables/useUI";
|
|
36
36
|
export { useDarkMode } from "./composables/useDarkMode";
|
|
37
37
|
export { useRequestQueue } from "./composables/useRequestQueue";
|
|
38
|
-
export { useBreakpoint } from "./composables/useBreakpoint";
|
|
38
|
+
export { useBreakpoint, r } from "./composables/useBreakpoint";
|
|
39
39
|
export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
|
|
40
40
|
export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
|
|
41
41
|
export { useMutationObserver } from "./composables/useMutationObserver";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vueless",
|
|
3
|
-
"version": "1.3.5-beta.
|
|
3
|
+
"version": "1.3.5-beta.3",
|
|
4
4
|
"description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
|
|
5
5
|
"author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
|
|
6
6
|
"homepage": "https://vueless.com",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
58
58
|
"@vue/test-utils": "^2.4.6",
|
|
59
59
|
"@vue/tsconfig": "^0.7.0",
|
|
60
|
-
"@vueless/storybook": "^1.
|
|
60
|
+
"@vueless/storybook": "^1.3.1",
|
|
61
61
|
"eslint": "^9.32.0",
|
|
62
62
|
"eslint-plugin-storybook": "^10.0.2",
|
|
63
63
|
"eslint-plugin-vue": "^10.3.0",
|
package/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import UTextDefaultConfig from "./ui.text-block/config";
|
|
2
2
|
import UAlertDefaultConfig from "./ui.text-alert/config";
|
|
3
|
-
import UEmptyDefaultConfig from "./ui.
|
|
3
|
+
import UEmptyDefaultConfig from "./ui.container-empty/config";
|
|
4
4
|
import UFileDefaultConfig from "./ui.text-file/config";
|
|
5
5
|
import UFilesDefaultConfig from "./ui.text-files/config";
|
|
6
6
|
import UHeaderDefaultConfig from "./ui.text-header/config";
|
|
@@ -12,6 +12,12 @@ export default /*tw*/ {
|
|
|
12
12
|
block: {
|
|
13
13
|
true: "w-full",
|
|
14
14
|
},
|
|
15
|
+
grow: {
|
|
16
|
+
true: "flex-grow",
|
|
17
|
+
},
|
|
18
|
+
shrink: {
|
|
19
|
+
true: "flex-shrink",
|
|
20
|
+
},
|
|
15
21
|
gap: {
|
|
16
22
|
none: "gap-0",
|
|
17
23
|
"3xs": "gap-0.5",
|
|
@@ -61,5 +67,7 @@ export default /*tw*/ {
|
|
|
61
67
|
wrap: false,
|
|
62
68
|
reverse: false,
|
|
63
69
|
block: false,
|
|
70
|
+
grow: false,
|
|
71
|
+
shrink: false,
|
|
64
72
|
},
|
|
65
73
|
};
|
|
@@ -140,6 +140,32 @@ describe("UCol.vue", () => {
|
|
|
140
140
|
expect(component.attributes("class")).toContain(expectedClasses);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
it("Grow – applies the correct grow class", () => {
|
|
144
|
+
const grow = true;
|
|
145
|
+
const expectedClasses = "flex-grow";
|
|
146
|
+
|
|
147
|
+
const component = mount(UCol, {
|
|
148
|
+
props: {
|
|
149
|
+
grow,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(component.attributes("class")).toContain(expectedClasses);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("Shrink – applies the correct shrink class", () => {
|
|
157
|
+
const shrink = true;
|
|
158
|
+
const expectedClasses = "flex-shrink";
|
|
159
|
+
|
|
160
|
+
const component = mount(UCol, {
|
|
161
|
+
props: {
|
|
162
|
+
shrink,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(component.attributes("class")).toContain(expectedClasses);
|
|
167
|
+
});
|
|
168
|
+
|
|
143
169
|
it("Tag – renders the correct HTML tag", () => {
|
|
144
170
|
const tags = ["div", "section", "article", "main", "aside", "nav", "span"];
|
|
145
171
|
|
|
@@ -49,6 +49,16 @@ export interface Props {
|
|
|
49
49
|
*/
|
|
50
50
|
block?: boolean;
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Allow flex item to grow to fill available space (flex-grow).
|
|
54
|
+
*/
|
|
55
|
+
grow?: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Allow flex item to shrink if necessary (flex-shrink).
|
|
59
|
+
*/
|
|
60
|
+
shrink?: boolean;
|
|
61
|
+
|
|
52
62
|
/**
|
|
53
63
|
* Allows changing HTML tag.
|
|
54
64
|
*/
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getDocsDescription,
|
|
7
7
|
} from "../../utils/storybook";
|
|
8
8
|
|
|
9
|
-
import UEmpty from "../../ui.
|
|
9
|
+
import UEmpty from "../../ui.container-empty/UEmpty.vue";
|
|
10
10
|
import UButton from "../../ui.button/UButton.vue";
|
|
11
11
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
12
12
|
import URow from "../../ui.container-row/URow.vue";
|
|
@@ -25,8 +25,8 @@ interface UEmptyArgs extends Props {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export default {
|
|
28
|
-
id: "
|
|
29
|
-
title: "
|
|
28
|
+
id: "5055",
|
|
29
|
+
title: "Containers / Empty",
|
|
30
30
|
component: UEmpty,
|
|
31
31
|
args: {
|
|
32
32
|
title: "No contacts",
|
|
@@ -95,12 +95,15 @@ DefaultSlot.args = {
|
|
|
95
95
|
`,
|
|
96
96
|
};
|
|
97
97
|
|
|
98
|
+
export const PlaceholderIcon = DefaultTemplate.bind({});
|
|
99
|
+
PlaceholderIcon.args = { placeholderIcon: "inbox", title: "No messages" };
|
|
100
|
+
|
|
98
101
|
export const FooterSlot = DefaultTemplate.bind({});
|
|
99
102
|
FooterSlot.args = {
|
|
100
103
|
slotTemplate: `
|
|
101
104
|
<template #footer>
|
|
102
105
|
<UButton
|
|
103
|
-
label="Add
|
|
106
|
+
label="Add"
|
|
104
107
|
size="sm"
|
|
105
108
|
color="grayscale"
|
|
106
109
|
variant="soft"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useTemplateRef } from "vue";
|
|
3
|
+
|
|
4
|
+
import { useUI } from "../composables/useUI";
|
|
5
|
+
import { getDefaults } from "../utils/ui";
|
|
6
|
+
|
|
7
|
+
import { COMPONENT_NAME } from "./constants";
|
|
8
|
+
import defaultConfig from "./config";
|
|
9
|
+
|
|
10
|
+
import type { Props, Config } from "./types";
|
|
11
|
+
|
|
12
|
+
defineOptions({ inheritAttrs: false });
|
|
13
|
+
|
|
14
|
+
withDefaults(defineProps<Props>(), {
|
|
15
|
+
...getDefaults<Props, Config>(defaultConfig, COMPONENT_NAME),
|
|
16
|
+
label: "",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
|
|
20
|
+
|
|
21
|
+
defineExpose({
|
|
22
|
+
/**
|
|
23
|
+
* A reference to the component's wrapper element for direct DOM manipulation.
|
|
24
|
+
* @property {HTMLDivElement}
|
|
25
|
+
*/
|
|
26
|
+
wrapperRef,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs } = useUI<Config>(defaultConfig);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div
|
|
34
|
+
ref="wrapper"
|
|
35
|
+
role="region"
|
|
36
|
+
:aria-label="label || 'Placeholder area'"
|
|
37
|
+
v-bind="wrapperAttrs"
|
|
38
|
+
:data-test="getDataTest()"
|
|
39
|
+
>
|
|
40
|
+
<div v-bind="contentAttrs">
|
|
41
|
+
<!--
|
|
42
|
+
@slot Use it to add custom content inside the placeholder.
|
|
43
|
+
@binding {string} label
|
|
44
|
+
-->
|
|
45
|
+
<slot :label="label">
|
|
46
|
+
<span v-if="label" v-bind="labelAttrs">{{ label }}</span>
|
|
47
|
+
</slot>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export default /*tw*/ {
|
|
2
|
+
wrapper: {
|
|
3
|
+
base: `
|
|
4
|
+
flex items-center justify-center w-full h-full
|
|
5
|
+
border-solid border-2 border-{color}/15
|
|
6
|
+
`,
|
|
7
|
+
variants: {
|
|
8
|
+
rounded: {
|
|
9
|
+
sm: "rounded-small",
|
|
10
|
+
md: "rounded-medium",
|
|
11
|
+
lg: "rounded-large",
|
|
12
|
+
none: "rounded-none",
|
|
13
|
+
},
|
|
14
|
+
dashed: {
|
|
15
|
+
true: "border-dashed",
|
|
16
|
+
},
|
|
17
|
+
dotted: {
|
|
18
|
+
true: "border-dotted",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
content: "flex items-center justify-center",
|
|
23
|
+
label: {
|
|
24
|
+
base: "text-{color} select-none",
|
|
25
|
+
variants: {
|
|
26
|
+
size: {
|
|
27
|
+
sm: "text-small",
|
|
28
|
+
md: "text-medium",
|
|
29
|
+
lg: "text-large",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaults: {
|
|
34
|
+
color: "neutral",
|
|
35
|
+
size: "md",
|
|
36
|
+
rounded: "md",
|
|
37
|
+
dashed: false,
|
|
38
|
+
dotted: false,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from "@storybook/addon-docs/blocks";
|
|
2
|
+
import * as PlaceholderStories from "./stories";
|
|
3
|
+
|
|
4
|
+
<Meta of={PlaceholderStories} />
|
|
5
|
+
|
|
6
|
+
<Title />
|
|
7
|
+
|
|
8
|
+
<Description />
|
|
9
|
+
|
|
10
|
+
<Primary />
|
|
11
|
+
|
|
12
|
+
<Controls />
|
|
13
|
+
|
|
14
|
+
<Stories />
|
|
15
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
2
|
+
import {
|
|
3
|
+
getArgs,
|
|
4
|
+
getArgTypes,
|
|
5
|
+
getSlotNames,
|
|
6
|
+
getSlotsFragment,
|
|
7
|
+
getDocsDescription,
|
|
8
|
+
} from "../../utils/storybook";
|
|
9
|
+
|
|
10
|
+
import UPlaceholder from "../UPlaceholder.vue";
|
|
11
|
+
import UCol from "../../ui.container-col/UCol.vue";
|
|
12
|
+
import URow from "../../ui.container-row/URow.vue";
|
|
13
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
14
|
+
import UHeader from "../../ui.text-header/UHeader.vue";
|
|
15
|
+
|
|
16
|
+
import type { Props } from "../types";
|
|
17
|
+
|
|
18
|
+
interface PlaceholderArgs extends Props {
|
|
19
|
+
slotTemplate?: string;
|
|
20
|
+
enum?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
id: "5052",
|
|
25
|
+
title: "Containers / Placeholder",
|
|
26
|
+
args: {
|
|
27
|
+
label: "Placeholder label.",
|
|
28
|
+
dashed: true,
|
|
29
|
+
},
|
|
30
|
+
argTypes: {
|
|
31
|
+
...getArgTypes(UPlaceholder.__name),
|
|
32
|
+
},
|
|
33
|
+
parameters: {
|
|
34
|
+
docs: {
|
|
35
|
+
...getDocsDescription(UPlaceholder.__name),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
} as Meta;
|
|
39
|
+
|
|
40
|
+
const DefaultTemplate: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs) => ({
|
|
41
|
+
components: { UPlaceholder, URow, UText },
|
|
42
|
+
setup: () => ({ args, slots: getSlotNames(UPlaceholder.__name) }),
|
|
43
|
+
template: `
|
|
44
|
+
<UPlaceholder v-bind="args" class="h-32">
|
|
45
|
+
${args.slotTemplate || getSlotsFragment("")}
|
|
46
|
+
</UPlaceholder>
|
|
47
|
+
`,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const EnumTemplate: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs, { argTypes }) => ({
|
|
51
|
+
components: { UPlaceholder, URow },
|
|
52
|
+
setup: () => ({ args, argTypes, getArgs }),
|
|
53
|
+
template: `
|
|
54
|
+
<URow>
|
|
55
|
+
<UPlaceholder
|
|
56
|
+
v-for="option in argTypes?.[args.enum]?.options"
|
|
57
|
+
v-bind="getArgs(args, option)"
|
|
58
|
+
:key="option"
|
|
59
|
+
class="h-32"
|
|
60
|
+
/>
|
|
61
|
+
</URow>
|
|
62
|
+
`,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const Default = DefaultTemplate.bind({});
|
|
66
|
+
Default.args = {};
|
|
67
|
+
|
|
68
|
+
export const NoLabel = DefaultTemplate.bind({});
|
|
69
|
+
NoLabel.args = { label: "" };
|
|
70
|
+
|
|
71
|
+
export const Sizes = EnumTemplate.bind({});
|
|
72
|
+
Sizes.args = { enum: "size", label: "{enumValue}" };
|
|
73
|
+
|
|
74
|
+
export const Rounded = EnumTemplate.bind({});
|
|
75
|
+
Rounded.args = { enum: "rounded", label: "{enumValue}" };
|
|
76
|
+
|
|
77
|
+
export const BorderStyle: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs) => ({
|
|
78
|
+
components: { UPlaceholder, URow },
|
|
79
|
+
setup: () => ({ args }),
|
|
80
|
+
template: `
|
|
81
|
+
<URow>
|
|
82
|
+
<UPlaceholder label="Solid" class="h-32" />
|
|
83
|
+
<UPlaceholder label="Dashed" dashed class="h-32" />
|
|
84
|
+
<UPlaceholder label="Dotted" dotted class="h-32" />
|
|
85
|
+
</URow>
|
|
86
|
+
`,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const Colors = EnumTemplate.bind({});
|
|
90
|
+
Colors.args = { enum: "color", label: "{enumValue}" };
|
|
91
|
+
|
|
92
|
+
export const WithSlot: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs) => ({
|
|
93
|
+
components: { UPlaceholder, UCol, UText, UHeader },
|
|
94
|
+
setup: () => ({ args }),
|
|
95
|
+
template: `
|
|
96
|
+
<UPlaceholder v-bind="args" class="h-32">
|
|
97
|
+
<UCol align="center" gap="2xs">
|
|
98
|
+
<UHeader size="lg">📦</UHeader>
|
|
99
|
+
<UText color="neutral">Custom slot content</UText>
|
|
100
|
+
</UCol>
|
|
101
|
+
</UPlaceholder>
|
|
102
|
+
`,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export const FixedSize: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs) => ({
|
|
106
|
+
components: { UPlaceholder },
|
|
107
|
+
setup: () => ({ args }),
|
|
108
|
+
template: `
|
|
109
|
+
<UPlaceholder label="Fixed size: 300x150" class="w-[300px] h-[150px]" />
|
|
110
|
+
`,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export const LayoutExample: StoryFn<PlaceholderArgs> = (args: PlaceholderArgs) => ({
|
|
114
|
+
components: { UPlaceholder, URow, UCol },
|
|
115
|
+
setup: () => ({ args }),
|
|
116
|
+
template: `
|
|
117
|
+
<URow align="stretch" class="h-96">
|
|
118
|
+
<UPlaceholder label="Sidebar" class="w-64" />
|
|
119
|
+
<UCol align="stretch" grow>
|
|
120
|
+
<UPlaceholder label="Header" class="h-16" />
|
|
121
|
+
<UPlaceholder label="Main Content" />
|
|
122
|
+
<UPlaceholder label="Footer" class="h-12" />
|
|
123
|
+
</UCol>
|
|
124
|
+
</URow>
|
|
125
|
+
`,
|
|
126
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { mount } from "@vue/test-utils";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
|
|
4
|
+
import UPlaceholder from "../UPlaceholder.vue";
|
|
5
|
+
|
|
6
|
+
import type { Props } from "../types";
|
|
7
|
+
|
|
8
|
+
describe("UPlaceholder.vue", () => {
|
|
9
|
+
describe("Props", () => {
|
|
10
|
+
it("Size – applies the correct size class", () => {
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: "text-small",
|
|
13
|
+
md: "text-medium",
|
|
14
|
+
lg: "text-large",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
Object.entries(sizeClasses).forEach(([size, classes]) => {
|
|
18
|
+
const component = mount(UPlaceholder, {
|
|
19
|
+
props: {
|
|
20
|
+
size: size as Props["size"],
|
|
21
|
+
label: "Test",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const labelElement = component.find("span");
|
|
26
|
+
|
|
27
|
+
expect(labelElement.attributes("class")).toContain(classes);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("Rounded – applies the correct rounded class", () => {
|
|
32
|
+
const roundedClasses = {
|
|
33
|
+
sm: "rounded-small",
|
|
34
|
+
md: "rounded-medium",
|
|
35
|
+
lg: "rounded-large",
|
|
36
|
+
none: "rounded-none",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
Object.entries(roundedClasses).forEach(([rounded, classes]) => {
|
|
40
|
+
const component = mount(UPlaceholder, {
|
|
41
|
+
props: {
|
|
42
|
+
rounded: rounded as Props["rounded"],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(component.attributes("class")).toContain(classes);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("Dashed – applies the correct border class", () => {
|
|
51
|
+
const dashed = true;
|
|
52
|
+
const expectedClass = "border-dashed";
|
|
53
|
+
|
|
54
|
+
const component = mount(UPlaceholder, {
|
|
55
|
+
props: {
|
|
56
|
+
dashed,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(component.attributes("class")).toContain(expectedClass);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("Dotted – applies the correct border class", () => {
|
|
64
|
+
const dotted = true;
|
|
65
|
+
const expectedClass = "border-dotted";
|
|
66
|
+
|
|
67
|
+
const component = mount(UPlaceholder, {
|
|
68
|
+
props: {
|
|
69
|
+
dotted,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(component.attributes("class")).toContain(expectedClass);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("Border – applies solid border class by default", () => {
|
|
77
|
+
const expectedClass = "border-solid";
|
|
78
|
+
|
|
79
|
+
const component = mount(UPlaceholder, {
|
|
80
|
+
props: {
|
|
81
|
+
dashed: false,
|
|
82
|
+
dotted: false,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(component.attributes("class")).toContain(expectedClass);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("Color – applies the correct color class", () => {
|
|
90
|
+
const color = "primary";
|
|
91
|
+
|
|
92
|
+
const component = mount(UPlaceholder, {
|
|
93
|
+
props: {
|
|
94
|
+
color,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(component.attributes("class")).toContain(`border-${color}`);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("Label – renders the correct label text", () => {
|
|
102
|
+
const label = "Test Label";
|
|
103
|
+
|
|
104
|
+
const component = mount(UPlaceholder, {
|
|
105
|
+
props: {
|
|
106
|
+
label,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(component.text()).toContain(label);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("Label – sets aria-label from label prop", () => {
|
|
114
|
+
const label = "Test Area";
|
|
115
|
+
|
|
116
|
+
const component = mount(UPlaceholder, {
|
|
117
|
+
props: {
|
|
118
|
+
label,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(component.attributes("aria-label")).toBe(label);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("Label – sets default aria-label when no label provided", () => {
|
|
126
|
+
const component = mount(UPlaceholder);
|
|
127
|
+
|
|
128
|
+
expect(component.attributes("aria-label")).toBe("Placeholder area");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("Role – applies the correct role attribute", () => {
|
|
132
|
+
const component = mount(UPlaceholder);
|
|
133
|
+
|
|
134
|
+
expect(component.attributes("role")).toBe("region");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("Data Test – applies the correct data-test attribute", () => {
|
|
138
|
+
const dataTest = "custom-test-id";
|
|
139
|
+
|
|
140
|
+
const component = mount(UPlaceholder, {
|
|
141
|
+
props: {
|
|
142
|
+
dataTest,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(component.attributes("data-test")).toBe(dataTest);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("Slots", () => {
|
|
151
|
+
it("Default – renders content from default slot", () => {
|
|
152
|
+
const slotContent = "Custom Content";
|
|
153
|
+
const slotClass = "custom-content";
|
|
154
|
+
|
|
155
|
+
const component = mount(UPlaceholder, {
|
|
156
|
+
slots: {
|
|
157
|
+
default: `<div class='${slotClass}'>${slotContent}</div>`,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(component.find(`.${slotClass}`).exists()).toBe(true);
|
|
162
|
+
expect(component.text()).toContain(slotContent);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("Exposed refs", () => {
|
|
167
|
+
it("wrapperRef – exposes wrapperRef", () => {
|
|
168
|
+
const component = mount(UPlaceholder);
|
|
169
|
+
|
|
170
|
+
expect(component.vm.wrapperRef).toBeDefined();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import defaultConfig from "./config";
|
|
2
|
+
|
|
3
|
+
import type { ComponentConfig } from "../types";
|
|
4
|
+
|
|
5
|
+
export type Config = typeof defaultConfig;
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
/**
|
|
9
|
+
* Placeholder label text.
|
|
10
|
+
*/
|
|
11
|
+
label?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Label text size.
|
|
15
|
+
*/
|
|
16
|
+
size?: "sm" | "md" | "lg";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Border radius size.
|
|
20
|
+
*/
|
|
21
|
+
rounded?: "sm" | "md" | "lg" | "none";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Use dashed border style.
|
|
25
|
+
*/
|
|
26
|
+
dashed?: boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Use dotted border style.
|
|
30
|
+
*/
|
|
31
|
+
dotted?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Border color.
|
|
35
|
+
*/
|
|
36
|
+
color?:
|
|
37
|
+
| "primary"
|
|
38
|
+
| "secondary"
|
|
39
|
+
| "error"
|
|
40
|
+
| "warning"
|
|
41
|
+
| "success"
|
|
42
|
+
| "info"
|
|
43
|
+
| "notice"
|
|
44
|
+
| "neutral"
|
|
45
|
+
| "grayscale";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Component config object.
|
|
49
|
+
*/
|
|
50
|
+
config?: ComponentConfig<Config>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Data-test attribute for automated testing.
|
|
54
|
+
*/
|
|
55
|
+
dataTest?: string | null;
|
|
56
|
+
}
|
|
@@ -12,6 +12,12 @@ export default /*tw*/ {
|
|
|
12
12
|
block: {
|
|
13
13
|
true: "w-full",
|
|
14
14
|
},
|
|
15
|
+
grow: {
|
|
16
|
+
true: "flex-grow",
|
|
17
|
+
},
|
|
18
|
+
shrink: {
|
|
19
|
+
true: "flex-shrink",
|
|
20
|
+
},
|
|
15
21
|
gap: {
|
|
16
22
|
none: "gap-0",
|
|
17
23
|
"2xs": "gap-1",
|
|
@@ -59,5 +65,7 @@ export default /*tw*/ {
|
|
|
59
65
|
wrap: false,
|
|
60
66
|
block: false,
|
|
61
67
|
reverse: false,
|
|
68
|
+
grow: false,
|
|
69
|
+
shrink: false,
|
|
62
70
|
},
|
|
63
71
|
};
|
|
@@ -138,6 +138,32 @@ describe("URow.vue", () => {
|
|
|
138
138
|
expect(component.attributes("class")).toContain(expectedClasses);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
+
it("Grow – applies the correct grow class", () => {
|
|
142
|
+
const grow = true;
|
|
143
|
+
const expectedClasses = "flex-grow";
|
|
144
|
+
|
|
145
|
+
const component = mount(URow, {
|
|
146
|
+
props: {
|
|
147
|
+
grow,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(component.attributes("class")).toContain(expectedClasses);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("Shrink – applies the correct shrink class", () => {
|
|
155
|
+
const shrink = true;
|
|
156
|
+
const expectedClasses = "flex-shrink";
|
|
157
|
+
|
|
158
|
+
const component = mount(URow, {
|
|
159
|
+
props: {
|
|
160
|
+
shrink,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(component.attributes("class")).toContain(expectedClasses);
|
|
165
|
+
});
|
|
166
|
+
|
|
141
167
|
it("Tag – renders the correct HTML tag", () => {
|
|
142
168
|
const tags = ["div", "section", "article", "main", "aside", "nav", "span"];
|
|
143
169
|
|
|
@@ -49,6 +49,16 @@ export interface Props {
|
|
|
49
49
|
*/
|
|
50
50
|
block?: boolean;
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Allow flex item to grow to fill available space (flex-grow).
|
|
54
|
+
*/
|
|
55
|
+
grow?: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Allow flex item to shrink if necessary (flex-shrink).
|
|
59
|
+
*/
|
|
60
|
+
shrink?: boolean;
|
|
61
|
+
|
|
52
62
|
/**
|
|
53
63
|
* Allows changing HTML tag.
|
|
54
64
|
*/
|
|
@@ -7,7 +7,7 @@ import { getDefaults } from "../utils/ui";
|
|
|
7
7
|
import { hasSlotContent } from "../utils/helper";
|
|
8
8
|
|
|
9
9
|
import UIcon from "../ui.image-icon/UIcon.vue";
|
|
10
|
-
import UEmpty from "../ui.
|
|
10
|
+
import UEmpty from "../ui.container-empty/UEmpty.vue";
|
|
11
11
|
|
|
12
12
|
import { COMPONENT_NAME } from "./constants";
|
|
13
13
|
import defaultConfig from "./config";
|
|
@@ -2,7 +2,7 @@ import { mount, VueWrapper } from "@vue/test-utils";
|
|
|
2
2
|
import { describe, it, expect } from "vitest";
|
|
3
3
|
|
|
4
4
|
import UDataList from "../UDataList.vue";
|
|
5
|
-
import UEmpty from "../../ui.
|
|
5
|
+
import UEmpty from "../../ui.container-empty/UEmpty.vue";
|
|
6
6
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
7
7
|
import draggable from "vuedraggable";
|
|
8
8
|
|
package/ui.data-table/UTable.vue
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "vue";
|
|
13
13
|
import { isEqual } from "lodash-es";
|
|
14
14
|
|
|
15
|
-
import UEmpty from "../ui.
|
|
15
|
+
import UEmpty from "../ui.container-empty/UEmpty.vue";
|
|
16
16
|
import UCheckbox from "../ui.form-checkbox/UCheckbox.vue";
|
|
17
17
|
import ULoaderProgress from "../ui.loader-progress/ULoaderProgress.vue";
|
|
18
18
|
import UTableRow from "./UTableRow.vue";
|
|
@@ -4,7 +4,7 @@ import { nextTick } from "vue";
|
|
|
4
4
|
|
|
5
5
|
import UTable from "../UTable.vue";
|
|
6
6
|
import UTableRow from "../UTableRow.vue";
|
|
7
|
-
import UEmpty from "../../ui.
|
|
7
|
+
import UEmpty from "../../ui.container-empty/UEmpty.vue";
|
|
8
8
|
import ULoaderProgress from "../../ui.loader-progress/ULoaderProgress.vue";
|
|
9
9
|
import UDivider from "../../ui.container-divider/UDivider.vue";
|
|
10
10
|
import {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|