Skip to content

Commit

Permalink
feat: add switch component
Browse files Browse the repository at this point in the history
  • Loading branch information
itupix committed Jun 18, 2024
1 parent 5a8273f commit f16d53e
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/core/src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -69,4 +71,5 @@
@include tooltip.Tooltip();
@include validation.Validation();
@include accordion.Accordion();
@include switch.Switch();
}
1 change: 1 addition & 0 deletions packages/core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
137 changes: 137 additions & 0 deletions packages/core/src/components/switch/switch.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
@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;
}

// Hover
input + .ods-switch-indicator:hover {
border-color: #636670;
}

input + .ods-switch-indicator:hover::before {
background-color: #636670;
}

// Checked
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 {
background-color: helpers.color('background-input');
transform: translateY(-50%) translateX(helpers.space(2));
}

input:checked + .ods-switch-indicator::after {
opacity: 1;
transform: translateY(-50%) translateX(helpers.space(1)) rotateZ(45deg)
scale(0.5);
}

input:checked + .ods-switch-indicator:hover {
background-color: #5666f9;
border-color: #5666f9;
}

input:checked + .ods-switch-indicator:hover::after {
border-color: #5666f9;
}

// Focus
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');
}

// Disabled
input:disabled + .ods-switch-indicator {
cursor: not-allowed;
}

input:disabled + .ods-switch-indicator,
input:disabled + .ods-switch-indicator:hover,
input:disabled + .ods-switch-indicator::after,
input:disabled + .ods-switch-indicator:hover::after {
border-color: helpers.color('border-disabled');
}

input:disabled:checked + .ods-switch-indicator,
input:disabled + .ods-switch-indicator::before {
background-color: helpers.color('background-disabled');
}

input:disabled:checked + .ods-switch-indicator::before {
background-color: #fff;
}
}

.ods-switch-indicator {
$border-width: helpers.space(0.25);

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: #828893;
border-radius: helpers.border-radius('full');
content: '';
display: inline-block;
height: 18px;
left: 1px;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: background-color linear 0.2s,
transform calc(var(--ods-transition-duration) * 2) ease;
width: 18px;
}

// 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: 50%;
transform: translateY(-50%) translateX(-100%) rotateZ(-45deg) scale(0);
transition: calc(var(--ods-transition-duration) * 2) ease;
transition-property: opacity, transform;
}
}
}
5 changes: 5 additions & 0 deletions packages/core/src/components/switch/switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface SwitchProps {
disabled?: boolean;
onChange?: (checked: boolean) => void;
className?: string;
}
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
12 changes: 12 additions & 0 deletions packages/react/src/components/switch/switch.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<SwitchProps>;

export const Playground: Story<SwitchProps> = {};
36 changes: 36 additions & 0 deletions packages/react/src/components/switch/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { c, classy, m } from '@onfido/castor';
import React from 'react';

export interface SwitchProps extends Omit<JsxInput, 'type' | 'onChange'> {
checked?: boolean;
disabled?: boolean;
onChange?: (checked: boolean) => void;
}

export const Switch = ({
id = `switch-${++idCount}`,
disabled,
className,
children,
onChange,
...props
}: SwitchProps) => (
<label
htmlFor={id}
className={classy(className, c('switch-label'), m({ disabled }))}
>
{children && <span>{children}</span>}
<input
{...props}
type="checkbox"
id={id}
disabled={disabled}
onChange={(ev) => onChange?.(ev.currentTarget.checked)}
/>
<span className={classy(c('switch-indicator'), m({ disabled }))}></span>
</label>
);

let idCount = 0;

type JsxInput = JSX.IntrinsicElements['input'];

0 comments on commit f16d53e

Please sign in to comment.