vira
Version:
A simple and highly versatile design system using element-vir.
102 lines (95 loc) • 3.08 kB
JavaScript
import { classMap, css, defineElementEvent, html, listen, onResize } from 'element-vir';
import { noNativeFormStyles, noUserSelect, viraAnimationDurations } from '../styles/index.js';
import { defineViraElement } from '../util/define-vira-element.js';
/**
* A wrapper element that can collapse and expand to fit its content dynamically and efficiently.
*
* @category Collapsible
* @category Elements
* @see https://electrovir.github.io/vira/book/elements/vira-collapsible-wrapper
*/
export const ViraCollapsibleWrapper = defineViraElement()({
tagName: 'vira-collapsible-wrapper',
state() {
return {
contentHeight: 0,
};
},
hostClasses: {
'vira-collapsible-wrapper-expand-on-print': ({ inputs }) => !!inputs.expandOnPrint,
},
slotNames: ['vira-collapsible-wrapper-header'],
styles: ({ hostClasses }) => css `
:host {
display: flex;
flex-direction: column;
max-width: 100%;
box-sizing: border-box;
}
.header-wrapper {
${noNativeFormStyles};
cursor: pointer;
}
.content-wrapper,
.collapsing-element {
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.collapsing-element {
transition: height ${viraAnimationDurations['vira-pretty-animation-duration'].value};
overflow: hidden;
&.collapsed {
${noUserSelect}
}
}
@media print {
:host(.${hostClasses['vira-collapsible-wrapper-expand-on-print'].name})
.collapsing-element {
height: auto !important;
overflow: visible !important;
transition: none !important;
}
}
`,
events: {
expandChange: defineElementEvent(),
},
render({ state, slotNames, updateState, dispatch, events, inputs }) {
const collapsingStyles = inputs.expanded
? css `
height: ${state.contentHeight}px;
`
: css `
height: 0;
`;
return html `
<button
class="header-wrapper"
${listen('click', () => {
dispatch(new events.expandChange(!inputs.expanded));
})}
>
<slot name=${slotNames['vira-collapsible-wrapper-header']}>Header</slot>
</button>
<div
class="collapsing-element ${classMap({
collapsed: !inputs.expanded,
})}"
style=${collapsingStyles}
disabled="disabled"
>
<div
${onResize(({ contentRect }) => {
updateState({
contentHeight: contentRect.height,
});
})}
class="content-wrapper"
>
<slot></slot>
</div>
</div>
`;
},
});