-
-
Save Jonarod/7ff2fe4f81aae39e431aa7a08ce815bc to your computer and use it in GitHub Desktop.
/** | |
* @usage: | |
* | |
* <CheckBox label="Foo" value="foo" v-model="MySelectedValues" /> | |
* <CheckBox label="Bar" value="bar" v-model="MySelectedValues" /> | |
* <CheckBox label="Baz" value="baz" v-model="MySelectedValues" /> | |
* | |
* data(){ | |
* return { | |
* MySelectedValues: [], | |
* } | |
* } | |
*/ | |
<template> | |
<label class="wrapper flex items-center"> | |
{{label}} | |
<input class="checkbox" type="checkbox" :checked="isChecked" :value="value" @change="updateInput"/> | |
<span class="checkmark"></span> | |
</label> | |
</template> | |
<script> | |
export default { | |
model: { | |
prop: 'modelValue', | |
event: 'change' | |
}, | |
props: { | |
"value": { type: String }, | |
"modelValue": { default: "" }, | |
"label": { type: String, required: true}, | |
"trueValue": { default: true }, | |
"falseValue": { default: false } | |
}, | |
computed: { | |
isChecked() { | |
if (this.modelValue instanceof Array) { | |
return this.modelValue.includes(this.value) | |
} | |
// Note that `true-value` and `false-value` are camelCase in the JS | |
return this.modelValue === this.trueValue | |
} | |
}, | |
methods: { | |
updateInput(event) { | |
let isChecked = event.target.checked | |
if (this.modelValue instanceof Array) { | |
let newValue = [...this.modelValue] | |
if (isChecked) { | |
newValue.push(this.value) | |
} else { | |
newValue.splice(newValue.indexOf(this.value), 1) | |
} | |
this.$emit('change', newValue) | |
} else { | |
this.$emit('change', isChecked ? this.trueValue : this.falseValue) | |
} | |
} | |
} | |
} | |
</script> | |
<style lang="postcss" scoped> | |
/* Customize the label (the wrapper) */ | |
.wrapper { | |
display: block; | |
position: relative; | |
padding-left: 35px; | |
margin-bottom: 6px; | |
cursor: pointer; | |
font-size: 22px; | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
user-select: none; | |
font-size: 16px; | |
} | |
/* Hide the browser's default checkbox */ | |
.wrapper input { | |
position: absolute; | |
opacity: 0; | |
cursor: pointer; | |
height: 0; | |
width: 0; | |
} | |
/* Create a custom checkbox */ | |
.checkmark { | |
position: absolute; | |
top: 0; | |
left: 0; | |
height: 21px; | |
width: 21px; | |
border-radius: 2px; | |
background-color: #eee; | |
border: 1px solid #ccc; | |
} | |
/* On mouse-over, add a grey background color */ | |
.wrapper:hover input ~ .checkmark { | |
background-color: #ccc; | |
} | |
/* When the checkbox is checked, add a blue background */ | |
.wrapper input:checked ~ .checkmark { | |
background-color: #1CD4A7; | |
} | |
/* Create the checkmark/indicator (hidden when not checked) */ | |
.checkmark:after { | |
content: ""; | |
position: absolute; | |
display: none; | |
} | |
/* Show the checkmark when checked */ | |
.wrapper input:checked ~ .checkmark:after { | |
display: block; | |
} | |
/* Style the checkmark/indicator */ | |
.wrapper .checkmark:after { | |
left: 7px; | |
top: 0px; | |
width: 7px; | |
height: 15px; | |
border: solid white; | |
border-width: 0 3px 3px 0; | |
-webkit-transform: rotate(45deg); | |
-ms-transform: rotate(45deg); | |
transform: rotate(45deg); | |
} | |
</style> |
thank you for this beautiful example, there's a problem tho:
if you are having a huge ammount of custom checkboxes that all rely on the same v-model, each change would trigger a rerender in all checkboxes.
i ran across this performance-bottleneck on having 200+ checkboxes.
any thoughts or solutions to this are welcome ;)
@gianpesto I'd rethink the UI you are building if it requires 200+ checkboxes on the same view 😄 🙈
Lifesaver, thanks for sharing!
@gianpesto
It should be more performant.
https://jsfiddle.net/ariel0196/euzn79gc/3/
Thank for Sharing!!
@gianpesto
It should be more performant.
https://jsfiddle.net/ariel0196/euzn79gc/3/
Hej! Nice simpe idea! ilikeilikeilike! 🥰
A vue 3 example based on @nevadskiy answer:
https://codesandbox.io/s/interesting-paper-yeydb?file=/src/components/Checkbox.vue
A vue 3 example based on @nevadskiy answer: https://codesandbox.io/s/interesting-paper-yeydb?file=/src/components/Checkbox.vue
thx you!
Thank for Sharing ♥
Thanks for sharing!
@gianpesto @nevadskiy Thank you both! I was having a tough time wrapping my head around this. Works with Pinia, too!
A vue 3 example based on @nevadskiy answer: https://codesandbox.io/s/interesting-paper-yeydb?file=/src/components/Checkbox.vue
Amazing. Thank you.
+tnx
A vue 3 example based on @nevadskiy answer: https://codesandbox.io/s/interesting-paper-yeydb?file=/src/components/Checkbox.vue
@juanaguillon great solution! However, I'm getting the following warning from typescript on the v-model attribute:
Type 'string | number | boolean | (string | number)[] | undefined' is not assignable to type 'any[] | Set<any> | Booleanish | undefined'.
Type 'string' is not assignable to type 'any[] | Set<any> | Booleanish | undefined'.
Any ideas?
Thanks for sharing this. It's working but since I'm using Vue 3 with Composition API, we need to use the event "update:modelValue" when using emit(), otherwise the array (if it's checkboxes group) won't work.
The event change doesn't work on its own, but if I remove it and only keeps "update:modelValue", it's fine!
Thanks for sharing this. It's working but since I'm using Vue 3 with Composition API, we need to use the event "update:modelValue" when using emit(), otherwise the array (if it's checkboxes group) won't work. The event change doesn't work on its own, but if I remove it and only keeps "update:modelValue", it's fine!
Can you share the working one? Thank you in advance! :)
Thanks for sharing this. It's working but since I'm using Vue 3 with Composition API, we need to use the event "update:modelValue" when using emit(), otherwise the array (if it's checkboxes group) won't work. The event change doesn't work on its own, but if I remove it and only keeps "update:modelValue", it's fine!
Can you share the working one? Thank you in advance! :)
Replied with the wrong account lol (work account) so here the same answer you probably received by mail but with my personal account
Template
<template>
<label :class="['checkbox']" :for="id">
<input type="checkbox" :id="id" tabindex="0" role="checkbox" @change="updateInput" :value="value" :checked="isChecked" @on-change="emit('onChange')">
{{label}}
</label>
</template>
script setup
import { defineProps, defineEmits, computed } from 'vue'
const props = defineProps({
id: {
type: String,
default: ""
},
label: {
type: String,
default: "",
required: true
},
value: {
type: [String, Object],
default: "",
},
checked: {
type: [Boolean, Array],
default: false,
},
disabled: {
type: Boolean,
default: false,
},
modelValue: { default: "" },
trueValue: { default: true },
falseValue: { default: false }
})
const emit = defineEmits(['onChange', 'update:modelValue']);
const isChecked = computed(() => {
if (props.modelValue instanceof Array) {
// console.log('props.modelValue', props.modelValue)
return props.modelValue.includes(props.value)
}
// Note that `true-value` and `false-value` are camelCase in the JS
return props.modelValue === props.trueValue
})
const updateInput = (e) => {
// emit("onChange", e.target.value);
// emit('update:modelValue', props.modelValue)
let isChecked = event.target.checked
console.log('isChecked', isChecked, props.modelValue instanceof Array)
if (props.modelValue instanceof Array) {
let newValue = [...props.modelValue]
// console.log('newValue', newValue)
if (isChecked) {
newValue.push(props.value)
// console.log('checked!', newValue)
} else {
newValue.splice(newValue.indexOf(props.value), 1)
}
// emit('change', newValue)
emit('update:modelValue', newValue)
} else {
// emit('change', isChecked ? props.trueValue : props.falseValue)
emit('update:modelValue', isChecked ? props.trueValue : props.falseValue)
}
};
Usage
<mycheckbox :label="name" :id="id" :value="myValue" v-model="arrayOrObjectOrStringWhatever" @on-change="doSth"></mycheckbox>
Why put aria-checked
on the label
element here? First, it's not supported on label
; second, it's useless on <input type="checkbox">
. The native checked attribute takes precedence.
<label :class="['checkbox']" :for="id" :aria-labelledby="`label-${props.id}`" :aria-checked="props.checked">
Why put
aria-checked
on thelabel
element here? First, it's not supported onlabel
; second, it's useless on<input type="checkbox">
. The native checked attribute takes precedence.<label :class="['checkbox']" :for="id" :aria-labelledby="`label-${props.id}`" :aria-checked="props.checked">
Oh thanks for noticing it. I think it was "garbage" of another tutorial I tried to follow and I mixed it with the content here. Imma remove it thanks
Thanks for sharing @flora-le :)
You're welcome @miqbalhakim ! Nice day
Thanks @flora-le. I was going to add that the same goes for putting aria-labelledby
inside a label
element, but it looks as though that's been removed too.
Thank's!!
thank you bro ! I am looking for this :)