-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from thep0y/main
0.1.2
- Loading branch information
Showing
6 changed files
with
320 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
.fluent-switch { | ||
align-items: flex-start; | ||
box-sizing: border-box; | ||
display: inline-flex; | ||
position: relative; | ||
|
||
&__input { | ||
top: 0; | ||
left: 0; | ||
box-sizing: border-box; | ||
cursor: pointer; | ||
height: 100%; | ||
margin: 0px; | ||
opacity: 0; | ||
position: absolute; | ||
width: calc(40px + 2 * var(--spacingHorizontalS)); | ||
|
||
&:checked ~ .fluent-switch__indicator > * { | ||
transform: translateX(20px); | ||
} | ||
|
||
&:enabled:checked ~ .fluent-switch__indicator { | ||
background-color: var(--colorCompoundBrandBackground); | ||
color: var(--colorNeutralForegroundInverted); | ||
border-color: var(--colorTransparentStroke); | ||
} | ||
|
||
&:enabled:checked:hover:active ~ .fluent-switch__indicator { | ||
background-color: var(--colorCompoundBrandBackgroundPressed); | ||
border-color: var(--colorTransparentStrokeInteractive); | ||
} | ||
|
||
&:enabled:checked:hover ~ .fluent-switch__indicator { | ||
background-color: var(--colorCompoundBrandBackgroundPressed); | ||
border-color: var(--colorTransparentStrokeInteractive); | ||
} | ||
|
||
&:enabled:checked ~ .fluent-switch__indicator { | ||
background-color: var(--colorCompoundBrandBackgroundPressed); | ||
border-color: var(--colorTransparentStrokeInteractive); | ||
} | ||
|
||
&:disabled { | ||
cursor: default; | ||
} | ||
|
||
&:disabled:not(:checked) ~ .fluent-switch__indicator { | ||
border-color: var(--colorNeutralStrokeDisabled); | ||
} | ||
|
||
&:disabled:checked ~ .fluent-switch__indicator { | ||
background-color: var(--colorNeutralBackgroundDisabled); | ||
border-color: var(--colorTransparentStrokeDisabled); | ||
} | ||
|
||
&:disabled ~ .fluent-switch__indicator { | ||
color: var(--colorNeutralForegroundDisabled); | ||
} | ||
|
||
&:disabled ~ .fluent-switch__label { | ||
cursor: default; | ||
color: var(--colorNeutralForegroundDisabled); | ||
} | ||
} | ||
|
||
&__label { | ||
margin-top: calc((20px - var(--lineHeightBase300)) / 2); | ||
margin-bottom: calc((20px - var(--lineHeightBase300)) / 2); | ||
cursor: pointer; | ||
padding-left: var(--spacingHorizontalXS); | ||
padding: var(--spacingVerticalS) var(--spacingHorizontalS); | ||
} | ||
|
||
&__indicator { | ||
color: var(--colorNeutralStrokeAccessible); | ||
border-color: var(--colorNeutralStrokeAccessible); | ||
border-radius: var(--borderRadiusCircular); | ||
border: 1px solid; | ||
line-height: 0; | ||
box-sizing: border-box; | ||
fill: currentcolor; | ||
flex-shrink: 0; | ||
font-size: 18px; | ||
height: 20px; | ||
margin: var(--spacingVerticalS) var(--spacingHorizontalS); | ||
pointer-events: none; | ||
transition-duration: var(--durationNormal); | ||
transition-timing-function: var(--curveEasyEase); | ||
transition-property: background, border, color; | ||
width: 40px; | ||
|
||
& > svg { | ||
transition-duration: var(--durationNormal); | ||
transition-timing-function: var(--curveEasyEase); | ||
transition-property: transform; | ||
} | ||
|
||
& > svg { | ||
display: inline; | ||
line-height: 0; | ||
} | ||
} | ||
|
||
&-label-above { | ||
flex-direction: column; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { addClassList } from "~/utils"; | ||
import "./index.scss"; | ||
import type { | ||
BaseNoChildrenComponentProps, | ||
HTMLDivElementProps, | ||
HTMLInputElementProps, | ||
} from "~/interface"; | ||
import { children, createSignal, lazy, mergeProps } from "solid-js"; | ||
import { BiSolidCircle } from "solid-icons/bi"; | ||
import type { JSX } from "solid-js"; | ||
|
||
const LazyLabel = lazy(() => import("~/components/label")); | ||
|
||
interface SwitchProps extends BaseNoChildrenComponentProps<HTMLDivElement> { | ||
/** | ||
* Defines the controlled checked state of the Switch. | ||
* If passed, Switch ignores the `defaultChecked` property. | ||
* This should only be used if the checked state is to be controlled at a higher level and there is a plan to pass the | ||
* correct value based on handling `onChange` events and re-rendering. | ||
* | ||
* @default false | ||
*/ | ||
checked?: boolean; | ||
|
||
/** | ||
* Defines whether the Switch is initially in a checked state or not when rendered. | ||
* | ||
* @default false | ||
*/ | ||
defaultChecked?: boolean; | ||
|
||
/** | ||
* The position of the label relative to the Switch. | ||
* | ||
* @default after | ||
*/ | ||
labelPosition?: "above" | "after" | "before"; | ||
|
||
/** | ||
* Callback to be called when the checked state value changes. | ||
*/ | ||
onChange?: (checked: boolean) => void; | ||
|
||
/** | ||
* The track and the thumb sliding over it indicating the on and off status of the Switch. | ||
*/ | ||
indicator?: JSX.Element; | ||
|
||
/** | ||
* Hidden input that handles the Switch's functionality. | ||
* | ||
* This is the PRIMARY slot: all native properties specified directly on the `<Switch>` tag will be applied to this | ||
* slot, except `className` and `style`, which remain on the root slot. | ||
*/ | ||
//input: HTMLInputElement; | ||
|
||
/** | ||
* The Switch's label. | ||
*/ | ||
label?: string; | ||
|
||
disabled?: boolean; | ||
required?: boolean; | ||
} | ||
|
||
const baseClassName = "fluent-switch"; | ||
|
||
const Switch = (props: SwitchProps) => { | ||
const merged = mergeProps( | ||
{ indicator: <BiSolidCircle />, labelPosition: "after" }, | ||
props, | ||
); | ||
|
||
const [isChecked, setIsChecked] = createSignal( | ||
merged.checked ?? merged.defaultChecked ?? false, | ||
); | ||
|
||
const handleChange: HTMLInputElementProps["onChange"] = (event) => { | ||
const checked = event.currentTarget.checked; | ||
|
||
if (merged.checked === undefined) { | ||
setIsChecked(checked); | ||
} | ||
|
||
merged.onChange?.(checked); | ||
}; | ||
|
||
const classList = () => | ||
addClassList({ | ||
base: baseClassName, | ||
others: { | ||
[`${baseClassName}-label-${merged.labelPosition}`]: | ||
merged.label && merged.labelPosition, | ||
[`${baseClassName}-disabled`]: merged.disabled, | ||
[`${merged.class}`]: merged.class, | ||
}, | ||
}); | ||
|
||
const onClick: HTMLDivElementProps["onClick"] = (event) => { | ||
if (merged.disabled) return; | ||
|
||
const target = event.target; | ||
if (target.className === `${baseClassName}__input`) { | ||
return; | ||
} | ||
const input: HTMLInputElement = event.currentTarget.querySelector( | ||
`.${baseClassName}__input`, | ||
)!; | ||
input.click(); | ||
}; | ||
|
||
const renderLabel = children(() => { | ||
if (!merged.label) return null; | ||
return ( | ||
<LazyLabel class={`${baseClassName}__label`} required={merged.required}> | ||
{merged.label} | ||
</LazyLabel> | ||
); | ||
}); | ||
|
||
return ( | ||
<div classList={classList()} onClick={onClick}> | ||
<input | ||
class={`${baseClassName}__input`} | ||
role="switch" | ||
type="checkbox" | ||
checked={merged.checked ?? isChecked()} | ||
aria-checked={merged.checked ?? isChecked()} | ||
onChange={handleChange} | ||
disabled={merged.disabled} | ||
/> | ||
|
||
{(merged.labelPosition === "before" || | ||
merged.labelPosition === "above") && | ||
renderLabel()} | ||
|
||
<div class={`${baseClassName}__indicator`} aria-hidden={true}> | ||
{merged.indicator} | ||
</div> | ||
|
||
{merged.labelPosition === "after" && renderLabel()} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Switch; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { createSignal } from "solid-js"; | ||
import Switch from "~/components/switch"; | ||
|
||
const Checked = () => { | ||
const [checked, setChecked] = createSignal(true); | ||
const onChange = (ev: boolean) => { | ||
setChecked(ev); | ||
console.log(ev); | ||
}; | ||
|
||
return ( | ||
<Switch | ||
checked={checked()} | ||
onChange={onChange} | ||
label={checked() ? "Checked" : "Unchecked"} | ||
/> | ||
); | ||
}; | ||
|
||
const SwitchDemo = () => { | ||
return ( | ||
<div> | ||
<div> | ||
<Switch /> | ||
<Switch label="This is a switch" /> | ||
</div> | ||
|
||
<div> | ||
<Checked /> | ||
</div> | ||
|
||
<div> | ||
<Switch disabled label="Unchecked and disabled" /> | ||
</div> | ||
|
||
<div> | ||
<Switch disabled label="Checked and disabled" checked /> | ||
</div> | ||
|
||
<div> | ||
<Switch label="With label before" labelPosition="before" /> | ||
|
||
<Switch label="With label above" labelPosition="above" /> | ||
|
||
<Switch label="With label after" labelPosition="after" /> | ||
</div> | ||
|
||
<div> | ||
<Switch | ||
style={{ "max-width": "400px" }} | ||
label="Here is an example of a Switch with a long label and it starts to wrap to a second line." | ||
/> | ||
</div> | ||
|
||
<div> | ||
<Switch required label="Required" /> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default SwitchDemo; |