Skip to content

Commit

Permalink
Implement switch mode to CheckboxRadioSwitch
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Apr 29, 2021
1 parent f725e38 commit cc6d258
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@

### General description

This is a standard input checkbox/radio design
This is a simple input checkbox, radio and switch design.
Please have a look at proper usage and recommendations: https://material.io/components/checkboxes

### Standard checkbox
```vue
<template>
<div>
<CheckboxRadio :checked.sync="sharingEnabled">Enable sharing</CheckboxRadio>
<CheckboxRadio :checked.sync="sharingEnabled" :disabled="true">Enable sharing</CheckboxRadio>
<CheckboxRadio :checked="sharingEnabled" :loading="loading" @update:checked="onToggle">Enable sharing</CheckboxRadio>
<CheckboxRadioSwitch :checked.sync="sharingEnabled">Enable sharing</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked.sync="sharingEnabled" :disabled="true">Enable sharing (disabled)</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked="sharingEnabled" :loading="loading" @update:checked="onToggle">Enable sharing (with request loading)</CheckboxRadioSwitch>
<br>
sharingEnabled: {{ sharingEnabled }}
</div>
Expand Down Expand Up @@ -63,8 +64,8 @@ export default {
```vue
<template>
<div>
<CheckboxRadio :checked.sync="sharingPermission" value="r" name="sharing_permission_radio" type="radio">Default permission read</CheckboxRadio>
<CheckboxRadio :checked.sync="sharingPermission" value="rw" name="sharing_permission_radio" type="radio">Default permission read+write</CheckboxRadio>
<CheckboxRadioSwitch :checked.sync="sharingPermission" value="r" name="sharing_permission_radio" type="radio">Default permission read</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked.sync="sharingPermission" value="rw" name="sharing_permission_radio" type="radio">Default permission read+write</CheckboxRadioSwitch>
<br>
sharingPermission: {{ sharingPermission }}
</div>
Expand All @@ -84,9 +85,9 @@ export default {
```vue
<template>
<div>
<CheckboxRadio :disabled="true" :checked.sync="sharingPermission" value="r" name="sharing_permission">Permission read</CheckboxRadio>
<CheckboxRadio :checked.sync="sharingPermission" value="w" name="sharing_permission">Permission write</CheckboxRadio>
<CheckboxRadio :checked.sync="sharingPermission" value="d" name="sharing_permission">Permission delete</CheckboxRadio>
<CheckboxRadioSwitch :disabled="true" :checked.sync="sharingPermission" value="r" name="sharing_permission">Permission read</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked.sync="sharingPermission" value="w" name="sharing_permission">Permission write</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked.sync="sharingPermission" value="d" name="sharing_permission">Permission delete</CheckboxRadioSwitch>
<br>
sharingPermission: {{ sharingPermission }}
</div>
Expand All @@ -102,32 +103,55 @@ export default {
</script>
```

### Standard switch
```vue
<template>
<div>
<CheckboxRadioSwitch :checked.sync="sharingEnabled" type="switch">Enable sharing</CheckboxRadioSwitch>
<CheckboxRadioSwitch :checked.sync="sharingEnabled" type="switch" :disabled="true">Enable sharing (disabled)</CheckboxRadioSwitch>
<br>
sharingEnabled: {{ sharingEnabled }}
</div>
</template>
<script>
export default {
data() {
return {
sharingEnabled: false,
}
},
}
</script>
```

</docs>

<template>
<element :is="wrapperElement"
:class="{
'checkbox-radio--checked': isChecked,
'checkbox-radio--disabled': disabled,
'checkbox-radio--indeterminate': indeterminate,
['checkbox-radio-switch-' + type]: type,
'checkbox-radio-switch--checked': isChecked,
'checkbox-radio-switch--disabled': disabled,
'checkbox-radio-switch--indeterminate': indeterminate,
}"
class="checkbox-radio">
:style="cssVars"
class="checkbox-radio-switch">
<input :id="id"
:checked="isChecked"
:disabled="disabled"
:indeterminate="indeterminate"
:name="name"
:type="type"
:type="inputType"
:value="value"
class="checkbox-radio__input"
class="checkbox-radio-switch__input"
@change="onToggle">

<label :for="id" class="checkbox-radio__label">
<div v-if="loading" class="icon-loading-small checkbox-radio__icon" />
<label :for="id" class="checkbox-radio-switch__label">
<div v-if="loading" class="icon-loading-small checkbox-radio-switch__icon" />
<icon :is="checkboxRadioIconElement"
v-else
:size="24"
class="checkbox-radio__icon"
:size="size"
class="checkbox-radio-switch__icon"
title=""
decorative />

Expand All @@ -143,15 +167,18 @@ import MinusBox from 'vue-material-design-icons/MinusBox'
import CheckboxMarked from 'vue-material-design-icons/CheckboxMarked'
import RadioboxMarked from 'vue-material-design-icons/RadioboxMarked'
import RadioboxBlank from 'vue-material-design-icons/RadioboxBlank'
import ToggleSwitchOff from 'vue-material-design-icons/ToggleSwitchOff'
import ToggleSwitch from 'vue-material-design-icons/ToggleSwitch'

import GenRandomId from '../../utils/GenRandomId'
import l10n from '../../mixins/l10n'

