mirror of
https://github.com/directus/directus.git
synced 2026-01-29 03:48:01 -05:00
Interface toggle (#459)
* Add icon prop overrides to checkbox * Fix color example in storybook * Add block style mode to checkbox base componet * Add toggle interface
This commit is contained in:
@@ -86,13 +86,17 @@ If you can't, you should listen to the `update:indeterminate` event and respond
|
||||
```
|
||||
|
||||
## Props
|
||||
| Prop | Description | Default |
|
||||
|-----------------|--------------------------------------------------------------------------|---------|
|
||||
| `value` | Value for checkbox. Similar to value attr on checkbox type input in HTML | `--` |
|
||||
| `inputValue` | Value that's used with `v-model`. Either boolean or array of values | `false` |
|
||||
| `label` | Label for the checkbox | `--` |
|
||||
| `disabled` | Disable the checkbox | `false` |
|
||||
| `indeterminate` | Show the indeterminate state | `false` |
|
||||
| Prop | Description | Default |
|
||||
|----------------------|--------------------------------------------------------------------------|---------------------------|
|
||||
| `value` | Value for checkbox. Similar to value attr on checkbox type input in HTML | `--` |
|
||||
| `inputValue` | Value that's used with `v-model`. Either boolean or array of values | `false` |
|
||||
| `label` | Label for the checkbox | `--` |
|
||||
| `disabled` | Disable the checkbox | `false` |
|
||||
| `indeterminate` | Show the indeterminate state | `false` |
|
||||
| `icon-on` | What icon to use for the on state | `check_box` |
|
||||
| `icon-off` | What icon to use for the off state | `check_box_outline_blank` |
|
||||
| `icon-indeterminate` | What icon to use for the indeterminate state | `indeterminate_check_box` |
|
||||
| `block` | Show as styled block. Matches input size | `block` |
|
||||
|
||||
## Slots
|
||||
| Slot | Description |
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { withKnobs, color } from '@storybook/addon-knobs';
|
||||
import { withKnobs, color, text } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import Vue from 'vue';
|
||||
import VCheckbox from '../v-checkbox';
|
||||
import markdown from './readme.md';
|
||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||
import { defineComponent, ref } from '@vue/composition-api';
|
||||
|
||||
Vue.component('v-checkbox', VCheckbox);
|
||||
|
||||
@@ -87,9 +88,9 @@ export const colors = () => ({
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<v-checkbox v-model="options" value="red" @change="onChange" :style="{'--v-checkbox-color': 'var(--red)'}" label="Red" />
|
||||
<v-checkbox v-model="options" value="blue" @change="onChange" :style="{'--v-checkbox-color': 'var(--blue)'}" label="Blue" />
|
||||
<v-checkbox v-model="options" value="yellow" @change="onChange" :style="{'--v-checkbox-color': 'var(--amber)'}" label="Yellow" />
|
||||
<v-checkbox v-model="options" value="red" @change="onChange" :style="{'--v-checkbox-color': 'var(--danger)'}" label="Danger" />
|
||||
<v-checkbox v-model="options" value="blue" @change="onChange" :style="{'--v-checkbox-color': 'var(--primary)'}" label="Primary" />
|
||||
<v-checkbox v-model="options" value="yellow" @change="onChange" :style="{'--v-checkbox-color': 'var(--secondary)'}" label="Secondary" />
|
||||
<v-checkbox v-model="options" value="custom" @change="onChange" :style="{'--v-checkbox-color': customColor}" label="Custom..." />
|
||||
</div>
|
||||
`,
|
||||
@@ -136,3 +137,53 @@ export const slots = () => ({
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
export const customIcons = () =>
|
||||
defineComponent({
|
||||
props: {
|
||||
iconOn: {
|
||||
default: text('Icon (On)', 'check'),
|
||||
},
|
||||
iconOff: {
|
||||
default: text('Icon (Off)', 'close'),
|
||||
},
|
||||
iconIndeterminate: {
|
||||
default: text('Icon (Indeterminate)', 'more_horiz'),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const checked = ref(false);
|
||||
const indeterminate = ref(false);
|
||||
return { checked, indeterminate };
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<v-checkbox
|
||||
v-model="checked"
|
||||
:indeterminate.sync="indeterminate"
|
||||
:icon-on="iconOn"
|
||||
:icon-off="iconOff"
|
||||
:icon-indeterminate="iconIndeterminate"
|
||||
label="Checkbox"
|
||||
/>
|
||||
<v-checkbox style="margin-top: 32px;" v-model="indeterminate" label="Indeterminate" />
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
export const blockStyle = () =>
|
||||
defineComponent({
|
||||
setup() {
|
||||
const checked = ref(false);
|
||||
const indeterminate = ref(false);
|
||||
return { checked, indeterminate };
|
||||
},
|
||||
template: `
|
||||
<v-checkbox
|
||||
style="max-width: 300px;"
|
||||
v-model="checked"
|
||||
label="Checkbox"
|
||||
block
|
||||
/>
|
||||
`,
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
role="checkbox"
|
||||
:aria-pressed="isChecked ? 'true' : 'false'"
|
||||
:disabled="disabled"
|
||||
:class="{ checked: isChecked, indeterminate }"
|
||||
:class="{ checked: isChecked, indeterminate, block }"
|
||||
>
|
||||
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
||||
<v-icon class="checkbox" :name="icon" />
|
||||
@@ -46,6 +46,22 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
iconOn: {
|
||||
type: String,
|
||||
default: 'check_box',
|
||||
},
|
||||
iconOff: {
|
||||
type: String,
|
||||
default: 'check_box_outline_blank',
|
||||
},
|
||||
iconIndeterminate: {
|
||||
type: String,
|
||||
default: 'indeterminate_check_box',
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const isChecked = computed<boolean>(() => {
|
||||
@@ -57,8 +73,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const icon = computed<string>(() => {
|
||||
if (props.indeterminate === true) return 'indeterminate_check_box';
|
||||
return isChecked.value ? 'check_box' : 'check_box_outline_blank';
|
||||
if (props.indeterminate === true) return props.iconIndeterminate;
|
||||
return isChecked.value ? props.iconOn : props.iconOff;
|
||||
});
|
||||
|
||||
return { isChecked, toggleInput, icon };
|
||||
@@ -90,8 +106,9 @@ export default defineComponent({
|
||||
@import '@/styles/mixins/no-wrap';
|
||||
|
||||
.v-checkbox {
|
||||
--v-checkbox-color: var(--foreground-normal);
|
||||
--v-checkbox-color: var(--primary);
|
||||
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0;
|
||||
@@ -129,10 +146,45 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
&.block {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: var(--input-height);
|
||||
padding: 10px; // 14 - 4 (border)
|
||||
background-color: var(--page-background);
|
||||
border: 2px solid var(--background-subdued);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--background-subdued);
|
||||
border-radius: var(--border-radius);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:disabled):not(.indeterminate).checked {
|
||||
.checkbox {
|
||||
--v-icon-color: var(--v-checkbox-color);
|
||||
}
|
||||
|
||||
&.block {
|
||||
border-color: var(--v-checkbox-color);
|
||||
|
||||
.label {
|
||||
color: var(--v-checkbox-color);
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-color: var(--v-checkbox-color);
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prepend,
|
||||
|
||||
@@ -3,6 +3,7 @@ import InterfaceTextarea from './textarea/';
|
||||
import InterfaceDivider from './divider/';
|
||||
import InterfaceNumeric from './numeric/';
|
||||
import InterfaceSlider from './slider/';
|
||||
import InterfaceToggle from './toggle/';
|
||||
|
||||
export const interfaces = [
|
||||
InterfaceTextInput,
|
||||
@@ -10,6 +11,7 @@ export const interfaces = [
|
||||
InterfaceNumeric,
|
||||
InterfaceSlider,
|
||||
InterfaceDivider,
|
||||
InterfaceToggle,
|
||||
];
|
||||
|
||||
export default interfaces;
|
||||
|
||||
38
src/interfaces/toggle/index.ts
Normal file
38
src/interfaces/toggle/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import InterfaceToggle from './toggle.vue';
|
||||
import { defineInterface } from '@/interfaces/define';
|
||||
|
||||
export default defineInterface(({ i18n }) => ({
|
||||
id: 'toggle',
|
||||
name: i18n.t('toggle'),
|
||||
icon: 'check_box',
|
||||
component: InterfaceToggle,
|
||||
options: [
|
||||
{
|
||||
field: 'iconOff',
|
||||
name: i18n.t('icon_off'),
|
||||
width: 'half',
|
||||
interface: 'icon',
|
||||
default_value: 'check_box_outline_blank',
|
||||
},
|
||||
{
|
||||
field: 'iconOn',
|
||||
name: i18n.t('icon_on'),
|
||||
width: 'half',
|
||||
interface: 'icon',
|
||||
default_value: 'check_box',
|
||||
},
|
||||
{
|
||||
field: 'label',
|
||||
name: i18n.t('label'),
|
||||
width: 'half',
|
||||
interface: 'text-input',
|
||||
},
|
||||
{
|
||||
field: 'color',
|
||||
name: i18n.t('color'),
|
||||
width: 'half',
|
||||
interface: 'color',
|
||||
default_value: 'var(--primary)',
|
||||
},
|
||||
],
|
||||
}));
|
||||
12
src/interfaces/toggle/readme.md
Normal file
12
src/interfaces/toggle/readme.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Toggle
|
||||
|
||||
Simple single styled checkbox
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|------------|----------------------------------------------------|---------------------------|
|
||||
| `color` | What color to use for the on state of the checkbox | `var(--primary)` |
|
||||
| `label` | What label to show next to the checkbox | `null` |
|
||||
| `icon-on` | What icon to show when the checkbox is checked | `check_box` |
|
||||
| `icon-off` | What icon to show when the checkbox is unchecked | `check_box_outline_blank` |
|
||||
48
src/interfaces/toggle/toggle.story.ts
Normal file
48
src/interfaces/toggle/toggle.story.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { withKnobs, text, color } from '@storybook/addon-knobs';
|
||||
import readme from './readme.md';
|
||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||
import { defineComponent, ref } from '@vue/composition-api';
|
||||
import RawValue from '../../../.storybook/raw-value.vue';
|
||||
|
||||
export default {
|
||||
title: 'Interfaces / Toggle',
|
||||
decorators: [withKnobs, withPadding],
|
||||
parameters: {
|
||||
notes: readme,
|
||||
},
|
||||
};
|
||||
|
||||
export const basic = () =>
|
||||
defineComponent({
|
||||
components: { RawValue },
|
||||
props: {
|
||||
iconOn: {
|
||||
default: text('Icon (On)', 'check_box'),
|
||||
},
|
||||
iconOff: {
|
||||
default: text('Icon (Off)', 'check_box_outline_blank'),
|
||||
},
|
||||
label: {
|
||||
default: text('Label', 'This is the label'),
|
||||
},
|
||||
color: {
|
||||
default: color('Color', '#2f80ed'),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const value = ref(false);
|
||||
return { value };
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<interface-toggle
|
||||
v-model="value"
|
||||
:icon-on="iconOn"
|
||||
:icon-off="iconOff"
|
||||
:color="color"
|
||||
:label="label"
|
||||
/>
|
||||
<raw-value>{{ value }}</raw-value>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
20
src/interfaces/toggle/toggle.test.ts
Normal file
20
src/interfaces/toggle/toggle.test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import InterfaceToggle from './toggle.vue';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import VCheckbox from '@/components/v-checkbox';
|
||||
import VIcon from '@/components/v-icon';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(VueCompositionAPI);
|
||||
localVue.component('v-checkbox', VCheckbox);
|
||||
localVue.component('v-icon', VIcon);
|
||||
|
||||
describe('Interfaces / Toggle', () => {
|
||||
it('Renders a v-checkbox', () => {
|
||||
const component = shallowMount(InterfaceToggle, {
|
||||
localVue,
|
||||
});
|
||||
|
||||
expect(component.find(VCheckbox).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
42
src/interfaces/toggle/toggle.vue
Normal file
42
src/interfaces/toggle/toggle.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<v-checkbox
|
||||
block
|
||||
:icon-on="iconOn"
|
||||
:icon-off="iconOff"
|
||||
:label="label"
|
||||
:input-value="value"
|
||||
@change="$listeners.input"
|
||||
:style="{
|
||||
'--v-checkbox-color': color,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from '@vue/composition-api';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: null,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
iconOn: {
|
||||
type: String,
|
||||
default: 'check_box',
|
||||
},
|
||||
iconOff: {
|
||||
type: String,
|
||||
default: 'check_box_outline_blank',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'var(--primary)',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -280,6 +280,11 @@
|
||||
"bold": "Bold",
|
||||
"subdued": "Subdued",
|
||||
|
||||
"toggle": "Toggle",
|
||||
"icon_on": "Icon (On)",
|
||||
"icon_off": "Icon (Off)",
|
||||
"label": "Label",
|
||||
|
||||
"about_directus": "About Directus",
|
||||
"activity_log": "Activity Log",
|
||||
"add_field_filter": "Add a field filter",
|
||||
|
||||
Reference in New Issue
Block a user