vueless 1.3.7-beta.1 → 1.3.7-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/components.d.ts +1 -0
- package/components.ts +1 -0
- package/constants.d.ts +2 -0
- package/constants.js +2 -0
- package/package.json +2 -2
- package/ui.container-col/UCol.vue +0 -1
- package/ui.container-collapsible/UCollapsible.vue +190 -0
- package/ui.container-collapsible/config.ts +45 -0
- package/ui.container-collapsible/constants.ts +1 -0
- package/ui.container-collapsible/storybook/docs.mdx +17 -0
- package/ui.container-collapsible/storybook/stories.ts +261 -0
- package/ui.container-collapsible/tests/UCollapsible.test.ts +571 -0
- package/ui.container-collapsible/types.ts +57 -0
- package/ui.dropdown/UDropdown.vue +324 -0
- package/ui.dropdown/config.ts +27 -0
- package/ui.dropdown/constants.ts +1 -0
- package/ui.dropdown/storybook/docs.mdx +17 -0
- package/ui.dropdown/storybook/stories.ts +286 -0
- package/ui.dropdown/tests/UDropdown.test.ts +631 -0
- package/ui.dropdown/types.ts +127 -0
- package/ui.dropdown-badge/UDropdownBadge.vue +119 -227
- package/ui.dropdown-badge/config.ts +18 -15
- package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +201 -67
- package/ui.dropdown-button/UDropdownButton.vue +121 -226
- package/ui.dropdown-button/config.ts +32 -28
- package/ui.dropdown-button/tests/UDropdownButton.test.ts +189 -73
- package/ui.dropdown-link/UDropdownLink.vue +123 -233
- package/ui.dropdown-link/config.ts +15 -18
- package/ui.dropdown-link/tests/UDropdownLink.test.ts +190 -71
- package/ui.form-listbox/UListbox.vue +2 -3
- package/ui.form-listbox/config.ts +2 -2
- package/ui.form-select/config.ts +1 -1
- package/utils/node/helper.js +6 -5
package/components.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { default as URow } from "./ui.container-row/URow.vue";
|
|
|
44
44
|
export { default as UGrid } from "./ui.container-grid/UGrid.vue";
|
|
45
45
|
export { default as UGroup } from "./ui.container-group/UGroup.vue";
|
|
46
46
|
export { default as UGroups } from "./ui.container-groups/UGroups.vue";
|
|
47
|
+
export { default as UCollapsible } from "./ui.container-collapsible/UCollapsible.vue";
|
|
47
48
|
export { default as UAccordion } from "./ui.container-accordion/UAccordion.vue";
|
|
48
49
|
export { default as UAccordionItem } from "./ui.container-accordion-item/UAccordionItem.vue";
|
|
49
50
|
export { default as UEmpty } from "./ui.container-empty/UEmpty.vue";
|
package/components.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { default as URow } from "./ui.container-row/URow.vue";
|
|
|
44
44
|
export { default as UGrid } from "./ui.container-grid/UGrid.vue";
|
|
45
45
|
export { default as UGroup } from "./ui.container-group/UGroup.vue";
|
|
46
46
|
export { default as UGroups } from "./ui.container-groups/UGroups.vue";
|
|
47
|
+
export { default as UCollapsible } from "./ui.container-collapsible/UCollapsible.vue";
|
|
47
48
|
export { default as UAccordion } from "./ui.container-accordion/UAccordion.vue";
|
|
48
49
|
export { default as UAccordionItem } from "./ui.container-accordion-item/UAccordionItem.vue";
|
|
49
50
|
export { default as UEmpty } from "./ui.container-empty/UEmpty.vue";
|
package/constants.d.ts
CHANGED
|
@@ -145,6 +145,7 @@ export namespace COMPONENTS {
|
|
|
145
145
|
let UButton: string;
|
|
146
146
|
let ULink: string;
|
|
147
147
|
let UToggle: string;
|
|
148
|
+
let UDropdown: string;
|
|
148
149
|
let UDropdownButton: string;
|
|
149
150
|
let UDropdownBadge: string;
|
|
150
151
|
let UDropdownLink: string;
|
|
@@ -183,6 +184,7 @@ export namespace COMPONENTS {
|
|
|
183
184
|
let UGrid: string;
|
|
184
185
|
let UGroup: string;
|
|
185
186
|
let UGroups: string;
|
|
187
|
+
let UCollapsible: string;
|
|
186
188
|
let UAccordion: string;
|
|
187
189
|
let UAccordionItem: string;
|
|
188
190
|
let UEmpty: string;
|
package/constants.js
CHANGED
|
@@ -249,6 +249,7 @@ export const COMPONENTS = {
|
|
|
249
249
|
UToggle: "ui.button-toggle",
|
|
250
250
|
|
|
251
251
|
/* Dropdowns */
|
|
252
|
+
UDropdown: "ui.dropdown",
|
|
252
253
|
UDropdownButton: "ui.dropdown-button",
|
|
253
254
|
UDropdownBadge: "ui.dropdown-badge",
|
|
254
255
|
UDropdownLink: "ui.dropdown-link",
|
|
@@ -293,6 +294,7 @@ export const COMPONENTS = {
|
|
|
293
294
|
UGrid: "ui.container-grid",
|
|
294
295
|
UGroup: "ui.container-group",
|
|
295
296
|
UGroups: "ui.container-groups",
|
|
297
|
+
UCollapsible: "ui.container-collapsible",
|
|
296
298
|
UAccordion: "ui.container-accordion",
|
|
297
299
|
UAccordionItem: "ui.container-accordion-item",
|
|
298
300
|
UEmpty: "ui.container-empty",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vueless",
|
|
3
|
-
"version": "1.3.7-beta.
|
|
3
|
+
"version": "1.3.7-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.3.
|
|
60
|
+
"@vueless/storybook": "^1.3.18",
|
|
61
61
|
"eslint": "^9.32.0",
|
|
62
62
|
"eslint-plugin-storybook": "^10.0.2",
|
|
63
63
|
"eslint-plugin-vue": "^10.3.0",
|
|
@@ -28,7 +28,6 @@ defineExpose({
|
|
|
28
28
|
* Get element / nested component attributes for each config token ✨
|
|
29
29
|
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
30
30
|
*/
|
|
31
|
-
|
|
32
31
|
const { getDataTest, wrapperAttrs } = useUI<Config>(defaultConfig);
|
|
33
32
|
</script>
|
|
34
33
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { nextTick, computed, ref, useId, useTemplateRef, watch } from "vue";
|
|
3
|
+
|
|
4
|
+
import { useUI } from "../composables/useUI";
|
|
5
|
+
import { getDefaults } from "../utils/ui";
|
|
6
|
+
|
|
7
|
+
import vClickOutside from "../v.click-outside/vClickOutside";
|
|
8
|
+
|
|
9
|
+
import defaultConfig from "./config";
|
|
10
|
+
import { COMPONENT_NAME } from "./constants";
|
|
11
|
+
|
|
12
|
+
import type { Props, Config } from "./types";
|
|
13
|
+
|
|
14
|
+
defineOptions({ inheritAttrs: false });
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
17
|
+
...getDefaults<Props, Config>(defaultConfig, COMPONENT_NAME),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits([
|
|
21
|
+
/**
|
|
22
|
+
* Triggers when the opened state changes.
|
|
23
|
+
* @property {boolean} value
|
|
24
|
+
*/
|
|
25
|
+
"update:open",
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Triggers when collapsible is opened.
|
|
29
|
+
*/
|
|
30
|
+
"open",
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Triggers when collapsible is closed.
|
|
34
|
+
*/
|
|
35
|
+
"close",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const internalIsOpened = ref(false);
|
|
39
|
+
const isClickingContent = ref(false);
|
|
40
|
+
|
|
41
|
+
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
|
|
42
|
+
const elementId = props.id || useId();
|
|
43
|
+
|
|
44
|
+
watch(
|
|
45
|
+
() => props.open,
|
|
46
|
+
(newValue) => {
|
|
47
|
+
if (newValue !== undefined) {
|
|
48
|
+
internalIsOpened.value = newValue;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{ immediate: true },
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const isOpened = computed({
|
|
55
|
+
get: () => internalIsOpened.value,
|
|
56
|
+
set: (value) => {
|
|
57
|
+
internalIsOpened.value = value;
|
|
58
|
+
emit("update:open", value);
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function handleClickOutside() {
|
|
63
|
+
if (isClickingContent.value || !props.closeOnOutside) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
hide();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toggle() {
|
|
71
|
+
if (props.disabled) return;
|
|
72
|
+
|
|
73
|
+
isOpened.value = !isOpened.value;
|
|
74
|
+
|
|
75
|
+
isOpened.value ? emit("open") : emit("close");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function hide() {
|
|
79
|
+
isOpened.value = false;
|
|
80
|
+
|
|
81
|
+
emit("close");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function show() {
|
|
85
|
+
if (props.disabled) return;
|
|
86
|
+
|
|
87
|
+
isOpened.value = true;
|
|
88
|
+
|
|
89
|
+
emit("open");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function onContentClick() {
|
|
93
|
+
if (!props.closeOnContent) {
|
|
94
|
+
isClickingContent.value = true;
|
|
95
|
+
|
|
96
|
+
nextTick(() => {
|
|
97
|
+
isClickingContent.value = false;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
hide();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
defineExpose({
|
|
107
|
+
/**
|
|
108
|
+
* A reference to the component's wrapper element for direct DOM manipulation.
|
|
109
|
+
* @property {HTMLDivElement}
|
|
110
|
+
*/
|
|
111
|
+
wrapperRef,
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Hides the collapsible content.
|
|
115
|
+
* @property {function}
|
|
116
|
+
*/
|
|
117
|
+
hide,
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Shows the collapsible content.
|
|
121
|
+
* @property {function}
|
|
122
|
+
*/
|
|
123
|
+
show,
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Toggles the collapsible visibility.
|
|
127
|
+
* @property {function}
|
|
128
|
+
*/
|
|
129
|
+
toggle,
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Indicates whether the collapsible is opened.
|
|
133
|
+
* @property {boolean}
|
|
134
|
+
*/
|
|
135
|
+
isOpened,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get element / nested component attributes for each config token ✨
|
|
140
|
+
* Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
|
|
141
|
+
*/
|
|
142
|
+
const mutatedProps = computed(() => ({
|
|
143
|
+
/* component state, not a props */
|
|
144
|
+
opened: isOpened.value,
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
const { getDataTest, config, wrapperAttrs, contentAttrs } = useUI<Config>(
|
|
148
|
+
defaultConfig,
|
|
149
|
+
mutatedProps,
|
|
150
|
+
);
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<template>
|
|
154
|
+
<div
|
|
155
|
+
:id="elementId"
|
|
156
|
+
ref="wrapper"
|
|
157
|
+
v-click-outside="handleClickOutside"
|
|
158
|
+
v-bind="wrapperAttrs"
|
|
159
|
+
tabindex="0"
|
|
160
|
+
:data-test="getDataTest()"
|
|
161
|
+
@click="toggle"
|
|
162
|
+
@keydown.enter="toggle"
|
|
163
|
+
>
|
|
164
|
+
<!--
|
|
165
|
+
@slot Use it to add custom trigger element for the collapsible.
|
|
166
|
+
@binding {boolean} opened
|
|
167
|
+
-->
|
|
168
|
+
<slot :opened="isOpened" />
|
|
169
|
+
|
|
170
|
+
<Transition v-bind="config.contentTransition">
|
|
171
|
+
<!--
|
|
172
|
+
@slot Use it to add collapsible content.
|
|
173
|
+
@binding {boolean} opened
|
|
174
|
+
-->
|
|
175
|
+
<div
|
|
176
|
+
v-if="isOpened"
|
|
177
|
+
v-bind="contentAttrs"
|
|
178
|
+
:data-test="getDataTest('content')"
|
|
179
|
+
@click.stop="onContentClick"
|
|
180
|
+
>
|
|
181
|
+
<!--
|
|
182
|
+
@slot Use it to add some content need to be shown.
|
|
183
|
+
@binding {boolean} opened
|
|
184
|
+
@binding {string} contentClasses
|
|
185
|
+
-->
|
|
186
|
+
<slot name="content" :opened="isOpened" :content-classes="contentAttrs.class" />
|
|
187
|
+
</div>
|
|
188
|
+
</Transition>
|
|
189
|
+
</div>
|
|
190
|
+
</template>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export default /*tw*/ {
|
|
2
|
+
wrapper: {
|
|
3
|
+
base: `
|
|
4
|
+
relative inline-block h-max rounded-medium transition cursor-pointer outline-transparent
|
|
5
|
+
focus-visible:outline-medium focus-visible:outline-offset-2 focus-visible:outline-primary`,
|
|
6
|
+
variants: {
|
|
7
|
+
disabled: {
|
|
8
|
+
true: "cursor-not-allowed",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
content: {
|
|
13
|
+
base: "z-10 w-fit",
|
|
14
|
+
variants: {
|
|
15
|
+
absolute: {
|
|
16
|
+
true: "absolute",
|
|
17
|
+
false: "",
|
|
18
|
+
},
|
|
19
|
+
yPosition: {
|
|
20
|
+
top: "bottom-full mb-1",
|
|
21
|
+
bottom: "top-full mt-1",
|
|
22
|
+
},
|
|
23
|
+
xPosition: {
|
|
24
|
+
left: "left-0",
|
|
25
|
+
right: "right-0",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
contentTransition: {
|
|
30
|
+
enterActiveClass: "ease-out duration-200",
|
|
31
|
+
enterFromClass: "opacity-0 scale-95",
|
|
32
|
+
enterToClass: "opacity-100 scale-100",
|
|
33
|
+
leaveActiveClass: "ease-in duration-150",
|
|
34
|
+
leaveFromClass: "opacity-100 scale-100",
|
|
35
|
+
leaveToClass: "opacity-0 scale-95",
|
|
36
|
+
},
|
|
37
|
+
defaults: {
|
|
38
|
+
absolute: true,
|
|
39
|
+
yPosition: "bottom",
|
|
40
|
+
xPosition: "left",
|
|
41
|
+
closeOnOutside: true,
|
|
42
|
+
closeOnContent: false,
|
|
43
|
+
disabled: false,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const COMPONENT_NAME = "UCollapsible";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/addon-docs/blocks";
|
|
2
|
+
import { getSource } from "../../utils/storybook.ts";
|
|
3
|
+
|
|
4
|
+
import * as stories from "./stories.ts";
|
|
5
|
+
import defaultConfig from "../config.ts?raw"
|
|
6
|
+
|
|
7
|
+
<Meta of={stories} />
|
|
8
|
+
<Title of={stories} />
|
|
9
|
+
<Subtitle of={stories} />
|
|
10
|
+
<Description of={stories} />
|
|
11
|
+
<Primary of={stories} />
|
|
12
|
+
<Controls of={stories.Default} />
|
|
13
|
+
<Stories of={stories} />
|
|
14
|
+
|
|
15
|
+
## Default config
|
|
16
|
+
<Source code={getSource(defaultConfig)} language="jsx" dark />
|
|
17
|
+
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
getArgs,
|
|
4
|
+
getArgTypes,
|
|
5
|
+
getSlotNames,
|
|
6
|
+
getSlotsFragment,
|
|
7
|
+
getDocsDescription,
|
|
8
|
+
} from "../../utils/storybook";
|
|
9
|
+
|
|
10
|
+
import UCollapsible from "../UCollapsible.vue";
|
|
11
|
+
import UButton from "../../ui.button/UButton.vue";
|
|
12
|
+
import UCard from "../../ui.container-card/UCard.vue";
|
|
13
|
+
import URow from "../../ui.container-row/URow.vue";
|
|
14
|
+
import UCol from "../../ui.container-col/UCol.vue";
|
|
15
|
+
|
|
16
|
+
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
17
|
+
import type { Props } from "../types";
|
|
18
|
+
|
|
19
|
+
interface UCollapsibleArgs extends Props {
|
|
20
|
+
slotTemplate?: string;
|
|
21
|
+
enum: "yPosition" | "xPosition";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultTemplate = `
|
|
25
|
+
<template #default="{ opened }">
|
|
26
|
+
<UButton :label="opened ? 'Close' : 'Open'" />
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template #content>
|
|
30
|
+
<UCard title="Collapsible Content" description="This is the collapsible content area." />
|
|
31
|
+
</template>
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
export default {
|
|
35
|
+
id: "5270",
|
|
36
|
+
title: "Containers / Collapsible",
|
|
37
|
+
component: UCollapsible,
|
|
38
|
+
args: {
|
|
39
|
+
slotTemplate: defaultTemplate,
|
|
40
|
+
},
|
|
41
|
+
argTypes: {
|
|
42
|
+
...getArgTypes(UCollapsible.__name),
|
|
43
|
+
},
|
|
44
|
+
parameters: {
|
|
45
|
+
docs: {
|
|
46
|
+
...getDocsDescription(UCollapsible.__name),
|
|
47
|
+
story: {
|
|
48
|
+
height: "300px",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
} as Meta;
|
|
53
|
+
|
|
54
|
+
const DefaultTemplate: StoryFn<UCollapsibleArgs> = (args: UCollapsibleArgs) => ({
|
|
55
|
+
components: { UCollapsible, UButton, UCard },
|
|
56
|
+
setup: () => ({ args, slots: getSlotNames(UCollapsible.__name) }),
|
|
57
|
+
template: `
|
|
58
|
+
<UCollapsible v-bind="args">
|
|
59
|
+
${args.slotTemplate || getSlotsFragment("")}
|
|
60
|
+
</UCollapsible>
|
|
61
|
+
`,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const EnumTemplate: StoryFn<UCollapsibleArgs> = (args: UCollapsibleArgs, { argTypes }) => ({
|
|
65
|
+
components: { UCollapsible, UButton, UCard, URow },
|
|
66
|
+
setup: () => ({ args, argTypes, getArgs }),
|
|
67
|
+
template: `
|
|
68
|
+
<URow>
|
|
69
|
+
<UCollapsible
|
|
70
|
+
v-for="option in argTypes?.[args.enum]?.options"
|
|
71
|
+
v-bind="getArgs(args, option)"
|
|
72
|
+
v-model:open="args.open"
|
|
73
|
+
:key="option"
|
|
74
|
+
>
|
|
75
|
+
${args.slotTemplate}
|
|
76
|
+
</UCollapsible>
|
|
77
|
+
</URow>
|
|
78
|
+
`,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const Default = DefaultTemplate.bind({});
|
|
82
|
+
Default.args = {};
|
|
83
|
+
|
|
84
|
+
export const YPositions = EnumTemplate.bind({});
|
|
85
|
+
YPositions.args = { enum: "yPosition", slotTemplate: defaultTemplate };
|
|
86
|
+
|
|
87
|
+
export const XPositions = EnumTemplate.bind({});
|
|
88
|
+
XPositions.args = { enum: "xPosition" };
|
|
89
|
+
|
|
90
|
+
export const NonAbsolute: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
91
|
+
components: { UCollapsible, UButton, UCard, UCol },
|
|
92
|
+
setup() {
|
|
93
|
+
return { args };
|
|
94
|
+
},
|
|
95
|
+
template: `
|
|
96
|
+
<UCol gap="md">
|
|
97
|
+
<UCollapsible :absolute="false">
|
|
98
|
+
<template #default="{ opened }">
|
|
99
|
+
<UButton :label="opened ? 'Close' : 'Open'" />
|
|
100
|
+
</template>
|
|
101
|
+
<template #content>
|
|
102
|
+
<UCard
|
|
103
|
+
title="Non-Absolute Content"
|
|
104
|
+
description="This content is not positioned absolutely, so it flows with the document."
|
|
105
|
+
/>
|
|
106
|
+
</template>
|
|
107
|
+
</UCollapsible>
|
|
108
|
+
<div class="p-4 border border-gray-300 rounded">
|
|
109
|
+
<p>This content appears below the collapsible and will be pushed down when it opens.</p>
|
|
110
|
+
</div>
|
|
111
|
+
</UCol>
|
|
112
|
+
`,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
export const VModel: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
116
|
+
components: { UCollapsible, UButton, UCard, URow },
|
|
117
|
+
setup() {
|
|
118
|
+
const isOpen = ref(false);
|
|
119
|
+
|
|
120
|
+
function toggle() {
|
|
121
|
+
isOpen.value = !isOpen.value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { args, isOpen, toggle };
|
|
125
|
+
},
|
|
126
|
+
template: `
|
|
127
|
+
<UCol gap="md">
|
|
128
|
+
<URow gap="md">
|
|
129
|
+
<UButton label="Toggle Collapsible" @click="toggle" />
|
|
130
|
+
<UButton :label="isOpen ? 'Close' : 'Open'" variant="outlined" @click="isOpen = !isOpen" />
|
|
131
|
+
</URow>
|
|
132
|
+
|
|
133
|
+
<UCollapsible v-model:open="isOpen">
|
|
134
|
+
<template #default="{ opened }">
|
|
135
|
+
<UButton :label="opened ? 'Opened' : 'Closed'" color="secondary" />
|
|
136
|
+
</template>
|
|
137
|
+
<template #content>
|
|
138
|
+
<UCard
|
|
139
|
+
title="Controlled Content"
|
|
140
|
+
description="This collapsible is controlled by v-model. Use the buttons above to toggle it."
|
|
141
|
+
/>
|
|
142
|
+
</template>
|
|
143
|
+
</UCollapsible>
|
|
144
|
+
</UCol>
|
|
145
|
+
`,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
export const CloseOnOutside: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
149
|
+
components: { UCollapsible, UButton, UCard },
|
|
150
|
+
setup() {
|
|
151
|
+
return { args };
|
|
152
|
+
},
|
|
153
|
+
template: `
|
|
154
|
+
<div class="p-8">
|
|
155
|
+
<p class="mb-4 text-sm text-gray-600">Click outside the collapsible content to close it.</p>
|
|
156
|
+
<UCollapsible close-on-outside>
|
|
157
|
+
<template #default="{ opened }">
|
|
158
|
+
<UButton :label="opened ? 'Close' : 'Open'" />
|
|
159
|
+
</template>
|
|
160
|
+
<template #content>
|
|
161
|
+
<UCard
|
|
162
|
+
title="Click Outside to Close"
|
|
163
|
+
description="This collapsible will close when you click outside of it."
|
|
164
|
+
/>
|
|
165
|
+
</template>
|
|
166
|
+
</UCollapsible>
|
|
167
|
+
</div>
|
|
168
|
+
`,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const CloseOnContent: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
172
|
+
components: { UCollapsible, UButton, UCard },
|
|
173
|
+
setup() {
|
|
174
|
+
return { args };
|
|
175
|
+
},
|
|
176
|
+
template: `
|
|
177
|
+
<div class="p-8">
|
|
178
|
+
<p class="mb-4 text-sm text-gray-600">Click on the content to close the collapsible.</p>
|
|
179
|
+
<UCollapsible close-on-content>
|
|
180
|
+
<template #default="{ opened }">
|
|
181
|
+
<UButton :label="opened ? 'Close' : 'Open'" />
|
|
182
|
+
</template>
|
|
183
|
+
<template #content>
|
|
184
|
+
<UCard
|
|
185
|
+
title="Click Content to Close"
|
|
186
|
+
description="Click anywhere on this card to close the collapsible."
|
|
187
|
+
/>
|
|
188
|
+
</template>
|
|
189
|
+
</UCollapsible>
|
|
190
|
+
</div>
|
|
191
|
+
`,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export const Disabled: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
195
|
+
components: { UCollapsible, UButton, UCard },
|
|
196
|
+
setup() {
|
|
197
|
+
return { args };
|
|
198
|
+
},
|
|
199
|
+
template: `
|
|
200
|
+
<UCollapsible disabled>
|
|
201
|
+
<template #default="{ opened }">
|
|
202
|
+
<UButton :label="opened ? 'Close' : 'Open'" disabled />
|
|
203
|
+
</template>
|
|
204
|
+
<template #content>
|
|
205
|
+
<UCard title="Disabled Content" description="This content cannot be shown." />
|
|
206
|
+
</template>
|
|
207
|
+
</UCollapsible>
|
|
208
|
+
`,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export const CustomContent: StoryFn<UCollapsibleArgs> = (args) => ({
|
|
212
|
+
components: { UCollapsible, UButton, UCard, URow },
|
|
213
|
+
setup() {
|
|
214
|
+
return { args };
|
|
215
|
+
},
|
|
216
|
+
template: `
|
|
217
|
+
<URow gap="xl">
|
|
218
|
+
<UCollapsible>
|
|
219
|
+
<template #default="{ opened }">
|
|
220
|
+
<UButton
|
|
221
|
+
:label="opened ? 'Hide Menu' : 'Show Menu'"
|
|
222
|
+
:left-icon="opened ? 'expand_less' : 'expand_more'"
|
|
223
|
+
/>
|
|
224
|
+
</template>
|
|
225
|
+
<template #content>
|
|
226
|
+
<UCard class="w-64">
|
|
227
|
+
<div class="p-4 space-y-2">
|
|
228
|
+
<div class="hover:bg-gray-100 p-2 rounded cursor-pointer">Menu Item 1</div>
|
|
229
|
+
<div class="hover:bg-gray-100 p-2 rounded cursor-pointer">Menu Item 2</div>
|
|
230
|
+
<div class="hover:bg-gray-100 p-2 rounded cursor-pointer">Menu Item 3</div>
|
|
231
|
+
<div class="hover:bg-gray-100 p-2 rounded cursor-pointer">Menu Item 4</div>
|
|
232
|
+
</div>
|
|
233
|
+
</UCard>
|
|
234
|
+
</template>
|
|
235
|
+
</UCollapsible>
|
|
236
|
+
|
|
237
|
+
<UCollapsible y-position="top">
|
|
238
|
+
<template #default="{ opened }">
|
|
239
|
+
<UButton
|
|
240
|
+
:label="opened ? 'Hide Tooltip' : 'Show Tooltip'"
|
|
241
|
+
variant="outlined"
|
|
242
|
+
/>
|
|
243
|
+
</template>
|
|
244
|
+
<template #content>
|
|
245
|
+
<UCard class="w-48">
|
|
246
|
+
<div class="p-3 text-sm">
|
|
247
|
+
This is a tooltip-like collapsible that appears above the trigger.
|
|
248
|
+
</div>
|
|
249
|
+
</UCard>
|
|
250
|
+
</template>
|
|
251
|
+
</UCollapsible>
|
|
252
|
+
</URow>
|
|
253
|
+
`,
|
|
254
|
+
});
|
|
255
|
+
CustomContent.parameters = {
|
|
256
|
+
docs: {
|
|
257
|
+
story: {
|
|
258
|
+
height: "300px",
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
};
|