quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
170 lines (134 loc) • 4.27 kB
JavaScript
import { h, computed } from 'vue'
import QBtn from '../btn/QBtn.js'
import QBtnGroup from '../btn-group/QBtnGroup.js'
import { createComponent } from '../../utils/private.create/create.js'
import { useFormInject, useFormProps } from '../../composables/use-form/private.use-form.js'
import { hMergeSlot } from '../../utils/private.render/render.js'
import { getBtnDesignAttr } from '../btn/use-btn.js'
export default createComponent({
name: 'QBtnToggle',
props: {
...useFormProps,
modelValue: {
required: true
},
options: {
type: Array,
required: true,
validator: v => v.every(
opt => ('label' in opt || 'icon' in opt || 'slot' in opt) && 'value' in opt
)
},
// To avoid seeing the active raise shadow through
// the transparent button, give it a color (even white)
color: String,
textColor: String,
toggleColor: {
type: String,
default: 'primary'
},
toggleTextColor: String,
outline: Boolean,
flat: Boolean,
unelevated: Boolean,
rounded: Boolean,
push: Boolean,
glossy: Boolean,
size: String,
padding: String,
noCaps: Boolean,
noWrap: Boolean,
dense: Boolean,
readonly: Boolean,
disable: Boolean,
stack: Boolean,
stretch: Boolean,
spread: Boolean,
clearable: Boolean,
ripple: {
type: [ Boolean, Object ],
default: true
}
},
emits: [ 'update:modelValue', 'clear', 'click' ],
setup (props, { slots, emit }) {
const hasActiveValue = computed(() =>
props.options.find(opt => opt.value === props.modelValue) !== void 0
)
const formAttrs = computed(() => ({
type: 'hidden',
name: props.name,
value: props.modelValue
}))
const injectFormInput = useFormInject(formAttrs)
const btnDesignAttr = computed(() => getBtnDesignAttr(props))
const btnOptionDesign = computed(() => ({
rounded: props.rounded,
dense: props.dense,
...btnDesignAttr.value
}))
const btnOptions = computed(() => props.options.map((item, i) => {
const { attrs, value, slot, ...opt } = item
return {
slot,
props: {
key: i,
'aria-pressed': value === props.modelValue ? 'true' : 'false',
...attrs,
...opt,
...btnOptionDesign.value,
disable: props.disable === true || opt.disable === true,
// Options that come from the button specific options first, then from general props
color: value === props.modelValue
? mergeOpt(opt, 'toggleColor')
: mergeOpt(opt, 'color'),
textColor: value === props.modelValue
? mergeOpt(opt, 'toggleTextColor')
: mergeOpt(opt, 'textColor'),
noCaps: mergeOpt(opt, 'noCaps') === true,
noWrap: mergeOpt(opt, 'noWrap') === true,
size: mergeOpt(opt, 'size'),
padding: mergeOpt(opt, 'padding'),
ripple: mergeOpt(opt, 'ripple'),
stack: mergeOpt(opt, 'stack') === true,
stretch: mergeOpt(opt, 'stretch') === true,
onClick (e) { set(value, item, e) }
}
}
}))
function set (value, opt, e) {
if (props.readonly !== true) {
if (props.modelValue === value) {
if (props.clearable === true) {
emit('update:modelValue', null, null)
emit('clear')
}
}
else {
emit('update:modelValue', value, opt)
}
emit('click', e)
}
}
function mergeOpt (opt, key) {
return opt[ key ] === void 0 ? props[ key ] : opt[ key ]
}
function getContent () {
const child = btnOptions.value.map(opt => {
return h(QBtn, opt.props, opt.slot !== void 0 ? slots[ opt.slot ] : void 0)
})
if (props.name !== void 0 && props.disable !== true && hasActiveValue.value === true) {
injectFormInput(child, 'push')
}
return hMergeSlot(slots.default, child)
}
return () => h(QBtnGroup, {
class: 'q-btn-toggle',
...btnDesignAttr.value,
rounded: props.rounded,
stretch: props.stretch,
glossy: props.glossy,
spread: props.spread
}, getContent)
}
})