mirror of
https://github.com/directus/directus.git
synced 2026-01-31 02:37:54 -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
|
## Props
|
||||||
| Prop | Description | Default |
|
| Prop | Description | Default |
|
||||||
|-----------------|--------------------------------------------------------------------------|---------|
|
|----------------------|--------------------------------------------------------------------------|---------------------------|
|
||||||
| `value` | Value for checkbox. Similar to value attr on checkbox type input in HTML | `--` |
|
| `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` |
|
| `inputValue` | Value that's used with `v-model`. Either boolean or array of values | `false` |
|
||||||
| `label` | Label for the checkbox | `--` |
|
| `label` | Label for the checkbox | `--` |
|
||||||
| `disabled` | Disable the checkbox | `false` |
|
| `disabled` | Disable the checkbox | `false` |
|
||||||
| `indeterminate` | Show the indeterminate state | `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
|
## Slots
|
||||||
| Slot | Description |
|
| 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 { action } from '@storybook/addon-actions';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VCheckbox from '../v-checkbox';
|
import VCheckbox from '../v-checkbox';
|
||||||
import markdown from './readme.md';
|
import markdown from './readme.md';
|
||||||
import withPadding from '../../../.storybook/decorators/with-padding';
|
import withPadding from '../../../.storybook/decorators/with-padding';
|
||||||
|
import { defineComponent, ref } from '@vue/composition-api';
|
||||||
|
|
||||||
Vue.component('v-checkbox', VCheckbox);
|
Vue.component('v-checkbox', VCheckbox);
|
||||||
|
|
||||||
@@ -87,9 +88,9 @@ export const colors = () => ({
|
|||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div>
|
<div>
|
||||||
<v-checkbox v-model="options" value="red" @change="onChange" :style="{'--v-checkbox-color': 'var(--red)'}" label="Red" />
|
<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(--blue)'}" label="Blue" />
|
<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(--amber)'}" label="Yellow" />
|
<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..." />
|
<v-checkbox v-model="options" value="custom" @change="onChange" :style="{'--v-checkbox-color': customColor}" label="Custom..." />
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
@@ -136,3 +137,53 @@ export const slots = () => ({
|
|||||||
</div>
|
</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"
|
role="checkbox"
|
||||||
:aria-pressed="isChecked ? 'true' : 'false'"
|
:aria-pressed="isChecked ? 'true' : 'false'"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:class="{ checked: isChecked, indeterminate }"
|
:class="{ checked: isChecked, indeterminate, block }"
|
||||||
>
|
>
|
||||||
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
<div class="prepend" v-if="$scopedSlots.prepend"><slot name="prepend" /></div>
|
||||||
<v-icon class="checkbox" :name="icon" />
|
<v-icon class="checkbox" :name="icon" />
|
||||||
@@ -46,6 +46,22 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
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 }) {
|
setup(props, { emit }) {
|
||||||
const isChecked = computed<boolean>(() => {
|
const isChecked = computed<boolean>(() => {
|
||||||
@@ -57,8 +73,8 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const icon = computed<string>(() => {
|
const icon = computed<string>(() => {
|
||||||
if (props.indeterminate === true) return 'indeterminate_check_box';
|
if (props.indeterminate === true) return props.iconIndeterminate;
|
||||||
return isChecked.value ? 'check_box' : 'check_box_outline_blank';
|
return isChecked.value ? props.iconOn : props.iconOff;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { isChecked, toggleInput, icon };
|
return { isChecked, toggleInput, icon };
|
||||||
@@ -90,8 +106,9 @@ export default defineComponent({
|
|||||||
@import '@/styles/mixins/no-wrap';
|
@import '@/styles/mixins/no-wrap';
|
||||||
|
|
||||||
.v-checkbox {
|
.v-checkbox {
|
||||||
--v-checkbox-color: var(--foreground-normal);
|
--v-checkbox-color: var(--primary);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 0;
|
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 {
|
&:not(:disabled):not(.indeterminate).checked {
|
||||||
.checkbox {
|
.checkbox {
|
||||||
--v-icon-color: var(--v-checkbox-color);
|
--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,
|
.prepend,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import InterfaceTextarea from './textarea/';
|
|||||||
import InterfaceDivider from './divider/';
|
import InterfaceDivider from './divider/';
|
||||||
import InterfaceNumeric from './numeric/';
|
import InterfaceNumeric from './numeric/';
|
||||||
import InterfaceSlider from './slider/';
|
import InterfaceSlider from './slider/';
|
||||||
|
import InterfaceToggle from './toggle/';
|
||||||
|
|
||||||
export const interfaces = [
|
export const interfaces = [
|
||||||
InterfaceTextInput,
|
InterfaceTextInput,
|
||||||
@@ -10,6 +11,7 @@ export const interfaces = [
|
|||||||
InterfaceNumeric,
|
InterfaceNumeric,
|
||||||
InterfaceSlider,
|
InterfaceSlider,
|
||||||
InterfaceDivider,
|
InterfaceDivider,
|
||||||
|
InterfaceToggle,
|
||||||
];
|
];
|
||||||
|
|
||||||
export default interfaces;
|
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",
|
"bold": "Bold",
|
||||||
"subdued": "Subdued",
|
"subdued": "Subdued",
|
||||||
|
|
||||||
|
"toggle": "Toggle",
|
||||||
|
"icon_on": "Icon (On)",
|
||||||
|
"icon_off": "Icon (Off)",
|
||||||
|
"label": "Label",
|
||||||
|
|
||||||
"about_directus": "About Directus",
|
"about_directus": "About Directus",
|
||||||
"activity_log": "Activity Log",
|
"activity_log": "Activity Log",
|
||||||
"add_field_filter": "Add a field filter",
|
"add_field_filter": "Add a field filter",
|
||||||
|
|||||||
Reference in New Issue
Block a user