vueless 1.2.8 → 1.2.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants.d.ts +4 -0
- package/constants.js +4 -0
- package/icons/storybook/rocket_launch.svg +1 -0
- package/index.d.ts +3 -1
- package/index.ts +3 -1
- package/package.json +9 -5
- package/plugin-vite.js +6 -1
- package/types.ts +14 -2
- package/ui.button/config.ts +4 -4
- package/ui.button/tests/UButton.test.ts +3 -3
- package/ui.button-toggle/config.ts +2 -2
- package/ui.container-accordion/UAccordion.vue +0 -1
- package/ui.container-accordion/config.ts +1 -1
- package/ui.container-accordion/storybook/stories.ts +13 -1
- package/ui.container-accordion-item/UAccordionItem.vue +17 -4
- package/ui.container-accordion-item/config.ts +1 -1
- package/ui.container-accordion-item/storybook/stories.ts +26 -1
- package/ui.container-accordion-item/tests/UAccordionItem.test.ts +186 -0
- package/ui.container-card/config.ts +1 -1
- package/ui.data-table/config.ts +4 -4
- package/ui.dropdown-badge/UDropdownBadge.vue +68 -3
- package/ui.dropdown-badge/config.ts +5 -1
- package/ui.dropdown-badge/storybook/stories.ts +280 -4
- package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +194 -0
- package/ui.dropdown-badge/types.ts +30 -0
- package/ui.dropdown-button/UDropdownButton.vue +69 -6
- package/ui.dropdown-button/config.ts +5 -1
- package/ui.dropdown-button/storybook/stories.ts +288 -3
- package/ui.dropdown-button/tests/UDropdownButton.test.ts +190 -0
- package/ui.dropdown-button/types.ts +30 -0
- package/ui.dropdown-link/UDropdownLink.vue +69 -6
- package/ui.dropdown-link/config.ts +5 -1
- package/ui.dropdown-link/storybook/stories.ts +281 -4
- package/ui.dropdown-link/tests/UDropdownLink.test.ts +194 -0
- package/ui.dropdown-link/types.ts +30 -0
- package/ui.form-calendar/config.ts +4 -2
- package/ui.form-checkbox/config.ts +1 -1
- package/ui.form-checkbox/tests/UCheckbox.test.ts +2 -2
- package/ui.form-checkbox-group/tests/UCheckboxGroup.test.ts +2 -2
- package/ui.form-date-picker-range/config.ts +1 -1
- package/ui.form-input/UInput.vue +4 -2
- package/ui.form-input/config.ts +1 -1
- package/ui.form-input/tests/UInput.test.ts +2 -2
- package/ui.form-input-counter/UInputCounter.vue +25 -1
- package/ui.form-input-counter/config.ts +7 -2
- package/ui.form-input-counter/tests/UInputCounter.test.ts +85 -1
- package/ui.form-input-counter/types.ts +25 -0
- package/ui.form-input-file/tests/UInputFile.test.ts +2 -2
- package/ui.form-input-number/UInputNumber.vue +15 -3
- package/ui.form-input-number/utilFormat.ts +17 -7
- package/ui.form-input-password/UInputPassword.vue +23 -1
- package/ui.form-label/ULabel.vue +10 -4
- package/ui.form-label/tests/ULabel.test.ts +29 -12
- package/ui.form-listbox/UListbox.vue +21 -9
- package/ui.form-listbox/config.ts +1 -1
- package/ui.form-listbox/storybook/stories.ts +188 -1
- package/ui.form-listbox/tests/UListbox.test.ts +36 -0
- package/ui.form-listbox/types.ts +5 -0
- package/ui.form-radio/config.ts +1 -1
- package/ui.form-radio/tests/URadio.test.ts +2 -2
- package/ui.form-radio-group/tests/URadioGroup.test.ts +2 -2
- package/ui.form-select/USelect.vue +20 -2
- package/ui.form-select/config.ts +2 -1
- package/ui.form-select/storybook/stories.ts +31 -4
- package/ui.form-select/tests/USelect.test.ts +143 -0
- package/ui.form-select/types.ts +10 -0
- package/ui.form-textarea/config.ts +1 -1
- package/ui.form-textarea/tests/UTextarea.test.ts +2 -2
- package/ui.text-alert/config.ts +1 -1
- package/ui.text-badge/config.ts +1 -1
- package/utils/helper.ts +4 -0
- package/utils/node/dynamicProps.d.ts +5 -2
- package/utils/node/dynamicProps.js +126 -53
- package/utils/node/helper.d.ts +10 -7
- package/utils/node/helper.js +59 -2
- package/utils/node/tailwindSafelist.js +9 -2
- package/utils/theme.ts +75 -31
- package/utils/ui.ts +32 -3
package/constants.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export const OUTLINE: "outline";
|
|
|
9
9
|
export const ROUNDING: "rounding";
|
|
10
10
|
export const DISABLED_OPACITY: "disabled-opacity";
|
|
11
11
|
export const LETTER_SPACING: "letter-spacing";
|
|
12
|
+
export const LIGHT_THEME: "light-theme";
|
|
13
|
+
export const DARK_THEME: "dark-theme";
|
|
12
14
|
export const COLOR_MODE_KEY: "vl-color-mode";
|
|
13
15
|
export const AUTO_MODE_KEY: "vl-auto-mode";
|
|
14
16
|
export const DARK_MODE_CLASS: "vl-dark";
|
|
@@ -28,6 +30,7 @@ export const DEFAULT_DISABLED_OPACITY: 50;
|
|
|
28
30
|
export const DEFAULT_LETTER_SPACING: 0;
|
|
29
31
|
export const PRIMARY_COLORS: string[];
|
|
30
32
|
export const STATE_COLORS: string[];
|
|
33
|
+
export const LAYOUT_COLORS: string[];
|
|
31
34
|
export const NEUTRAL_COLORS: string[];
|
|
32
35
|
export const COLOR_SHADES: number[];
|
|
33
36
|
export const DEFAULT_LIGHT_THEME: {
|
|
@@ -128,6 +131,7 @@ export namespace SYSTEM_CONFIG_KEY {
|
|
|
128
131
|
let unstyled: string;
|
|
129
132
|
let transition: string;
|
|
130
133
|
let colors: string;
|
|
134
|
+
let props: string;
|
|
131
135
|
}
|
|
132
136
|
export const ICON_NON_PROPS_DEFAULTS: string[];
|
|
133
137
|
export namespace DIRECTIVES {
|
package/constants.js
CHANGED
|
@@ -17,6 +17,8 @@ export const OUTLINE = "outline";
|
|
|
17
17
|
export const ROUNDING = "rounding";
|
|
18
18
|
export const DISABLED_OPACITY = "disabled-opacity";
|
|
19
19
|
export const LETTER_SPACING = "letter-spacing";
|
|
20
|
+
export const LIGHT_THEME = "light-theme";
|
|
21
|
+
export const DARK_THEME = "dark-theme";
|
|
20
22
|
|
|
21
23
|
/* Vueless color mode keys */
|
|
22
24
|
export const COLOR_MODE_KEY = "vl-color-mode";
|
|
@@ -70,6 +72,7 @@ export const STATE_COLORS = [
|
|
|
70
72
|
NEUTRAL_COLOR,
|
|
71
73
|
GRAYSCALE_COLOR,
|
|
72
74
|
];
|
|
75
|
+
export const LAYOUT_COLORS = ["text", "border", "bg"];
|
|
73
76
|
export const NEUTRAL_COLORS = ["slate", "gray", "zinc", "neutral", "stone"];
|
|
74
77
|
export const COLOR_SHADES = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
75
78
|
|
|
@@ -222,6 +225,7 @@ export const SYSTEM_CONFIG_KEY = {
|
|
|
222
225
|
unstyled: "unstyled",
|
|
223
226
|
transition: "transition",
|
|
224
227
|
colors: "colors",
|
|
228
|
+
props: "props",
|
|
225
229
|
...CVA_CONFIG_KEY,
|
|
226
230
|
};
|
|
227
231
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 -960 960 960"><path d="m188.91-556.74 101.7 43.33q17.28-33.85 36.71-67.18 19.42-33.32 40.98-63.89l-76.37-15.28-103.02 103.02Zm156.39 84.11 131.33 131.56q58.44-27.43 107.48-59.59 49.04-32.17 79.8-62.93 80.05-80.28 117.81-163.84Q819.48-711 823.2-819.2q-108.2 3.72-191.77 41.48-83.56 37.76-163.6 117.81-30.76 30.76-62.93 79.8-32.16 49.04-59.6 107.48Zm231.16-99.59q-19.76-19.76-19.76-48.66 0-28.9 19.76-48.66 19.76-19.53 48.54-19.53 28.78 0 48.54 19.53 19.53 19.76 19.53 48.66 0 28.9-19.53 48.66Q653.78-552.7 625-552.7q-28.78 0-48.54-19.52ZM560.5-184.91l103.26-103.02-15.28-76.37q-30.57 21.56-63.89 41.1-33.33 19.55-67.18 36.59l43.09 101.7Zm331.26-702.61q8.52 143.17-35.08 257.09-43.59 113.91-142.11 212.67-1.48 1.24-2.96 2.84-1.48 1.59-2.72 2.83l22 110.48q3.48 17.15-1.74 33.19-5.22 16.03-17.65 28.46L537.87-65.33l-88.83-206.84-172.87-172.87-206.84-88.83L244.2-707.5q12.43-12.43 28.34-17.65 15.92-5.22 33.07-1.74l110.48 22q1.24-1.24 2.71-2.1 1.48-.86 2.96-2.1 98.76-99 212.79-142.98 114.04-43.97 257.21-35.45ZM141.59-324.04q36.91-36.92 89.56-37.3 52.65-.38 89.33 36.3 36.43 36.43 36.05 89.2-.38 52.77-37.05 89.45-27.68 27.67-85.64 45.63-57.97 17.96-168.64 32 14.04-110.67 31.5-169.26 17.45-58.59 44.89-86.02Zm47.98 48.74q-13.77 14.76-23.93 44.84-10.16 30.09-18.16 78.66 48.56-8 78.65-18.41 30.09-10.4 44.61-24.16 18.28-16.04 18.54-40.35.26-24.3-16.78-42.58-18.28-17.05-42.59-16.55-24.3.5-40.34 18.55Z"/></svg>
|
package/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export {
|
|
|
17
17
|
} from "./utils/helper";
|
|
18
18
|
export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
|
|
19
19
|
export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
|
|
20
|
-
export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar } from "./utils/theme";
|
|
20
|
+
export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar, setRootCSSVariables } from "./utils/theme";
|
|
21
21
|
export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
|
|
22
22
|
/* adapters */
|
|
23
23
|
export { default as defaultEnLocale } from "./adapter.locale/locales/en";
|
|
@@ -140,6 +140,8 @@ export type {
|
|
|
140
140
|
NestedComponent,
|
|
141
141
|
ComponentConfig,
|
|
142
142
|
ComponentDefaults,
|
|
143
|
+
ComponentCustomProp,
|
|
144
|
+
ComponentCustomProps,
|
|
143
145
|
CreateVuelessOptions,
|
|
144
146
|
/* Color and theme types */
|
|
145
147
|
StateColors,
|
package/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export {
|
|
|
23
23
|
} from "./utils/helper";
|
|
24
24
|
export { isMac, isPWA, isIOS, isAndroid, isMobileApp, isWindows } from "./utils/platform";
|
|
25
25
|
export { cx, cva, compose, getDefaults, setVuelessConfig, setColor, vuelessConfig } from "./utils/ui";
|
|
26
|
-
export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar } from "./utils/theme";
|
|
26
|
+
export { getTheme, setTheme, resetTheme, normalizeThemeConfig, cssVar, setRootCSSVariables } from "./utils/theme";
|
|
27
27
|
export { getArgs, getArgTypes, getSlotNames, getSlotsFragment, getSource, getDocsDescription } from "./utils/storybook";
|
|
28
28
|
/* adapters */
|
|
29
29
|
export { default as defaultEnLocale } from "./adapter.locale/locales/en";
|
|
@@ -146,6 +146,8 @@ export type {
|
|
|
146
146
|
NestedComponent,
|
|
147
147
|
ComponentConfig,
|
|
148
148
|
ComponentDefaults,
|
|
149
|
+
ComponentCustomProp,
|
|
150
|
+
ComponentCustomProps,
|
|
149
151
|
CreateVuelessOptions,
|
|
150
152
|
/* Color and theme types */
|
|
151
153
|
StateColors,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vueless",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.10-beta.0",
|
|
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",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"release:icons": "npx node .scripts/icons",
|
|
19
19
|
"release:types": "rm -f ./node_modules/.tmp/tsconfig.app.tsbuildinfo && npx tsc --project tsconfig.build.json",
|
|
20
20
|
"release:prepare": "npm run release:icons && rm -rf dist && mkdir -p dist && cp -r src/. package.json LICENSE README.md dist/ && npx node .scripts/writeLocales && npm run release:types",
|
|
21
|
-
"release:beta": "release-it --
|
|
22
|
-
"release:patch": "release-it patch --ci
|
|
23
|
-
"release:minor": "release-it minor --ci
|
|
24
|
-
"release:major": "release-it major --ci
|
|
21
|
+
"release:beta": "release-it --increment=prerelease --preRelease=beta --ci --no-git.tag --no-github.release",
|
|
22
|
+
"release:patch": "release-it patch --ci",
|
|
23
|
+
"release:minor": "release-it minor --ci",
|
|
24
|
+
"release:major": "release-it major --ci",
|
|
25
25
|
"lint": "eslint --no-fix src/ test/ .storybook/ .vueless/ .scripts/",
|
|
26
26
|
"lint:fix": "eslint --fix src/ test/ .storybook/ .vueless/ .scripts/",
|
|
27
27
|
"lint:ci": "eslint --no-fix --max-warnings=0",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@material-symbols/svg-500": "^0.35.0",
|
|
49
49
|
"@release-it/bumper": "^7.0.5",
|
|
50
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
50
51
|
"@tsconfig/node20": "^20.1.6",
|
|
51
52
|
"@types/jsdom": "^21.1.7",
|
|
52
53
|
"@types/node": "^24.1.0",
|
|
@@ -106,6 +107,9 @@
|
|
|
106
107
|
"types": "./modules.d.ts"
|
|
107
108
|
}
|
|
108
109
|
},
|
|
110
|
+
"overrides": {
|
|
111
|
+
"conventional-changelog-conventionalcommits": "8.0.0"
|
|
112
|
+
},
|
|
109
113
|
"resolutions": {
|
|
110
114
|
"jackspeak": "2.3.6"
|
|
111
115
|
},
|
package/plugin-vite.js
CHANGED
|
@@ -129,9 +129,14 @@ export const Vueless = function (options = {}) {
|
|
|
129
129
|
await cacheMergedConfigs({ vuelessSrcDir, basePath });
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
/* set custom prop types */
|
|
133
133
|
await setCustomPropTypes({ vuelessSrcDir, basePath });
|
|
134
134
|
|
|
135
|
+
/* build web-types.json with delay for right custom props behavior */
|
|
136
|
+
setTimeout(async () => {
|
|
137
|
+
await buildWebTypes({ vuelessSrcDir, basePath });
|
|
138
|
+
}, 2000);
|
|
139
|
+
|
|
135
140
|
/* collect used in project colors for tailwind safelist */
|
|
136
141
|
await createTailwindSafelist({ env, srcDir: vuelessSrcDir, targetFiles, basePath, debug });
|
|
137
142
|
|
package/types.ts
CHANGED
|
@@ -198,7 +198,7 @@ export type MergedThemeConfig = Omit<ThemeConfig, "text | outline | rounding"> &
|
|
|
198
198
|
|
|
199
199
|
export type UnknownObject = Record<string, unknown>;
|
|
200
200
|
export type UnknownArray = unknown[];
|
|
201
|
-
export type UnknownType = string | number | boolean | UnknownObject | undefined | null;
|
|
201
|
+
export type UnknownType = string | number | boolean | UnknownObject | undefined | null | unknown;
|
|
202
202
|
|
|
203
203
|
export type ComponentNames = keyof Components & string; // keys union
|
|
204
204
|
|
|
@@ -325,7 +325,19 @@ export interface NestedComponent {
|
|
|
325
325
|
|
|
326
326
|
export type ComponentDefaults = {
|
|
327
327
|
color?: string;
|
|
328
|
-
[key: string]:
|
|
328
|
+
[key: string]: UnknownType;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export type ComponentCustomProps = {
|
|
332
|
+
[key: string]: ComponentCustomProp;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export type ComponentCustomProp = {
|
|
336
|
+
required?: boolean;
|
|
337
|
+
ignore?: boolean;
|
|
338
|
+
values?: string[];
|
|
339
|
+
default?: UnknownType;
|
|
340
|
+
description?: string;
|
|
329
341
|
};
|
|
330
342
|
|
|
331
343
|
export interface CVA {
|
package/ui.button/config.ts
CHANGED
|
@@ -2,7 +2,7 @@ export default /*tw*/ {
|
|
|
2
2
|
button: {
|
|
3
3
|
base: `
|
|
4
4
|
flex items-center justify-center font-medium !leading-snug whitespace-nowrap
|
|
5
|
-
border border-
|
|
5
|
+
border border-solid transition cursor-pointer
|
|
6
6
|
focus-visible:outline-medium focus-visible:outline-offset-2 focus-visible:outline-{color}
|
|
7
7
|
disabled:cursor-not-allowed disabled:outline-0 disabled:outline-offset-0
|
|
8
8
|
`,
|
|
@@ -17,7 +17,7 @@ export default /*tw*/ {
|
|
|
17
17
|
},
|
|
18
18
|
variant: {
|
|
19
19
|
solid: `
|
|
20
|
-
|
|
20
|
+
bg-{color} border-transparent text-inverted
|
|
21
21
|
hover:bg-{color}-lifted
|
|
22
22
|
active:bg-{color}-accented
|
|
23
23
|
disabled:!bg-{color}/(--vl-disabled-opacity)
|
|
@@ -35,13 +35,13 @@ export default /*tw*/ {
|
|
|
35
35
|
disabled:!text-{color}/(--vl-disabled-opacity) disabled:!bg-{color}/5 disabled:!border-{color}/10
|
|
36
36
|
`,
|
|
37
37
|
soft: `
|
|
38
|
-
text-{color} bg-{color}/5
|
|
38
|
+
text-{color} bg-{color}/5 border-transparent
|
|
39
39
|
hover:text-{color}-lifted hover:bg-{color}-lifted/10
|
|
40
40
|
active:text-{color}-accented active:bg-{color}-accented/15
|
|
41
41
|
disabled:!text-{color}/(--vl-disabled-opacity) disabled:!bg-{color}/5
|
|
42
42
|
`,
|
|
43
43
|
ghost: `
|
|
44
|
-
text-{color} bg-transparent
|
|
44
|
+
text-{color} bg-transparent border-transparent
|
|
45
45
|
hover:text-{color}-lifted hover:bg-{color}-lifted/10
|
|
46
46
|
active:text-{color}-accented active:bg-{color}-accented/15
|
|
47
47
|
disabled:!text-{color}/(--vl-disabled-opacity) disabled:!bg-transparent
|
|
@@ -12,11 +12,11 @@ describe("UButton.vue", () => {
|
|
|
12
12
|
it("Variant – applies the correct variant class", async () => {
|
|
13
13
|
const color = "primary";
|
|
14
14
|
const variants = {
|
|
15
|
-
solid: "text-inverted
|
|
15
|
+
solid: "bg-primary border-transparent text-inverted",
|
|
16
16
|
outlined: "text-primary border-primary",
|
|
17
17
|
subtle: "text-primary bg-primary/5 border-primary/15",
|
|
18
|
-
soft: "text-primary bg-primary/5",
|
|
19
|
-
ghost: "text-primary bg-transparent",
|
|
18
|
+
soft: "text-primary bg-primary/5 border-transparent",
|
|
19
|
+
ghost: "text-primary bg-transparent border-transparent",
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
Object.entries(variants).forEach(([variant, classes]) => {
|
|
@@ -12,7 +12,7 @@ export default /*tw*/ {
|
|
|
12
12
|
},
|
|
13
13
|
split: {
|
|
14
14
|
true: "flex-wrap",
|
|
15
|
-
false: "flex-nowrap gap-px p-1 w-fit border rounded-medium border-default",
|
|
15
|
+
false: "flex-nowrap gap-px p-1 w-fit border border-solid rounded-medium border-default",
|
|
16
16
|
},
|
|
17
17
|
disabled: {
|
|
18
18
|
true: "bg-lifted cursor-not-allowed",
|
|
@@ -26,7 +26,7 @@ export default /*tw*/ {
|
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
28
|
divider: {
|
|
29
|
-
base: "my-1 border-r border-muted last:hidden",
|
|
29
|
+
base: "my-1 border-r border-solid border-muted last:hidden",
|
|
30
30
|
variants: {
|
|
31
31
|
split: {
|
|
32
32
|
true: "hidden",
|
|
@@ -109,7 +109,6 @@ const { getDataTest, accordionItemAttrs, accordionAttrs } = useUI<Config>(defaul
|
|
|
109
109
|
<UAccordionItem
|
|
110
110
|
v-for="(option, index) in options"
|
|
111
111
|
:key="index"
|
|
112
|
-
:model-value="selectedItem"
|
|
113
112
|
:value="option.value"
|
|
114
113
|
:title="option.title"
|
|
115
114
|
:description="option.description"
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "../../utils/storybook";
|
|
9
9
|
|
|
10
10
|
import UAccordion from "../../ui.container-accordion/UAccordion.vue";
|
|
11
|
+
import UAccordionItem from "../../ui.container-accordion-item/UAccordionItem.vue";
|
|
11
12
|
import UButton from "../../ui.button/UButton.vue";
|
|
12
13
|
import ULink from "../../ui.button-link/ULink.vue";
|
|
13
14
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
@@ -68,7 +69,7 @@ export default {
|
|
|
68
69
|
} as Meta;
|
|
69
70
|
|
|
70
71
|
const DefaultTemplate: StoryFn<UAccordionArgs> = (args: UAccordionArgs) => ({
|
|
71
|
-
components: { UAccordion, ULink, UButton, UCol, URow, UIcon },
|
|
72
|
+
components: { UAccordion, UAccordionItem, ULink, UButton, UCol, URow, UIcon },
|
|
72
73
|
setup: () => ({ args, slots: getSlotNames(UAccordion.__name) }),
|
|
73
74
|
template: `
|
|
74
75
|
<UAccordion v-bind="args" v-model="args.modelValue">
|
|
@@ -110,3 +111,14 @@ Sizes.args = {
|
|
|
110
111
|
},
|
|
111
112
|
],
|
|
112
113
|
};
|
|
114
|
+
|
|
115
|
+
export const DefaultSlot: StoryFn<UAccordionArgs> = (args: UAccordionArgs, { argTypes }) => ({
|
|
116
|
+
components: { UAccordion, UAccordionItem },
|
|
117
|
+
setup: () => ({ args, argTypes, getArgs }),
|
|
118
|
+
template: `
|
|
119
|
+
<UAccordion v-model="args.modelValue">
|
|
120
|
+
<UAccordionItem title="Custom Accordion Item 1" description="Custom Accordion Item 1" value="1" />
|
|
121
|
+
<UAccordionItem title="Custom Accordion Item 2" description="Custom Accordion Item 2" value="2" />
|
|
122
|
+
</UAccordion>
|
|
123
|
+
`,
|
|
124
|
+
});
|
|
@@ -122,7 +122,13 @@ const {
|
|
|
122
122
|
<div ref="wrapper" v-bind="wrapperAttrs" :data-test="getDataTest()" @click="onClickItem">
|
|
123
123
|
<div v-bind="bodyAttrs">
|
|
124
124
|
<div v-bind="titleAttrs" ref="title">
|
|
125
|
-
|
|
125
|
+
<!--
|
|
126
|
+
@slot Use it to add custom title content.
|
|
127
|
+
@binding {string} title
|
|
128
|
+
-->
|
|
129
|
+
<slot name="title" :title="title">
|
|
130
|
+
{{ title }}
|
|
131
|
+
</slot>
|
|
126
132
|
<!--
|
|
127
133
|
@slot Use it to add something instead of the toggle icon.
|
|
128
134
|
@binding {string} icon-name
|
|
@@ -140,11 +146,18 @@ const {
|
|
|
140
146
|
</div>
|
|
141
147
|
|
|
142
148
|
<div
|
|
143
|
-
v-if="description"
|
|
149
|
+
v-if="description || hasSlotContent(slots['description'])"
|
|
144
150
|
:id="`description-${elementId}`"
|
|
145
151
|
v-bind="descriptionAttrs"
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
>
|
|
153
|
+
<!--
|
|
154
|
+
@slot Use it to add custom description content.
|
|
155
|
+
@binding {string} description
|
|
156
|
+
-->
|
|
157
|
+
<slot name="description" :description="description">
|
|
158
|
+
{{ description }}
|
|
159
|
+
</slot>
|
|
160
|
+
</div>
|
|
148
161
|
|
|
149
162
|
<div v-if="isOpened && hasSlotContent(slots['default'])" v-bind="contentAttrs">
|
|
150
163
|
<!-- @slot Use it to add accordion content. -->
|
|
@@ -58,11 +58,12 @@ const EnumTemplate: StoryFn<UAccordionItemArgs> = (args: UAccordionItemArgs, { a
|
|
|
58
58
|
components: { UAccordionItem, UCol },
|
|
59
59
|
setup: () => ({ args, argTypes, getArgs }),
|
|
60
60
|
template: `
|
|
61
|
-
<UCol gap="
|
|
61
|
+
<UCol gap="lg">
|
|
62
62
|
<UAccordionItem
|
|
63
63
|
v-for="option in argTypes?.[args.enum]?.options"
|
|
64
64
|
v-bind="getArgs(args, option)"
|
|
65
65
|
:key="option"
|
|
66
|
+
class="py-0"
|
|
66
67
|
/>
|
|
67
68
|
</UCol>
|
|
68
69
|
`,
|
|
@@ -112,3 +113,27 @@ ToggleSlot.args = {
|
|
|
112
113
|
</template>
|
|
113
114
|
`,
|
|
114
115
|
};
|
|
116
|
+
|
|
117
|
+
export const TitleSlot = DefaultTemplate.bind({});
|
|
118
|
+
TitleSlot.args = {
|
|
119
|
+
slotTemplate: `
|
|
120
|
+
<template #title="{ title }">
|
|
121
|
+
<URow gap="xs" align="center">
|
|
122
|
+
<UIcon name="rocket_launch" size="xs" color="primary" />
|
|
123
|
+
<span>{{ title }}</span>
|
|
124
|
+
</URow>
|
|
125
|
+
</template>
|
|
126
|
+
`,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const DescriptionSlot = DefaultTemplate.bind({});
|
|
130
|
+
DescriptionSlot.args = {
|
|
131
|
+
slotTemplate: `
|
|
132
|
+
<template #description="{ description }">
|
|
133
|
+
<URow gap="xs" align="start">
|
|
134
|
+
<UIcon name="info" size="xs" color="accented" />
|
|
135
|
+
<span>{{ description }}</span>
|
|
136
|
+
</URow>
|
|
137
|
+
</template>
|
|
138
|
+
`,
|
|
139
|
+
};
|
|
@@ -176,6 +176,104 @@ describe("UAccordionItem", () => {
|
|
|
176
176
|
expect(toggleElement.attributes("data-opened")).toBe("true");
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
+
// Title slot
|
|
180
|
+
it("renders custom content in title slot", () => {
|
|
181
|
+
const title = "Original Title";
|
|
182
|
+
const slotClass = "custom-title";
|
|
183
|
+
const slotContent = "Custom Title Content";
|
|
184
|
+
|
|
185
|
+
const component = mount(UAccordionItem, {
|
|
186
|
+
props: { title },
|
|
187
|
+
slots: {
|
|
188
|
+
title: `<div class="${slotClass}">${slotContent}</div>`,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(component.find(`.${slotClass}`).exists()).toBe(true);
|
|
193
|
+
expect(component.find(`.${slotClass}`).text()).toBe(slotContent);
|
|
194
|
+
|
|
195
|
+
expect(component.text()).not.toContain(title);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Title slot bindings
|
|
199
|
+
it("provides title binding to title slot", () => {
|
|
200
|
+
const title = "Test Title";
|
|
201
|
+
const slotClass = "custom-title";
|
|
202
|
+
|
|
203
|
+
const component = mount(UAccordionItem, {
|
|
204
|
+
props: { title },
|
|
205
|
+
slots: {
|
|
206
|
+
title: `
|
|
207
|
+
<template #default="{ title }">
|
|
208
|
+
<div class="${slotClass}" :data-title="title"></div>
|
|
209
|
+
</template>
|
|
210
|
+
`,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const titleElement = component.find(`.${slotClass}`);
|
|
215
|
+
|
|
216
|
+
expect(titleElement.exists()).toBe(true);
|
|
217
|
+
expect(titleElement.attributes("data-title")).toBe(title);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Description slot
|
|
221
|
+
it("renders custom content in description slot", () => {
|
|
222
|
+
const description = "Original Description";
|
|
223
|
+
const slotClass = "custom-description";
|
|
224
|
+
const slotContent = "Custom Description Content";
|
|
225
|
+
|
|
226
|
+
const component = mount(UAccordionItem, {
|
|
227
|
+
props: { description },
|
|
228
|
+
slots: {
|
|
229
|
+
description: `<div class="${slotClass}">${slotContent}</div>`,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(component.find(`.${slotClass}`).exists()).toBe(true);
|
|
234
|
+
expect(component.find(`.${slotClass}`).text()).toBe(slotContent);
|
|
235
|
+
|
|
236
|
+
expect(component.text()).not.toContain(description);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Description slot bindings
|
|
240
|
+
it("provides description binding to description slot", () => {
|
|
241
|
+
const description = "Test Description";
|
|
242
|
+
const slotClass = "custom-description";
|
|
243
|
+
|
|
244
|
+
const component = mount(UAccordionItem, {
|
|
245
|
+
props: { description },
|
|
246
|
+
slots: {
|
|
247
|
+
description: `
|
|
248
|
+
<template #default="{ description }">
|
|
249
|
+
<div class="${slotClass}" :data-description="description"></div>
|
|
250
|
+
</template>
|
|
251
|
+
`,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const descriptionElement = component.find(`.${slotClass}`);
|
|
256
|
+
|
|
257
|
+
expect(descriptionElement.exists()).toBe(true);
|
|
258
|
+
expect(descriptionElement.attributes("data-description")).toBe(description);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Description slot with empty prop
|
|
262
|
+
it("renders description slot even when description prop is empty", () => {
|
|
263
|
+
const slotClass = "custom-description";
|
|
264
|
+
const slotContent = "Custom Description Content";
|
|
265
|
+
|
|
266
|
+
const component = mount(UAccordionItem, {
|
|
267
|
+
props: {},
|
|
268
|
+
slots: {
|
|
269
|
+
description: `<div class="${slotClass}">${slotContent}</div>`,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(component.find(`.${slotClass}`).exists()).toBe(true);
|
|
274
|
+
expect(component.find(`.${slotClass}`).text()).toBe(slotContent);
|
|
275
|
+
});
|
|
276
|
+
|
|
179
277
|
// Default slot
|
|
180
278
|
it("renders default slot content when accordion is opened", async () => {
|
|
181
279
|
const slotContent = "Custom accordion content";
|
|
@@ -221,6 +319,94 @@ describe("UAccordionItem", () => {
|
|
|
221
319
|
|
|
222
320
|
expect(component.find("[vl-key='content']").exists()).toBe(false);
|
|
223
321
|
});
|
|
322
|
+
|
|
323
|
+
// Slot interactions with accordion behavior
|
|
324
|
+
it("title slot content is clickable and toggles accordion", async () => {
|
|
325
|
+
const title = "Test Title";
|
|
326
|
+
const slotClass = "custom-title";
|
|
327
|
+
const slotContent = "Custom Title Content";
|
|
328
|
+
|
|
329
|
+
const component = mount(UAccordionItem, {
|
|
330
|
+
props: { title },
|
|
331
|
+
slots: {
|
|
332
|
+
title: `<div class="${slotClass}">${slotContent}</div>`,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const titleElement = component.find(`.${slotClass}`);
|
|
337
|
+
|
|
338
|
+
expect(titleElement.exists()).toBe(true);
|
|
339
|
+
|
|
340
|
+
await titleElement.trigger("click");
|
|
341
|
+
|
|
342
|
+
const emitted = component.emitted("click");
|
|
343
|
+
|
|
344
|
+
expect(emitted).toBeTruthy();
|
|
345
|
+
expect(emitted?.[0]).toEqual([expect.any(String), true]);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("description slot content is not clickable and does not toggle accordion", async () => {
|
|
349
|
+
const description = "Test Description";
|
|
350
|
+
const slotClass = "custom-description";
|
|
351
|
+
const slotContent = "Custom Description Content";
|
|
352
|
+
|
|
353
|
+
const component = mount(UAccordionItem, {
|
|
354
|
+
props: { description },
|
|
355
|
+
slots: {
|
|
356
|
+
description: `<div class="${slotClass}">${slotContent}</div>`,
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const descriptionElement = component.find(`.${slotClass}`);
|
|
361
|
+
|
|
362
|
+
expect(descriptionElement.exists()).toBe(true);
|
|
363
|
+
|
|
364
|
+
await descriptionElement.trigger("click");
|
|
365
|
+
|
|
366
|
+
const emitted = component.emitted("click");
|
|
367
|
+
|
|
368
|
+
expect(emitted).toBeFalsy();
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("title slot preserves accordion toggle functionality", async () => {
|
|
372
|
+
const title = "Test Title";
|
|
373
|
+
const description = "Test Description";
|
|
374
|
+
const slotClass = "custom-title";
|
|
375
|
+
const slotContent = "Custom Title Content";
|
|
376
|
+
|
|
377
|
+
const component = mount(UAccordionItem, {
|
|
378
|
+
props: { title, description },
|
|
379
|
+
slots: {
|
|
380
|
+
title: `<div class="${slotClass}">${slotContent}</div>`,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(component.find("[id^='description-']").classes()).not.toContain("opacity-100");
|
|
385
|
+
|
|
386
|
+
await component.find(`.${slotClass}`).trigger("click");
|
|
387
|
+
expect(component.find("[id^='description-']").classes()).toContain("opacity-100");
|
|
388
|
+
|
|
389
|
+
await component.find(`.${slotClass}`).trigger("click");
|
|
390
|
+
expect(component.find("[id^='description-']").classes()).not.toContain("opacity-100");
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("description slot content is always visible when slot is provided", () => {
|
|
394
|
+
const slotClass = "custom-description";
|
|
395
|
+
const slotContent = "Custom Description Content";
|
|
396
|
+
|
|
397
|
+
const component = mount(UAccordionItem, {
|
|
398
|
+
props: {},
|
|
399
|
+
slots: {
|
|
400
|
+
description: `<div class="${slotClass}">${slotContent}</div>`,
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Description slot content should be visible even without description prop
|
|
405
|
+
const descriptionElement = component.find(`.${slotClass}`);
|
|
406
|
+
|
|
407
|
+
expect(descriptionElement.exists()).toBe(true);
|
|
408
|
+
expect(descriptionElement.text()).toBe(slotContent);
|
|
409
|
+
});
|
|
224
410
|
});
|
|
225
411
|
|
|
226
412
|
// Events
|
package/ui.data-table/config.ts
CHANGED
|
@@ -2,7 +2,7 @@ export default /*tw*/ {
|
|
|
2
2
|
wrapper: "relative w-full overflow-auto",
|
|
3
3
|
headerCounterBase: "mr-1.5 pr-1.5 font-medium text-medium",
|
|
4
4
|
stickyHeader: {
|
|
5
|
-
base: "fixed top-0 flex items-center z-30 overflow-hidden border rounded-none",
|
|
5
|
+
base: "fixed top-0 flex items-center z-30 overflow-hidden border border-solid rounded-none",
|
|
6
6
|
variants: {
|
|
7
7
|
stickedHeader: {
|
|
8
8
|
false: "absolute",
|
|
@@ -29,12 +29,12 @@ export default /*tw*/ {
|
|
|
29
29
|
stickyHeaderLoader: "{ULoaderProgress} absolute top-auto bottom-0",
|
|
30
30
|
headerActionsCheckbox: "{UCheckbox}",
|
|
31
31
|
headerActionsCounter: "{>headerCounterBase} -ml-1.5",
|
|
32
|
-
tableWrapper: "border border-muted rounded-medium bg-default overflow-x-auto",
|
|
32
|
+
tableWrapper: "border border-solid border-muted rounded-medium bg-default overflow-x-auto",
|
|
33
33
|
table: "min-w-full border-none text-medium w-full table-auto",
|
|
34
34
|
header:
|
|
35
35
|
"border-b border-muted [&>tr:first-child>*]:first:rounded-tl-medium [&>tr:last-child>*]:last:rounded-tr-medium relative",
|
|
36
36
|
headerRow: "",
|
|
37
|
-
beforeHeaderRow: "border border-muted",
|
|
37
|
+
beforeHeaderRow: "border border-solid border-muted",
|
|
38
38
|
beforeHeaderCell: "{>headerCellBase}",
|
|
39
39
|
headerCellBase: {
|
|
40
40
|
base: "p-4 text-medium font-normal text-lifted text-left text-nowrap",
|
|
@@ -100,7 +100,7 @@ export default /*tw*/ {
|
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
102
|
stickyFooterRow: `
|
|
103
|
-
fixed bottom-0 -ml-px border border-
|
|
103
|
+
fixed bottom-0 -ml-px border-b border-solid border-muted bg-default
|
|
104
104
|
collapse group-[*]/footer-fixed:[visibility:inherit]
|
|
105
105
|
`,
|
|
106
106
|
i18n: {
|