From 61d86870f6e1f811d1674755a41d7083f3db8f0d Mon Sep 17 00:00:00 2001 From: Riccardo Perra Date: Sat, 11 Nov 2023 20:55:13 +0100 Subject: [PATCH] docs(changeset): improve SegmentedControl --- .changeset/gentle-ghosts-think.md | 5 ++ .../SegmentedControl/SegmentedControl.tsx | 78 ++++--------------- .../SegmentedControlContext.tsx | 35 +++++++++ .../SegmentedControl/SegmentedControlItem.tsx | 30 +++++++ packages/kit/src/index.tsx | 8 +- 5 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 .changeset/gentle-ghosts-think.md create mode 100644 packages/kit/src/components/SegmentedControl/SegmentedControlContext.tsx create mode 100644 packages/kit/src/components/SegmentedControl/SegmentedControlItem.tsx diff --git a/.changeset/gentle-ghosts-think.md b/.changeset/gentle-ghosts-think.md new file mode 100644 index 0000000..5647551 --- /dev/null +++ b/.changeset/gentle-ghosts-think.md @@ -0,0 +1,5 @@ +--- +"@codeui/kit": patch +--- + +improve SegmentedControl diff --git a/packages/kit/src/components/SegmentedControl/SegmentedControl.tsx b/packages/kit/src/components/SegmentedControl/SegmentedControl.tsx index b086cb8..5bd0c92 100644 --- a/packages/kit/src/components/SegmentedControl/SegmentedControl.tsx +++ b/packages/kit/src/components/SegmentedControl/SegmentedControl.tsx @@ -1,19 +1,14 @@ import { Tabs } from "@kobalte/core"; -import { mergeRefs } from "@kobalte/utils"; import { debounce } from "@solid-primitives/scheduled"; -import { - Accessor, - createContext, - createSignal, - onCleanup, - onMount, - splitProps, - useContext, -} from "solid-js"; +import { onMount, splitProps } from "solid-js"; import { SlotProp } from "../../utils/component"; import { mergeClasses } from "../../utils/css"; import * as styles from "./SegmentedControl.css"; import { SegmentedControlVariants } from "./SegmentedControl.css"; +import { + createSegmentedControlContextState, + SegmentedControlContext, +} from "./SegmentedControlContext"; type TypedTabsRootProps = { value?: T; @@ -21,7 +16,7 @@ type TypedTabsRootProps = { onChange?: (value: T) => void; }; -type SegmentedControlProps = Omit< +export type SegmentedControlProps = Omit< Tabs.TabsRootProps, "orientation" | keyof TypedTabsRootProps > & @@ -30,16 +25,6 @@ type SegmentedControlProps = Omit< type SegmentedControlSlot = "root" | "list" | "indicator"; -interface SegmentedControlContextState { - items: Accessor; - - mountItem(el: HTMLElement): void; - - unmountItem(el: HTMLElement): void; -} - -const SegmentedControlContext = createContext(); - export function SegmentedControl(props: SegmentedControlProps) { const [local, others] = splitProps(props, [ "class", @@ -71,26 +56,18 @@ export function SegmentedControl(props: SegmentedControlProps) { }), ); - const [items, setItems] = createSignal([]); - - const context: SegmentedControlContextState = { - items, - mountItem(el) { - setItems(items => items.concat(el)); - }, - unmountItem(el) { - setItems(items => items.filter(item => item !== el)); - }, - }; + const contextState = createSegmentedControlContextState(); const handleListResize = debounce(() => { - const selectedItem = items().find(item => item.hasAttribute("data-selected")); - if (!selectedItem) { + const selectedControl = contextState + .items() + .find(item => item.hasAttribute("data-selected")); + if (!selectedControl) { return; } - indicatorRef.style.width = `${selectedItem.clientWidth}px`; - indicatorRef.style.transform = `translateX(${selectedItem.offsetLeft}px)`; - }, 50); + indicatorRef.style.width = `${selectedControl.clientWidth}px`; + indicatorRef.style.transform = `translateX(${selectedControl.offsetLeft}px)`; + }, 0); onMount(() => { const resizeObserver = new ResizeObserver(handleListResize); @@ -98,7 +75,7 @@ export function SegmentedControl(props: SegmentedControlProps) { }); return ( - + ); } - -type SegmentedControlItemProps = Tabs.TabsTriggerProps; - -export function SegmentedControlItem(props: SegmentedControlItemProps) { - const [local, others] = splitProps(props, ["class"]); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const context = useContext(SegmentedControlContext)!; - let ref: HTMLElement; - - const classes = () => mergeClasses(styles.segment, local.class); - - onMount(() => { - context.mountItem(ref); - onCleanup(() => context.unmountItem(ref)); - }); - - return ( - (ref = el))} - {...others} - /> - ); -} diff --git a/packages/kit/src/components/SegmentedControl/SegmentedControlContext.tsx b/packages/kit/src/components/SegmentedControl/SegmentedControlContext.tsx new file mode 100644 index 0000000..b5adcd2 --- /dev/null +++ b/packages/kit/src/components/SegmentedControl/SegmentedControlContext.tsx @@ -0,0 +1,35 @@ +import { Accessor, createContext, createSignal, useContext } from "solid-js"; + +export interface SegmentedControlContextState { + items: Accessor; + + mountItem(el: HTMLElement): void; + + unmountItem(el: HTMLElement): void; +} + +export const SegmentedControlContext = createContext(); + +export function useSegmentedControlContext() { + const context = useContext(SegmentedControlContext); + if (context === undefined) { + throw new Error( + "[@codeui/kit]: `useSegmentedControlContext` must be used within a `SegmentedControl` component", + ); + } + return context; +} + +export function createSegmentedControlContextState() { + const [items, setItems] = createSignal([]); + const context: SegmentedControlContextState = { + items, + mountItem(el) { + setItems(items => items.concat(el)); + }, + unmountItem(el) { + setItems(items => items.filter(item => item !== el)); + }, + }; + return context; +} diff --git a/packages/kit/src/components/SegmentedControl/SegmentedControlItem.tsx b/packages/kit/src/components/SegmentedControl/SegmentedControlItem.tsx new file mode 100644 index 0000000..5099c46 --- /dev/null +++ b/packages/kit/src/components/SegmentedControl/SegmentedControlItem.tsx @@ -0,0 +1,30 @@ +import { onCleanup, onMount, splitProps } from "solid-js"; +import { mergeClasses } from "../../utils/css"; +import * as styles from "./SegmentedControl.css"; +import { Tabs } from "@kobalte/core"; +import { mergeRefs } from "@kobalte/utils"; +import { useSegmentedControlContext } from "./SegmentedControlContext"; + +export type SegmentedControlItemProps = Tabs.TabsTriggerProps; + +export function SegmentedControlItem(props: SegmentedControlItemProps) { + const [local, others] = splitProps(props, ["class"]); + const context = useSegmentedControlContext(); + let ref: HTMLElement; + + const classes = () => mergeClasses(styles.segment, local.class); + + onMount(() => { + context.mountItem(ref); + onCleanup(() => context.unmountItem(ref)); + }); + + return ( + (ref = el))} + {...others} + /> + ); +} diff --git a/packages/kit/src/index.tsx b/packages/kit/src/index.tsx index f8d665d..f13e257 100644 --- a/packages/kit/src/index.tsx +++ b/packages/kit/src/index.tsx @@ -45,10 +45,10 @@ export { Tooltip } from "./components/Tooltip/Tooltip"; export { Tabs, TabsHeader, TabsList, TabsContent } from "./components/Tabs/Tabs"; -export { - SegmentedControl, - SegmentedControlItem, -} from "./components/SegmentedControl/SegmentedControl"; +export { SegmentedControl } from "./components/SegmentedControl/SegmentedControl"; +export type { SegmentedControlProps } from "./components/SegmentedControl/SegmentedControl"; +export { SegmentedControlItem } from "./components/SegmentedControl/SegmentedControlItem"; +export type { SegmentedControlItemProps } from "./components/SegmentedControl/SegmentedControlItem"; export { SvgIcon } from "./icons/SvgIcon"; export * as icons from "./icons";