export const TYPE_CHECKBOX = 'checkbox'
export const TYPE_RADIO = 'radio'
export const TYPE_SWITCH = 'switch'

export default {
name: 'CheckboxRadio',
name: 'CheckboxRadioSwitch',

mixins: [l10n],

Expand All @@ -162,7 +189,7 @@ export default {
*/
id: {
type: String,
default: () => 'checkbox-radio-' + GenRandomId(),
default: () => 'checkbox-radio-switch-' + GenRandomId(),
validator: id => id.trim() !== '',
},

Expand All @@ -180,7 +207,7 @@ export default {
type: {
type: String,
default: 'checkbox',
validator: type => type === TYPE_CHECKBOX || type === TYPE_RADIO,
validator: type => type === TYPE_CHECKBOX || type === TYPE_RADIO || type === TYPE_SWITCH,
},

/**
Expand Down Expand Up @@ -233,6 +260,38 @@ export default {
},

computed: {
/**
* Icon size
@returns {number}
*/
size() {
return this.type === TYPE_SWITCH
? 36
: 24
},

/**
* Css local variables for this component
* @returns {Object}
*/
cssVars() {
return {
'--icon-size': this.size + 'px',
}
},

/**
* Return the input type.
* Switch is not an official type
* @returns {string}
*/
inputType() {
if (this.type === TYPE_RADIO) {
return TYPE_RADIO
}
return TYPE_CHECKBOX
},

/**
* Check if that entry is checked
* If value is defined, we use that as the checked value
Expand All @@ -254,13 +313,21 @@ export default {
* @returns {Component}
*/
checkboxRadioIconElement() {
if (this.type === 'radio') {
if (this.type === TYPE_RADIO) {
if (this.isChecked) {
return RadioboxMarked
}
return RadioboxBlank
}

// Switch
if (this.type === TYPE_SWITCH) {
if (this.isChecked) {
return ToggleSwitch
}
return ToggleSwitchOff
}

// Checkbox
if (this.indeterminate) {
return MinusBox
Expand All @@ -275,9 +342,19 @@ export default {
mounted() {
if (this.name && this.type === TYPE_CHECKBOX) {
if (!Array.isArray(this.checked)) {
throw new Error('When using groups of checkboxes, the updated value will be an array')
throw new Error('When using groups of checkboxes, the updated value will be an array.')
}
}

// https://material.io/components/checkboxes#usage
if (this.name && this.type === TYPE_SWITCH) {
throw new Error('Switches are not made to be used for data sets. Please use checkboxes instead.')
}

// https://material.io/components/checkboxes#usage
if (typeof this.checked !== 'boolean' && this.type === TYPE_SWITCH) {
throw new Error('Switches can only be used with boolean as checked prop.')
}
},

methods: {
Expand All @@ -292,6 +369,12 @@ export default {
return
}

// If this is a radio, there can only be one value
if (this.type === TYPE_SWITCH) {
this.$emit('update:checked', !this.isChecked)
return
}

// If the initial value was a boolean, let's keep it that way
if (typeof this.checked === 'boolean') {
this.$emit('update:checked', !this.isChecked)
Expand Down Expand Up @@ -319,7 +402,7 @@ export default {
<style lang="scss" scoped>
$spacing: 4px;

.checkbox-radio {
.checkbox-radio-switch {
display: flex;

&__input {
Expand Down Expand Up @@ -349,21 +432,31 @@ $spacing: 4px;
// Remove the left margin of material design icons to align text
margin-left: -2px;
color: var(--color-primary-element);
width: 24px;
height: 24px;
width: var(--icon-size);
height: var(--icon-size);
}

&--disabled &__label {
opacity: $opacity_disabled;
.checkbox-radio__icon {
.checkbox-radio-switch__icon {
color: var(--color-text-light)
}
}

&:not(&--disabled) {
.checkbox-radio__input:hover + .checkbox-radio__label,
.checkbox-radio__input:focus + .checkbox-radio__label {
background-color: var(--color-primary-light);
&:not(&--disabled) &__input:hover + &__label,
&:not(&--disabled) &__input:focus + &__label {
background-color: var(--color-primary-light);
}

// Switch specific rules
&-switch {
&:not(&--checked) &__icon {
color: var(--color-text-lighter);
}

// If checked AND disabled, use the fade primary colour
&--disabled.checkbox-radio-switch--checked &__icon {
color: var(--color-primary-element-light);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
*
*/

import CheckboxRadio from './CheckboxRadio'
import CheckboxRadioSwitch from './CheckboxRadioSwitch'

export default CheckboxRadio
export default CheckboxRadioSwitch
4 changes: 2 additions & 2 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import AppSidebarTab from './AppSidebarTab'
import Avatar from './Avatar'
import Breadcrumb from './Breadcrumb'
import Breadcrumbs from './Breadcrumbs'
import CheckboxRadio from './CheckboxRadio'
import CheckboxRadioSwitch from './CheckboxRadioSwitch'
import ColorPicker from './ColorPicker'
import Content from './Content'
import DatetimePicker from './DatetimePicker'
Expand Down Expand Up @@ -97,7 +97,7 @@ export {
Avatar,
Breadcrumb,
Breadcrumbs,
CheckboxRadio,
CheckboxRadioSwitch,
ColorPicker,
Content,
DatetimePicker,
Expand Down

0 comments on commit cc6d258

Please sign in to comment.