Skip to content

Commit

Permalink
Add HeadlessToggle
Browse files Browse the repository at this point in the history
  • Loading branch information
lxsmnsyc committed Sep 26, 2021
1 parent e5c079b commit 4131a35
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
115 changes: 115 additions & 0 deletions packages/solid-headless/src/headless/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
createContext,
createEffect,
createSignal,
JSX,
untrack,
useContext,
} from 'solid-js';

export interface HeadlessToggleOptions {
checked?: boolean;
defaultChecked?: boolean;
disabled?: boolean;
onChange?: (state?: boolean) => void;
}

export interface HeadlessToggleProperties {
checked(): boolean | undefined;
setState(newState?: boolean): void;
disabled(): boolean;
}

export function useHeadlessToggle(
options: HeadlessToggleOptions = {},
): HeadlessToggleProperties {
const [signal, setSignal] = createSignal(untrack(() => options.defaultChecked));

let initial = true;

createEffect(() => {
const value = options.checked;
if (initial) {
initial = false;
} else {
setSignal(!!value);
}
});

return {
checked() {
return signal();
},
setState(value) {
if (!options.disabled) {
setSignal(value);
options.onChange?.(value);
}
},
disabled() {
return !!options.disabled;
},
};
}

const HeadlessToggleContext = createContext<HeadlessToggleProperties>();

export type HeadlessToggleRootRenderProp = (
(properties: HeadlessToggleProperties) => JSX.Element
);

function isHeadlessToggleRootRenderProp(
children: HeadlessToggleRootRenderProp | JSX.Element,
): children is HeadlessToggleRootRenderProp {
return typeof children === 'function' && children.length > 0;
}

export interface HeadlessToggleRootProps extends HeadlessToggleOptions {
children?: HeadlessToggleRootRenderProp | JSX.Element;
}

export function HeadlessToggleRoot(props: HeadlessToggleRootProps): JSX.Element {
const properties = useHeadlessToggle(props);
return (
<HeadlessToggleContext.Provider value={properties}>
{(() => {
const body = props.children;
if (isHeadlessToggleRootRenderProp(body)) {
return body(properties);
}
return body;
})()}
</HeadlessToggleContext.Provider>
);
}

export function useHeadlessToggleChild(): HeadlessToggleProperties {
const properties = useContext(HeadlessToggleContext);
if (properties) {
return properties;
}
throw new Error('`useToggleChild` must be used within ToggleRoot.');
}

export type HeadlessToggleChildRenderProp = (
(properties: HeadlessToggleProperties) => JSX.Element
);

function isHeadlessToggleChildRenderProp(
children: HeadlessToggleChildRenderProp | JSX.Element,
): children is HeadlessToggleChildRenderProp {
return typeof children === 'function' && children.length > 0;
}

export interface HeadlessToggleChildProps {
children?: HeadlessToggleChildRenderProp | JSX.Element;
}

export function HeadlessToggleChild(props: HeadlessToggleChildProps): JSX.Element {
const properties = useHeadlessToggleChild();
const body = props.children;
if (isHeadlessToggleChildRenderProp(body)) {
return body(properties);
}
return body;
}
12 changes: 12 additions & 0 deletions packages/solid-headless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ export {
useHeadlessSelectOption,
useHeadlessSelectOptionChild,
} from './headless/Select';
export {
HeadlessToggleChild,
HeadlessToggleChildProps,
HeadlessToggleChildRenderProp,
HeadlessToggleOptions,
HeadlessToggleProperties,
HeadlessToggleRoot,
HeadlessToggleRootProps,
HeadlessToggleRootRenderProp,
useHeadlessToggle,
useHeadlessToggleChild,
} from './headless/Toggle';

// Tailwind
export {
Expand Down

0 comments on commit 4131a35

Please sign in to comment.