From fc94de00678c62ffaf6d0f490e8a070cfbead181 Mon Sep 17 00:00:00 2001 From: Rijk van Zanten Date: Thu, 23 Apr 2020 09:12:12 -0400 Subject: [PATCH] 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 --- src/components/v-checkbox/readme.md | 18 +++--- src/components/v-checkbox/v-checkbox.story.ts | 59 ++++++++++++++++-- src/components/v-checkbox/v-checkbox.vue | 60 +++++++++++++++++-- src/interfaces/index.ts | 2 + src/interfaces/toggle/index.ts | 38 ++++++++++++ src/interfaces/toggle/readme.md | 12 ++++ src/interfaces/toggle/toggle.story.ts | 48 +++++++++++++++ src/interfaces/toggle/toggle.test.ts | 20 +++++++ src/interfaces/toggle/toggle.vue | 42 +++++++++++++ src/lang/en-US/index.json | 5 ++ 10 files changed, 289 insertions(+), 15 deletions(-) create mode 100644 src/interfaces/toggle/index.ts create mode 100644 src/interfaces/toggle/readme.md create mode 100644 src/interfaces/toggle/toggle.story.ts create mode 100644 src/interfaces/toggle/toggle.test.ts create mode 100644 src/interfaces/toggle/toggle.vue diff --git a/src/components/v-checkbox/readme.md b/src/components/v-checkbox/readme.md index e0bf88df08..d745ae8c57 100644 --- a/src/components/v-checkbox/readme.md +++ b/src/components/v-checkbox/readme.md @@ -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 | diff --git a/src/components/v-checkbox/v-checkbox.story.ts b/src/components/v-checkbox/v-checkbox.story.ts index 3378b7b29b..dacb7a833a 100644 --- a/src/components/v-checkbox/v-checkbox.story.ts +++ b/src/components/v-checkbox/v-checkbox.story.ts @@ -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: `
- - - + + +
`, @@ -136,3 +137,53 @@ export const slots = () => ({ `, }); + +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: ` +
+ + +
+ `, + }); + +export const blockStyle = () => + defineComponent({ + setup() { + const checked = ref(false); + const indeterminate = ref(false); + return { checked, indeterminate }; + }, + template: ` + + `, + }); diff --git a/src/components/v-checkbox/v-checkbox.vue b/src/components/v-checkbox/v-checkbox.vue index 66560dd1d9..bea6cd1e96 100644 --- a/src/components/v-checkbox/v-checkbox.vue +++ b/src/components/v-checkbox/v-checkbox.vue @@ -6,7 +6,7 @@ role="checkbox" :aria-pressed="isChecked ? 'true' : 'false'" :disabled="disabled" - :class="{ checked: isChecked, indeterminate }" + :class="{ checked: isChecked, indeterminate, block }" >
@@ -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(() => { @@ -57,8 +73,8 @@ export default defineComponent({ }); const icon = computed(() => { - 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, diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index e28af214da..ebed39a18e 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -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; diff --git a/src/interfaces/toggle/index.ts b/src/interfaces/toggle/index.ts new file mode 100644 index 0000000000..4a881946bc --- /dev/null +++ b/src/interfaces/toggle/index.ts @@ -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)', + }, + ], +})); diff --git a/src/interfaces/toggle/readme.md b/src/interfaces/toggle/readme.md new file mode 100644 index 0000000000..fa6fb5bd1a --- /dev/null +++ b/src/interfaces/toggle/readme.md @@ -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` | diff --git a/src/interfaces/toggle/toggle.story.ts b/src/interfaces/toggle/toggle.story.ts new file mode 100644 index 0000000000..8f8c867414 --- /dev/null +++ b/src/interfaces/toggle/toggle.story.ts @@ -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: ` +
+ + {{ value }} +
+ `, + }); diff --git a/src/interfaces/toggle/toggle.test.ts b/src/interfaces/toggle/toggle.test.ts new file mode 100644 index 0000000000..db2f3d8184 --- /dev/null +++ b/src/interfaces/toggle/toggle.test.ts @@ -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); + }); +}); diff --git a/src/interfaces/toggle/toggle.vue b/src/interfaces/toggle/toggle.vue new file mode 100644 index 0000000000..b29f9e391e --- /dev/null +++ b/src/interfaces/toggle/toggle.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json index 687f5e71f0..3b396ffe34 100644 --- a/src/lang/en-US/index.json +++ b/src/lang/en-US/index.json @@ -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",