diff --git a/packages/core/src/components/index.scss b/packages/core/src/components/index.scss index fc1dd84aa..93892b2e6 100644 --- a/packages/core/src/components/index.scss +++ b/packages/core/src/components/index.scss @@ -44,6 +44,8 @@ @forward './validation/validation'; @use './accordion/accordion.scss'; @forward './accordion/accordion.scss'; +@use './switch/switch.scss'; +@forward './switch/switch.scss'; @mixin components() { @include asterisk.Asterisk(); @@ -69,4 +71,5 @@ @include tooltip.Tooltip(); @include validation.Validation(); @include accordion.Accordion(); + @include switch.Switch(); } diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index cf9e912c1..faa986538 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -12,6 +12,7 @@ export * from './progress/progress'; export * from './radio/radio'; export * from './select/select'; export * from './spinner/spinner'; +export * from './switch/switch'; export * from './textarea/textarea'; export * from './tooltip/tooltip'; export * from './validation/validation'; diff --git a/packages/core/src/components/switch/switch.scss b/packages/core/src/components/switch/switch.scss new file mode 100644 index 000000000..f9a9a989c --- /dev/null +++ b/packages/core/src/components/switch/switch.scss @@ -0,0 +1,121 @@ +@use '../../animations'; +@use '../../helpers'; +@use '../../mixins'; + +@mixin Switch() { + @if not mixins.includes('Switch') { + @include _Switch(); + } +} + +@mixin _Switch() { + .ods-switch-label { + align-items: center; + align-items: flex-start; + color: helpers.color('content-main'); + cursor: pointer; + display: inline-grid; + gap: helpers.space(1); + grid-template-columns: 1fr auto; + position: relative; + + input { + opacity: 0; + position: absolute; + } + + &.-disabled { + cursor: not-allowed; + } + + &.-disabled .ods-switch-indicator, + &.-disabled .ods-switch-indicator::before, + &.-disabled .ods-switch-indicator::after { + border-color: helpers.color('border-disabled'); + } + + &.-disabled input:checked + .ods-switch-indicator { + background-color: helpers.color('background-disabled'); + } + + input:checked + .ods-switch-indicator { + background-color: helpers.color('background-input-selected'); + border-color: helpers.color('border-input-selected'); + } + + input:checked + .ods-switch-indicator::before { + border-color: helpers.color('border-input-selected'); + transform: translateX(70%); + } + + input:checked + .ods-switch-indicator::after { + opacity: 1; + transform: translateX(70%) rotateZ(45deg) scale(0.5); + } + + input:focus + .ods-switch-indicator { + box-shadow: // + 0 0 0 helpers.space(0.25) helpers.color('border-focus-inner'), + 0 0 0 helpers.space(0.5) helpers.color('border-action-focus'); + } + + input:not(:disabled):hover + .ods-switch-indicator::before { + border-color: helpers.color('border-input-hover'); + } + + input:checked:not(:disabled) + .ods-switch-indicator:hover { + background-color: helpers.color('primary-600'); + border-color: helpers.color('primary-600'); + } + + input:checked:not(:disabled) + .ods-switch-indicator:hover::before, + input:checked:not(:disabled) + .ods-switch-indicator:hover::after { + border-color: helpers.color('primary-600'); + } + } + + .ods-switch-indicator { + $border-width: 2px; + + background-color: helpers.color('background-input'); + border: $border-width solid helpers.color('border-input'); + border-radius: helpers.border-radius('full'); + box-sizing: border-box; + height: helpers.space(3); + margin: helpers.space(0.5); + position: relative; + transition: var(--ods-transition-duration) ease; + transition-property: background-color, border-color; + width: helpers.space(5); + + // slider + &::before { + background-color: helpers.color('background-input'); + border: $border-width solid helpers.color('border-input'); + border-radius: helpers.border-radius('full'); + content: ''; + display: inline-block; + left: -$border-width; + padding: helpers.space(1.25); + position: absolute; + top: -$border-width; + transition: transform calc(var(--ods-transition-duration) * 2) ease; + } + + // check icon + &::after { + border: solid helpers.color('border-input-selected'); + border-width: 0 helpers.space(0.5) helpers.space(0.5) 0; + content: ''; + display: inline-block; + left: helpers.space(1.625); + opacity: 0; + padding: helpers.space(1) helpers.space(0.5); + position: relative; + top: helpers.space(-0.125); + transform: translateX(-100%) rotateZ(-45deg) scale(0); + transition: calc(var(--ods-transition-duration) * 2) ease; + transition-property: opacity, transform; + } + } +} diff --git a/packages/core/src/components/switch/switch.ts b/packages/core/src/components/switch/switch.ts new file mode 100644 index 000000000..1eadf58c7 --- /dev/null +++ b/packages/core/src/components/switch/switch.ts @@ -0,0 +1,5 @@ +export interface SwitchProps { + disabled?: boolean; + onChange?: (checked: boolean) => void; + className?: string; +} diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index b19eb3447..7c2efbe88 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -20,6 +20,7 @@ export * from './select/option'; export * from './select/option-group'; export * from './select/select'; export * from './spinner/spinner'; +export * from './switch/switch'; export * from './textarea/textarea'; export * from './tooltip/tooltip'; export * from './validation/validation'; diff --git a/packages/react/src/components/switch/switch.stories.tsx b/packages/react/src/components/switch/switch.stories.tsx new file mode 100644 index 000000000..79ab9a922 --- /dev/null +++ b/packages/react/src/components/switch/switch.stories.tsx @@ -0,0 +1,12 @@ +import { Switch, SwitchProps } from '@onfido/castor-react'; +import { Meta, Story } from '../../../../../docs'; + +export default { + title: 'React/Switch', + component: Switch, + argTypes: {}, + args: {}, + parameters: { display: 'flex' }, +} as Meta; + +export const Playground: Story = {}; diff --git a/packages/react/src/components/switch/switch.tsx b/packages/react/src/components/switch/switch.tsx new file mode 100644 index 000000000..25a780743 --- /dev/null +++ b/packages/react/src/components/switch/switch.tsx @@ -0,0 +1,36 @@ +import { c, classy, m } from '@onfido/castor'; +import React from 'react'; + +export interface SwitchProps extends Omit { + checked?: boolean; + disabled?: boolean; + onChange?: (checked: boolean) => void; +} + +export const Switch = ({ + id = `switch-${++idCount}`, + disabled, + className, + children, + onChange, + ...props +}: SwitchProps) => ( + +); + +let idCount = 0; + +type JsxInput = JSX.IntrinsicElements['input'];