Giter Site home page Giter Site logo

smastrom / vue-collapsed Goto Github PK

View Code? Open in Web Editor NEW
118.0 2.0 8.0 213 KB

:weight_lifting_man: CSS height transition from any to auto and vice versa for Vue and Nuxt. Accordion ready.

Home Page: https://vue-collapsed.pages.dev

License: MIT License

TypeScript 34.29% HTML 0.63% Vue 57.45% Shell 0.14% CSS 7.49%
accordion accordion-component collapse collapse-component dynamic-height vue vue-accordion vue-collapse vue3

vue-collapsed's Introduction

npm dependencies npm bundle size downloads GitHub Workflow Status

Vue Collapsed

Dynamic CSS height transition from any to auto and vice versa. Accordion ready.

Examples and Demo - Stackblitz


Check out my other packages for Vue and Nuxt:

πŸ”” Notivue
Fully-featured notification system for Vue and Nuxt.
Visit repo βž”

πŸŒ€ Vue Global Loader
Global loaders made easy for Vue and Nuxt.
Visit repo βž”

πŸ‘Œ Vue Use Active Scroll
Accurate TOC/sidebar links without compromises.
Visit repo βž”

πŸ”₯ Vue Use Fixed Header
Turn your boring fixed header into a smart one with three lines of code.
Visit repo βž”


Installation

npm i vue-collapsed
#Β yarn add vue-collapsed
# pnpm add vue-collapsed
# bun add vue-collapsed

Props

Name Description Type Required
when Value to control collapse boolean βœ…
baseHeight Collapsed height in px, defaults to 0. number ❌
as Tag to use instead of div keyof HTMLElementTagNameMap ❌

Emits

Name Description Type
@expand Expand transition start () => void
@expanded Expand transition completed () => void
@collapse Collapse transition start () => void
@collapsed Collapse transition completed () => void

Usage

<script setup>
import { ref } from 'vue'
import { Collapse } from 'vue-collapsed'

const isExpanded = ref(false)
</script>

<template>
  <button @click="isExpanded = !isExpanded">Trigger</button>

  <Collapse :when="isExpanded">
    <p>{{ 'Collapsed '.repeat(100) }}</p>
  </Collapse>
</template>

Automatic transition (default behavior)

By default, if no height transition is specified the following one is automatically added to the Collapse element:

height var(--vc-auto-duration) cubic-bezier(0.33, 1, 0.68, 1)

--vc-auto-duration is calculated in background and corresponds to an optimal transition duration based on your content height.

This is the recommended way to use this package unless you want to customize the transition.

Custom transition

If you prefer to use a custom duration or easing, add a class to Collapse that transitions the height property:

<Collapse :when="isExpanded" class="v-collapse">
  <p>{{ 'Collapsed '.repeat(100) }}</p>
</Collapse>
.v-collapse {
  transition: height 300ms ease-out;
  /* or transition: height var(--vc-auto-duration) ease-in-out */
}

Multiple transitions

To transition other properties use the attribute data-collapse:

Transition From Enter Leave
Expand collapsed expanding expanded
Collapse expanded collapsing collapsed
.v-collapse {
  --dur-easing: var(--vc-auto-duration) cubic-bezier(0.33, 1, 0.68, 1);
  transition:
    height var(--dur-easing),
    opacity var(--dur-easing);
}

.v-collapse[data-collapse='expanded'],
.v-collapse[data-collapse='expanding'] {
  opacity: 1;
}

.v-collapse[data-collapse='collapsed'],
.v-collapse[data-collapse='collapsing'] {
  opacity: 0;
}

Or to use different easings/durations for expand and collapse:

.v-collapse[data-collapse='expanding'] {
  transition: height 600ms ease-in-out;
}

.v-collapse[data-collapse='collapsing'] {
  transition: height 300ms ease-out;
}

Above values can also be accessed using v-slot:

