@instruments/materia-ui 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/command/command-dialog.d.ts +5 -1
- package/dist/components/command/command-dialog.js +1 -1
- package/dist/components/command/command-input.js +5 -2
- package/dist/components/segmented-control/segmented-control-root.js +6 -3
- package/dist/components/segmented-control/segmented-control-variants.js +4 -1
- package/dist/components/swatch-selector/swatch-selector-item.js +4 -1
- package/dist/data/docs-manifest.js +2 -1
- package/dist/tokens/segmented.d.ts +8 -0
- package/dist/tokens/segmented.js +26 -0
- package/package.json +1 -1
|
@@ -10,7 +10,11 @@ export type CommandDialogProps<Item> = Omit<React.ComponentProps<typeof Dialog>,
|
|
|
10
10
|
description?: string;
|
|
11
11
|
/** Extra class names applied to the `DialogContent` wrapper. */
|
|
12
12
|
className?: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Whether to show the dialog close button. Off by default: the button sits
|
|
15
|
+
* over the search field, and Escape or a click outside closes the palette.
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
14
18
|
showCloseButton?: boolean;
|
|
15
19
|
/** Extra props applied to the rendered dialog content element. */
|
|
16
20
|
contentProps?: Omit<React.ComponentProps<typeof DialogContent>, "children"> & React.HTMLAttributes<HTMLDivElement> & {
|
|
@@ -14,7 +14,7 @@ import { Command } from "../command/command-root.js";
|
|
|
14
14
|
* @bestPractice Pass `filter={null}` to list the entries unfiltered, for example when the application filters them itself.
|
|
15
15
|
*/
|
|
16
16
|
export const CommandDialog = (props) => {
|
|
17
|
-
const { title = "Command Palette", description = "Search for a command to run…", children, className, showCloseButton =
|
|
17
|
+
const { title = "Command Palette", description = "Search for a command to run…", children, className, showCloseButton = false, contentProps, filter, groups: _groups, items: _items, limit, mode, ...dialogProps } = props;
|
|
18
18
|
// Narrow on `props` so the palette receives exactly one of items or groups.
|
|
19
19
|
const source = props.groups === undefined
|
|
20
20
|
? { items: props.items }
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete";
|
|
4
|
-
import { focusRing } from "@instruments/materia-ui/tokens/focus-ring";
|
|
5
4
|
import { cn } from "@instruments/materia-ui/utils/cn";
|
|
6
5
|
import { Search } from "lucide-react";
|
|
7
6
|
import { Icon } from "../icon.js";
|
|
@@ -9,4 +8,8 @@ import { Icon } from "../icon.js";
|
|
|
9
8
|
* CommandInput renders the search field for a Command; typing filters the list.
|
|
10
9
|
* Import from "@instruments/materia-ui/components/command".
|
|
11
10
|
*/
|
|
12
|
-
export const CommandInput = ({ className, ...props }) => (_jsxs(AutocompletePrimitive.InputGroup, { className: "flex h-9 items-center gap-2 border-b px-3", "data-component": "command-input-wrapper", "data-slot": "command-input-wrapper", children: [_jsx(Icon, { className: "shrink-0 opacity-50", icon: Search, size: "xs" }), _jsx(AutocompletePrimitive.Input, { className: cn(
|
|
11
|
+
export const CommandInput = ({ className, ...props }) => (_jsxs(AutocompletePrimitive.InputGroup, { className: "flex h-9 items-center gap-2 border-b px-3", "data-component": "command-input-wrapper", "data-slot": "command-input-wrapper", children: [_jsx(Icon, { className: "shrink-0 opacity-50", icon: Search, size: "xs" }), _jsx(AutocompletePrimitive.Input, { className: cn(
|
|
12
|
+
// No ring: the field is the palette's only input and always holds
|
|
13
|
+
// focus, so the caret marks it; a ring would collide with the
|
|
14
|
+
// dialog's rounded top edge.
|
|
15
|
+
"placeholder:text-muted-foreground text-paragraph-sm flex h-full w-full bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-50", className), "data-component": "command-input", "data-slot": "command-input", ...props })] }));
|
|
@@ -6,7 +6,7 @@ import { ScrollFade } from "@instruments/materia-ui/components/scroll-fade";
|
|
|
6
6
|
import { cn } from "@instruments/materia-ui/utils/cn";
|
|
7
7
|
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { getResponsiveSizeOverrides, resolveResponsiveBase, } from "../../tokens/responsive.js";
|
|
9
|
-
import { SEGMENTED_GAP, SEGMENTED_SHELL_BASE,
|
|
9
|
+
import { SEGMENTED_GAP, SEGMENTED_SHELL_BASE, SEGMENTED_SHELL_PADDING_X, SEGMENTED_SHELL_PADDING_Y, } from "../../tokens/segmented.js";
|
|
10
10
|
import { Flex } from "../flex.js";
|
|
11
11
|
import { SegmentedControlContext } from "./segmented-control-context.js";
|
|
12
12
|
import { useSingleSelection } from "./use-single-selection.js";
|
|
@@ -65,10 +65,13 @@ const SegmentedControl = ({ className, size = "sm", fullWidth = false, value, de
|
|
|
65
65
|
viewport?.removeEventListener("scroll", checkScroll);
|
|
66
66
|
};
|
|
67
67
|
}, []);
|
|
68
|
-
return (_jsx(ToggleGroup, { className: cn(SEGMENTED_SHELL_BASE, "group/segmented-control inline-flex h-auto max-w-full items-center overflow-hidden",
|
|
68
|
+
return (_jsx(ToggleGroup, { className: cn(SEGMENTED_SHELL_BASE, "group/segmented-control inline-flex h-auto max-w-full items-center overflow-hidden", SEGMENTED_SHELL_PADDING_X[resolvedSize], getResponsiveSizeOverrides(size, SEGMENTED_SHELL_PADDING_X), fullWidth && "flex w-full", className), "data-component": "segmented-control", "data-size": resolvedSize, "data-slot": "segmented-control", onValueChange: (groupValue) => {
|
|
69
69
|
// Single-select group: Base UI sends `[]` when the active item is
|
|
70
70
|
// re-clicked; the selection hook swallows that deselect.
|
|
71
71
|
selection.onValueChange(groupValue[0] ?? "");
|
|
72
|
-
}, value: [selection.value], ...props, children: _jsx(SegmentedControlContext.Provider, { value: contextValue, children: _jsxs("div", { className: "relative min-w-0 flex-1", ref: containerRef, children: [_jsx(ScrollArea, { hideScrollbar: true, orientation: "horizontal", children: _jsx(Flex, { align: "center", className: cn(
|
|
72
|
+
}, value: [selection.value], ...props, children: _jsx(SegmentedControlContext.Provider, { value: contextValue, children: _jsxs("div", { className: "relative min-w-0 flex-1", ref: containerRef, children: [_jsx(ScrollArea, { hideScrollbar: true, orientation: "horizontal", children: _jsx(Flex, { align: "center", className: cn(
|
|
73
|
+
// Vertical padding sits inside the scroll area, which clips,
|
|
74
|
+
// so each item's hit area reaches the shell's full height.
|
|
75
|
+
SEGMENTED_SHELL_PADDING_Y[resolvedSize], getResponsiveSizeOverrides(size, SEGMENTED_SHELL_PADDING_Y), SEGMENTED_GAP[resolvedSize], getResponsiveSizeOverrides(size, SEGMENTED_GAP), fullWidth && "w-full"), inline: true, children: children }) }), showEndFade && (_jsx(ScrollFade, { className: "rounded-r-full", orientation: "horizontal", position: "end", size: "sm", theme: "gray" }))] }) }) }));
|
|
73
76
|
};
|
|
74
77
|
export { SegmentedControl };
|
|
@@ -20,7 +20,10 @@ const triggerPaddingCompoundVariants = typedEntries(SEGMENTED_TRIGGER_PADDING).m
|
|
|
20
20
|
* Structure mirrors `tabsTriggerVariants` (pill): the `size` variant only sets
|
|
21
21
|
* text size; ALL padding is applied through size-keyed compound variants.
|
|
22
22
|
*/
|
|
23
|
-
export const segmentedControlItemVariants = cva(cn(focusRing(), "text-muted-foreground relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full font-medium whitespace-nowrap",
|
|
23
|
+
export const segmentedControlItemVariants = cva(cn(focusRing(), "text-muted-foreground relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full font-medium whitespace-nowrap",
|
|
24
|
+
// An invisible hit area overhanging the segment; the root's scroll area
|
|
25
|
+
// trims it to the track, so a tap anywhere in the track's height lands.
|
|
26
|
+
"after:absolute after:inset-x-0 after:-inset-y-3", `transition-color-shadow-border ${DISABLED}`, "data-pressed:text-foreground dark:text-gray-500 dark:data-pressed:text-gray-100", "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"), {
|
|
24
27
|
compoundVariants: triggerPaddingCompoundVariants,
|
|
25
28
|
defaultVariants: {
|
|
26
29
|
size: "sm",
|
|
@@ -24,7 +24,10 @@ export const SwatchSelectorItem = ({ value, color, children, size: sizeOverride,
|
|
|
24
24
|
const isSelected = selectedValue === value;
|
|
25
25
|
const button = (_jsx(TogglePrimitive, { "aria-label": props["aria-label"] ?? `Select ${value}`, className: cn(
|
|
26
26
|
// Focus styles only - visual handled by Swatch
|
|
27
|
-
"inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full",
|
|
27
|
+
"relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full",
|
|
28
|
+
// An invisible hit area at least 44px tall and 5px wider each side, so
|
|
29
|
+
// a small swatch fills its slot in a 10px-gapped row.
|
|
30
|
+
"after:absolute after:-inset-x-[5px] after:top-1/2 after:h-full after:min-h-11 after:-translate-y-1/2", focusRing(), "disabled:cursor-not-allowed disabled:opacity-50", className), "data-component": "swatch-selector-item", "data-slot": "swatch-selector-item", onClick: (event) => {
|
|
28
31
|
event.stopPropagation();
|
|
29
32
|
}, value: value, ...props, children: _jsx(Swatch, { ...(color === undefined ? {} : { color }), selected: isSelected, size: resolvedSize, unavailable: unavailable, children: children }) }));
|
|
30
33
|
if (unavailable) {
|
|
@@ -2638,7 +2638,8 @@ export const docsManifest = {
|
|
|
2638
2638
|
name: "showCloseButton",
|
|
2639
2639
|
type: "boolean",
|
|
2640
2640
|
required: false,
|
|
2641
|
-
|
|
2641
|
+
defaultValue: "false",
|
|
2642
|
+
description: "Whether to show the dialog close button. Off by default: the button sits\nover the search field, and Escape or a click outside closes the palette.",
|
|
2642
2643
|
},
|
|
2643
2644
|
{
|
|
2644
2645
|
name: "contentProps",
|
|
@@ -20,6 +20,14 @@ import type { ComponentSize } from "./size.js";
|
|
|
20
20
|
export declare const SEGMENTED_SHELL_BASE = "rounded-full bg-surface-inset text-muted-foreground";
|
|
21
21
|
/** Shared shell padding scale by size. */
|
|
22
22
|
export declare const SEGMENTED_SHELL_PADDING: Record<ComponentSize, string>;
|
|
23
|
+
/**
|
|
24
|
+
* The horizontal half of `SEGMENTED_SHELL_PADDING`. SegmentedControl puts this
|
|
25
|
+
* on the shell and the vertical half inside its scroll area, so each item's
|
|
26
|
+
* hit area can fill the shell's full height.
|
|
27
|
+
*/
|
|
28
|
+
export declare const SEGMENTED_SHELL_PADDING_X: Record<ComponentSize, string>;
|
|
29
|
+
/** The vertical half of `SEGMENTED_SHELL_PADDING`. */
|
|
30
|
+
export declare const SEGMENTED_SHELL_PADDING_Y: Record<ComponentSize, string>;
|
|
23
31
|
/**
|
|
24
32
|
* Shared trigger/item text-size scale by size. `SegmentedControl` items,
|
|
25
33
|
* `TabsTrigger` and `TabNavigationLink` all read it, so they render identical
|
package/dist/tokens/segmented.js
CHANGED
|
@@ -28,6 +28,32 @@ export const SEGMENTED_SHELL_PADDING = {
|
|
|
28
28
|
xl: "p-1",
|
|
29
29
|
xs: "p-0.5",
|
|
30
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* The horizontal half of `SEGMENTED_SHELL_PADDING`. SegmentedControl puts this
|
|
33
|
+
* on the shell and the vertical half inside its scroll area, so each item's
|
|
34
|
+
* hit area can fill the shell's full height.
|
|
35
|
+
*/
|
|
36
|
+
export const SEGMENTED_SHELL_PADDING_X = {
|
|
37
|
+
"2xl": "px-1.5",
|
|
38
|
+
"2xs": "px-0.5",
|
|
39
|
+
"3xl": "px-1.5",
|
|
40
|
+
base: "px-0.5",
|
|
41
|
+
lg: "px-1",
|
|
42
|
+
sm: "px-0.5",
|
|
43
|
+
xl: "px-1",
|
|
44
|
+
xs: "px-0.5",
|
|
45
|
+
};
|
|
46
|
+
/** The vertical half of `SEGMENTED_SHELL_PADDING`. */
|
|
47
|
+
export const SEGMENTED_SHELL_PADDING_Y = {
|
|
48
|
+
"2xl": "py-1.5",
|
|
49
|
+
"2xs": "py-0.5",
|
|
50
|
+
"3xl": "py-1.5",
|
|
51
|
+
base: "py-0.5",
|
|
52
|
+
lg: "py-1",
|
|
53
|
+
sm: "py-0.5",
|
|
54
|
+
xl: "py-1",
|
|
55
|
+
xs: "py-0.5",
|
|
56
|
+
};
|
|
31
57
|
/**
|
|
32
58
|
* Shared trigger/item text-size scale by size. `SegmentedControl` items,
|
|
33
59
|
* `TabsTrigger` and `TabNavigationLink` all read it, so they render identical
|