import type { JSX, ValidComponent } from "@solidjs/web"; import { dynamic } from "@solidjs/web"; import { omit } from "solid-js"; import { useCompositeRootContext } from "../internals/composite/composite-context.ts"; import { useCompositeListItem } from "../internals/composite/composite-list.tsx"; import { mergeRootProps } from "../internals/merge-root-props"; import { resolveClassStyle } from "../internals/resolve-class-style"; import type { BaseUIComponentProps, NativeButtonProps, Orientation, } from "../internals/types"; import { useButton } from "../internals/use-button/use-button"; import { useToolbarGroupContext } from "./toolbar-group-context.ts"; import { type ToolbarRootItemMetadata, useToolbarRootContext, } from "./toolbar-root-context.ts"; export interface ToolbarButtonState { disabled: boolean; orientation: Orientation; focusable: boolean; } export interface ToolbarButtonProps extends NativeButtonProps, BaseUIComponentProps<"button", ToolbarButtonState> { /** Tag/component to render. Defaults to `"button"`. */ as?: ValidComponent; /** * When `true` the item is disabled. * @default false */ disabled?: boolean | undefined; /** * When `true` the item remains focusable when disabled. * @default true */ focusableWhenDisabled?: boolean | undefined; } /** * A button inside a ``. Participates in the toolbar's arrow-key * navigation through `` + `useCompositeItem`-style wiring. */ export function ToolbarButton(props: ToolbarButtonProps): JSX.Element { const toolbar = useToolbarRootContext(); const group = useToolbarGroupContext(); const focusableWhenDisabled = (): boolean => props.focusableWhenDisabled ?? true; const ownDisabled = (): boolean => props.disabled === true; const disabled = (): boolean => toolbar.disabled() || group.disabled() || ownDisabled(); const nativeButton = (): boolean => props.nativeButton ?? true; const orientation = (): Orientation => toolbar.orientation(); const { props: buttonProps, ref: buttonRef } = useButton({ disabled, focusableWhenDisabled, native: nativeButton, }); const { ref: listItemRef, index: itemIndex } = useCompositeListItem({ get metadata() { return { focusableWhenDisabled: focusableWhenDisabled() }; }, }); const rootContext = useCompositeRootContext(true); const composedRef = (el: HTMLElement | null): void => { buttonRef(el); listItemRef(el); }; const compositeTabIndex = (): number | undefined => { if (!rootContext) return undefined; return rootContext.highlightedIndex() === itemIndex() ? 0 : -1; }; const onCompositeFocus = (): void => { if (!rootContext) return; const i = itemIndex(); if (i >= 0) rootContext.onHighlightedIndexChange(i); }; const userClassStyle = resolveClassStyle(props, () => ({ disabled: disabled(), orientation: orientation(), focusable: !disabled() || focusableWhenDisabled(), })); const userHtmlProps = omit( props, "as", "class", "style", "attrs", "disabled", "focusableWhenDisabled", "nativeButton", ) as Record; const internalProps = { ref: composedRef, get tabIndex() { return compositeTabIndex(); }, onFocus: onCompositeFocus, get "data-orientation"() { return orientation(); }, get "data-disabled"(): "" | undefined { return disabled() ? "" : undefined; }, }; const merged = mergeRootProps( mergeRootProps(mergeRootProps(internalProps, buttonProps), userClassStyle), userHtmlProps, ); const Tag = dynamic(() => props.as ?? "button"); return ; }