<Collapse :when="isExpanded" class="v-collapse" v-slot="{ state }">
  {{ state === 'collapsing' ? 'Collapsing content...' : null }}
</Collapse>

Example - Accordion

<script setup>
import { reactive } from 'vue'
import { Collapse } from 'vue-collapsed'

const questions = reactive([
  {
    title: 'Question one',
    answer: 'Answer one',
    isExpanded: false // Initial value
  },
  {
    title: 'Question two',
    answer: 'Answer two',
    isExpanded: false
  },
  {
    title: 'Question three',
    answer: 'Answer three',
    isExpanded: false
  }
])

function handleAccordion(selectedIndex) {
  questions.forEach((_, index) => {
    questions[index].isExpanded = index === selectedIndex ? !questions[index].isExpanded : false
  })
}

/**
 * For individual control you might use:
 *
 * function handleMultiple(index) {
 *   questions[index].isExpanded = !questions[index].isExpanded
 * }
 */
</script>

<template>
  <div v-for="(question, index) in questions" :key="question.title">
    <button @click="handleAccordion(index)">
      {{ question.title }}
    </button>
    <Collapse :when="questions[index].isExpanded">
      <p>
        {{ question.answer }}
      </p>
    </Collapse>
  </div>
</template>

Accessibility

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).

You should only add aria attributes to the Collapse element according to your use case.

<script setup>
import { ref, computed } from 'vue'
import { Collapse } from 'vue-collapsed'

const isExpanded = ref(false)

const TOGGLE_ID = 'toggle-id'
const COLLAPSE_ID = 'collapse-id'

const toggleAttrs = computed(() => ({
  id: TOGGLE_ID,
  'aria-controls': COLLAPSE_ID,
  'aria-expanded': isExpanded.value
}))

const collapseAttrs = {
  role: 'region',
  id: COLLAPSE_ID,
  'aria-labelledby': TOGGLE_ID
}

function handleCollapse() {
  isExpanded.value = !isExpanded.value
}
</script>

<template>
  <div>
    <button v-bind="toggleAttrs" @click="handleCollapse">This a panel.</button>
    <Collapse v-bind="collapseAttrs" :when="isExpanded">
      <p>{{ 'Collapsed '.repeat(100) }}</p>
    </Collapse>
  </div>
</template>

Manually disabling transitions

<template>
  <Collapse :when="isExpanded" class="instant-collapse">
    <p>{{ 'Collapsed '.repeat(100) }}</p>
  </Collapse>
</template>

<style>
.instant-collapse {
  transition: none;
}
</style>

License

MIT

vue-collapsed's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

vue-collapsed's Issues

Is it possible to only half-collapse an element?

Hi, I'm looking for a way to circumvent the CSS transition height-auto dilemma, which prevents proper CSS transitions when the height of a DOM element is unknown. With your library I've only managed to show either 0% height (hidden) or 100% height of a DOM element. Is there any way to show, let's say height: 40vh in the collapsed state and upon click expand it to 100%?

Not animations in Firefox 124

Hello.

In Firefox Developer Edition 124.0b7 (all extensions are disabled), the collapsible list started to open and close instantly, without any animations. You can check that on the Vue Collapsed site: https://vue-collapsed.pages.dev/. This does not happen in Firefox 123 or Chrome 122. That said, I only want to make you aware of that - I don't think there is something necessarily wrong with vue-collapsed: after a quick debugging I noticed that transitionend event no longer fires, which causes this behavior. The root cause might be a bug in a new Firefox or Vue.

Collapse transition never finishing for custom zoom of the page

In the code the onCollapse function is called only when the following condition is satisfied:

    if (baseHeight.value === getComputedHeight(collapseRef)) {
        onCollapsed()
    }

However the computed height of the element is not always exact.

For instance I have an element that is 42px in height. When running in chrome, when user zooms the page out to 90% the computed height value is no longer 42px but 41.9965px. So this condition fails, and the state of the collapsible is never updated.

