Skip to content

Commit

Permalink
docs(changeset): improve SegmentedControl
Browse files Browse the repository at this point in the history
riccardoperra committed Nov 11, 2023
1 parent ef33eb7 commit 61d8687
Showing 5 changed files with 89 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-ghosts-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codeui/kit": patch
---

improve SegmentedControl
78 changes: 15 additions & 63 deletions packages/kit/src/components/SegmentedControl/SegmentedControl.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
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<T> = {
value?: T;
defaultValue?: T;
onChange?: (value: T) => void;
};

type SegmentedControlProps = Omit<
export type SegmentedControlProps = Omit<
Tabs.TabsRootProps,
"orientation" | keyof TypedTabsRootProps<string>
> &
@@ -30,16 +25,6 @@ type SegmentedControlProps = Omit<

type SegmentedControlSlot = "root" | "list" | "indicator";

interface SegmentedControlContextState {
items: Accessor<HTMLElement[]>;

mountItem(el: HTMLElement): void;

unmountItem(el: HTMLElement): void;
}

const SegmentedControlContext = createContext<SegmentedControlContextState>();

export function SegmentedControl(props: SegmentedControlProps) {
const [local, others] = splitProps(props, [
"class",
@@ -71,34 +56,26 @@ export function SegmentedControl(props: SegmentedControlProps) {
}),
);

const [items, setItems] = createSignal<HTMLElement[]>([]);

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);
resizeObserver.observe(listRef);
});

return (
<SegmentedControlContext.Provider value={context}>
<SegmentedControlContext.Provider value={contextState}>
<Tabs.Root
data-cui={"segmentedControl"}
data-disabled={disabled()}
@@ -124,28 +101,3 @@ export function SegmentedControl(props: SegmentedControlProps) {
</SegmentedControlContext.Provider>
);
}

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 (
<Tabs.Trigger
data-cui={"segmentedControlItem"}
class={classes()}
ref={mergeRefs(props.ref, el => (ref = el))}
{...others}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Accessor, createContext, createSignal, useContext } from "solid-js";

export interface SegmentedControlContextState {
items: Accessor<HTMLElement[]>;

mountItem(el: HTMLElement): void;

unmountItem(el: HTMLElement): void;
}

export const SegmentedControlContext = createContext<SegmentedControlContextState>();

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<HTMLElement[]>([]);
const context: SegmentedControlContextState = {
items,
mountItem(el) {
setItems(items => items.concat(el));
},
unmountItem(el) {
setItems(items => items.filter(item => item !== el));
},
};
return context;
}
Original file line number Diff line number Diff line change
@@ -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 (
<Tabs.Trigger
data-cui={"segmentedControlItem"}
class={classes()}
ref={mergeRefs(props.ref, el => (ref = el))}
{...others}
/>
);
}
8 changes: 4 additions & 4 deletions packages/kit/src/index.tsx
Original file line number Diff line number Diff line change
@@ -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";

0 comments on commit 61d8687

Please sign in to comment.