Skip to content

Commit

Permalink
New Component: Toggle Group (huntabyte#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Oct 30, 2023
1 parent 6a1e214 commit de0284c
Show file tree
Hide file tree
Showing 16 changed files with 504 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-toes-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"bits-ui": minor
---

New Component: Toggle Group
30 changes: 30 additions & 0 deletions content/components/toggle-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Toggle Group
description: An interactive component that toggles between two states.
---

<script>
import { APISection, ComponentPreview, ToggleGroupDemo } from '@/components'
export let schemas;
</script>

<ComponentPreview name="toggle-group-demo" comp="ToggleGroup">

<ToggleGroupDemo slot="preview" />

</ComponentPreview>

## Structure

```svelte
<script lang="ts">
import { ToggleGroup } from "bits-ui";
</script>
<ToggleGroup.Root>
<ToggleGroup.Item value="bold">bold</ToggleGroup.Item>
<ToggleGroup.Item value="italic">italic</ToggleGroup.Item>
</ToggleGroup.Root>
```

🚧 **UNDER CONSTRUCTION** 🚧
11 changes: 11 additions & 0 deletions content/components/toggle.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ title: Toggle
description: An interactive component that toggles between two states.
---

<script>
import { APISection, ComponentPreview, ToggleDemo } from '@/components'
export let schemas;
</script>

<ComponentPreview name="toggle-demo" comp="Toggle">

<ToggleDemo slot="preview" />

</ComponentPreview>

## Structure

```svelte
Expand Down
1 change: 1 addition & 0 deletions src/components/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export { default as SliderDemo } from "./slider-demo.svelte";
export { default as SwitchDemo } from "./switch-demo.svelte";
export { default as TabsDemo } from "./tabs-demo.svelte";
export { default as ToggleDemo } from "./toggle-demo.svelte";
export { default as ToggleGroupDemo } from "./toggle-group-demo.svelte";
export { default as TooltipDemo } from "./tooltip-demo.svelte";
25 changes: 25 additions & 0 deletions src/components/demos/toggle-group-demo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts">
import { ToggleGroup } from "$lib";
import { Bold, Italic } from "lucide-svelte";
import { toggleVariants } from "@/components/ui/toggle";
let value: string[] | undefined = undefined;
$: console.log(value);
</script>

<ToggleGroup.Root bind:value kind="multiple">
<ToggleGroup.Item
class={toggleVariants({ variant: "default" })}
aria-label="toggle bold"
value="bold"
>
<Bold class="h-4 w-4" />
</ToggleGroup.Item>
<ToggleGroup.Item
class={toggleVariants({ variant: "default" })}
aria-label="toggle italic"
value="italic"
>
<Italic class="h-4 w-4" />
</ToggleGroup.Item>
</ToggleGroup.Root>
5 changes: 5 additions & 0 deletions src/config/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ export const navigation: Navigation = {
href: "/docs/components/toggle",
items: []
},
{
title: "Toggle Group",
href: "/docs/components/toggle-group",
items: []
},
{
title: "Tooltip",
href: "/docs/components/tooltip",
Expand Down
2 changes: 2 additions & 0 deletions src/content/api-reference/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const bits = [
"switch",
"tabs",
"toggle",
"toggle-group",
"tooltip"
] as const;

Expand Down Expand Up @@ -75,6 +76,7 @@ export const apiSchemas: Record<Bit, APISchema[]> = {
slider: [],
tabs: [],
toggle: [],
"toggle-group": [],
tooltip: []
};

Expand Down
1 change: 1 addition & 0 deletions src/lib/bits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export * as Slider from "./slider/index.js";
export * as Switch from "./switch/index.js";
export * as Tabs from "./tabs/index.js";
export * as Toggle from "./toggle/index.js";
export * as ToggleGroup from "./toggle-group/index.js";
export * as Tooltip from "./tooltip/index.js";
60 changes: 60 additions & 0 deletions src/lib/bits/toggle-group/components/ToggleGroup.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { setCtx, getAttrs } from "../ctx.js";
import type { Events, Props } from "../types.js";
type T = $$Generic<"single" | "multiple">;
type $$Props = Props<T>;
type $$Events = Events;
export let kind: $$Props["kind"] = "single" as T;
export let disabled: $$Props["disabled"] = undefined;
export let loop: $$Props["loop"] = undefined;
export let value: $$Props["value"] = undefined;
export let orientation: $$Props["orientation"] = undefined;
export let onValueChange: $$Props["onValueChange"] = undefined;
export let asChild = false;
const {
elements: { root },
states: { value: localValue },
updateOption
} = setCtx<T>({
disabled,
type: kind,
defaultValue: value,
loop,
orientation,
onValueChange: (({ next }: { next: $$Props["value"] }) => {
if (Array.isArray(next)) {
onValueChange?.(next);
value = next;
return next;
}
if (value !== next) {
onValueChange?.(next);
value = next;
}
return next;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as any
});
$: value !== undefined && localValue.set(value);
$: updateOption("disabled", disabled);
$: updateOption("loop", loop);
$: updateOption("type", kind);
$: updateOption("orientation", orientation);
$: builder = $root;
const attrs = getAttrs("root");
</script>

{#if asChild}
<slot {builder} {attrs} />
{:else}
<div use:melt={builder} {...$$restProps} {...attrs}>
<slot {builder} {attrs} />
</div>
{/if}
35 changes: 35 additions & 0 deletions src/lib/bits/toggle-group/components/ToggleGroupItem.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { getCtx, getAttrs } from "../ctx";
import type { ItemProps } from "../types";
import { createDispatcher } from "$lib/internal";
type $$Props = ItemProps;
export let value: $$Props["value"];
export let disabled: $$Props["disabled"] = false;
export let asChild: $$Props["asChild"] = false;
const {
elements: { item }
} = getCtx();
const dispatch = createDispatcher();
$: builder = $item({ value, disabled });
const attrs = getAttrs("item");
</script>

{#if asChild}
<slot {builder} {attrs} />
{:else}
<button
use:melt={builder}
{...$$restProps}
{...attrs}
on:m-click={dispatch}
on:m-keydown={dispatch}
>
<slot {builder} {attrs} />
</button>
{/if}
27 changes: 27 additions & 0 deletions src/lib/bits/toggle-group/ctx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createBitAttrs, getOptionUpdater, removeUndefined } from "$lib/internal/index.js";
import {
createToggleGroup,
type CreateToggleGroupProps,
type ToggleGroup as ToggleGroupReturn
} from "@melt-ui/svelte";
import { getContext, setContext } from "svelte";

const NAME = "toggle-group";
const PARTS = ["root", "item"] as const;

export const getAttrs = createBitAttrs(NAME, PARTS);

type GetReturn = ToggleGroupReturn;

export function setCtx<T extends "single" | "multiple">(props: CreateToggleGroupProps<T>) {
const toggleGroup = createToggleGroup(removeUndefined(props));
setContext(NAME, toggleGroup);
return {
...toggleGroup,
updateOption: getOptionUpdater(toggleGroup.options)
};
}

export function getCtx() {
return getContext<GetReturn>(NAME);
}
11 changes: 11 additions & 0 deletions src/lib/bits/toggle-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Root from "./components/ToggleGroup.svelte";
import Item from "./components/ToggleGroupItem.svelte";

export {
Root,
Item,
//
Root as ToggleGroup,
Item as ToggleGroupItem
};
export * from "./types.js";
43 changes: 43 additions & 0 deletions src/lib/bits/toggle-group/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { CreateToggleGroupProps } from "@melt-ui/svelte";
import type {
AsChild,
Expand,
HTMLDivAttributes,
OmitValue,
OnChangeFn
} from "$lib/internal/index.js";
import type { HTMLButtonAttributes } from "svelte/elements";
import type { CustomEventHandler } from "$lib/index.js";

type Props<T extends "single" | "multiple"> = Expand<
OmitValue<CreateToggleGroupProps> & {
value?: CreateToggleGroupProps<T>["defaultValue"];
onValueChange?: OnChangeFn<CreateToggleGroupProps<T>["defaultValue"]>;
kind?: T;
}
> &
AsChild &
HTMLDivAttributes;

type ItemProps = {
value: string;
disabled?: boolean;
} & AsChild &
HTMLButtonAttributes;

type Events<T extends Element = HTMLButtonElement> = {
click: CustomEventHandler<MouseEvent, T>;
keydown: CustomEventHandler<KeyboardEvent, T>;
};

export type {
Props,
ItemProps,
//
Props as ToggleGroupProps,
ItemProps as ToggleGroupItemProps,
//
Events,
//
Events as ToggleGroupEvents
};
Loading

0 comments on commit de0284c

Please sign in to comment.