This is a subpixel rendering issue that caused it, so I think a proper solution would be to trim/round the computed value. I don't think a sub-pixel precision is required and it would fix this issue.

On an unrelated note: floating point numbers shouldn't be compared using equality operator.

When section is expanded, overflow: hidden (among other differences) is added in Firefox but not Chrome

Hi,

I ran into the following issue in Firefox (v 124-124.0.1):

I have a dropdown within a collapsible section. The part of the dropdown which is outside the slide container is cut off in Firefox but not in Chrome:

a) Firefox -
Screenshot 2024-03-27 at 3 57 43 PM

b) Chrome -
Screenshot 2024-03-27 at 4 00 49 PM

It looks like the reason is that in Firefox, some additional CSS classes are added which are not added in Chrome. Additionally, the state is not the same (expanded/collapsed in Chrome vs expanding/collapsing in Firefox):

a) Firefox -
Screenshot 2024-03-27 at 4 11 48 PM

b) Chrome -
Screenshot 2024-03-27 at 4 11 20 PM

Replication

You can see this effect in the stackblitz link provided on the github readme if you inspect one of the sections:

a) Firefox -
Screenshot 2024-03-27 at 3 47 51 PM

b) Chrome -
Screenshot 2024-03-27 at 3 47 38 PM

Please can anything be done about this so that Firefox is consistent with Chrome, i.e. it doesn't have those additional classes? Or is there an appropriate workaround I can use?

Thanks!

SSR problem: layout shift

In Nuxt.js from server I get plain html without base styles (like display: none; if content is collapsed), and styles applied only on mounted. It will be great if proper styles will come from server so no layout shift will be there.

Overflow hidden on default slot prevents from setting correct height on element

Hi, I am trying to create Collapse which contains to parts:

  1. Div with non scrollable content
  2. Div with scrollable content, that should have height of 100% of expanded collapse

I have to set overflow: hidden to all items scrollable content is nested in, but setting overflow: hidden to top item inside collapse make it always 0px height. Maybe I missed something. Thanks in advance

<Collapse>
 <div class="flex flex-col h-full overflow-hidden">
  // This is non scrollable
  <div>...</div>

  // This is scrollable
  <div class="overflow-auto h-full flex flex-col">...</div>
 <div>
</Collapse>

use vue's events instead of binding callbacks

instead of having to bind the callbacks with props, why not just use vue's own emits?
So this:

    <Collapse
      :when="selected"
      :onExpanded="() => scrollIntoView(index)"
      class="v-collapse"
    >
      <p>
        Hello world
      </p>
    </Collapse>

would become this:

    <Collapse
      :when="selected"
      @expanded="() => scrollIntoView(index)"
      class="v-collapse"
    >
      <p>
        Hello world
      </p>
    </Collapse>

This seems more intuitive when working with vue (at least to me).

Error when build with vue-tsc

Terminal command: vue-tsc --noEmit && vite build --mode production

node_modules/vue-collapsed/dist/index.d.ts:1:15 - error TS1005: ',' expected.

1 import { type PropType } from 'vue';

If I delete the word type in the index.d.ts, the build runs normally. Please tell me how to fix this error so that you don't have to go to the root of the add-on after each update of the packages.

Vue-collapsed version - 1.1.3

Horizontal Collapse

Great component. Simple and sweet.
Any plans for horizontal collapse?
Would you review a PR if I find the time to add it on my own / any strong recommendations on how to code the horizontal variant?

Named export 'Collapse' not found.

Named export 'Collapse' not found. The requested module 'file:///Users/sleblanc/Github/mrleblanc101.github.io/node_modules/vue-collapsed/dist/index.umd.js' is a CommonJS module, which may not support all module.exports as named exports.

[BUG] Sometimes it stays in mode "expanding"

On some occasion, seamingly random, the Collapse stays in "expanding" mode.
This prevents the event expanded to trigger and makes the data-collapse value stay as "expanding".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.