Skip to content

Commit

Permalink
menu radio group check
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte committed May 11, 2024
1 parent 679b154 commit b3e5683
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 183 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
<script lang="ts">
import type { ArrowProps } from "../index.js";
import { useMenuArrow } from "../menu.svelte.js";
import { FloatingLayer } from "$lib/bits/utilities/floating-layer/index.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
let { el = $bindable(), ...restProps }: ArrowProps = $props();
const state = useMenuArrow();
const mergedProps = $derived(mergeProps(restProps, state.props as any));
</script>

<FloatingLayer.Arrow bind:el {...restProps} />
<FloatingLayer.Arrow bind:el {...mergedProps} />
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
onSelect: box.with(() => handleSelect),
});
function handleSelect() {
onSelect();
function handleSelect(e: Event) {
onSelect(e);
state.toggleChecked();
}
Expand Down
20 changes: 8 additions & 12 deletions packages/bits-ui/src/lib/bits/menu/components/menu-group.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { setGroupCtx } from "../ctx.js";
import type { GroupProps } from "../index.js";
type $$Props = GroupProps;
export let asChild: $$Props["asChild"] = false;
export let el: $$Props["el"] = undefined;
import { useMenuGroup } from "../menu.svelte.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
const { group, id, getAttrs } = setGroupCtx();
const attrs = getAttrs("group");
let { asChild, children, child, el = $bindable(), ...restProps }: GroupProps = $props();
$: builder = $group(id);
$: Object.assign(builder, attrs);
const state = useMenuGroup();
const mergedProps = $derived(mergeProps(restProps, state.props));
</script>

{#if asChild}
<slot {builder} />
{@render child?.({ props: mergedProps })}
{:else}
<div bind:this={el} use:melt={builder} {...$$restProps}>
<slot {builder} />
<div {...mergedProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
22 changes: 8 additions & 14 deletions packages/bits-ui/src/lib/bits/menu/components/menu-label.svelte
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { getGroupLabel } from "../ctx.js";
import type { LabelProps } from "../index.js";
import { useMenuLabel } from "../menu.svelte.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
type $$Props = LabelProps;
let { asChild, children, child, el = $bindable(), ...restProps }: LabelProps = $props();
export let asChild: $$Props["asChild"] = false;
export let el: $$Props["el"] = undefined;
const { groupLabel, id, getAttrs } = getGroupLabel();
const attrs = getAttrs("label");
$: builder = $groupLabel(id);
$: Object.assign(builder, attrs);
const state = useMenuLabel();
const mergedProps = $derived(mergeProps(restProps, state.props));
</script>

{#if asChild}
<slot {builder} />
{@render child?.({ props: mergedProps })}
{:else}
<div bind:this={el} use:melt={builder} {...$$restProps}>
<slot {builder} />
<div {...mergedProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -1,41 +1,39 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { setRadioGroupCtx } from "../ctx.js";
import { box } from "runed";
import type { RadioGroupProps } from "../index.js";
import { useMenuRadioGroup } from "../menu.svelte.js";
import { noop } from "$lib/internal/callbacks.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
type $$Props = RadioGroupProps;
let {
asChild,
children,
child,
el = $bindable(),
value = $bindable(""),
onValueChange = noop,
...restProps
}: RadioGroupProps = $props();
export let value: $$Props["value"] = undefined;
export let onValueChange: $$Props["onValueChange"] = undefined;
export let asChild: $$Props["asChild"] = false;
export let el: $$Props["el"] = undefined;
const {
elements: { radioGroup },
states: { value: localValue },
getAttrs,
} = setRadioGroupCtx({
defaultValue: value,
onValueChange: ({ next }) => {
if (next != null && next !== value) {
onValueChange?.(next);
value = next;
const state = useMenuRadioGroup({
value: box.with(
() => value,
(v) => {
if (value !== v) {
value = v;
onValueChange(v);
}
}
return next;
},
),
});
const attrs = getAttrs("radio-group");
$: value !== undefined && localValue.set(value);
$: builder = $radioGroup;
$: Object.assign(builder, attrs);
const mergedProps = $derived(mergeProps(restProps, state.props));
</script>

{#if asChild}
<slot {builder} />
{@render child?.({ props: mergedProps })}
{:else}
<div bind:this={el} use:melt={builder} {...$$restProps}>
<slot {builder} />
<div {...mergedProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { setRadioItem } from "../ctx.js";
import type { RadioItemEvents, RadioItemProps } from "../index.js";
import { createDispatcher } from "$lib/internal/events.js";
import { box } from "runed";
import type { RadioItemProps } from "../index.js";
import { useMenuRadioItem } from "../menu.svelte.js";
import { useId } from "$lib/internal/useId.svelte.js";
import { noop } from "$lib/internal/callbacks.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
type $$Props = RadioItemProps;
type $$Events = RadioItemEvents;
export let value: $$Props["value"];
export let disabled = false;
export let asChild: $$Props["asChild"] = false;
export let el: $$Props["el"] = undefined;
let {
asChild,
children,
child,
el = $bindable(),
value,
onSelect = noop,
id = useId(),
disabled = false,
...restProps
}: RadioItemProps = $props();
const {
elements: { radioItem },
getAttrs,
} = setRadioItem(value);
const state = useMenuRadioItem({
value: box.with(() => value),
id: box.with(() => id),
disabled: box.with(() => disabled),
onSelect: box.with(() => handleSelect),
});
const attrs = getAttrs("radio-item");
const dispatch = createDispatcher();
function handleSelect(e: Event) {
onSelect(e);
if (e.defaultPrevented) return;
state.selectValue();
}
$: builder = $radioItem({ value, disabled });
$: Object.assign(builder, attrs);
const mergedProps = $derived(mergeProps(restProps, state.props));
</script>

{#if asChild}
<slot {builder} />
{@render child?.({ props: mergedProps })}
{:else}
<div
bind:this={el}
use:melt={builder}
{...$$restProps}
on:m-click={dispatch}
on:m-focusin={dispatch}
on:m-focusout={dispatch}
on:m-keydown={dispatch}
on:m-pointerdown={dispatch}
on:m-pointerleave={dispatch}
on:m-pointermove={dispatch}
on:pointerenter
>
<slot {builder} />
<div {...mergedProps} bind:this={el}>
{@render children?.({ checked: state.isChecked })}
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script lang="ts">
import type { SeparatorProps } from "../index.js";
import { useMenuSeparator } from "../menu.svelte.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
let { el = $bindable(), asChild, child, children, ...restProps }: SeparatorProps = $props();
const mergedProps = $derived(
mergeProps(restProps, { role: "separator", "aria-orientation": "horizontal" })
);
const state = useMenuSeparator();
const mergedProps = $derived(mergeProps(restProps, state.props));
</script>

{#if asChild}
Expand Down
6 changes: 5 additions & 1 deletion packages/bits-ui/src/lib/bits/menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ export type {
MenuSubContentProps as SubContentProps,
MenuSeparatorProps as SeparatorProps,
MenuArrowProps as ArrowProps,
MenucheckboxItemProps as CheckboxItemProps,
MenuCheckboxItemProps as CheckboxItemProps,
MenuLabelProps as LabelProps,
MenuGroupProps as GroupProps,
MenuRadioGroupProps as RadioGroupProps,
MenuRadioItemProps as RadioItemProps,
} from "./types.js";
50 changes: 34 additions & 16 deletions packages/bits-ui/src/lib/bits/menu/menu.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ import { afterTick } from "$lib/internal/afterTick.js";
const TRIGGER_ATTR = "data-menu-trigger";
const CONTENT_ATTR = "data-menu-content";
const ITEM_ATTR = "data-menu-item";
const SEPARATOR_ATTR = "data-menu-separator";
const SUB_TRIGGER_ATTR = "data-menu-subtrigger";
const CHECKBOX_ITEM_ATTR = "data-menu-checkbox-item";
const GROUP_ATTR = "data-menu-group";
const LABEL_ATTR = "data-menu-label";
const RADIO_GROUP_ATTR = "data-menu-radio-group";
const RADIO_ITEM_ATTR = "data-menu-radio-item";
const ARROW_ATTR = "data-menu-arrow";

const [setMenuRootContext] = createContext<MenuRootState>("Menu.Root");

Expand Down Expand Up @@ -667,22 +669,30 @@ class MenuCheckboxItemState {
}

class MenuGroupState {
props = $derived.by(
() =>
({
role: "group",
[GROUP_ATTR]: "",
}) as const
);
props = {
role: "group",
[GROUP_ATTR]: "",
} as const;
}

class MenuLabelState {
props = $derived.by(
() =>
({
[LABEL_ATTR]: "",
}) as const
);
props = {
[LABEL_ATTR]: "",
} as const;
}

class MenuSeparatorState {
props = {
[SEPARATOR_ATTR]: "",
role: "separator",
"aria-orientation": "horizontal",
} as const;
}

class MenuArrowState {
props = {
[ARROW_ATTR]: "",
} as const;
}

type MenuRadioGroupStateProps = WritableBoxedValues<{
Expand Down Expand Up @@ -726,7 +736,7 @@ class MenuRadioItemState {
#item: MenuItemState;
#value: MenuRadioItemStateProps["value"];
#group: MenuRadioGroupState;
#isChecked = $derived.by(() => this.#group.value.value === this.#value.value);
isChecked = $derived.by(() => this.#group.value.value === this.#value.value);

constructor(props: MenuRadioItemStateProps, item: MenuItemState, group: MenuRadioGroupState) {
this.#item = item;
Expand All @@ -744,8 +754,8 @@ class MenuRadioItemState {
[RADIO_ITEM_ATTR]: "",
...this.#item.props,
role: "menuitemradio",
"aria-checked": getAriaChecked(this.#isChecked),
"data-state": getCheckedState(this.#isChecked),
"aria-checked": getAriaChecked(this.isChecked),
"data-state": getCheckedState(this.isChecked),
}) as const
);
}
Expand Down Expand Up @@ -869,3 +879,11 @@ export function useMenuGroup() {
export function useMenuLabel() {
return new MenuLabelState();
}

export function useMenuSeparator() {
return new MenuSeparatorState();
}

export function useMenuArrow() {
return new MenuArrowState();
}
Loading

0 comments on commit b3e5683

Please sign in to comment.