quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
274 lines (226 loc) • 6.52 kB
JavaScript
import { h, ref, computed, getCurrentInstance, toRaw } from 'vue'
import useDark, {
useDarkProps
} from '../../composables/private.use-dark/use-dark.js'
import useSize, {
useSizeProps
} from '../../composables/private.use-size/use-size.js'
import useRefocusTarget from '../../composables/private.use-refocus-target/use-refocus-target.js'
import {
useFormInject,
useFormProps
} from '../../composables/use-form/private.use-form.js'
import optionSizes from '../../utils/private.option-sizes/option-sizes.js'
import { stopAndPrevent } from '../../utils/event/event.js'
import { hSlot, hMergeSlot } from '../../utils/private.render/render.js'
export const useCheckboxProps = {
...useDarkProps,
...useSizeProps,
...useFormProps,
modelValue: {
required: true,
default: null
},
val: {},
trueValue: { default: true },
falseValue: { default: false },
indeterminateValue: { default: null },
checkedIcon: String,
uncheckedIcon: String,
indeterminateIcon: String,
toggleOrder: {
type: String,
validator: v => v === 'tf' || v === 'ft'
},
toggleIndeterminate: Boolean,
label: String,
leftLabel: Boolean,
color: String,
keepColor: Boolean,
dense: Boolean,
disable: Boolean,
tabindex: [String, Number]
}
export const useCheckboxEmits = ['update:modelValue']
export default function useCheckbox(type, getInner) {
const { props, slots, emit, proxy } = getCurrentInstance()
const { $q } = proxy
const isDark = useDark(props, $q)
const rootRef = ref(null)
const { refocusTargetEl, refocusTarget } = useRefocusTarget(props, rootRef)
const sizeStyle = useSize(props, optionSizes)
const modelIsArray = computed(
() => props.val !== void 0 && Array.isArray(props.modelValue)
)
const index = computed(() => {
const val = toRaw(props.val)
return modelIsArray.value === true
? props.modelValue.findIndex(opt => toRaw(opt) === val)
: -1
})
const isTrue = computed(() =>
modelIsArray.value === true
? index.value !== -1
: toRaw(props.modelValue) === toRaw(props.trueValue)
)
const isFalse = computed(() =>
modelIsArray.value === true
? index.value === -1
: toRaw(props.modelValue) === toRaw(props.falseValue)
)
const isIndeterminate = computed(
() => isTrue.value === false && isFalse.value === false
)
const tabindex = computed(() =>
props.disable === true ? -1 : props.tabindex || 0
)
const classes = computed(
() =>
`q-${type} cursor-pointer no-outline row inline no-wrap items-center` +
(props.disable === true ? ' disabled' : '') +
(isDark.value === true ? ` q-${type}--dark` : '') +
(props.dense === true ? ` q-${type}--dense` : '') +
(props.leftLabel === true ? ' reverse' : '')
)
const innerClass = computed(() => {
const state =
isTrue.value === true
? 'truthy'
: isFalse.value === true
? 'falsy'
: 'indet'
const color =
props.color !== void 0 &&
(props.keepColor === true ||
(type === 'toggle' ? isTrue.value === true : isFalse.value !== true))
? ` text-${props.color}`
: ''
return `q-${type}__inner relative-position non-selectable q-${type}__inner--${state}${color}`
})
const formAttrs = computed(() => {
const prop = { type: 'checkbox' }
if (props.name !== void 0) {
Object.assign(prop, {
// see https://vuejs.org/guide/extras/render-function.html#creating-vnodes (.prop)
'.checked': isTrue.value,
'^checked': isTrue.value === true ? 'checked' : void 0,
name: props.name,
value: modelIsArray.value === true ? props.val : props.trueValue
})
}
return prop
})
const injectFormInput = useFormInject(formAttrs)
const attributes = computed(() => {
const attrs = {
tabindex: tabindex.value,
role: type === 'toggle' ? 'switch' : 'checkbox',
'aria-label': props.label,
'aria-checked':
isIndeterminate.value === true
? 'mixed'
: isTrue.value === true
? 'true'
: 'false'
}
if (props.disable === true) {
attrs['aria-disabled'] = 'true'
}
return attrs
})
function onClick(e) {
if (e !== void 0) {
stopAndPrevent(e)
refocusTarget(e)
}
if (props.disable !== true) {
emit('update:modelValue', getNextValue(), e)
}
}
function getNextValue() {
if (modelIsArray.value === true) {
if (isTrue.value === true) {
const val = props.modelValue.slice()
val.splice(index.value, 1)
return val
}
return props.modelValue.concat([props.val])
}
if (isTrue.value === true) {
if (props.toggleOrder !== 'ft' || props.toggleIndeterminate === false) {
return props.falseValue
}
} else if (isFalse.value === true) {
if (props.toggleOrder === 'ft' || props.toggleIndeterminate === false) {
return props.trueValue
}
} else {
return props.toggleOrder !== 'ft' ? props.trueValue : props.falseValue
}
return props.indeterminateValue
}
function onKeydown(e) {
if (e.keyCode === 13 || e.keyCode === 32) {
stopAndPrevent(e)
}
}
function onKeyup(e) {
if (e.keyCode === 13 || e.keyCode === 32) {
onClick(e)
}
}
const getInnerContent = getInner(isTrue, isIndeterminate)
// expose public methods
Object.assign(proxy, { toggle: onClick })
return () => {
const inner = getInnerContent()
if (props.disable !== true) {
injectFormInput(
inner,
'unshift',
` q-${type}__native absolute q-ma-none q-pa-none`
)
}
const child = [
h(
'div',
{
class: innerClass.value,
style: sizeStyle.value,
'aria-hidden': 'true'
},
inner
)
]
if (refocusTargetEl.value !== null) {
child.push(refocusTargetEl.value)
}
const label =
props.label !== void 0
? hMergeSlot(slots.default, [props.label])
: hSlot(slots.default)
if (label !== void 0) {
child.push(
h(
'div',
{
class: `q-${type}__label q-anchor--skip`
},
label
)
)
}
return h(
'div',
{
ref: rootRef,
class: classes.value,
...attributes.value,
onClick,
onKeydown,
onKeyup
},
child
)
}
}