import { defineComponent } from "vue"; import type { CSSProperties, VNode } from "vue"; import generateClass from "@darwin-studio/vue-ui/src/utils/generate-class"; import sleep from "@darwin-studio/vue-ui/src/utils/sleep"; import { EVENT_NAME } from "@darwin-studio/vue-ui/src/constants/event-name"; import type { CommonProps } from "./types"; import { dDetailsProps as props } from "./props"; import { dDetailsSetup as setup } from "./setup"; import { getTransitionDuration } from "./utils"; import config from "./config"; /** * Renders details element with summary. */ export default defineComponent({ name: config.details.name, props, // TODO: move to the config ??? // TODO: v-model: open | value ??? setup, // TODO: move to the config ??? emits: [EVENT_NAME.TOGGLE, EVENT_NAME.UPDATE_OPEN], // TODO: move to the config ??? computed: { // TODO: computed(() => { // return Object.values(state.value).some(status => status); // TODO: typing // TODO: naming commonProps(): CommonProps { return { hideSummaryAfter: this.injection.hideSummaryAfter || this.hideSummaryAfter, disabled: this.injection.disabled || this.disabled, colorScheme: this.injection.colorScheme || this.colorScheme, padding: this.injection.padding || this.padding, rounding: this.injection.rounding || this.rounding, size: this.injection.size || this.size, transition: this.injection.transition || this.transition, }; }, // TODO: computed(() => { transitionDuration(): number { return getTransitionDuration(this.commonProps.transition) * 1000; }, summaryClasses(): (string | undefined)[] { return [ generateClass.outline( this.commonProps.colorScheme, this.commonProps.size ), generateClass.transition(this.commonProps.transition), this.innerOpen && this.isExpanded ? config.summaryOpenClass : undefined, ]; }, renderSummary(): VNode { return ( {this.$slots.summary?.() || this.summary} {!this.commonProps?.hideSummaryAfter && this.renderSummaryAfter} ); }, renderSummaryAfter(): VNode[] | VNode { return ( {this.$slots.summaryAfter?.() || config.summaryAfterContent} ); }, contentClasses(): (string | undefined)[] { return [generateClass.transition(this.commonProps.transition)]; }, contentStyles(): CSSProperties | undefined { if (this.isMounted) { const hasHeight = this.innerOpen && this.isExpanded; return { height: hasHeight ? `${this.contentHeight}px` : 0, padding: hasHeight ? `var(--padding)` : undefined, }; } return {}; }, // TODO: rounding.full -> the bottom of the content is out of the container renderContent(): VNode { return (
{this.$slots.default?.() || this.content}
); }, classes(): (string | undefined)[] { return [ config.details.class, this.commonProps.disabled ? config.details.disabledClass : undefined, this.commonProps.disabled ? config.details.disabledColorSchemeClass : undefined, generateClass.colorScheme(this.commonProps.colorScheme), generateClass.font(this.commonProps.size), ...generateClass.padding( this.commonProps.padding, this.commonProps.size ), generateClass.rounding(this.commonProps.rounding), generateClass.size(this.commonProps.size), generateClass.transition(this.commonProps.transition), ]; }, }, watch: { open: { handler() { if (this.open !== this.innerOpen) { this.toggle(); } }, immediate: true, }, [config.openIdsInjectionPath]: { handler() { if (typeof this.injection.openIds === "undefined") { return; } const open = this.injection.openIds?.includes(this.id); if (open !== this.innerOpen) { this.toggle(); } }, immediate: true, }, }, methods: { async toggle(event?: MouseEvent): Promise { event?.preventDefault(); // TODO: test case if (this.commonProps.disabled) { return; } if (this.innerOpen) { this.isExpanded = false; await sleep(this.transitionDuration); this.innerOpen = false; } else { this.innerOpen = true; await sleep(0); this.isExpanded = true; } /** * Emits on toggle with MouseEvent payload * @event toggle * @type {event: Event} */ this.$emit(EVENT_NAME.TOGGLE, event); /** * Emits on toggle with current open state * @event update:open * @type {open: boolean} */ this.$emit(EVENT_NAME.UPDATE_OPEN, this.innerOpen); // TODO: update open this.whenChange?.(this.id, this.innerOpen); this.injection.whenChange?.(this.id, this.innerOpen); }, }, /** * @slot $slots.summary * Use instead of props.summary to fully customize summary element content * */ /** * @slot $slots.summaryAfter * Use instead of props.summaryAfter to fully customize after summary element * */ /** * @slot $slots.default * Use instead of props.content to fully customize details content * */ render(): VNode { return (
{this.renderSummary} {this.renderContent}
); }, });