UNPKG

vue-material-adapter

Version:

Vue 3 wrapper arround Material Components for the Web

136 lines (114 loc) 3.31 kB
import { MDCBannerFoundation } from '@material/banner/index.js'; import { FocusTrap } from '@material/dom/focus-trap.js'; import { computed, onBeforeUnmount, onMounted, reactive, toRefs, watch, } from 'vue'; import bannerContent from './banner-content.vue'; const focusTrapFactory_ = (element, options) => new FocusTrap(element, options); export default { name: 'mcw-banner', props: { centered: Boolean, modelValue: Boolean, text: String, primaryAction: String, secondaryAction: String, mobile: Boolean, fixed: Boolean, disableAutoClose: Boolean, icon: { type: String, default: () => '' }, }, components: { bannerContent }, setup(props, { emit }) { const uiState = reactive({ classes: { 'mdc-banner--centered': props.centered, 'mdc-banner--mobile-stacked': props.mobile, }, styles: {}, root: undefined, contentEl: undefined, primaryActionEl: undefined, }); let foundation; let focusTrap; const onOpen = nv => { if (nv) { foundation.open(); } else { foundation.close(nv); } }; const close = reason => { foundation.close(reason); }; const onContentClick = target => { if (target == 1) { foundation.handleSecondaryActionClick(props.disableAutoClose); } else { foundation.handlePrimaryActionClick(props.disableAutoClose); } }; const adapter = { addClass: className => { uiState.classes = { ...uiState.classes, [className]: true }; // add class immediately otherwise vue rendering makes it happen in the wrong order, but also // need it on classes so vue rendering/repaints keep the value. uiState.root.classList.add(className); }, getContentHeight: () => { return uiState.contentEl.offsetHeight; }, notifyClosed: reason => { emit('mdcbanner:closed', { reason }); }, notifyClosing: reason => { emit('update:modelValue', false); emit('mdcbanner:closing', { reason }); }, notifyOpened: () => emit('mdcbanner:opened', {}), notifyOpening: () => { emit('mdcbanner:opening', {}); }, notifyActionClicked: action => { emit('mdcbanner:actionclicked', { action }); }, releaseFocus: () => { focusTrap.releaseFocus(); }, removeClass: className => { // eslint-disable-next-line no-unused-vars const { [className]: removed, ...rest } = uiState.classes; uiState.classes = rest; }, setStyleProperty: (property, value) => (uiState.styles = { ...uiState.styles, [property]: value }), trapFocus: () => { focusTrap.trapFocus(); }, }; const haveIcon = computed(() => !!props.icon); watch( () => props.modelValue, nv => { onOpen(nv); }, ); onMounted(() => { focusTrap = focusTrapFactory_(uiState.root, { initialFocusEl: uiState.primaryActionEl, }); foundation = new MDCBannerFoundation(adapter); foundation.init(); }); onBeforeUnmount(() => { foundation?.destroy(); }); return { ...toRefs(uiState), onContentClick, haveIcon, close }; }, };