33 | As an interesting side note, as a head without a body, I envy the dead. Kids don't
34 | turn rotten just from watching TV. Bender, I didn't know you liked cooking. That's so
35 | cute. That's right, baby. I ain't your loverboy Flexo, the guy you love so much. You
36 | even love anyone pretending to be him! I'll tell them you went down prying the
37 | wedding ring off his cold, dead finger.
38 |
33 | As an interesting side note, as a head without a body, I envy the dead. Kids don't
34 | turn rotten just from watching TV. Bender, I didn't know you liked cooking. That's so
35 | cute. That's right, baby. I ain't your loverboy Flexo, the guy you love so much. You
36 | even love anyone pretending to be him! I'll tell them you went down prying the
37 | wedding ring off his cold, dead finger.
38 |
49 | As an interesting side note, as a head without a body, I envy the dead. Kids don't
50 | turn rotten just from watching TV. Bender, I didn't know you liked cooking. That's so
51 | cute. That's right, baby. I ain't your loverboy Flexo, the guy you love so much. You
52 | even love anyone pretending to be him! I'll tell them you went down prying the
53 | wedding ring off his cold, dead finger.
54 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/playground/utils/fakeData.ts:
--------------------------------------------------------------------------------
1 | export const fakeData = [
2 | {
3 | title: 'Do you name inanimate objects?',
4 | answer:
5 | "She sat down with her notebook in her hand, her mind wandering to faraway places. She paused and considered all that had happened. It hadn't gone as expected. When the day began she thought it was going to be a bad one, but as she sat recalling the day's events to write them down, she had to admit, it had been a rather marvelous day.",
6 | },
7 | {
8 | title: 'Is there any place that you have no desire to visit? Why?',
9 | answer:
10 | 'He swung back the fishing pole and cast the line which ell 25 feet away into the river. The lure landed in the perfect spot and he was sure he would soon get a bite. He never expected that the bite would come from behind in the form of a bear.',
11 | },
12 | {
13 | title: "What's your story about breaking the law?",
14 | answer:
15 | "He walked down the steps from the train station in a bit of a hurry knowing the secrets in the briefcase must be secured as quickly as possible. Bounding down the steps, he heard something behind him and quickly turned in a panic. There was nobody there but a pair of old worn-out shoes were placed neatly on the steps he had just come down. Had he past them without seeing them? It didn't seem possible. He was about to turn and be on his way when a deep chill filled his body.",
16 | },
17 | {
18 | title: "What's the last thing that you broke and how did it happen?",
19 | answer:
20 | "What were they eating? It didn't taste like anything she had ever eaten before and although she was famished, she didn't dare ask. She knew the answer would be one she didn't want to hear. Things aren't going well at all with mom today. She is just a limp noodle and wants to sleep all the time. I sure hope that things get better soon. There simply wasn't a whole lot he could have done in that particular moment. There simply wasn't a whole lot he could have done in that particular moment. There simply wasn't a whole lot he could have done in that particular moment. There simply wasn't a whole lot he could have done in that particular moment.",
21 | },
22 | {
23 | title: 'How do you approach life?',
24 | answer:
25 | "Things aren't going well at all with mom today. She is just a limp noodle and wants to sleep all the time. I sure hope that things get better soon.",
26 | },
27 | {
28 | title: 'Have you ever had or witnessed a drop the mic moment?',
29 | answer:
30 | "There wasn't a whole lot he could do at that moment. He played the situation again and again in his head looking at what he might have done differently to make the situation better. No matter how many times he relived the situation in his head, there was never really a good alternative course of action. There simply wasn't a whole lot he could have done in that particular moment. There wasn't a whole lot he could do at that moment. He played the situation again and again in his head looking at what he might have done differently to make the situation better. No matter how many times he relived the situation in his head, there was never really a good alternative course of action. There simply wasn't a whole lot he could have done in that particular moment.",
31 | },
32 | ]
33 |
--------------------------------------------------------------------------------
/playground/assets/style.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 639px) {
2 | #app {
3 | width: 100%;
4 | }
5 | }
6 |
7 | :root {
8 | --AccentColor: #41b883;
9 | --AccentLightColor: #97e8c4;
10 | --ForegroundColor: #c5c5c5;
11 | --ForegroundColorLight: #929292;
12 | --BackgroundColor: #1a1a1a;
13 | --BackgroundAlternateColor: #242424;
14 | --BackgroundAlphaColor: #41b8833d;
15 | --ArticleBorder: 1px solid var(--ForegroundColorLight);
16 | }
17 |
18 | * {
19 | box-sizing: border-box;
20 | }
21 |
22 | h1,
23 | h2,
24 | p {
25 | margin: 0;
26 | padding: 0;
27 | }
28 |
29 | body {
30 | --Padding: 20px;
31 | margin: 0;
32 | padding: var(--Padding);
33 | font-synthesis: none;
34 | text-rendering: optimizeLegibility;
35 | line-height: 1.75;
36 | -moz-osx-font-smoothing: grayscale;
37 | background: var(--BackgroundColor);
38 | -webkit-tap-highlight-color: transparent;
39 | color: var(--ForegroundColor);
40 | display: flex;
41 | flex-direction: column;
42 | align-items: center;
43 | justify-content: center;
44 | font-family:
45 | Inter,
46 | -apple-system,
47 | BlinkMacSystemFont,
48 | 'Segoe UI',
49 | Roboto,
50 | Oxygen,
51 | Ubuntu,
52 | Cantarell,
53 | 'Fira Sans',
54 | 'Droid Sans',
55 | 'Helvetica Neue',
56 | sans-serif;
57 | }
58 |
59 | main {
60 | width: 100%;
61 | }
62 |
63 | h2 {
64 | font-size: 1.25rem;
65 | margin: 0;
66 | }
67 |
68 | footer {
69 | font-size: 0.865rem;
70 | }
71 |
72 | .AppHeader {
73 | margin-bottom: 70px;
74 | display: flex;
75 | align-items: center;
76 | justify-content: space-between;
77 | }
78 |
79 | .AppHeader h1 {
80 | font-size: 2rem;
81 | }
82 |
83 | .AppHeader svg {
84 | fill: var(--ForegroundColor);
85 | width: 40px;
86 | height: 40px;
87 | }
88 |
89 | .AppHeader a:hover svg {
90 | fill: var(--AccentColor);
91 | }
92 |
93 | .Header {
94 | display: flex;
95 | align-items: center;
96 | justify-content: space-between;
97 | margin-bottom: 30px;
98 | }
99 |
100 | .HeaderTitle {
101 | display: flex;
102 | flex-direction: column;
103 | gap: 0;
104 | margin: 0;
105 | padding: 0;
106 | }
107 |
108 | .HeaderTitle > p {
109 | text-transform: uppercase;
110 | font-size: 0.725rem;
111 | color: var(--AccentColor);
112 | margin: 0;
113 | display: flex;
114 | gap: 0.5rem;
115 | align-items: center;
116 | }
117 |
118 | .ActionButton {
119 | background: none;
120 | cursor: pointer;
121 | margin: 0;
122 | display: flex;
123 | align-items: center;
124 | color: var(--AccentColor);
125 | font-weight: 700;
126 | border: 1px solid var(--AccentColor);
127 | padding: 0.3rem 0.5rem;
128 | border-radius: 7px;
129 | transition: all 150ms ease-out;
130 | }
131 |
132 | .ActionButton:hover {
133 | background: var(--BackgroundAlphaColor);
134 | color: var(--AccentLightColor);
135 | }
136 |
137 | .Panel {
138 | width: 100%;
139 | font-size: 1rem;
140 | color: var(--ForegroundColor);
141 | text-align: left;
142 | font-weight: 600;
143 | }
144 |
145 | .Panel:hover {
146 | color: var(--AccentColor);
147 | }
148 |
149 | .Active {
150 | color: var(--AccentColor);
151 | }
152 |
153 | .CodeLink {
154 | white-space: nowrap;
155 | font-size: 0.925rem;
156 | color: var(--AccentColor);
157 | }
158 |
159 | .CodeLink:hover {
160 | color: var(--ForegroundColor);
161 | }
162 |
163 | .Wrapper {
164 | margin-bottom: 80px;
165 | }
166 |
167 | .Section {
168 | background: var(--BackgroundAlternateColor);
169 | width: 100%;
170 | max-width: 600px;
171 | border-top: var(--ArticleBorder);
172 | margin: 0;
173 | }
174 |
175 | .Section:last-of-type {
176 | border-bottom: var(--ArticleBorder);
177 | }
178 |
179 | .Section button {
180 | width: 100%;
181 | padding: 20px 10px;
182 | border: none;
183 | background: none;
184 | cursor: pointer;
185 | }
186 |
187 | .NestedCollapse {
188 | margin: 0 20px 20px;
189 | border-top: var(--ArticleBorder);
190 | border-bottom: var(--ArticleBorder);
191 | }
192 |
193 | .CollapseContent {
194 | padding: 0 10px 10px;
195 | margin: 0;
196 | color: var(--ForegroundColorLight);
197 | font-size: 1rem;
198 | }
199 |
200 | @media (min-width: 640px) {
201 | main {
202 | width: 600px;
203 | }
204 |
205 | h2 {
206 | font-size: 1.5rem;
207 | }
208 |
209 | footer {
210 | font-size: 0.925rem;
211 | }
212 |
213 | .Panel {
214 | font-size: 1.125rem;
215 | }
216 |
217 | .Section button {
218 | padding: 20px 10px;
219 | }
220 |
221 | .CollapseContent {
222 | padding: 0 20px 20px;
223 | margin: 0;
224 | color: var(--ForegroundColorLight);
225 | }
226 |
227 | .AppHeader h1 {
228 | font-size: 2.5rem;
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/packages/vue-collapsed/tests/App.vue:
--------------------------------------------------------------------------------
1 |
67 |
68 |
69 |
75 |
{{ countExpand }}
76 |
{{ countExpanded }}
77 |
{{ countCollapse }}
78 |
{{ countCollapsed }}
79 |
80 |
81 |
82 |
83 |
84 |
96 |
107 |
108 | As an interesting side note, as a head without a body, I envy the dead. Kids don't
109 | turn rotten just from watching TV. Bender, I didn't know you liked cooking. That's so
110 | cute. That's right, baby. I ain't your loverboy Flexo, the guy you love so much. You
111 | even love anyone pretending to be him! I'll tell them you went down prying the
112 | wedding ring off his cold, dead finger. As an interesting side note, as a head
113 | without a body, I envy the dead. Kids don't turn rotten just from watching TV.
114 | Bender, I didn't know you liked cooking. That's so cute. That's right, baby. I ain't
115 | your loverboy Flexo, the guy you love so much. You even love anyone pretending to be
116 | him! I'll tell them you went down prying the wedding ring off his cold, dead finger.
117 | As an interesting side note, as a head without a body, I envy the dead. Kids don't
118 | turn rotten just from watching TV. Bender, I didn't know you liked cooking. That's so
119 | cute. That's right, baby. I ain't your loverboy Flexo, the guy you love so much. You
120 | even love anyone pretending to be him! I'll tell them you went down prying the
121 | wedding ring off his cold, dead finger. As an interesting side note, as a head
122 | without a body, I envy the dead. Kids don't turn rotten just from watching TV.
123 | Bender, I didn't know you liked cooking. That's so cute. That's right, baby. I ain't
124 | your loverboy Flexo, the guy you love so much. You even love anyone pretending to be
125 | him! I'll tell them you went down prying the wedding ring off his cold, dead finger.
126 |
127 |
128 |
129 |
130 |
131 |
132 |
179 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |     
2 |
3 | # Vue Collapsed
4 |
5 | Dynamic CSS height transition from _any to auto_ and vice versa. Accordion ready.
6 |
7 | [Examples and Demo](https://vue-collapsed.pages.dev) - [Stackblitz](https://stackblitz.com/edit/vue-dmjqey?file=src/App.vue)
8 |
9 |
10 |
11 | Check out my other packages for Vue and Nuxt:
12 |
13 | > 🔔 **Notivue**
14 | > _Fully-featured notification system for Vue and Nuxt._
15 | > [Visit repo ➔ ](https://github.com/smastrom/notivue)
16 |
17 | > 🌀 **Vue Global Loader**
18 | > _Global loaders made easy for Vue and Nuxt._
19 | > [Visit repo ➔ ](https://github.com/smastrom/vue-global-loader)
20 |
21 | > 👌 **Vue Use Active Scroll**
22 | > _Accurate TOC/sidebar links without compromises._
23 | > [Visit repo ➔ ](https://github.com/smastrom/vue-use-active-scroll)
24 |
25 | > 🔥 **Vue Use Fixed Header**
26 | > _Turn your boring fixed header into a smart one with three lines of code._
27 | > [Visit repo ➔ ](https://github.com/smastrom/vue-use-fixed-header)
28 |
29 |
30 |
31 | ## Installation
32 |
33 | ```shell
34 | npm i vue-collapsed
35 | # yarn add vue-collapsed
36 | # pnpm add vue-collapsed
37 | # bun add vue-collapsed
38 | ```
39 |
40 | ## Props
41 |
42 | | Name | Description | Type | Required |
43 | | ------------ | ---------------------------------------- | ----------------------------- | ------------------ |
44 | | `when` | Value to control collapse | boolean | :white_check_mark: |
45 | | `baseHeight` | Collapsed height in px, defaults to `0`. | number | :x: |
46 | | `as` | Tag to use instead of `div` | _keyof_ HTMLElementTagNameMap | :x: |
47 |
48 | ## Emits
49 |
50 | | Name | Description | Type |
51 | | ------------ | ----------------------------- | ---------- |
52 | | `@expand` | Expand transition start | () => void |
53 | | `@expanded` | Expand transition completed | () => void |
54 | | `@collapse` | Collapse transition start | () => void |
55 | | `@collapsed` | Collapse transition completed | () => void |
56 |
57 | ## Usage
58 |
59 | ```vue
60 |
66 |
67 |
68 |
69 |
70 |
71 |
{{ 'Collapsed '.repeat(100) }}
72 |
73 |
74 | ```
75 |
76 | ## Automatic transition (default behavior)
77 |
78 | By default, if no height transition is specified the following one is automatically added to the Collapse element:
79 |
80 | `height var(--vc-auto-duration) cubic-bezier(0.33, 1, 0.68, 1)`
81 |
82 | `--vc-auto-duration` is calculated in background and corresponds to an optimal transition duration based on your content height.
83 |
84 | This is the recommended way to use this package unless you want to customize the transition.
85 |
86 | ## Custom transition
87 |
88 | If you prefer to use a custom duration or easing, add a class to Collapse that transitions the `height` property:
89 |
90 | ```vue
91 |
92 |
202 |
203 | ```
204 |
205 | ## Accessibility
206 |
207 | `vue-collapsed` automatically detects if users prefer reduced motion and will disable transitions accordingly while keeping the same API behavior (emitting events and post-transition styles).
208 |
209 | You should only add `aria` attributes to the Collapse element according to your use case.
210 |
211 | ```vue
212 |
237 |
238 |
239 |