vira
Version:
A simple and highly versatile design system using element-vir.
175 lines (159 loc) • 5.58 kB
JavaScript
import { css, defineElementEvent, html, listen, nothing, testId } from 'element-vir';
import { ChevronUp16Icon } from '../icons/index.js';
import { viraFontCssVars } from '../styles/font.js';
import { viraFormCssVars } from '../styles/form-styles.js';
import { defineViraElement } from '../util/define-vira-element.js';
import { ViraCollapsibleWrapper } from './vira-collapsible-wrapper.element.js';
import { ViraIcon } from './vira-icon.element.js';
/**
* A combination ViraCard and ViraCollapsibleWrapper with a collapse caret.
*
* @category Elements
* @see https://electrovir.github.io/vira/book/elements/vira-checkbox
*/
export const ViraCollapsibleCard = defineViraElement()({
tagName: 'vira-collapsible-card',
testIds: ['openCaret'],
events: {
expandToggle: defineElementEvent(),
},
state({ inputs }) {
return {
isExpanded: !!inputs.startExpanded,
};
},
hostClasses: {
'vira-collapsible-card-expanded': ({ state }) => state.isExpanded,
'vira-collapsible-card-expansion-blocked': ({ inputs }) => !!inputs.blockExpansion,
'vira-collapsible-card-card-styles': ({ inputs }) => !inputs.rawCollapsible,
},
cssVars: {
'vira-collapsible-card-content-gap': '16px',
},
styles: ({ hostClasses, cssVars }) => css `
:host {
display: inline-flex;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
}
${hostClasses['vira-collapsible-card-expanded'].selector} .open-caret {
transform: rotate(180deg);
}
${ViraCollapsibleWrapper} {
flex-grow: 1;
max-width: 100%;
}
${hostClasses['vira-collapsible-card-card-styles'].selector} {
border-radius: ${viraFormCssVars['vira-form-wrapper-radius'].value};
& ${ViraCollapsibleWrapper} {
border: 1px solid ${viraFormCssVars['vira-form-border-color'].value};
border-radius: ${viraFormCssVars['vira-form-wrapper-radius'].value};
}
& .card-header {
padding: 8px 16px;
}
& .card-content {
padding: 8px 16px 8px 16px;
}
}
${hostClasses['vira-collapsible-card-expansion-blocked'].selector} {
& .header-wrapper {
cursor: default;
}
}
.card-header {
font-weight: ${viraFontCssVars['vira-font-weight-bold'].value};
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
& .header-filler {
flex-grow: 1;
}
}
.card-content {
display: flex;
flex-direction: column;
align-items: stretch;
gap: ${cssVars['vira-collapsible-card-content-gap'].value};
overflow-x: auto;
overflow-y: hidden;
}
@media print {
${ViraCollapsibleWrapper} {
border: none !important;
}
.card-header {
padding: 8px 0 !important;
}
.card-content {
overflow: visible !important;
padding: 8px 0 16px 0 !important;
}
.open-caret {
display: none;
}
}
`,
slotNames: [
'vira-collapsible-card-header',
],
render({ inputs, slotNames, state, updateState, testIds, dispatch, events }) {
if (inputs.blockExpansion) {
updateState({
isExpanded: true,
});
}
const wrapperContentTemplate = html `
<div class="card-content">
<slot></slot>
</div>
`;
const wrapperHeaderTemplate = inputs.hideHeader
? nothing
: html `
<div class="card-header">
<slot name=${slotNames['vira-collapsible-card-header']}>
<div class="header-filler"></div>
</slot>
${inputs.blockExpansion
? nothing
: html `
<${ViraIcon.assign({
icon: ChevronUp16Icon,
fitContainer: true,
})}
${testId(testIds.openCaret)}
class="open-caret"
></${ViraIcon}>
`}
</div>
`;
return html `
<${ViraCollapsibleWrapper.assign({
expanded: state.isExpanded,
expandOnPrint: inputs.expandOnPrint ?? false,
})}
${listen(ViraCollapsibleWrapper.events.expandChange, (event) => {
event.stopImmediatePropagation();
if (inputs.blockExpansion) {
return;
}
updateState({
isExpanded: event.detail,
});
dispatch(new events.expandToggle(event.detail));
})}
>
<div
class="header-wrapper"
slot=${ViraCollapsibleWrapper.slotNames['vira-collapsible-wrapper-header']}
>
${wrapperHeaderTemplate}
</div>
${wrapperContentTemplate}
</${ViraCollapsibleWrapper}>
`;
},
});