@mlola-ui/registry 1.0.2 → 1.0.4
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/components/accordion.json +2 -2
- package/components/alert.json +2 -2
- package/components/app-shell.json +1 -1
- package/components/badge.json +2 -2
- package/components/breadcrumb.json +2 -2
- package/components/button.json +1 -1
- package/components/calendar.json +1 -1
- package/components/carousel.json +3 -3
- package/components/checkbox.json +2 -2
- package/components/color-picker.json +5 -5
- package/components/combobox.json +2 -2
- package/components/command-menu.json +1 -1
- package/components/context-menu.json +1 -1
- package/components/date-picker.json +2 -2
- package/components/dropdown-menu.json +2 -2
- package/components/dropzone.json +2 -2
- package/components/empty-state.json +1 -1
- package/components/hover-card.json +1 -1
- package/components/modal.json +2 -2
- package/components/number-input.json +2 -2
- package/components/otp-input.json +1 -1
- package/components/pagination.json +1 -1
- package/components/progress.json +1 -1
- package/components/segmented-control.json +2 -2
- package/components/select.json +3 -3
- package/components/sheet.json +2 -2
- package/components/slider.json +1 -1
- package/components/status-icon.json +1 -1
- package/components/stepper.json +2 -2
- package/components/tabs.json +1 -1
- package/components/tag-input.json +2 -2
- package/components/textarea.json +1 -1
- package/components/time-picker.json +2 -2
- package/components/timeline.json +2 -2
- package/components/toast.json +2 -2
- package/components/toggle.json +1 -1
- package/components/tour.json +2 -2
- package/generated/catalog.ts +40 -40
- package/index.json +1 -1
- package/package.json +1 -1
- package/snapshot/index.json +65 -65
- package/snapshot/items/components/accordion.json +2 -2
- package/snapshot/items/components/alert.json +2 -2
- package/snapshot/items/components/app-shell.json +1 -1
- package/snapshot/items/components/badge.json +2 -2
- package/snapshot/items/components/breadcrumb.json +2 -2
- package/snapshot/items/components/button.json +1 -1
- package/snapshot/items/components/calendar.json +1 -1
- package/snapshot/items/components/carousel.json +3 -3
- package/snapshot/items/components/checkbox.json +2 -2
- package/snapshot/items/components/color-picker.json +5 -5
- package/snapshot/items/components/combobox.json +2 -2
- package/snapshot/items/components/command-menu.json +1 -1
- package/snapshot/items/components/context-menu.json +1 -1
- package/snapshot/items/components/date-picker.json +2 -2
- package/snapshot/items/components/dropdown-menu.json +2 -2
- package/snapshot/items/components/dropzone.json +2 -2
- package/snapshot/items/components/empty-state.json +1 -1
- package/snapshot/items/components/hover-card.json +1 -1
- package/snapshot/items/components/modal.json +2 -2
- package/snapshot/items/components/number-input.json +2 -2
- package/snapshot/items/components/otp-input.json +1 -1
- package/snapshot/items/components/pagination.json +1 -1
- package/snapshot/items/components/progress.json +1 -1
- package/snapshot/items/components/segmented-control.json +2 -2
- package/snapshot/items/components/select.json +3 -3
- package/snapshot/items/components/sheet.json +2 -2
- package/snapshot/items/components/slider.json +1 -1
- package/snapshot/items/components/status-icon.json +1 -1
- package/snapshot/items/components/stepper.json +2 -2
- package/snapshot/items/components/tabs.json +1 -1
- package/snapshot/items/components/tag-input.json +2 -2
- package/snapshot/items/components/textarea.json +1 -1
- package/snapshot/items/components/time-picker.json +2 -2
- package/snapshot/items/components/timeline.json +2 -2
- package/snapshot/items/components/toast.json +2 -2
- package/snapshot/items/components/toggle.json +1 -1
- package/snapshot/items/components/tour.json +2 -2
- package/snapshot/source/packages/components/alert/alert.tsx +1 -1
- package/snapshot/source/packages/components/badge/badge.tsx +2 -2
- package/snapshot/source/packages/components/breadcrumb/breadcrumb.tsx +1 -1
- package/snapshot/source/packages/components/carousel/carousel.tsx +1 -0
- package/snapshot/source/packages/components/checkbox/checkbox.tsx +3 -2
- package/snapshot/source/packages/components/color-picker/color-picker.tsx +15 -13
- package/snapshot/source/packages/components/color-picker/color.ts +1 -1
- package/snapshot/source/packages/components/combobox/combobox.tsx +33 -6
- package/snapshot/source/packages/components/date-picker/date-picker.tsx +5 -3
- package/snapshot/source/packages/components/dropzone/dropzone.tsx +1 -1
- package/snapshot/source/packages/components/empty-state/empty-state.tsx +1 -1
- package/snapshot/source/packages/components/hover-card/hover-card.tsx +1 -1
- package/snapshot/source/packages/components/number-input/number-input.tsx +6 -5
- package/snapshot/source/packages/components/otp-input/otp-input.tsx +6 -4
- package/snapshot/source/packages/components/progress/progress.tsx +1 -1
- package/snapshot/source/packages/components/segmented-control/segmented-control.tsx +5 -3
- package/snapshot/source/packages/components/select/select.tsx +6 -5
- package/snapshot/source/packages/components/tag-input/tag-input.tsx +7 -6
- package/snapshot/source/packages/components/textarea/textarea.tsx +1 -1
- package/snapshot/source/packages/components/time-picker/time-picker.tsx +8 -5
- package/snapshot/source/packages/components/timeline/timeline.tsx +1 -1
- package/snapshot/source/packages/components/toast/toast.tsx +1 -1
- package/snapshot/source/packages/components/toggle/toggle.tsx +1 -0
- package/snapshot/source/packages/components/tour/tour.tsx +3 -3
- package/snapshot/source-manifest.json +24 -24
|
@@ -116,11 +116,36 @@ export function ComboboxList({ id, options, active, onActiveChange, onPick, isSe
|
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
/**
|
|
119
|
+
/** The visible band a list can open into: the viewport, cut down by any ancestor that clips its content. */
|
|
120
|
+
function visibleBand(element: HTMLElement) {
|
|
121
|
+
let top = 0;
|
|
122
|
+
let bottom = window.innerHeight;
|
|
123
|
+
for (let node = element.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
124
|
+
const style = getComputedStyle(node);
|
|
125
|
+
if (/(hidden|clip|auto|scroll)/.test(style.overflowY) || /(hidden|clip|auto|scroll)/.test(style.overflow)) {
|
|
126
|
+
const rect = node.getBoundingClientRect();
|
|
127
|
+
top = Math.max(top, rect.top);
|
|
128
|
+
bottom = Math.min(bottom, rect.bottom);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { top, bottom };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Open the list below the field unless there is too little room there and
|
|
136
|
+
* more above, counting only space that is actually visible (a card that clips
|
|
137
|
+
* its content counts as an edge). The room found is left on the field as
|
|
138
|
+
* --ml-combobox-room, so the list never grows past what can be seen.
|
|
139
|
+
*/
|
|
120
140
|
export function sideFor(element: HTMLElement | null, room = 280): "top" | "bottom" {
|
|
121
141
|
if (!element) return "bottom";
|
|
122
142
|
const rect = element.getBoundingClientRect();
|
|
123
|
-
|
|
143
|
+
const band = visibleBand(element);
|
|
144
|
+
const below = band.bottom - rect.bottom - 12;
|
|
145
|
+
const above = rect.top - band.top - 12;
|
|
146
|
+
const side = below < room && above > below ? "top" : "bottom";
|
|
147
|
+
element.style.setProperty("--ml-combobox-room", `${Math.max(120, Math.floor(side === "top" ? above : below))}px`);
|
|
148
|
+
return side;
|
|
124
149
|
}
|
|
125
150
|
|
|
126
151
|
export interface ComboboxProps {
|
|
@@ -148,7 +173,7 @@ export interface ComboboxProps {
|
|
|
148
173
|
* long lists such as countries, people or repositories. Arrows move, Enter
|
|
149
174
|
* chooses, Escape restores; with `onCreate`, a missing option can be added.
|
|
150
175
|
*/
|
|
151
|
-
export function Combobox({
|
|
176
|
+
export const Combobox = React.forwardRef<HTMLInputElement, ComboboxProps>(function Combobox({
|
|
152
177
|
options,
|
|
153
178
|
value,
|
|
154
179
|
defaultValue = null,
|
|
@@ -164,7 +189,7 @@ export function Combobox({
|
|
|
164
189
|
required,
|
|
165
190
|
id,
|
|
166
191
|
className,
|
|
167
|
-
}: ComboboxProps) {
|
|
192
|
+
}: ComboboxProps, ref) {
|
|
168
193
|
const autoId = React.useId();
|
|
169
194
|
const fieldId = id ?? autoId;
|
|
170
195
|
const listId = `${fieldId}-list`;
|
|
@@ -253,6 +278,7 @@ export function Combobox({
|
|
|
253
278
|
</span>
|
|
254
279
|
) : null}
|
|
255
280
|
<input
|
|
281
|
+
ref={ref}
|
|
256
282
|
id={fieldId}
|
|
257
283
|
className="ml-input ml-combobox-input"
|
|
258
284
|
data-leading={chosen?.leading && !typed.current ? "" : undefined}
|
|
@@ -286,7 +312,7 @@ export function Combobox({
|
|
|
286
312
|
{clearable && current && !disabled ? (
|
|
287
313
|
<button
|
|
288
314
|
type="button"
|
|
289
|
-
className="ml-combobox-clear"
|
|
315
|
+
className="ml-combobox-clear" data-hit="expand"
|
|
290
316
|
aria-label="Clear"
|
|
291
317
|
tabIndex={-1}
|
|
292
318
|
onMouseDown={(event) => event.preventDefault()}
|
|
@@ -317,4 +343,5 @@ export function Combobox({
|
|
|
317
343
|
</div>
|
|
318
344
|
</Field>
|
|
319
345
|
);
|
|
320
|
-
}
|
|
346
|
+
});
|
|
347
|
+
Combobox.displayName = "Combobox";
|
|
@@ -78,7 +78,7 @@ export function rangePresets(today = todayOf()): DatePreset[] {
|
|
|
78
78
|
* beside two months. The field shows the choice in the reader's own format;
|
|
79
79
|
* the value stays an ISO date, so it round-trips through forms and APIs.
|
|
80
80
|
*/
|
|
81
|
-
export function DatePicker(props: DatePickerProps) {
|
|
81
|
+
export const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(function DatePicker(props: DatePickerProps, ref) {
|
|
82
82
|
const { label, hint, error, placeholder, min, max, disabledDates, enabledDates, isDisabled, presets, weekStart, locale = "en", clearable = true, disabled, required, id, today, className } = props;
|
|
83
83
|
const mode = props.mode ?? "single";
|
|
84
84
|
const autoId = React.useId();
|
|
@@ -111,6 +111,7 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
111
111
|
|
|
112
112
|
const trigger = (
|
|
113
113
|
<button
|
|
114
|
+
ref={ref}
|
|
114
115
|
id={fieldId}
|
|
115
116
|
type="button"
|
|
116
117
|
className="ml-input ml-date-picker-trigger"
|
|
@@ -175,11 +176,12 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
175
176
|
</div>
|
|
176
177
|
</Popover>
|
|
177
178
|
{clearable && text && !disabled ? (
|
|
178
|
-
<button type="button" className="ml-date-picker-clear" aria-label="Clear date" onClick={() => commit(null)}>
|
|
179
|
+
<button type="button" className="ml-date-picker-clear" data-hit="expand" aria-label="Clear date" onClick={() => commit(null)}>
|
|
179
180
|
<IconX aria-hidden="true" size="0.9em" />
|
|
180
181
|
</button>
|
|
181
182
|
) : null}
|
|
182
183
|
</div>
|
|
183
184
|
</Field>
|
|
184
185
|
);
|
|
185
|
-
}
|
|
186
|
+
});
|
|
187
|
+
DatePicker.displayName = "DatePicker";
|
|
@@ -161,7 +161,7 @@ export function Dropzone({ onFiles, files = [], onRemove, accept, maxSize, maxFi
|
|
|
161
161
|
{file.status === "error" ? file.error ?? "Upload failed" : file.status === "uploading" ? `${Math.round(file.progress ?? 0)}%` : formatBytes(file.size)}
|
|
162
162
|
</span>
|
|
163
163
|
{onRemove ? (
|
|
164
|
-
<button type="button" className="ml-dropzone-remove" aria-label={`Remove ${file.name}`} onClick={() => onRemove(file)}>
|
|
164
|
+
<button type="button" className="ml-dropzone-remove" data-hit="expand" aria-label={`Remove ${file.name}`} onClick={() => onRemove(file)}>
|
|
165
165
|
<IconX aria-hidden="true" size="0.875em" />
|
|
166
166
|
</button>
|
|
167
167
|
) : null}
|
|
@@ -8,7 +8,7 @@ export interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>,
|
|
|
8
8
|
description?: React.ReactNode;
|
|
9
9
|
/** The way forward: usually one primary action and one secondary. */
|
|
10
10
|
actions?: React.ReactNode;
|
|
11
|
-
/** "page"
|
|
11
|
+
/** "page" centers in a large area; "inline" sits inside a card or table. */
|
|
12
12
|
size?: "inline" | "page";
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -6,7 +6,7 @@ import { cx } from "../_internal/react";
|
|
|
6
6
|
import { useFloating, usePortalNode, type Align, type Side } from "../_internal/floating";
|
|
7
7
|
|
|
8
8
|
export interface HoverCardProps {
|
|
9
|
-
/** The link or name that shows the card; it keeps its own
|
|
9
|
+
/** The link or name that shows the card; it keeps its own behavior. */
|
|
10
10
|
children: React.ReactElement<Record<string, unknown>>;
|
|
11
11
|
/** The preview: a profile, a page summary, a repository. */
|
|
12
12
|
content: React.ReactNode;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { IconMinus, IconPlus } from "@mlola-ui/icons";
|
|
5
5
|
import { Field, fieldDescription } from "../input/input";
|
|
6
|
-
import { cx } from "../_internal/react";
|
|
6
|
+
import { composeRefs, cx } from "../_internal/react";
|
|
7
7
|
import { parseNumber, settle, stepValue } from "./number";
|
|
8
8
|
|
|
9
9
|
export { parseNumber, settle, stepValue } from "./number";
|
|
@@ -40,7 +40,7 @@ export interface NumberInputProps {
|
|
|
40
40
|
* (Shift for ten), Page keys take large steps, Home and End go to the
|
|
41
41
|
* bounds, and holding − or + keeps counting.
|
|
42
42
|
*/
|
|
43
|
-
export function NumberInput({
|
|
43
|
+
export const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(function NumberInput({
|
|
44
44
|
value,
|
|
45
45
|
defaultValue = null,
|
|
46
46
|
onValueChange,
|
|
@@ -60,7 +60,7 @@ export function NumberInput({
|
|
|
60
60
|
required,
|
|
61
61
|
id,
|
|
62
62
|
className,
|
|
63
|
-
}: NumberInputProps) {
|
|
63
|
+
}: NumberInputProps, ref) {
|
|
64
64
|
const autoId = React.useId();
|
|
65
65
|
const fieldId = id ?? autoId;
|
|
66
66
|
const [inner, setInner] = React.useState<number | null>(defaultValue);
|
|
@@ -144,7 +144,7 @@ export function NumberInput({
|
|
|
144
144
|
<Field id={fieldId} label={label} hint={hint} error={error} required={required} disabled={disabled} className={cx("ml-number-input", className)}>
|
|
145
145
|
<div className="ml-number-input-control" data-disabled={disabled || undefined} data-invalid={error ? "" : undefined}>
|
|
146
146
|
<input
|
|
147
|
-
ref={input}
|
|
147
|
+
ref={composeRefs(input, ref)}
|
|
148
148
|
id={fieldId}
|
|
149
149
|
className="ml-number-input-field"
|
|
150
150
|
role="spinbutton"
|
|
@@ -181,4 +181,5 @@ export function NumberInput({
|
|
|
181
181
|
</div>
|
|
182
182
|
</Field>
|
|
183
183
|
);
|
|
184
|
-
}
|
|
184
|
+
});
|
|
185
|
+
NumberInput.displayName = "NumberInput";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { useControllableState } from "../_internal/react";
|
|
4
|
+
import { composeRefs, useControllableState } from "../_internal/react";
|
|
5
5
|
import { Field, fieldDescription } from "../input/input";
|
|
6
6
|
|
|
7
7
|
export interface OtpInputProps {
|
|
@@ -28,7 +28,7 @@ export interface OtpInputProps {
|
|
|
28
28
|
* forward, Backspace moves back, arrows move freely, and pasting or the
|
|
29
29
|
* phone's SMS autofill fills every box at once.
|
|
30
30
|
*/
|
|
31
|
-
export function OtpInput({
|
|
31
|
+
export const OtpInput = React.forwardRef<HTMLInputElement, OtpInputProps>(function OtpInput({
|
|
32
32
|
length = 6,
|
|
33
33
|
value,
|
|
34
34
|
defaultValue = "",
|
|
@@ -42,7 +42,7 @@ export function OtpInput({
|
|
|
42
42
|
groupSize,
|
|
43
43
|
id,
|
|
44
44
|
className,
|
|
45
|
-
}: OtpInputProps) {
|
|
45
|
+
}: OtpInputProps, ref) {
|
|
46
46
|
const [code, setCode] = useControllableState({ value, defaultValue, onChange: onValueChange });
|
|
47
47
|
const boxes = React.useRef<Array<HTMLInputElement | null>>([]);
|
|
48
48
|
const generated = React.useId();
|
|
@@ -75,6 +75,7 @@ export function OtpInput({
|
|
|
75
75
|
<input
|
|
76
76
|
ref={(node) => {
|
|
77
77
|
boxes.current[index] = node;
|
|
78
|
+
if (index === 0) composeRefs(ref)(node);
|
|
78
79
|
}}
|
|
79
80
|
id={index === 0 ? fieldId : undefined}
|
|
80
81
|
className="ml-otp-box"
|
|
@@ -113,4 +114,5 @@ export function OtpInput({
|
|
|
113
114
|
</div>
|
|
114
115
|
</Field>
|
|
115
116
|
);
|
|
116
|
-
}
|
|
117
|
+
});
|
|
118
|
+
OtpInput.displayName = "OtpInput";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cx } from "../_internal/react";
|
|
5
5
|
|
|
6
|
-
/** The meaning of the fill, from the theme's
|
|
6
|
+
/** The meaning of the fill, from the theme's color roles. */
|
|
7
7
|
export type ProgressTone = "primary" | "info" | "success" | "warning" | "danger";
|
|
8
8
|
type ProgressSize = "sm" | "md" | "lg";
|
|
9
9
|
export interface ProgressProps {
|
|
@@ -17,7 +17,7 @@ export interface SegmentedControlProps extends React.HTMLAttributes<HTMLDivEleme
|
|
|
17
17
|
onValueChange?: (value: string) => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function SegmentedControl({
|
|
20
|
+
export const SegmentedControl = React.forwardRef<HTMLDivElement, SegmentedControlProps>(function SegmentedControl({
|
|
21
21
|
options,
|
|
22
22
|
label,
|
|
23
23
|
value,
|
|
@@ -25,7 +25,7 @@ export function SegmentedControl({
|
|
|
25
25
|
onValueChange,
|
|
26
26
|
className,
|
|
27
27
|
...props
|
|
28
|
-
}: SegmentedControlProps) {
|
|
28
|
+
}: SegmentedControlProps, ref) {
|
|
29
29
|
const [current, setCurrent] = useControllableState({
|
|
30
30
|
value,
|
|
31
31
|
defaultValue: defaultValue ?? options[0]?.value ?? "",
|
|
@@ -48,6 +48,7 @@ export function SegmentedControl({
|
|
|
48
48
|
return (
|
|
49
49
|
<div
|
|
50
50
|
{...props}
|
|
51
|
+
ref={ref}
|
|
51
52
|
className={cx("ml-segmented-control", className)}
|
|
52
53
|
role="radiogroup"
|
|
53
54
|
aria-label={label}
|
|
@@ -75,4 +76,5 @@ export function SegmentedControl({
|
|
|
75
76
|
})}
|
|
76
77
|
</div>
|
|
77
78
|
);
|
|
78
|
-
}
|
|
79
|
+
});
|
|
80
|
+
SegmentedControl.displayName = "SegmentedControl";
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
import { rovingIndex } from "@mlola-ui/behavior/logic";
|
|
6
6
|
import { useFloating, usePortalNode } from "../_internal/floating";
|
|
7
|
-
import { cx, useControllableState } from "../_internal/react";
|
|
7
|
+
import { composeRefs, cx, useControllableState } from "../_internal/react";
|
|
8
8
|
import { IconCheck, IconChevronDown, IconX } from "@mlola-ui/icons";
|
|
9
9
|
|
|
10
10
|
export interface SelectOption {
|
|
@@ -56,7 +56,7 @@ interface SelectMultipleProps {
|
|
|
56
56
|
|
|
57
57
|
export type SelectProps = SelectBaseProps & (SelectSingleProps | SelectMultipleProps);
|
|
58
58
|
|
|
59
|
-
export function Select(props: SelectProps) {
|
|
59
|
+
export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function Select(props: SelectProps, ref) {
|
|
60
60
|
const {
|
|
61
61
|
options,
|
|
62
62
|
maxVisibleChips = 3,
|
|
@@ -209,7 +209,7 @@ export function Select(props: SelectProps) {
|
|
|
209
209
|
{label ? <span id={labelId} className={hideLabel ? "ml-visually-hidden" : "ml-select-label"}>{label}</span> : null}
|
|
210
210
|
<div ref={controlRef} className="ml-select-control">
|
|
211
211
|
<button
|
|
212
|
-
ref={triggerRef}
|
|
212
|
+
ref={composeRefs(triggerRef, ref)}
|
|
213
213
|
type="button"
|
|
214
214
|
role="combobox"
|
|
215
215
|
aria-haspopup="listbox"
|
|
@@ -256,7 +256,7 @@ export function Select(props: SelectProps) {
|
|
|
256
256
|
<button
|
|
257
257
|
type="button"
|
|
258
258
|
aria-label={`Clear ${label ?? "selection"}`}
|
|
259
|
-
className="ml-select-clear"
|
|
259
|
+
className="ml-select-clear" data-hit="expand"
|
|
260
260
|
onClick={() => {
|
|
261
261
|
if (multiple) setSelectedValues([]);
|
|
262
262
|
else setSelectedValue("");
|
|
@@ -329,4 +329,5 @@ export function Select(props: SelectProps) {
|
|
|
329
329
|
{error ? <p id={errorId} role="alert" className="ml-select-error">{error}</p> : null}
|
|
330
330
|
</div>
|
|
331
331
|
);
|
|
332
|
-
}
|
|
332
|
+
});
|
|
333
|
+
Select.displayName = "Select";
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { IconX } from "@mlola-ui/icons";
|
|
5
5
|
import { ComboboxList, filterOptions, sideFor, type ComboboxOption } from "../combobox/combobox";
|
|
6
6
|
import { Field, fieldDescription } from "../input/input";
|
|
7
|
-
import { cx } from "../_internal/react";
|
|
7
|
+
import { composeRefs, cx } from "../_internal/react";
|
|
8
8
|
|
|
9
9
|
export interface TagInputProps {
|
|
10
10
|
value?: string[];
|
|
@@ -43,7 +43,7 @@ export function splitTags(text: string) {
|
|
|
43
43
|
* marks the last tag and then removes it, and arrows move between tags.
|
|
44
44
|
* Suggestions, validation and a limit are optional.
|
|
45
45
|
*/
|
|
46
|
-
export function TagInput({
|
|
46
|
+
export const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(function TagInput({
|
|
47
47
|
value,
|
|
48
48
|
defaultValue = [],
|
|
49
49
|
onValueChange,
|
|
@@ -60,7 +60,7 @@ export function TagInput({
|
|
|
60
60
|
required,
|
|
61
61
|
id,
|
|
62
62
|
className,
|
|
63
|
-
}: TagInputProps) {
|
|
63
|
+
}: TagInputProps, ref) {
|
|
64
64
|
const autoId = React.useId();
|
|
65
65
|
const fieldId = id ?? autoId;
|
|
66
66
|
const listId = `${fieldId}-list`;
|
|
@@ -195,7 +195,7 @@ export function TagInput({
|
|
|
195
195
|
<li key={tag} className="ml-tag-input-tag" data-armed={armed === index || undefined}>
|
|
196
196
|
<span className="ml-tag-input-text">{tag}</span>
|
|
197
197
|
{disabled ? null : (
|
|
198
|
-
<button type="button" className="ml-tag-input-remove" tabIndex={-1} aria-label={`Remove ${tag}`} onMouseDown={(event) => event.preventDefault()} onClick={() => remove(index)}>
|
|
198
|
+
<button type="button" className="ml-tag-input-remove" data-hit="expand" tabIndex={-1} aria-label={`Remove ${tag}`} onMouseDown={(event) => event.preventDefault()} onClick={() => remove(index)}>
|
|
199
199
|
<IconX aria-hidden="true" size="0.8em" />
|
|
200
200
|
</button>
|
|
201
201
|
)}
|
|
@@ -203,7 +203,7 @@ export function TagInput({
|
|
|
203
203
|
))}
|
|
204
204
|
</ul>
|
|
205
205
|
<input
|
|
206
|
-
ref={input}
|
|
206
|
+
ref={composeRefs(input, ref)}
|
|
207
207
|
id={fieldId}
|
|
208
208
|
className="ml-tag-input-field"
|
|
209
209
|
role={suggestions ? "combobox" : undefined}
|
|
@@ -270,4 +270,5 @@ export function TagInput({
|
|
|
270
270
|
</p>
|
|
271
271
|
</Field>
|
|
272
272
|
);
|
|
273
|
-
}
|
|
273
|
+
});
|
|
274
|
+
TagInput.displayName = "TagInput";
|
|
@@ -17,7 +17,7 @@ export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextArea
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Multi-line text that grows as it is written. With a `maxLength`, a counter
|
|
20
|
-
* appears near the limit and turns to the danger
|
|
20
|
+
* appears near the limit and turns to the danger color past it.
|
|
21
21
|
*/
|
|
22
22
|
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
23
23
|
({ label, hint, error, maxRows = 12, showCount = false, containerClassName, className, id, disabled, required, rows = 3, maxLength, onChange, value, defaultValue, style, "aria-describedby": describedBy, ...props }, ref) => {
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { IconClock } from "@mlola-ui/icons";
|
|
5
5
|
import { Field, fieldDescription } from "../input/input";
|
|
6
6
|
import { Popover } from "../popover/popover";
|
|
7
|
-
import { cx } from "../_internal/react";
|
|
7
|
+
import { composeRefs, cx } from "../_internal/react";
|
|
8
8
|
import { fromMinutes, isTimeAllowed, nearestAllowed, nextAllowed, toMinutes, type TimeMatchers, type TimeRules } from "./times";
|
|
9
9
|
|
|
10
10
|
export type { TimeMatcher, TimeMatchers } from "./times";
|
|
@@ -51,7 +51,7 @@ type Segment = "hour" | "minute" | "period";
|
|
|
51
51
|
* the field moves on by itself. A quick list offers common times. The value
|
|
52
52
|
* is always "HH:MM" in 24-hour time, whatever the reader sees.
|
|
53
53
|
*/
|
|
54
|
-
export function TimePicker({
|
|
54
|
+
export const TimePicker = React.forwardRef<HTMLSpanElement, TimePickerProps>(function TimePicker({
|
|
55
55
|
label,
|
|
56
56
|
hint,
|
|
57
57
|
error,
|
|
@@ -70,7 +70,7 @@ export function TimePicker({
|
|
|
70
70
|
required,
|
|
71
71
|
id,
|
|
72
72
|
className,
|
|
73
|
-
}: TimePickerProps) {
|
|
73
|
+
}: TimePickerProps, ref) {
|
|
74
74
|
const autoId = React.useId();
|
|
75
75
|
const fieldId = id ?? autoId;
|
|
76
76
|
const [inner, setInner] = React.useState<string | null>(defaultValue);
|
|
@@ -185,7 +185,7 @@ export function TimePicker({
|
|
|
185
185
|
const options = enabledTimes === undefined ? grid : [...new Set([...grid, ...exact])].sort((a, b) => a - b).filter((at) => isTimeAllowed(at, rules));
|
|
186
186
|
const shownAt = total !== null ? options.reduce((best, at) => (Math.abs(at - total) < Math.abs(best - total) ? at : best), options[0] ?? low) : (options.find((at) => isTimeAllowed(at, rules)) ?? low);
|
|
187
187
|
|
|
188
|
-
// Open the list
|
|
188
|
+
// Open the list centered on the current time, once the popover has laid out.
|
|
189
189
|
// Only the list scrolls, never the page.
|
|
190
190
|
React.useEffect(() => {
|
|
191
191
|
if (!open) return;
|
|
@@ -204,6 +204,8 @@ export function TimePicker({
|
|
|
204
204
|
ref: (element: HTMLSpanElement | null) => {
|
|
205
205
|
if (element) segments.current.set(segment, element);
|
|
206
206
|
else segments.current.delete(segment);
|
|
207
|
+
// The forwarded ref is the first segment: the one a form focuses.
|
|
208
|
+
if (segment === "hour") composeRefs(ref)(element);
|
|
207
209
|
},
|
|
208
210
|
role: "spinbutton",
|
|
209
211
|
tabIndex: disabled ? -1 : 0,
|
|
@@ -283,4 +285,5 @@ export function TimePicker({
|
|
|
283
285
|
</div>
|
|
284
286
|
</Field>
|
|
285
287
|
);
|
|
286
|
-
}
|
|
288
|
+
});
|
|
289
|
+
TimePicker.displayName = "TimePicker";
|
|
@@ -19,7 +19,7 @@ Timeline.displayName = "Timeline";
|
|
|
19
19
|
interface TimelineItemProps extends Omit<React.LiHTMLAttributes<HTMLLIElement>, "title"> {
|
|
20
20
|
/** Progress through a sequence: done, happening now, or still to come. */
|
|
21
21
|
status?: TimelineStatus;
|
|
22
|
-
/**
|
|
22
|
+
/** Colors an event marker, for activity feeds: a failed deploy, a merged change. */
|
|
23
23
|
tone?: TimelineTone;
|
|
24
24
|
time?: React.ReactNode;
|
|
25
25
|
/** A machine-readable time for `<time dateTime>`. */
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { IconCircleCheck, IconCircleX, IconInfo, IconTriangleAlert, IconX } from "@mlola-ui/icons";
|
|
5
5
|
import { cx } from "../_internal/react";
|
|
6
6
|
|
|
7
|
-
/** The meaning of the message, from the theme's
|
|
7
|
+
/** The meaning of the message, from the theme's color roles. */
|
|
8
8
|
export type ToastTone = "neutral" | "info" | "success" | "warning" | "danger";
|
|
9
9
|
export type ToastPosition = "top-right" | "top-center" | "bottom-right" | "bottom-center";
|
|
10
10
|
|
|
@@ -30,6 +30,7 @@ const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
30
30
|
data-state={on ? "checked" : "unchecked"}
|
|
31
31
|
data-size={size}
|
|
32
32
|
className={cx("ml-toggle", className)}
|
|
33
|
+
data-hit="expand"
|
|
33
34
|
onClick={(event) => {
|
|
34
35
|
onClick?.(event);
|
|
35
36
|
if (!event.defaultPrevented) setOn(!on);
|
|
@@ -8,7 +8,7 @@ import { usePortalNode, type Side } from "../_internal/floating";
|
|
|
8
8
|
import { placeFloating } from "../_internal/anchor";
|
|
9
9
|
|
|
10
10
|
export interface TourStep {
|
|
11
|
-
/** A CSS selector or a ref for the element to point at; none shows the card
|
|
11
|
+
/** A CSS selector or a ref for the element to point at; none shows the card centered. */
|
|
12
12
|
target?: string | React.RefObject<HTMLElement | null>;
|
|
13
13
|
title: string;
|
|
14
14
|
body?: React.ReactNode;
|
|
@@ -55,7 +55,7 @@ export function Tour({ steps, open, onOpenChange, step: stepProp, defaultStep =
|
|
|
55
55
|
const titleId = React.useId();
|
|
56
56
|
const current = steps[index];
|
|
57
57
|
|
|
58
|
-
// Place the card beside the spotlight whenever the target moves;
|
|
58
|
+
// Place the card beside the spotlight whenever the target moves; center it when there is none.
|
|
59
59
|
React.useLayoutEffect(() => {
|
|
60
60
|
const element = card.current;
|
|
61
61
|
if (!element) return;
|
|
@@ -128,7 +128,7 @@ export function Tour({ steps, open, onOpenChange, step: stepProp, defaultStep =
|
|
|
128
128
|
<div
|
|
129
129
|
ref={card}
|
|
130
130
|
className="ml-tour-card"
|
|
131
|
-
data-
|
|
131
|
+
data-centered={rect ? undefined : ""}
|
|
132
132
|
role="dialog"
|
|
133
133
|
aria-modal="true"
|
|
134
134
|
aria-label={`${label}: step ${index + 1} of ${steps.length}`}
|
|
@@ -8,43 +8,43 @@
|
|
|
8
8
|
"packages/components/accordion/accordion.tsx": "sha256-0ALaLJd1fvj8Vbx86HLBKm3qUItrtwbrGJTykVfNSMg=",
|
|
9
9
|
"packages/components/activity-heatmap/activity-heatmap.tsx": "sha256-fMGDlXKpOfIOy0hmbpR8hKrbbN3fC386tXK27A21ZwM=",
|
|
10
10
|
"packages/components/activity-heatmap/calendar.ts": "sha256-NB+AjXY5Xc+KzR8MMTRBgZxdzOeoHhkW0KQOW7WJPuQ=",
|
|
11
|
-
"packages/components/alert/alert.tsx": "sha256-
|
|
11
|
+
"packages/components/alert/alert.tsx": "sha256-j680akXvCIh4MTVnUo79acvVrv51XyQ3UBJ5FBAy8Dw=",
|
|
12
12
|
"packages/components/app-shell/app-shell.tsx": "sha256-XAq82XOTH3ZR+hxTkYFu8P7Ih2aI6ERy/TpXc2VMUwM=",
|
|
13
13
|
"packages/components/avatar/avatar.tsx": "sha256-hvZUGhatN95ZyX6p6NBhTRf6h1/rrJ+bJrqBMtIspq0=",
|
|
14
|
-
"packages/components/badge/badge.tsx": "sha256
|
|
15
|
-
"packages/components/breadcrumb/breadcrumb.tsx": "sha256-
|
|
14
|
+
"packages/components/badge/badge.tsx": "sha256-flEH8S2jZ/64n3Y/ROhC9+9rvF7wXaN0xLcMMwqNOVQ=",
|
|
15
|
+
"packages/components/breadcrumb/breadcrumb.tsx": "sha256-Mt9mvmwwDgdp5/+mj5byIrh8+hicsp7R6UcvUlK0yog=",
|
|
16
16
|
"packages/components/button/button.tsx": "sha256-khqYxcV3hlZ+Q+cYNTRXEpAfIijDFSvkuDJXMPtlk7g=",
|
|
17
17
|
"packages/components/calendar/calendar.tsx": "sha256-uE22LI4jpU8ErR/oBmAkeKiu+Rp0bz3IXZVCurycpwc=",
|
|
18
18
|
"packages/components/calendar/dates.ts": "sha256-mygYkoC70av2ZQ0vkxf885trwW97Wd10I4dptYQe+4A=",
|
|
19
19
|
"packages/components/card/card.tsx": "sha256-aMKbv5DWYYWq7GZAFR9O1fuYE0H61WCjkvEbRMOd894=",
|
|
20
|
-
"packages/components/carousel/carousel.tsx": "sha256-
|
|
21
|
-
"packages/components/checkbox/checkbox.tsx": "sha256-
|
|
22
|
-
"packages/components/color-picker/color-picker.tsx": "sha256-
|
|
23
|
-
"packages/components/color-picker/color.ts": "sha256-
|
|
24
|
-
"packages/components/combobox/combobox.tsx": "sha256-
|
|
20
|
+
"packages/components/carousel/carousel.tsx": "sha256-pamugpB6Ujt9Ylxd37jNF/ZwSb8iRkW/MoYe12nY3i4=",
|
|
21
|
+
"packages/components/checkbox/checkbox.tsx": "sha256-hz4YbGwOxv/aJpJHrLXR4MKSWJGyjzoJEIGvdhyML9M=",
|
|
22
|
+
"packages/components/color-picker/color-picker.tsx": "sha256-E+QB6MbSxg+3AZX3A7bOkst4arxgpmFL2ZmochoWCl8=",
|
|
23
|
+
"packages/components/color-picker/color.ts": "sha256-YqSgYjzn7XfPWAHuyzbwOhbg/ScaSfB7V73e9w+OjJs=",
|
|
24
|
+
"packages/components/combobox/combobox.tsx": "sha256-CvuYc/hJoS7yw6QM9vv1SIkwflx0Kwd95UjchtC+AOs=",
|
|
25
25
|
"packages/components/command-menu/command-menu.tsx": "sha256-mF1z92yrPY609pjx5Dt1l5EhGHl9EWV8F8Sq0hyJRw4=",
|
|
26
26
|
"packages/components/context-menu/context-menu.tsx": "sha256-YgYsIzvLGFcJG1XX5zFJwrid5Z1Kn0lyWVzmLKmbMVU=",
|
|
27
|
-
"packages/components/date-picker/date-picker.tsx": "sha256-
|
|
27
|
+
"packages/components/date-picker/date-picker.tsx": "sha256-rsmDweD3RVfKTXR2i+/pnTb/MsrDAZXmHAYBvcUGxig=",
|
|
28
28
|
"packages/components/dropdown-menu/dropdown-menu.tsx": "sha256-xgybCu9HBxIieafu5CjAtY4aCOQoaAj/VUCH6D6vLP8=",
|
|
29
|
-
"packages/components/dropzone/dropzone.tsx": "sha256-
|
|
30
|
-
"packages/components/empty-state/empty-state.tsx": "sha256-
|
|
31
|
-
"packages/components/hover-card/hover-card.tsx": "sha256-
|
|
29
|
+
"packages/components/dropzone/dropzone.tsx": "sha256-tHCCpEWE6839UeLOszTo+R2vAL4Sp9UxuRdLkFLfnq4=",
|
|
30
|
+
"packages/components/empty-state/empty-state.tsx": "sha256-uCKqYpr9vkozXZHpdl4GFtdX9ijcGOriCHvgyarBqRw=",
|
|
31
|
+
"packages/components/hover-card/hover-card.tsx": "sha256-AyRbxfqDOYBgGlgjgrtQZm5DipArLEzQn6XnzjPd0ro=",
|
|
32
32
|
"packages/components/input/input.tsx": "sha256-lbbheo+ydQTzyujTf0W7DD96TogOjifCyqj9c3h3W0g=",
|
|
33
33
|
"packages/components/kbd/kbd.tsx": "sha256-wrPqzn1e82zJYoKzO7u94Bgq4+nPc0DXLc8GJ/4/3nY=",
|
|
34
34
|
"packages/components/modal/modal.tsx": "sha256-vybxgBGYCrMYvJRnO1wDAc2VFQylu+mQIf5OZIcabX4=",
|
|
35
|
-
"packages/components/number-input/number-input.tsx": "sha256-
|
|
35
|
+
"packages/components/number-input/number-input.tsx": "sha256-BDOyvCNtMULD/HK7uWHsYbGg79lrRDrVqtGQQrYHpmM=",
|
|
36
36
|
"packages/components/number-input/number.ts": "sha256-IrHCQrEhI5ein9ifMFuSViSoxQUBoT3tNmmCu/paGGM=",
|
|
37
|
-
"packages/components/otp-input/otp-input.tsx": "sha256-
|
|
37
|
+
"packages/components/otp-input/otp-input.tsx": "sha256-QURn8g5HqCQXBTdeENPbVxoPlqs3ZtoAPLRlFjQDxmI=",
|
|
38
38
|
"packages/components/pagination/pagination.tsx": "sha256-FBwk8tq4JcsK2D0dlWEYiDSRx3owgBmGzEIlVRaNBoE=",
|
|
39
39
|
"packages/components/popover/popover.tsx": "sha256-/kTiAuFklU4IuP97XaiCUGdEpKtNyMPLPS359mfzPHw=",
|
|
40
40
|
"packages/components/priority-icon/labels.ts": "sha256-zhmJIb7/gS16wU+xCJ7hkReBi9q5RI3SMwdjkJvvvKU=",
|
|
41
41
|
"packages/components/priority-icon/priority-icon.tsx": "sha256-3D6hFRpht3YuWouLhakE2DFdldavmJx0B/povEDwrtQ=",
|
|
42
|
-
"packages/components/progress/progress.tsx": "sha256-
|
|
42
|
+
"packages/components/progress/progress.tsx": "sha256-9OjecRNPWn7Bfe+C5IyXaKsW1NJae6X5gco1o0X5XkE=",
|
|
43
43
|
"packages/components/radio-group/radio-group.tsx": "sha256-jhnSEBLkdpe8dv5us/Q1sMsQF4sKGRgDXWGKm2JSFew=",
|
|
44
44
|
"packages/components/rating/rating.tsx": "sha256-zgPOQXzAiRE4qGNPQ8qat3SP1XSI/7567kA5VaT51P0=",
|
|
45
45
|
"packages/components/resizable/resizable.tsx": "sha256-40sK2UyIR9Ek72KAu5nvbr44tKEY6JhC5Ftb++Wf6cI=",
|
|
46
|
-
"packages/components/segmented-control/segmented-control.tsx": "sha256-
|
|
47
|
-
"packages/components/select/select.tsx": "sha256-
|
|
46
|
+
"packages/components/segmented-control/segmented-control.tsx": "sha256-MCBG5XHFWCwbACctsYKbqe8mvSSR6pf60tyRgdCDKDA=",
|
|
47
|
+
"packages/components/select/select.tsx": "sha256-m8QmKUGC4386XcklosSntal0XwUAFjKosTBQSF0dduQ=",
|
|
48
48
|
"packages/components/sheet/sheet.tsx": "sha256-EIn3OeRDmXGHbYf/uihRh8aw3LmzarIFiyC9DUsL1jg=",
|
|
49
49
|
"packages/components/skeleton/skeleton.tsx": "sha256-2Fk72NbLr3+CrXH7jGbHNsc3t+vskMqSyySe/UwbGCs=",
|
|
50
50
|
"packages/components/slider/slider.tsx": "sha256-l+VrBqPYHIIcemiVixxwDxNDB0HEMmYRZekx/H5HroQ=",
|
|
@@ -53,15 +53,15 @@
|
|
|
53
53
|
"packages/components/stepper/stepper.tsx": "sha256-uI/dRmkbijlixHiGUceWcpaDzpEixrzQ1UTmoPbGZAY=",
|
|
54
54
|
"packages/components/table/table.tsx": "sha256-ed1Qn2sIpxRvGdPDAb5ih5AcXr5PmHTHdlrJLYDYRp8=",
|
|
55
55
|
"packages/components/tabs/tabs.tsx": "sha256-kWAQjE9mshqZL7V3NJTUiurp6M5Nu3vx5O1LsIgdV5Y=",
|
|
56
|
-
"packages/components/tag-input/tag-input.tsx": "sha256
|
|
57
|
-
"packages/components/textarea/textarea.tsx": "sha256-
|
|
58
|
-
"packages/components/time-picker/time-picker.tsx": "sha256-
|
|
56
|
+
"packages/components/tag-input/tag-input.tsx": "sha256-EK7xJTe3Y/S5Kn3vS0zBQg9lC+RVoGvky6HOAt+DnAw=",
|
|
57
|
+
"packages/components/textarea/textarea.tsx": "sha256-C9jMYZ63w+m6bgdLvHjvULRuoCe26q5zbgS4+ahWzr8=",
|
|
58
|
+
"packages/components/time-picker/time-picker.tsx": "sha256-og8Cbf+e8oFYewfagUgjRzLHSIIlAZh+1OpAn9yNq70=",
|
|
59
59
|
"packages/components/time-picker/times.ts": "sha256-V/4uqFZPn9gVnyx8cB9vwp/RocmTYaPmLxjfy8DPQOI=",
|
|
60
|
-
"packages/components/timeline/timeline.tsx": "sha256-
|
|
61
|
-
"packages/components/toast/toast.tsx": "sha256-
|
|
62
|
-
"packages/components/toggle/toggle.tsx": "sha256-
|
|
60
|
+
"packages/components/timeline/timeline.tsx": "sha256-nVg7lBcAUAzqEb0YrHagJXSaHE2aWWE9bUbQMWQY6PU=",
|
|
61
|
+
"packages/components/toast/toast.tsx": "sha256-FvZnwN6Lk/QEHmcNxo7sVlEvR3Lx54aT5cH9lwrthhE=",
|
|
62
|
+
"packages/components/toggle/toggle.tsx": "sha256-8qPHeSliHNdzQS7OLJ47BrvRR75MYyUGJlK4s21XS/U=",
|
|
63
63
|
"packages/components/tooltip/tooltip.tsx": "sha256-NbaOUmlUbBlUIdovchrWwH2QqzuvXB97DjnJnrHf4Gc=",
|
|
64
|
-
"packages/components/tour/tour.tsx": "sha256-
|
|
64
|
+
"packages/components/tour/tour.tsx": "sha256-mrAE8aVDBVIibGvQah9Gh3mzIiWDFxB6aObzqFo5xXU=",
|
|
65
65
|
"packages/components/virtual-list/virtual-list.tsx": "sha256-raGEFn4HlAOIohqZcy0DgptsnUIPS/6bWLYeTe+M9E8=",
|
|
66
66
|
"packages/components/virtual-list/virtual.ts": "sha256-PyDIj3C2Vwb1i4ZssxSlYjgKmZPBIFLZFNq8YCW2Z78="
|
|
67
67
|
}
|