quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
189 lines (153 loc) • 4.49 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 =>
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
)
}
})