@stll/ui 0.59.0 → 0.60.0
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/README.md +120 -4
- package/dist/calendar/day-calendar.d.ts +14 -4
- package/dist/calendar/day-calendar.js +52 -20
- package/dist/calendar/day-calendar.logic.d.ts +3 -0
- package/dist/calendar/day-calendar.logic.js +4 -1
- package/dist/components/accordion.d.ts +17 -3
- package/dist/components/accordion.js +17 -3
- package/dist/components/button.d.ts +56 -5
- package/dist/components/button.js +97 -15
- package/dist/components/button.native-classes.d.ts +17 -0
- package/dist/components/button.native-classes.js +176 -0
- package/dist/components/button.native.d.ts +5 -2
- package/dist/components/button.native.js +17 -134
- package/dist/components/button.shared.d.ts +24 -2
- package/dist/components/button.shared.js +15 -1
- package/dist/components/combobox.d.ts +3 -1
- package/dist/components/combobox.js +3 -2
- package/dist/components/date-picker-popover.d.ts +2 -0
- package/dist/components/date-picker-popover.js +5 -3
- package/dist/components/details-grid.d.ts +21 -2
- package/dist/components/details-grid.js +75 -20
- package/dist/components/form.d.ts +9 -3
- package/dist/components/form.js +24 -3
- package/dist/components/input-control.d.ts +9 -0
- package/dist/components/input-control.js +10 -1
- package/dist/components/input.d.ts +7 -1
- package/dist/components/input.js +4 -4
- package/dist/components/label.d.ts +24 -2
- package/dist/components/label.js +21 -2
- package/dist/components/number-input.d.ts +9 -2
- package/dist/components/number-input.js +18 -6
- package/dist/components/textarea.d.ts +14 -1
- package/dist/components/textarea.js +11 -5
- package/dist/index.d.ts +10 -6
- package/dist/index.js +6 -3
- package/dist/kanban/index.d.ts +5 -2
- package/dist/kanban/index.js +4 -1
- package/dist/kanban/sortable-edge.d.ts +39 -0
- package/dist/kanban/sortable-edge.js +27 -0
- package/dist/kanban/sortable-interactions.d.ts +145 -0
- package/dist/kanban/sortable-interactions.js +503 -0
- package/dist/kanban/sortable-interactions.logic.d.ts +79 -0
- package/dist/kanban/sortable-interactions.logic.js +210 -0
- package/dist/kanban/subgroup-board.d.ts +27 -2
- package/dist/kanban/subgroup-board.js +26 -1
- package/dist/kanban/virtual-cell.d.ts +18 -1
- package/dist/kanban/virtual-cell.js +70 -17
- package/package.json +5 -1
|
@@ -5,6 +5,7 @@ import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
|
|
|
5
5
|
import { ScrollArea } from "./scroll-area.js";
|
|
6
6
|
import { containedHandler } from "../hooks/use-contained-handler.js";
|
|
7
7
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
8
|
+
import "../lib/field-trigger.js";
|
|
8
9
|
import { Input } from "./input.js";
|
|
9
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import * as React$1 from "react";
|
|
@@ -94,8 +95,8 @@ const COMBOBOX_ACTION_SIZE_CLASS_NAMES = {
|
|
|
94
95
|
[CONTROL_SIZE.default]: "end-0.5",
|
|
95
96
|
[CONTROL_SIZE.lg]: "end-0.5"
|
|
96
97
|
};
|
|
97
|
-
const ComboboxTrigger = ({ className, ...props }) => /* @__PURE__ */ jsx(Combobox$1.Trigger, {
|
|
98
|
-
className,
|
|
98
|
+
const ComboboxTrigger = ({ className, variant = "plain", ...props }) => /* @__PURE__ */ jsx(Combobox$1.Trigger, {
|
|
99
|
+
className: cn(variant === "field" && "border-input bg-background text-foreground ring-ring/24 focus-visible:border-ring aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 dark:aria-invalid:ring-destructive/24 relative inline-flex min-h-9 w-full min-w-36 items-center justify-center gap-2 rounded-lg border px-[calc(--spacing(3)-1px)] text-start text-base shadow-xs/5 outline-none select-none not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] focus-visible:ring-[3px] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-8 sm:text-sm dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none", className),
|
|
99
100
|
"data-slot": "combobox-trigger",
|
|
100
101
|
...props
|
|
101
102
|
});
|
|
@@ -39,6 +39,8 @@ type DatePickerPopoverProps = {
|
|
|
39
39
|
* the control still has height; pass a call-to-action ("Select date…") to
|
|
40
40
|
* make the empty state self-explanatory. */
|
|
41
41
|
placeholderLabel?: string;
|
|
42
|
+
/** Replaces the formatted date in the trigger, as a week range does. */
|
|
43
|
+
triggerLabel?: string;
|
|
42
44
|
clearLabel?: string;
|
|
43
45
|
defaultOpen?: boolean;
|
|
44
46
|
open?: boolean;
|
|
@@ -3,8 +3,8 @@ import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "../icons.js";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { DirectionalIcon } from "./directional-icon.js";
|
|
6
|
-
import { Input } from "./input.js";
|
|
7
6
|
import { FIELD_TRIGGER_CLASS_NAME } from "../lib/field-trigger.js";
|
|
7
|
+
import { Input } from "./input.js";
|
|
8
8
|
import { getLocaleWeekInfo, getWeekendDays } from "../lib/week.js";
|
|
9
9
|
import { DATE_PICKER_MODE, formatDateTimeValue, getHourOptions, getMinuteOptions, getTimeFieldNames, joinPickerTime, localDateFromTimestamp, millisecondsUntilNextLocalDate, parseDateTimeValue, resolveCalendarViewMonth, shiftCalendarDate, splitPickerTime } from "./date-picker-popover.logic.js";
|
|
10
10
|
import { Popover, PopoverContent as PopoverPopup, PopoverTrigger } from "./popover.js";
|
|
@@ -252,8 +252,10 @@ const PICKER_SIZE_CLASS_NAMES = {
|
|
|
252
252
|
cell: "pointer-coarse:min-h-11 pointer-coarse:text-sm"
|
|
253
253
|
}
|
|
254
254
|
};
|
|
255
|
+
/** The trigger keeps its height with an em dash when nothing has a label. */
|
|
256
|
+
const firstLabel = (...labels) => labels.find((label) => label !== null && label !== void 0) ?? "—";
|
|
255
257
|
const DatePickerPopoverContent = (props) => {
|
|
256
|
-
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, clearLabel = "Clear date", defaultOpen = false, open: controlledOpen, triggerRef, focusTriggerOnOpen = false, dateInputLabel, outOfRangeLabel, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
258
|
+
const { id, labelledBy, className, disabled, hideClear, size = "compact", variant = "inline", mode = DATE_PICKER_MODE.date, value: rawValue, onChange, locale, isOverdue = false, showIcon = true, placeholderLabel, triggerLabel, clearLabel = "Clear date", defaultOpen = false, open: controlledOpen, triggerRef, focusTriggerOnOpen = false, dateInputLabel, outOfRangeLabel, onOpenChange, todayLabel: todayLabelProp, overdueLabel, minDate, maxDate, isDateDisabled, layer = "default", today } = props;
|
|
257
259
|
const selection = resolveSelection(mode, rawValue);
|
|
258
260
|
const value = selection.date;
|
|
259
261
|
const labels = resolvePopupLabels(props);
|
|
@@ -300,7 +302,7 @@ const DatePickerPopoverContent = (props) => {
|
|
|
300
302
|
isDayDisabled,
|
|
301
303
|
onChange
|
|
302
304
|
});
|
|
303
|
-
const displayLabel = formatSelection(locale, selection)
|
|
305
|
+
const displayLabel = firstLabel(triggerLabel, formatSelection(locale, selection), placeholderLabel);
|
|
304
306
|
const selectDay = (date) => {
|
|
305
307
|
switch (selection.mode) {
|
|
306
308
|
case DATE_PICKER_MODE.date:
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
2
|
//#region src/components/details-grid.d.ts
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* - `sheet`: a fact sheet; small uppercase labels, up to three pairs per row
|
|
5
|
+
* as the container widens.
|
|
6
|
+
* - `list`: one pair per row in body type with a muted label, for a short
|
|
7
|
+
* list inside a card or dialog (who is signed in, which organization).
|
|
8
|
+
*/
|
|
9
|
+
type DetailsVariant = "sheet" | "list";
|
|
10
|
+
/**
|
|
11
|
+
* - `horizontal`: each label beside its value. In a `list`, every label
|
|
12
|
+
* shares one column sized to the longest.
|
|
13
|
+
* - `vertical`: each label above its value, for values too long to share a
|
|
14
|
+
* row (a server address, a list of origins).
|
|
15
|
+
*/
|
|
16
|
+
type DetailsOrientation = "horizontal" | "vertical";
|
|
17
|
+
type DetailsGridProps = React$1.ComponentProps<"dl"> & {
|
|
18
|
+
orientation?: DetailsOrientation;
|
|
19
|
+
variant?: DetailsVariant;
|
|
20
|
+
};
|
|
21
|
+
declare const DetailsGrid: ({ className, orientation, variant, ...props }: DetailsGridProps) => React$1.JSX.Element;
|
|
4
22
|
type DetailsItemProps = React$1.ComponentProps<"div"> & {
|
|
5
23
|
label: React$1.ReactNode;
|
|
24
|
+
/** `wide` spans every pair column of a `sheet`. */
|
|
6
25
|
span?: "wide";
|
|
7
26
|
};
|
|
8
27
|
declare const DetailsItem: ({ label, children, span, className, ...props }: DetailsItemProps) => React$1.JSX.Element;
|
|
9
28
|
//#endregion
|
|
10
|
-
export { DetailsGrid, DetailsItem };
|
|
29
|
+
export { DetailsGrid, DetailsItem, type DetailsOrientation, type DetailsVariant };
|
|
@@ -1,26 +1,81 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
3
4
|
//#region src/components/details-grid.tsx
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
children: /* @__PURE__ */ jsx("dl", {
|
|
8
|
-
className: cn("grid grid-cols-[repeat(auto-fit,minmax(min(100%,20rem),1fr))] gap-x-6 gap-y-2 font-sans @min-[64rem]:grid-cols-3", className),
|
|
9
|
-
"data-slot": "details-grid",
|
|
10
|
-
...props
|
|
11
|
-
})
|
|
12
|
-
});
|
|
13
|
-
const DetailsItem = ({ label, children, span, className, ...props }) => /* @__PURE__ */ jsxs("div", {
|
|
14
|
-
className: cn("grid min-w-0 grid-cols-[minmax(0,7rem)_minmax(0,1fr)] items-start gap-x-4 text-xs", span === "wide" && "col-span-full", className),
|
|
15
|
-
"data-slot": "details-item",
|
|
16
|
-
...props,
|
|
17
|
-
children: [/* @__PURE__ */ jsx("dt", {
|
|
18
|
-
className: "text-foreground-disabled min-w-0 font-medium tracking-wide wrap-anywhere uppercase",
|
|
19
|
-
children: label
|
|
20
|
-
}), /* @__PURE__ */ jsx("dd", {
|
|
21
|
-
className: "text-foreground-strong-muted min-w-0 wrap-anywhere",
|
|
22
|
-
children
|
|
23
|
-
})]
|
|
5
|
+
const DetailsShapeContext = createContext({
|
|
6
|
+
orientation: "horizontal",
|
|
7
|
+
variant: "sheet"
|
|
24
8
|
});
|
|
9
|
+
const LIST_CLASS_NAME = {
|
|
10
|
+
horizontal: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-x-4 gap-y-1 text-sm",
|
|
11
|
+
vertical: "flex min-w-0 flex-col gap-2 text-sm"
|
|
12
|
+
};
|
|
13
|
+
const ITEM_CLASS_NAME = {
|
|
14
|
+
list: {
|
|
15
|
+
horizontal: "col-span-full grid grid-cols-subgrid items-center",
|
|
16
|
+
vertical: "flex min-w-0 flex-col gap-2"
|
|
17
|
+
},
|
|
18
|
+
sheet: {
|
|
19
|
+
horizontal: "grid min-w-0 grid-cols-[minmax(0,7rem)_minmax(0,1fr)] items-start gap-x-4 text-xs",
|
|
20
|
+
vertical: "flex min-w-0 flex-col gap-1 text-xs"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const LABEL_CLASS_NAME = {
|
|
24
|
+
list: {
|
|
25
|
+
horizontal: "text-muted-foreground min-w-0",
|
|
26
|
+
vertical: "font-medium"
|
|
27
|
+
},
|
|
28
|
+
sheet: {
|
|
29
|
+
horizontal: "text-foreground-disabled min-w-0 font-medium tracking-wide wrap-anywhere uppercase",
|
|
30
|
+
vertical: "text-foreground-disabled min-w-0 font-medium tracking-wide wrap-anywhere uppercase"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const VALUE_CLASS_NAME = {
|
|
34
|
+
list: "min-w-0 wrap-anywhere",
|
|
35
|
+
sheet: "text-foreground-strong-muted min-w-0 wrap-anywhere"
|
|
36
|
+
};
|
|
37
|
+
const DetailsGrid = ({ className, orientation = "horizontal", variant = "sheet", ...props }) => {
|
|
38
|
+
const shape = {
|
|
39
|
+
orientation,
|
|
40
|
+
variant
|
|
41
|
+
};
|
|
42
|
+
if (variant === "list") return /* @__PURE__ */ jsx(DetailsShapeContext.Provider, {
|
|
43
|
+
value: shape,
|
|
44
|
+
children: /* @__PURE__ */ jsx("dl", {
|
|
45
|
+
className: cn(LIST_CLASS_NAME[orientation], className),
|
|
46
|
+
"data-orientation": orientation,
|
|
47
|
+
"data-slot": "details-grid",
|
|
48
|
+
"data-variant": variant,
|
|
49
|
+
...props
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
return /* @__PURE__ */ jsx(DetailsShapeContext.Provider, {
|
|
53
|
+
value: shape,
|
|
54
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
55
|
+
className: "@container min-w-0 [contain-intrinsic-inline-size:20rem]",
|
|
56
|
+
"data-slot": "details-grid-container",
|
|
57
|
+
children: /* @__PURE__ */ jsx("dl", {
|
|
58
|
+
className: cn("grid grid-cols-[repeat(auto-fit,minmax(min(100%,20rem),1fr))] gap-x-6 gap-y-2 font-sans @min-[64rem]:grid-cols-3", className),
|
|
59
|
+
"data-slot": "details-grid",
|
|
60
|
+
...props
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const DetailsItem = ({ label, children, span, className, ...props }) => {
|
|
66
|
+
const { orientation, variant } = useContext(DetailsShapeContext);
|
|
67
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
68
|
+
className: cn(ITEM_CLASS_NAME[variant][orientation], variant === "sheet" && span === "wide" && "col-span-full", className),
|
|
69
|
+
"data-slot": "details-item",
|
|
70
|
+
...props,
|
|
71
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
72
|
+
className: LABEL_CLASS_NAME[variant][orientation],
|
|
73
|
+
children: label
|
|
74
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
75
|
+
className: VALUE_CLASS_NAME[variant],
|
|
76
|
+
children
|
|
77
|
+
})]
|
|
78
|
+
});
|
|
79
|
+
};
|
|
25
80
|
//#endregion
|
|
26
81
|
export { DetailsGrid, DetailsItem };
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { Form as Form$1 } from "@base-ui/react/form";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
2
3
|
//#region src/components/form.d.ts
|
|
3
|
-
declare const
|
|
4
|
+
declare const formVariants: (props?: ({
|
|
5
|
+
density?: "compact" | "default" | "snug" | null | undefined;
|
|
6
|
+
layout?: "inline" | "stack" | null | undefined;
|
|
7
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
|
+
type FormProps = Form$1.Props & VariantProps<typeof formVariants> & {
|
|
4
9
|
dirty?: boolean;
|
|
5
10
|
onDiscard?: (() => void) | undefined;
|
|
6
|
-
}
|
|
11
|
+
};
|
|
12
|
+
declare const Form: ({ className, density, layout, dirty, onDiscard, children, ...props }: FormProps) => import("react").JSX.Element;
|
|
7
13
|
//#endregion
|
|
8
|
-
export { Form };
|
|
14
|
+
export { Form, type FormProps, formVariants };
|
package/dist/components/form.js
CHANGED
|
@@ -2,10 +2,31 @@
|
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { DialogFormState } from "./dialog-form-state.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
5
6
|
import { Form as Form$1 } from "@base-ui/react/form";
|
|
6
7
|
//#region src/components/form.tsx
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const formVariants = cva("flex w-full", {
|
|
9
|
+
defaultVariants: {
|
|
10
|
+
density: "default",
|
|
11
|
+
layout: "stack"
|
|
12
|
+
},
|
|
13
|
+
variants: {
|
|
14
|
+
density: {
|
|
15
|
+
default: "gap-4",
|
|
16
|
+
snug: "gap-3",
|
|
17
|
+
compact: "gap-2"
|
|
18
|
+
},
|
|
19
|
+
layout: {
|
|
20
|
+
stack: "flex-col",
|
|
21
|
+
inline: "flex-row items-center"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const Form = ({ className, density, layout, dirty = false, onDiscard, children, ...props }) => /* @__PURE__ */ jsxs(Form$1, {
|
|
26
|
+
className: cn(formVariants({
|
|
27
|
+
density,
|
|
28
|
+
layout
|
|
29
|
+
}), className),
|
|
9
30
|
"data-slot": "form",
|
|
10
31
|
...props,
|
|
11
32
|
children: [/* @__PURE__ */ jsx(DialogFormState, {
|
|
@@ -14,4 +35,4 @@ const Form = ({ className, dirty = false, onDiscard, children, ...props }) => /*
|
|
|
14
35
|
}), children]
|
|
15
36
|
});
|
|
16
37
|
//#endregion
|
|
17
|
-
export { Form };
|
|
38
|
+
export { Form, formVariants };
|
|
@@ -16,4 +16,13 @@ export declare const INPUT_SIZE_CLASS_NAMES: {
|
|
|
16
16
|
readonly default: undefined;
|
|
17
17
|
readonly lg: "min-h-9.5 leading-9.5 sm:min-h-8.5 sm:leading-8.5";
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* A field edited where it is read: an option's name in a list, a page
|
|
21
|
+
* heading, the summary under it. No box until focused, the type, leading and
|
|
22
|
+
* colour of the text around it, so the row keeps the height of its text; a
|
|
23
|
+
* focus ring marks it while editing. A slim inline inset gives the ring and
|
|
24
|
+
* the caret room, and a matching negative margin keeps the text in line with
|
|
25
|
+
* its neighbours.
|
|
26
|
+
*/
|
|
27
|
+
export declare const INLINE_FIELD_CLASS_NAME = "placeholder:text-foreground-placeholder focus-visible:ring-ring/30 -mx-1 min-h-0 w-full min-w-0 rounded-sm bg-transparent px-1 py-0 font-[inherit] text-[length:inherit] leading-[inherit] text-inherit outline-none focus-visible:ring-2";
|
|
19
28
|
//#endregion
|
|
@@ -17,5 +17,14 @@ const INPUT_SIZE_CLASS_NAMES = {
|
|
|
17
17
|
[CONTROL_SIZE.default]: void 0,
|
|
18
18
|
[CONTROL_SIZE.lg]: "min-h-9.5 leading-9.5 sm:min-h-8.5 sm:leading-8.5"
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* A field edited where it is read: an option's name in a list, a page
|
|
22
|
+
* heading, the summary under it. No box until focused, the type, leading and
|
|
23
|
+
* colour of the text around it, so the row keeps the height of its text; a
|
|
24
|
+
* focus ring marks it while editing. A slim inline inset gives the ring and
|
|
25
|
+
* the caret room, and a matching negative margin keeps the text in line with
|
|
26
|
+
* its neighbours.
|
|
27
|
+
*/
|
|
28
|
+
const INLINE_FIELD_CLASS_NAME = "placeholder:text-foreground-placeholder focus-visible:ring-ring/30 -mx-1 min-h-0 w-full min-w-0 rounded-sm bg-transparent px-1 py-0 font-[inherit] text-[length:inherit] leading-[inherit] text-inherit outline-none focus-visible:ring-2";
|
|
20
29
|
//#endregion
|
|
21
|
-
export { INPUT_CONTROL_CLASS_NAME, INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES };
|
|
30
|
+
export { INLINE_FIELD_CLASS_NAME, INPUT_CONTROL_CLASS_NAME, INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES };
|
|
@@ -7,8 +7,14 @@ type InputProps = Omit<Input$1.Props & React$1.RefAttributes<HTMLInputElement>,
|
|
|
7
7
|
size?: ControlSize | number;
|
|
8
8
|
style?: React$1.CSSProperties;
|
|
9
9
|
unstyled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A borderless field in a line of text: it edits a value where it is read,
|
|
12
|
+
* with no box until focused and the parent's type, leading and colour. See
|
|
13
|
+
* `INLINE_FIELD_CLASS_NAME`.
|
|
14
|
+
*/
|
|
15
|
+
inline?: boolean;
|
|
10
16
|
nativeInput?: boolean;
|
|
11
17
|
};
|
|
12
|
-
declare const Input: ({ className, size, unstyled, nativeInput, dir, onChange, ...props }: InputProps) => React$1.JSX.Element;
|
|
18
|
+
declare const Input: ({ className, size, unstyled, inline, nativeInput, dir, onChange, ...props }: InputProps) => React$1.JSX.Element;
|
|
13
19
|
//#endregion
|
|
14
20
|
export { Input, type InputProps };
|
package/dist/components/input.js
CHANGED
|
@@ -3,11 +3,11 @@ import { SearchIcon } from "../icons.js";
|
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
5
5
|
import { isStructuredInputType, useContentDir } from "../hooks/use-content-dir.js";
|
|
6
|
-
import { INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES } from "./input-control.js";
|
|
6
|
+
import { INLINE_FIELD_CLASS_NAME, INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES } from "./input-control.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Input as Input$1 } from "@base-ui/react/input";
|
|
9
9
|
//#region src/components/input.tsx
|
|
10
|
-
const Input = ({ className, size = CONTROL_SIZE.default, unstyled = false, nativeInput = false, dir, onChange, ...props }) => {
|
|
10
|
+
const Input = ({ className, size = CONTROL_SIZE.default, unstyled = false, inline = false, nativeInput = false, dir, onChange, ...props }) => {
|
|
11
11
|
const controlSize = typeof size === "number" ? CONTROL_SIZE.default : size;
|
|
12
12
|
const contentDir = useContentDir({
|
|
13
13
|
dir: dir ?? (isStructuredInputType(props.type) ? "ltr" : void 0),
|
|
@@ -18,9 +18,9 @@ const Input = ({ className, size = CONTROL_SIZE.default, unstyled = false, nativ
|
|
|
18
18
|
contentDir.trackValue(event.currentTarget.value);
|
|
19
19
|
onChange?.(event);
|
|
20
20
|
};
|
|
21
|
-
const inputClassName = cn(INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES[controlSize], unstyled && "px-0", props.type === "search" && "ps-8 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none");
|
|
21
|
+
const inputClassName = cn(inline ? INLINE_FIELD_CLASS_NAME : INPUT_ELEMENT_CLASS_NAME, !inline && INPUT_SIZE_CLASS_NAMES[controlSize], unstyled && !inline && "px-0", props.type === "search" && "ps-8 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none");
|
|
22
22
|
return /* @__PURE__ */ jsxs("span", {
|
|
23
|
-
className: cn(!unstyled && "border-input bg-background text-foreground ring-ring/24 has-autofill:bg-foreground/4 has-focus-visible:border-ring has-aria-invalid:border-destructive/36 has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 relative inline-flex w-full rounded-lg border text-base shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:ring-[3px] has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none sm:text-sm dark:not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:min-h-11", className) || void 0,
|
|
23
|
+
className: cn(inline ? "relative inline-flex w-full" : !unstyled && "border-input bg-background text-foreground ring-ring/24 has-autofill:bg-foreground/4 has-focus-visible:border-ring has-aria-invalid:border-destructive/36 has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 relative inline-flex w-full rounded-lg border text-base shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:ring-[3px] has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none sm:text-sm dark:not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] pointer-coarse:min-h-11", className) || void 0,
|
|
24
24
|
"data-size": size,
|
|
25
25
|
"data-slot": "input-control",
|
|
26
26
|
dir: contentDir.dir,
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import { useRender } from "@base-ui/react/use-render";
|
|
2
2
|
//#region src/components/label.d.ts
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* - `default`: the caption above or beside a control.
|
|
5
|
+
* - `choice`: one row of a list of choices, wrapping its radio or checkbox so
|
|
6
|
+
* the whole row picks it; it highlights on hover and rings while the
|
|
7
|
+
* control inside has keyboard focus.
|
|
8
|
+
* - `choice-card`: a bordered card wrapping its radio, for choices that carry
|
|
9
|
+
* a description; the chosen card takes the primary border.
|
|
10
|
+
* - `muted`: a quiet caption in small type, for a filter bar or a dense side
|
|
11
|
+
* form where the controls, not their names, carry the eye.
|
|
12
|
+
*
|
|
13
|
+
* The choice variants dim and stop inviting a click while their control is
|
|
14
|
+
* disabled.
|
|
15
|
+
*/
|
|
16
|
+
declare const LABEL_VARIANT_CLASS_NAME: {
|
|
17
|
+
readonly default: "text-foreground inline-flex items-center gap-2 text-base/4.5 font-medium sm:text-sm/4";
|
|
18
|
+
readonly choice: "hover:bg-muted/60 has-focus-visible:ring-ring flex min-h-11 cursor-pointer items-center gap-2 rounded-sm px-1 py-1.5 text-sm has-focus-visible:ring-2 has-disabled:cursor-not-allowed has-disabled:opacity-64";
|
|
19
|
+
readonly "choice-card": "has-checked:border-primary has-checked:bg-muted/50 flex min-h-11 cursor-pointer items-start gap-3 rounded-md border p-3 has-disabled:cursor-not-allowed has-disabled:opacity-50";
|
|
20
|
+
readonly muted: "text-muted-foreground inline-flex items-center gap-2 text-xs/4 font-medium";
|
|
21
|
+
};
|
|
22
|
+
type LabelVariant = keyof typeof LABEL_VARIANT_CLASS_NAME;
|
|
23
|
+
declare function Label({ className, render, variant, ...props }: useRender.ComponentProps<"label"> & {
|
|
24
|
+
variant?: LabelVariant;
|
|
25
|
+
}): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
4
26
|
//#endregion
|
|
5
|
-
export { Label };
|
|
27
|
+
export { Label, type LabelVariant };
|
package/dist/components/label.js
CHANGED
|
@@ -3,9 +3,28 @@ import { cn } from "../lib/utils.js";
|
|
|
3
3
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
4
4
|
import { useRender } from "@base-ui/react/use-render";
|
|
5
5
|
//#region src/components/label.tsx
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* - `default`: the caption above or beside a control.
|
|
8
|
+
* - `choice`: one row of a list of choices, wrapping its radio or checkbox so
|
|
9
|
+
* the whole row picks it; it highlights on hover and rings while the
|
|
10
|
+
* control inside has keyboard focus.
|
|
11
|
+
* - `choice-card`: a bordered card wrapping its radio, for choices that carry
|
|
12
|
+
* a description; the chosen card takes the primary border.
|
|
13
|
+
* - `muted`: a quiet caption in small type, for a filter bar or a dense side
|
|
14
|
+
* form where the controls, not their names, carry the eye.
|
|
15
|
+
*
|
|
16
|
+
* The choice variants dim and stop inviting a click while their control is
|
|
17
|
+
* disabled.
|
|
18
|
+
*/
|
|
19
|
+
const LABEL_VARIANT_CLASS_NAME = {
|
|
20
|
+
default: "text-foreground inline-flex items-center gap-2 text-base/4.5 font-medium sm:text-sm/4",
|
|
21
|
+
choice: "hover:bg-muted/60 has-focus-visible:ring-ring flex min-h-11 cursor-pointer items-center gap-2 rounded-sm px-1 py-1.5 text-sm has-focus-visible:ring-2 has-disabled:cursor-not-allowed has-disabled:opacity-64",
|
|
22
|
+
"choice-card": "has-checked:border-primary has-checked:bg-muted/50 flex min-h-11 cursor-pointer items-start gap-3 rounded-md border p-3 has-disabled:cursor-not-allowed has-disabled:opacity-50",
|
|
23
|
+
muted: "text-muted-foreground inline-flex items-center gap-2 text-xs/4 font-medium"
|
|
24
|
+
};
|
|
25
|
+
function Label({ className, render, variant = "default", ...props }) {
|
|
7
26
|
const defaultProps = {
|
|
8
|
-
className: cn(
|
|
27
|
+
className: cn(LABEL_VARIANT_CLASS_NAME[variant], className),
|
|
9
28
|
"data-slot": "label"
|
|
10
29
|
};
|
|
11
30
|
return useRender({
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { ControlSize } from "../lib/control-size.js";
|
|
2
2
|
import { NumberField } from "@base-ui/react/number-field";
|
|
3
|
+
import { ReactNode } from "react";
|
|
3
4
|
//#region src/components/number-input.d.ts
|
|
4
5
|
type NumberInputProps = Omit<NumberField.Root.Props, "children" | "className" | "render"> & {
|
|
5
6
|
className?: string;
|
|
6
7
|
inputClassName?: string;
|
|
7
|
-
inputProps?: Omit<NumberField.Input.Props, "className" | "defaultValue" | "id" | "onChange" | "type" | "value">;
|
|
8
|
+
inputProps?: Omit<NumberField.Input.Props, "className" | "defaultValue" | "id" | "onChange" | "render" | "type" | "value">;
|
|
8
9
|
size?: ControlSize;
|
|
10
|
+
/**
|
|
11
|
+
* The unit the value is in ("pt", "%"), shown muted at the end of the
|
|
12
|
+
* control so the number reads with its unit while the typed text stays a
|
|
13
|
+
* bare number.
|
|
14
|
+
*/
|
|
15
|
+
suffix?: ReactNode;
|
|
9
16
|
};
|
|
10
17
|
/**
|
|
11
18
|
* A numeric control that keeps the user's editable text separate from its
|
|
12
19
|
* canonical numeric value. Parsed changes are reported through `onValueChange`;
|
|
13
20
|
* formatting and clamping happen only at Base UI's commit boundaries.
|
|
14
21
|
*/
|
|
15
|
-
declare const NumberInput: ({ className, inputClassName, inputProps, dir, size, ...props }: NumberInputProps) => import("react").JSX.Element;
|
|
22
|
+
declare const NumberInput: ({ className, inputClassName, inputProps, dir, size, suffix, ...props }: NumberInputProps) => import("react").JSX.Element;
|
|
16
23
|
//#endregion
|
|
17
24
|
export { NumberInput, type NumberInputProps };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
4
4
|
import { INPUT_CONTROL_CLASS_NAME, INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES } from "./input-control.js";
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { NumberField } from "@base-ui/react/number-field";
|
|
7
7
|
//#region src/components/number-input.tsx
|
|
8
8
|
/**
|
|
@@ -10,18 +10,30 @@ import { NumberField } from "@base-ui/react/number-field";
|
|
|
10
10
|
* canonical numeric value. Parsed changes are reported through `onValueChange`;
|
|
11
11
|
* formatting and clamping happen only at Base UI's commit boundaries.
|
|
12
12
|
*/
|
|
13
|
-
const NumberInput = ({ className, inputClassName, inputProps, dir = "ltr", size = CONTROL_SIZE.default, ...props }) => /* @__PURE__ */
|
|
13
|
+
const NumberInput = ({ className, inputClassName, inputProps, dir = "ltr", size = CONTROL_SIZE.default, suffix, ...props }) => /* @__PURE__ */ jsxs(NumberField.Root, {
|
|
14
14
|
...props,
|
|
15
15
|
className: cn(INPUT_CONTROL_CLASS_NAME, className),
|
|
16
16
|
"data-size": size,
|
|
17
17
|
"data-slot": "number-input-control",
|
|
18
18
|
dir,
|
|
19
19
|
render: void 0,
|
|
20
|
-
children: /* @__PURE__ */ jsx(NumberField.Input, {
|
|
20
|
+
children: [/* @__PURE__ */ jsx(NumberField.Input, {
|
|
21
21
|
...inputProps,
|
|
22
|
-
className: cn(INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES[size], inputClassName),
|
|
23
|
-
"data-slot": "number-input"
|
|
24
|
-
|
|
22
|
+
className: cn(INPUT_ELEMENT_CLASS_NAME, INPUT_SIZE_CLASS_NAMES[size], "tabular-nums", suffix !== void 0 && "pe-1", inputClassName),
|
|
23
|
+
"data-slot": "number-input",
|
|
24
|
+
render: (inputElementProps, state) => /* @__PURE__ */ jsx("input", {
|
|
25
|
+
...inputElementProps,
|
|
26
|
+
"aria-valuemax": props.max,
|
|
27
|
+
"aria-valuemin": props.min,
|
|
28
|
+
"aria-valuenow": state.value ?? void 0,
|
|
29
|
+
dir,
|
|
30
|
+
role: "spinbutton"
|
|
31
|
+
})
|
|
32
|
+
}), suffix === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
33
|
+
className: "text-muted-foreground pointer-events-none flex shrink-0 items-center pe-2.5 text-xs",
|
|
34
|
+
"data-slot": "number-input-suffix",
|
|
35
|
+
children: suffix
|
|
36
|
+
})]
|
|
25
37
|
});
|
|
26
38
|
//#endregion
|
|
27
39
|
export { NumberInput };
|
|
@@ -4,7 +4,20 @@ import { ControlSize } from "../lib/control-size.js";
|
|
|
4
4
|
type TextareaProps = React$1.ComponentProps<"textarea"> & {
|
|
5
5
|
size?: ControlSize | number;
|
|
6
6
|
unstyled?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* A borderless field in a line of text: it edits text where it is read
|
|
9
|
+
* (the summary under a heading), with no box until focused, the parent's
|
|
10
|
+
* type and colour, no floor height, and grows with its content. See
|
|
11
|
+
* `INLINE_FIELD_CLASS_NAME`.
|
|
12
|
+
*/
|
|
13
|
+
inline?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* `content` (default) grows with what is typed. `fixed` keeps the height
|
|
16
|
+
* its `rows` give and scrolls, with no resize grip: an editor inside a
|
|
17
|
+
* table cell or a row must not push its neighbours around.
|
|
18
|
+
*/
|
|
19
|
+
fieldSizing?: "content" | "fixed";
|
|
7
20
|
};
|
|
8
|
-
declare const Textarea: ({ className, size, unstyled, dir, onChange, ...props }: TextareaProps) => React$1.JSX.Element;
|
|
21
|
+
declare const Textarea: ({ className, size, unstyled, inline, fieldSizing, dir, onChange, ...props }: TextareaProps) => React$1.JSX.Element;
|
|
9
22
|
//#endregion
|
|
10
23
|
export { Textarea, type TextareaProps };
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { CONTROL_SIZE } from "../lib/control-size.js";
|
|
4
4
|
import { useContentDir } from "../hooks/use-content-dir.js";
|
|
5
|
+
import { INLINE_FIELD_CLASS_NAME } from "./input-control.js";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
7
8
|
import { Field } from "@base-ui/react/field";
|
|
8
9
|
//#region src/components/textarea.tsx
|
|
9
|
-
const Textarea = ({ className, size = CONTROL_SIZE.default, unstyled = false, dir, onChange, ...props }) => {
|
|
10
|
+
const Textarea = ({ className, size = CONTROL_SIZE.default, unstyled = false, inline = false, fieldSizing = "content", dir, onChange, ...props }) => {
|
|
10
11
|
const controlSize = typeof size === "number" ? CONTROL_SIZE.default : size;
|
|
11
12
|
const contentDir = useContentDir({
|
|
12
13
|
dir,
|
|
@@ -18,11 +19,11 @@ const Textarea = ({ className, size = CONTROL_SIZE.default, unstyled = false, di
|
|
|
18
19
|
onChange?.(event);
|
|
19
20
|
};
|
|
20
21
|
return /* @__PURE__ */ jsx("span", {
|
|
21
|
-
className: cn(!unstyled && "border-input bg-background text-foreground ring-ring/24 has-focus-visible:border-ring has-aria-invalid:border-destructive/36 has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 relative inline-flex w-full rounded-lg border text-base shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:ring-[3px] has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none sm:text-sm dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]", className) || void 0,
|
|
22
|
+
className: cn(inline && "relative inline-flex w-full", !unstyled && !inline && "border-input bg-background text-foreground ring-ring/24 has-focus-visible:border-ring has-aria-invalid:border-destructive/36 has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 dark:bg-input/32 dark:has-aria-invalid:ring-destructive/24 relative inline-flex w-full rounded-lg border text-base shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-focus-visible:ring-[3px] has-disabled:opacity-64 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none sm:text-sm dark:not-has-disabled:has-not-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)]", className) || void 0,
|
|
22
23
|
"data-size": size,
|
|
23
24
|
"data-slot": "textarea-control",
|
|
24
25
|
children: /* @__PURE__ */ jsx(Field.Control, { render: (defaultProps) => /* @__PURE__ */ jsx("textarea", {
|
|
25
|
-
className: cn("
|
|
26
|
+
className: cn(inline ? cn(INLINE_FIELD_CLASS_NAME, "resize-none") : cn("w-full rounded-[inherit] px-[calc(--spacing(3)-1px)] py-[calc(--spacing(1.5)-1px)] outline-none", TEXTAREA_SIZE_CLASS_NAMES[controlSize], fieldSizing === "content" && TEXTAREA_FLOOR_CLASS_NAMES[controlSize]), fieldSizing === "content" ? "field-sizing-content" : "field-sizing-fixed resize-none"),
|
|
26
27
|
"data-slot": "textarea",
|
|
27
28
|
...mergeProps(defaultProps, props, { onChange: handleChange }),
|
|
28
29
|
dir: contentDir.dir
|
|
@@ -30,9 +31,14 @@ const Textarea = ({ className, size = CONTROL_SIZE.default, unstyled = false, di
|
|
|
30
31
|
});
|
|
31
32
|
};
|
|
32
33
|
const TEXTAREA_SIZE_CLASS_NAMES = {
|
|
33
|
-
[CONTROL_SIZE.sm]: "
|
|
34
|
+
[CONTROL_SIZE.sm]: "px-[calc(--spacing(2.5)-1px)] py-[calc(--spacing(1)-1px)]",
|
|
34
35
|
[CONTROL_SIZE.default]: void 0,
|
|
35
|
-
[CONTROL_SIZE.lg]: "
|
|
36
|
+
[CONTROL_SIZE.lg]: "py-[calc(--spacing(2)-1px)]"
|
|
37
|
+
};
|
|
38
|
+
const TEXTAREA_FLOOR_CLASS_NAMES = {
|
|
39
|
+
[CONTROL_SIZE.sm]: "min-h-16.5 max-sm:min-h-19.5",
|
|
40
|
+
[CONTROL_SIZE.default]: "min-h-17.5 max-sm:min-h-20.5",
|
|
41
|
+
[CONTROL_SIZE.lg]: "min-h-18.5 max-sm:min-h-21.5"
|
|
36
42
|
};
|
|
37
43
|
//#endregion
|
|
38
44
|
export { Textarea };
|