diff --git a/docs/spec.md b/docs/spec.md index c3e602fe..abd45f27 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -157,10 +157,10 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec; #### OneOfParams -| Property | Type | Required | Description | -| :--------- | :----------------------------------------- | :------: | :--------------------------------------------- | -| toggler | `'select'` `'radio'` `'card'` `'checkbox'` | | Switch type | -| booleanMap | `Record<'true' 'false', string>` | | Special object for oneof toggler type checkbox | +| Property | Type | Required | Description | +| :--------- | :---------------------------------------------------- | :------: | :--------------------------------------------- | +| toggler | `'select'` `'radio'` `'card'` `'checkbox'` `'switch'` | | Switch type | +| booleanMap | `Record<'true' 'false', string>` | | Special object for oneof toggler type checkbox | #### FileInput diff --git a/src/lib/core/types/specs.ts b/src/lib/core/types/specs.ts index 89afd28e..ffa42d47 100644 --- a/src/lib/core/types/specs.ts +++ b/src/lib/core/types/specs.ts @@ -123,7 +123,7 @@ export interface ObjectSpec< order?: string[]; link?: LinkType; oneOfParams?: { - toggler?: 'select' | 'radio' | 'card' | 'checkbox'; + toggler?: 'select' | 'radio' | 'card' | 'checkbox' | 'switch'; booleanMap?: Record<'true' | 'false', string>; }; placeholder?: string; diff --git a/src/lib/kit/components/Views/OneOfView/OneOfView.tsx b/src/lib/kit/components/Views/OneOfView/OneOfView.tsx index 23ffb83f..797b2f44 100644 --- a/src/lib/kit/components/Views/OneOfView/OneOfView.tsx +++ b/src/lib/kit/components/Views/OneOfView/OneOfView.tsx @@ -31,7 +31,11 @@ const OneOfViewComponent: React.FC = (props) => { const valueKey = React.useMemo(() => Object.keys(value)[0], [value]); const valueName = React.useMemo(() => { - if (spec.viewSpec.oneOfParams?.toggler === 'checkbox' && specBooleanMap) { + if ( + (spec.viewSpec.oneOfParams?.toggler === 'checkbox' || + spec.viewSpec.oneOfParams?.toggler === 'switch') && + specBooleanMap + ) { return objectKeys(specBooleanMap).find((key) => specBooleanMap[key] === valueKey); } diff --git a/src/lib/kit/hooks/useOneOf/useOneOf.scss b/src/lib/kit/hooks/useOneOf/useOneOf.scss index a741c739..566191ec 100644 --- a/src/lib/kit/hooks/useOneOf/useOneOf.scss +++ b/src/lib/kit/hooks/useOneOf/useOneOf.scss @@ -36,4 +36,10 @@ display: flex; align-items: center; } + + &__switch { + height: var(--df-use-oneof-switch-height, $df-switch-height); + display: flex; + align-items: center; + } } diff --git a/src/lib/kit/hooks/useOneOf/useOneOf.tsx b/src/lib/kit/hooks/useOneOf/useOneOf.tsx index b79a845c..68a96c93 100644 --- a/src/lib/kit/hooks/useOneOf/useOneOf.tsx +++ b/src/lib/kit/hooks/useOneOf/useOneOf.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {Checkbox, SegmentedRadioGroup, Select} from '@gravity-ui/uikit'; +import {Checkbox, SegmentedRadioGroup, Select, Switch} from '@gravity-ui/uikit'; import isObjectLike from 'lodash/isObjectLike'; import some from 'lodash/some'; @@ -61,7 +61,7 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => { [setOneOfValue, input.onChange, oneOfValue], ); - const onCheckboxChange = React.useCallback( + const onCheckedChange = React.useCallback( (checked: boolean) => { if (specBooleanMap) { const value = String(checked) as 'true' | 'false'; @@ -73,7 +73,7 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => { [onOneOfChange, specBooleanMap], ); - const checkboxValue = React.useMemo(() => { + const checkedValue = React.useMemo(() => { if (specBooleanMap) { const keyBooleanMap = objectKeys(specBooleanMap).find( (key) => specBooleanMap[key] === oneOfValue, @@ -123,11 +123,12 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => { } if ( - spec.viewSpec.oneOfParams?.toggler === 'checkbox' && + (spec.viewSpec.oneOfParams?.toggler === 'checkbox' || + spec.viewSpec.oneOfParams?.toggler === 'switch') && options.length === 2 && specBooleanMap ) { - return 'checkbox'; + return spec.viewSpec.oneOfParams.toggler; } return 'radio'; @@ -176,8 +177,21 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => { return (
+
+ ); + } + + if (togglerType === 'switch') { + return ( +
+ @@ -208,8 +222,8 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => { options, onOneOfChange, specProperties, - onCheckboxChange, - checkboxValue, + onCheckedChange, + checkedValue, ]); const toggler = React.useMemo(() => { diff --git a/src/lib/kit/styles/variables.scss b/src/lib/kit/styles/variables.scss index 0bcb5bad..18a8a692 100644 --- a/src/lib/kit/styles/variables.scss +++ b/src/lib/kit/styles/variables.scss @@ -42,6 +42,9 @@ $df-popover-item-margin-bottom: 6px; // Checbox height $df-checkbox-height: 28px; +// Switch height +$df-switch-height: 28px; + // Fonts $defaultFontFamily: var(--g-text-body-font-family); $titleFontFamily: 'YS Display', helveticaneue, arial, helvetica, sans-serif; // stylelint-disable-line diff --git a/src/stories/ObjectOneOf.stories.tsx b/src/stories/ObjectOneOf.stories.tsx index 2c72ad21..7c74f7b4 100644 --- a/src/stories/ObjectOneOf.stories.tsx +++ b/src/stories/ObjectOneOf.stories.tsx @@ -61,25 +61,7 @@ const baseSpec: ObjectSpec = { }, }; -const excludeOptions = [ - 'viewSpec.type', - 'viewSpec.placeholder', - 'viewSpec.delimiter', - 'viewSpec.inputProps', - 'viewSpec.layoutProps', -]; - -const template = (spec: ObjectSpec = baseSpec) => { - const Template: StoryFn = (__, {viewMode}) => ( - - ); - - return Template; -}; - -export const OneOf = template(); - -export const OneOfCheckbox = template({ +const baseToggleSpec: ObjectSpec = { ...baseSpec, properties: { internal: { @@ -121,4 +103,41 @@ export const OneOfCheckbox = template({ }, }, }, +}; + +const excludeOptions = [ + 'viewSpec.type', + 'viewSpec.placeholder', + 'viewSpec.delimiter', + 'viewSpec.inputProps', + 'viewSpec.layoutProps', +]; + +const template = (spec: ObjectSpec = baseSpec) => { + const Template: StoryFn = (__, {viewMode}) => ( + + ); + + return Template; +}; + +export const OneOf = template(); + +export const OneOfCheckbox = template({ + ...baseToggleSpec, +}); + +export const OneOfSwitch = template({ + ...baseToggleSpec, + viewSpec: { + ...baseToggleSpec.viewSpec, + order: ['external', 'internal'], + oneOfParams: { + toggler: 'switch', + booleanMap: { + true: 'external', + false: 'internal', + }, + }, + }, }); diff --git a/src/stories/ObjectOneOfFlat.stories.tsx b/src/stories/ObjectOneOfFlat.stories.tsx index ff7de4e2..b8a0d338 100644 --- a/src/stories/ObjectOneOfFlat.stories.tsx +++ b/src/stories/ObjectOneOfFlat.stories.tsx @@ -61,25 +61,7 @@ const baseSpec: ObjectSpec = { }, }; -const excludeOptions = [ - 'viewSpec.type', - 'viewSpec.placeholder', - 'viewSpec.delimiter', - 'viewSpec.inputProps', - 'viewSpec.layoutProps', -]; - -const template = (spec: ObjectSpec = baseSpec) => { - const Template: StoryFn = (__, {viewMode}) => ( - - ); - - return Template; -}; - -export const OneOfFlat = template(); - -export const OneOfFlatCheckbox = template({ +const baseToggleSpec: ObjectSpec = { ...baseSpec, properties: { empty: { @@ -121,4 +103,41 @@ export const OneOfFlatCheckbox = template({ }, }, }, +}; + +const excludeOptions = [ + 'viewSpec.type', + 'viewSpec.placeholder', + 'viewSpec.delimiter', + 'viewSpec.inputProps', + 'viewSpec.layoutProps', +]; + +const template = (spec: ObjectSpec = baseSpec) => { + const Template: StoryFn = (__, {viewMode}) => ( + + ); + + return Template; +}; + +export const OneOfFlat = template(); + +export const OneOfFlatCheckbox = template({ + ...baseToggleSpec, +}); + +export const OneOfFlatSwitch = template({ + ...baseToggleSpec, + viewSpec: { + ...baseToggleSpec.viewSpec, + order: ['empty', 'external'], + oneOfParams: { + toggler: 'switch', + booleanMap: { + true: 'external', + false: 'empty', + }, + }, + }, }); diff --git a/src/stories/components/InputPreview/constants.ts b/src/stories/components/InputPreview/constants.ts index 7903e35f..357d906b 100644 --- a/src/stories/components/InputPreview/constants.ts +++ b/src/stories/components/InputPreview/constants.ts @@ -528,7 +528,7 @@ const oneOfParams: ObjectSpec = { properties: { toggler: { type: SpecTypes.String, - enum: ['―', 'radio', 'select', 'card', 'checkbox'], + enum: ['―', 'radio', 'select', 'card', 'checkbox', 'switch'], viewSpec: {type: 'select', layout: 'row', layoutTitle: 'Switch type'}, }, booleanMap: {