quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
160 lines (125 loc) • 4.16 kB
JavaScript
import { h, ref, computed, provide, getCurrentInstance } from 'vue'
import QBtn from '../btn/QBtn.js'
import QIcon from '../icon/QIcon.js'
import useFab, { useFabProps } from './use-fab.js'
import useModelToggle, { useModelToggleProps, useModelToggleEmits } from '../../composables/private/use-model-toggle.js'
import { createComponent } from '../../utils/private/create.js'
import { hSlot, hMergeSlot } from '../../utils/private/render.js'
import { fabKey } from '../../utils/private/symbols.js'
import uid from '../../utils/uid.js'
const directions = [ 'up', 'right', 'down', 'left' ]
const alignValues = [ 'left', 'center', 'right' ]
export default createComponent({
name: 'QFab',
props: {
...useFabProps,
...useModelToggleProps,
icon: String,
activeIcon: String,
hideIcon: Boolean,
hideLabel: {
default: null
},
direction: {
type: String,
default: 'right',
validator: v => directions.includes(v)
},
persistent: Boolean,
verticalActionsAlign: {
type: String,
default: 'center',
validator: v => alignValues.includes(v)
}
},
emits: useModelToggleEmits,
setup (props, { slots }) {
const triggerRef = ref(null)
const showing = ref(props.modelValue === true)
const targetUid = uid()
const { proxy: { $q } } = getCurrentInstance()
const { formClass, labelProps } = useFab(props, showing)
const hideOnRouteChange = computed(() => props.persistent !== true)
const { hide, toggle } = useModelToggle({
showing,
hideOnRouteChange
})
const slotScope = computed(() => ({ opened: showing.value }))
const classes = computed(() =>
'q-fab z-fab row inline justify-center'
+ ` q-fab--align-${ props.verticalActionsAlign } ${ formClass.value }`
+ (showing.value === true ? ' q-fab--opened' : ' q-fab--closed')
)
const actionClass = computed(() =>
'q-fab__actions flex no-wrap inline'
+ ` q-fab__actions--${ props.direction }`
+ ` q-fab__actions--${ showing.value === true ? 'opened' : 'closed' }`
)
const actionAttrs = computed(() => {
const attrs = {
id: targetUid,
role: 'menu'
}
if (showing.value !== true) {
attrs[ 'aria-hidden' ] = 'true'
}
return attrs
})
const iconHolderClass = computed(() =>
'q-fab__icon-holder '
+ ` q-fab__icon-holder--${ showing.value === true ? 'opened' : 'closed' }`
)
function getIcon (kebab, camel) {
const slotFn = slots[ kebab ]
const classes = `q-fab__${ kebab } absolute-full`
return slotFn === void 0
? h(QIcon, { class: classes, name: props[ camel ] || $q.iconSet.fab[ camel ] })
: h('div', { class: classes }, slotFn(slotScope.value))
}
function getTriggerContent () {
const child = []
props.hideIcon !== true && child.push(
h('div', { class: iconHolderClass.value }, [
getIcon('icon', 'icon'),
getIcon('active-icon', 'activeIcon')
])
)
if (props.label !== '' || slots.label !== void 0) {
child[ labelProps.value.action ](
h('div', labelProps.value.data, slots.label !== void 0 ? slots.label(slotScope.value) : [ props.label ])
)
}
return hMergeSlot(slots.tooltip, child)
}
provide(fabKey, {
showing,
onChildClick (evt) {
hide(evt)
if (triggerRef.value !== null) {
triggerRef.value.$el.focus()
}
}
})
return () => h('div', {
class: classes.value
}, [
h(QBtn, {
ref: triggerRef,
class: formClass.value,
...props,
noWrap: true,
stack: props.stacked,
align: void 0,
icon: void 0,
label: void 0,
noCaps: true,
fab: true,
'aria-expanded': showing.value === true ? 'true' : 'false',
'aria-haspopup': 'true',
'aria-controls': targetUid,
onClick: toggle
}, getTriggerContent),
h('div', { class: actionClass.value, ...actionAttrs.value }, hSlot(slots.default))
])
}
})