@antadesign/anta 0.3.1 → 0.3.3
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/anta_helpers.d.ts +111 -0
- package/dist/anta_helpers.js +90 -1
- package/dist/calendar-core.d.ts +126 -0
- package/dist/calendar-core.js +289 -0
- package/dist/components/Button.d.ts +9 -1
- package/dist/components/Button.js +12 -18
- package/dist/components/Calendar.d.ts +80 -0
- package/dist/components/Calendar.js +220 -0
- package/dist/components/Checkbox.d.ts +21 -15
- package/dist/components/Checkbox.js +17 -7
- package/dist/components/Expander.d.ts +7 -1
- package/dist/components/Expander.js +4 -2
- package/dist/components/Input.d.ts +16 -8
- package/dist/components/Input.js +19 -11
- package/dist/components/InputDate.d.ts +87 -0
- package/dist/components/InputDate.js +278 -0
- package/dist/components/InputDate.module.css +1 -0
- package/dist/components/Menu.d.ts +5 -1
- package/dist/components/Menu.js +6 -2
- package/dist/components/MenuItem.d.ts +48 -7
- package/dist/components/MenuItem.js +71 -7
- package/dist/components/MenuSeparator.d.ts +13 -2
- package/dist/components/MenuSeparator.js +12 -2
- package/dist/components/Progress.d.ts +4 -1
- package/dist/components/Progress.js +4 -3
- package/dist/components/RadioGroup.d.ts +17 -15
- package/dist/components/RadioGroup.js +8 -8
- package/dist/components/Select.d.ts +255 -0
- package/dist/components/Select.js +290 -0
- package/dist/components/Select.module.css +1 -0
- package/dist/components/Tab.d.ts +37 -0
- package/dist/components/Tab.js +5 -0
- package/dist/components/TabPanel.d.ts +22 -0
- package/dist/components/TabPanel.js +5 -0
- package/dist/components/Tabs.d.ts +129 -0
- package/dist/components/Tabs.js +176 -0
- package/dist/components/Tabs.module.css +1 -0
- package/dist/components/Tag.d.ts +10 -9
- package/dist/components/Tag.js +2 -2
- package/dist/components/Text.d.ts +6 -5
- package/dist/components/Title.d.ts +4 -2
- package/dist/components/Tooltip.d.ts +13 -1
- package/dist/components/Tooltip.js +9 -0
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-calendar.css +1 -0
- package/dist/elements/a-calendar.d.ts +76 -0
- package/dist/elements/a-calendar.js +190 -0
- package/dist/elements/a-checkbox.css +1 -1
- package/dist/elements/a-checkbox.d.ts +1 -2
- package/dist/elements/a-checkbox.js +5 -5
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.d.ts +16 -5
- package/dist/elements/a-expander.js +65 -9
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-icon.shapes.d.ts +6 -1
- package/dist/elements/a-icon.shapes.js +9 -0
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +6 -0
- package/dist/elements/a-input.js +28 -9
- package/dist/elements/a-menu-group.css +1 -1
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu-item.d.ts +14 -4
- package/dist/elements/a-menu-item.js +17 -0
- package/dist/elements/a-menu-separator.css +1 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.d.ts +51 -8
- package/dist/elements/a-menu.js +285 -42
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-radio-group.d.ts +1 -3
- package/dist/elements/a-radio-group.js +13 -13
- package/dist/elements/a-radio.css +1 -1
- package/dist/elements/a-radio.d.ts +3 -11
- package/dist/elements/a-radio.js +3 -34
- package/dist/elements/a-tab.css +1 -0
- package/dist/elements/a-tab.d.ts +6 -0
- package/dist/elements/a-tab.js +15 -0
- package/dist/elements/a-tabpanel.css +1 -0
- package/dist/elements/a-tabs.css +1 -0
- package/dist/elements/a-tabs.d.ts +28 -0
- package/dist/elements/a-tabs.js +157 -0
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-text.d.ts +14 -13
- package/dist/elements/a-text.js +53 -23
- package/dist/elements/a-title.css +1 -1
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.d.ts +16 -8
- package/dist/elements/a-tooltip.js +62 -16
- package/dist/elements/index.d.ts +4 -0
- package/dist/elements/index.js +10 -0
- package/dist/general_types.d.ts +252 -30
- package/dist/index.d.ts +15 -1
- package/dist/index.js +43 -1
- package/dist/jsx-runtime.d.ts +20 -7
- package/dist/jsx-runtime.js +6 -0
- package/dist/reset.css +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +4 -2
package/dist/components/Input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
|
-
import { nativeStateChange, toneStyle } from "../anta_helpers";
|
|
2
|
+
import { nativeStateChange, toneStyle, roundStyle } from "../anta_helpers";
|
|
3
3
|
import { Button } from "./Button";
|
|
4
4
|
import { Icon } from "./Icon";
|
|
5
5
|
const presence = (on) => on ? "" : void 0;
|
|
@@ -20,7 +20,8 @@ const INPUTMODE_BY_TYPE = {
|
|
|
20
20
|
email: "email",
|
|
21
21
|
tel: "tel",
|
|
22
22
|
url: "url",
|
|
23
|
-
number: "numeric"
|
|
23
|
+
number: "numeric",
|
|
24
|
+
search: "search"
|
|
24
25
|
};
|
|
25
26
|
const STATUS_ICON = {
|
|
26
27
|
critical: "warning-diamond",
|
|
@@ -36,6 +37,7 @@ const Input = ({
|
|
|
36
37
|
statusIcon,
|
|
37
38
|
tone,
|
|
38
39
|
size,
|
|
40
|
+
round,
|
|
39
41
|
value,
|
|
40
42
|
defaultValue,
|
|
41
43
|
multiline,
|
|
@@ -62,7 +64,7 @@ const Input = ({
|
|
|
62
64
|
step,
|
|
63
65
|
onInput,
|
|
64
66
|
onChange,
|
|
65
|
-
|
|
67
|
+
onValueChange,
|
|
66
68
|
onClearClick,
|
|
67
69
|
onClearInput,
|
|
68
70
|
children,
|
|
@@ -72,10 +74,15 @@ const Input = ({
|
|
|
72
74
|
}) => {
|
|
73
75
|
const statusTone = status && status !== "neutral" ? status : void 0;
|
|
74
76
|
const glyph = statusIcon === void 0 ? statusTone ? STATUS_ICON[statusTone] : void 0 : statusIcon;
|
|
77
|
+
const isSearch = type === "search";
|
|
78
|
+
const nativeType = isSearch ? void 0 : type;
|
|
79
|
+
const resolvedLeading = leading ?? (isSearch ? /* @__PURE__ */ jsx(Icon, { shape: "search" }) : void 0);
|
|
80
|
+
const resolvedClearable = isSearch ? clearable ?? true : clearable;
|
|
75
81
|
return /* @__PURE__ */ jsxs(
|
|
76
82
|
"a-input",
|
|
77
83
|
{
|
|
78
84
|
size: size && size !== "medium" ? size : void 0,
|
|
85
|
+
round: round ? "" : void 0,
|
|
79
86
|
value,
|
|
80
87
|
defaultvalue: defaultValue,
|
|
81
88
|
multiline: presence(multiline || rows != null),
|
|
@@ -83,7 +90,7 @@ const Input = ({
|
|
|
83
90
|
maxrows: maxRows != null ? String(maxRows) : void 0,
|
|
84
91
|
status: statusTone,
|
|
85
92
|
tone: tone || void 0,
|
|
86
|
-
type: !multiline && rows == null ?
|
|
93
|
+
type: !multiline && rows == null ? nativeType : void 0,
|
|
87
94
|
name,
|
|
88
95
|
placeholder,
|
|
89
96
|
disabled: presence(disabled),
|
|
@@ -100,23 +107,23 @@ const Input = ({
|
|
|
100
107
|
max,
|
|
101
108
|
step,
|
|
102
109
|
"aria-invalid": status === "critical" ? "true" : void 0,
|
|
103
|
-
oninput: onInput ||
|
|
110
|
+
oninput: onInput || onValueChange ? (e) => {
|
|
104
111
|
onInput?.(e);
|
|
105
|
-
|
|
112
|
+
onValueChange?.(e, attrsOf(e));
|
|
106
113
|
} : void 0,
|
|
107
|
-
onchange: onChange ||
|
|
114
|
+
onchange: onChange || onValueChange ? (e) => {
|
|
108
115
|
onChange?.(e);
|
|
109
|
-
|
|
116
|
+
onValueChange?.(e, attrsOf(e));
|
|
110
117
|
} : void 0,
|
|
111
118
|
onclearclick: onClearClick ? (e) => onClearClick(nativeStateChange(e).event) : void 0,
|
|
112
119
|
onclearinput: onClearInput ? (e) => onClearInput(nativeStateChange(e).event) : void 0,
|
|
113
120
|
class: className,
|
|
114
|
-
style: toneStyle(tone, "--input-tone-source", style),
|
|
121
|
+
style: roundStyle(round, "--input-round", toneStyle(tone, "--input-tone-source", style)),
|
|
115
122
|
...rest,
|
|
116
123
|
children: [
|
|
117
124
|
label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
resolvedLeading != null && /* @__PURE__ */ jsx("span", { slot: "leading", style: { display: "contents" }, children: resolvedLeading }),
|
|
126
|
+
resolvedClearable && // A real <a-button> (light DOM → fully styled, keyboard-focusable) in
|
|
120
127
|
// the element's `clear` slot — the element owns its visibility (shown
|
|
121
128
|
// only when filled + editable). It fires the bubbling `clearrequest`
|
|
122
129
|
// event via a-button's global listener, so clearing works even without
|
|
@@ -130,6 +137,7 @@ const Input = ({
|
|
|
130
137
|
{
|
|
131
138
|
priority: "tertiary",
|
|
132
139
|
size,
|
|
140
|
+
round: !!round,
|
|
133
141
|
icon: "x",
|
|
134
142
|
"aria-label": "Clear",
|
|
135
143
|
"data-custom-event": "clearrequest"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { BaseProps } from '../general_types';
|
|
2
|
+
import type { IconShape } from '../elements/a-icon.shapes';
|
|
3
|
+
/** Snapshot passed as the 2nd argument to `onValueChange` — the new ISO value
|
|
4
|
+
* (`''` when cleared) plus the field name (mirrors `Input` / `Calendar`). */
|
|
5
|
+
export interface InputDateChangeAttrs {
|
|
6
|
+
value: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
}
|
|
9
|
+
/** `<InputDate>` props — `Calendar`'s date surface plus `Input`'s field surface. */
|
|
10
|
+
export interface InputDateProps extends Omit<BaseProps, 'children'> {
|
|
11
|
+
/** Controlled value — ISO `YYYY-MM-DD` (or `YYYY-MM-DDTHH:mm` with `time`), `''`
|
|
12
|
+
* for empty. Pair with `onValueChange`; the field and calendar follow it and a
|
|
13
|
+
* pick only *requests* a change. */
|
|
14
|
+
value?: string;
|
|
15
|
+
/** Initial value for the uncontrolled case. */
|
|
16
|
+
defaultValue?: string;
|
|
17
|
+
/** Earliest selectable date (ISO). Earlier days disable, and a typed date before
|
|
18
|
+
* it stays uncommitted. */
|
|
19
|
+
min?: string;
|
|
20
|
+
/** Latest selectable date (ISO). */
|
|
21
|
+
max?: string;
|
|
22
|
+
/** BCP-47 locale driving the display format, the placeholder mask, parsing order,
|
|
23
|
+
* and the calendar.
|
|
24
|
+
* @defaultValue navigator.language */
|
|
25
|
+
locale?: string;
|
|
26
|
+
/** Form field name — the ISO value submits under this key. */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** Field size. Also sizes the calendar and the time row.
|
|
29
|
+
* @defaultValue medium */
|
|
30
|
+
size?: 'small' | 'medium' | 'large';
|
|
31
|
+
/** Disable the field and the calendar. */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
/** Field label, above the control. */
|
|
34
|
+
label?: React.ReactNode;
|
|
35
|
+
/** Helper text under the field. Replaced by a format hint while the entry is
|
|
36
|
+
* unrecognized. */
|
|
37
|
+
hint?: React.ReactNode;
|
|
38
|
+
/** Placeholder shown when empty.
|
|
39
|
+
* @defaultValue the locale's format mask (e.g. `MM/DD/YYYY`, `MM/DD/YYYY HH:MM` with `time`) */
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
/** Validation/feedback tone. An unrecognized entry forces `critical` until fixed.
|
|
42
|
+
* @defaultValue neutral */
|
|
43
|
+
status?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
|
|
44
|
+
/** Round the field corners — `true` for fully round, or a number / CSS length. */
|
|
45
|
+
round?: boolean | number | string;
|
|
46
|
+
/** Show a clear button once the field has a value. */
|
|
47
|
+
clearable?: boolean;
|
|
48
|
+
/** Leading icon at the start of the field. */
|
|
49
|
+
icon?: IconShape;
|
|
50
|
+
/** Include a time. The value becomes ISO `YYYY-MM-DDTHH:mm`, the field parses a
|
|
51
|
+
* trailing time after a space (`06/15/2026 14:30`, `… 2:30pm`), and the menu
|
|
52
|
+
* shows a time row (hours : minutes, an AM/PM toggle in 12-hour locales, then a
|
|
53
|
+
* Done button) under the calendar. Picking a day keeps the menu open so you can
|
|
54
|
+
* set the time. */
|
|
55
|
+
time?: boolean;
|
|
56
|
+
/** Force the time cycle when `time` is on: `true` for 12-hour (AM/PM), `false`
|
|
57
|
+
* for 24-hour. Omit to follow the locale (`en-US` → 12-hour, most others → 24). */
|
|
58
|
+
hour12?: boolean;
|
|
59
|
+
/** Fired after the value resolves (a recognized entry, a calendar pick, a time
|
|
60
|
+
* change, or a clear), with the new ISO value (`''` when cleared) and a
|
|
61
|
+
* `{ value, name }` snapshot. An unrecognized entry does not fire it. */
|
|
62
|
+
onValueChange?: (value: string, attrs: InputDateChangeAttrs) => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* `<InputDate>` — a date (or date-time) field that replaces the native
|
|
66
|
+
* `type="date"` / `type="datetime-local"` input, **composed** from `Input` +
|
|
67
|
+
* `Menu` + `Calendar` (no `a-inputdate` element; the wrapper is the coordinator).
|
|
68
|
+
* The field accepts free text and resolves it on commit (blur / Enter) with a
|
|
69
|
+
* lenient, locale-aware parser, then rewrites the entry to the canonical format;
|
|
70
|
+
* an unrecognized entry marks the field `critical` and keeps the text. The trailing
|
|
71
|
+
* calendar button opens a `Calendar` in a menu (a field click just types); with
|
|
72
|
+
* `time`, a time row sits under the grid. The value is an ISO `YYYY-MM-DD` string,
|
|
73
|
+
* or `YYYY-MM-DDTHH:mm` with `time`.
|
|
74
|
+
*
|
|
75
|
+
* Controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
|
|
76
|
+
* Requires `@antadesign/anta/elements` (client-side only).
|
|
77
|
+
*
|
|
78
|
+
* @remarks Not SSR-safe (it embeds `Calendar`): it reads the current date
|
|
79
|
+
* (`Temporal.Now`) and locale (`navigator.language`) at render, so server and
|
|
80
|
+
* client output diverge and hydration mismatches. Render it client-side only.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* <InputDate label="Starts" time defaultValue="2026-06-15T09:00" onValueChange={(v) => save(v)} />
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare const InputDate: ({ value, defaultValue, min, max, locale, name, size, disabled, label, hint, placeholder, status, round, clearable, icon, time, hour12, onValueChange, className, style, ...rest }: InputDateProps) => any;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
|
+
import cn from "clsx";
|
|
3
|
+
import { useMemo, useState } from "../jsx-runtime";
|
|
4
|
+
import { Temporal } from "temporal-polyfill";
|
|
5
|
+
import {
|
|
6
|
+
parseISODate,
|
|
7
|
+
parseDateInput,
|
|
8
|
+
formatDateInput,
|
|
9
|
+
dateFormatPattern,
|
|
10
|
+
parseDateTimeInput,
|
|
11
|
+
formatDateTimeInput,
|
|
12
|
+
dateTimeFormatPattern,
|
|
13
|
+
usesHour12
|
|
14
|
+
} from "../calendar-core";
|
|
15
|
+
import { Input } from "./Input";
|
|
16
|
+
import { Menu } from "./Menu";
|
|
17
|
+
import { Calendar } from "./Calendar";
|
|
18
|
+
import { Button } from "./Button";
|
|
19
|
+
import { Icon } from "./Icon";
|
|
20
|
+
import { Tabs } from "./Tabs";
|
|
21
|
+
import styles from "./InputDate.module.css";
|
|
22
|
+
const InputDate = ({
|
|
23
|
+
value,
|
|
24
|
+
defaultValue,
|
|
25
|
+
min,
|
|
26
|
+
max,
|
|
27
|
+
locale,
|
|
28
|
+
name,
|
|
29
|
+
size,
|
|
30
|
+
disabled,
|
|
31
|
+
label,
|
|
32
|
+
hint,
|
|
33
|
+
placeholder,
|
|
34
|
+
status,
|
|
35
|
+
round,
|
|
36
|
+
clearable,
|
|
37
|
+
icon,
|
|
38
|
+
time,
|
|
39
|
+
hour12,
|
|
40
|
+
onValueChange,
|
|
41
|
+
className,
|
|
42
|
+
style,
|
|
43
|
+
...rest
|
|
44
|
+
}) => {
|
|
45
|
+
const resolvedLocale = locale || (typeof navigator !== "undefined" ? navigator.language : "en-US");
|
|
46
|
+
const minD = parseISODate(min);
|
|
47
|
+
const maxD = parseISODate(max);
|
|
48
|
+
const twelveHour = time ? hour12 ?? usesHour12(resolvedLocale) : false;
|
|
49
|
+
const pattern = useMemo(
|
|
50
|
+
() => placeholder ?? (time ? dateTimeFormatPattern(resolvedLocale, twelveHour) : dateFormatPattern(resolvedLocale)),
|
|
51
|
+
[placeholder, time, resolvedLocale, twelveHour]
|
|
52
|
+
);
|
|
53
|
+
const fmt = (iso) => {
|
|
54
|
+
if (!iso) return "";
|
|
55
|
+
const d = parseISODate(iso.slice(0, 10));
|
|
56
|
+
if (!d) return "";
|
|
57
|
+
if (!time) return formatDateInput(d, resolvedLocale);
|
|
58
|
+
const hasT = iso.length >= 16 && iso[10] === "T";
|
|
59
|
+
const dt = d.toPlainDateTime(Temporal.PlainTime.from(hasT ? iso.slice(11, 16) : "00:00"));
|
|
60
|
+
return formatDateTimeInput(dt, resolvedLocale, twelveHour);
|
|
61
|
+
};
|
|
62
|
+
const normalize = (iso) => !iso || !time ? iso : iso.length <= 10 ? `${iso}T00:00` : iso.slice(0, 16);
|
|
63
|
+
const controlled = value !== void 0;
|
|
64
|
+
const [internal, setInternal] = useState(() => normalize(defaultValue));
|
|
65
|
+
const current = controlled ? value : internal;
|
|
66
|
+
const dateISO = current ? current.slice(0, 10) : "";
|
|
67
|
+
const hasTime = !!current && current.length >= 16 && current[10] === "T";
|
|
68
|
+
const h24 = hasTime ? parseInt(current.slice(11, 13), 10) || 0 : 0;
|
|
69
|
+
const curM = hasTime ? current.slice(14, 16) : "00";
|
|
70
|
+
const curMer = h24 < 12 ? "AM" : "PM";
|
|
71
|
+
const curHourShown = twelveHour ? String((h24 + 11) % 12 + 1).padStart(2, "0") : String(h24).padStart(2, "0");
|
|
72
|
+
const [text, setText] = useState(() => fmt(current));
|
|
73
|
+
const [open, setOpen] = useState(false);
|
|
74
|
+
const [invalid, setInvalid] = useState(false);
|
|
75
|
+
const [hourText, setHourText] = useState(curHourShown);
|
|
76
|
+
const [minuteText, setMinuteText] = useState(curM);
|
|
77
|
+
const meridiem = curMer;
|
|
78
|
+
const [lastValue, setLastValue] = useState(current);
|
|
79
|
+
if (current !== lastValue) {
|
|
80
|
+
setLastValue(current);
|
|
81
|
+
setText(fmt(current));
|
|
82
|
+
setInvalid(false);
|
|
83
|
+
}
|
|
84
|
+
const [lastTime, setLastTime] = useState(`${h24}:${curM}`);
|
|
85
|
+
if (time && `${h24}:${curM}` !== lastTime) {
|
|
86
|
+
setLastTime(`${h24}:${curM}`);
|
|
87
|
+
setHourText(curHourShown);
|
|
88
|
+
setMinuteText(curM);
|
|
89
|
+
}
|
|
90
|
+
const commit = (iso) => {
|
|
91
|
+
if (!controlled) setInternal(iso || void 0);
|
|
92
|
+
onValueChange?.(iso, { value: iso, name });
|
|
93
|
+
};
|
|
94
|
+
const clampPad = (v, hi) => String(Math.min(Math.max(parseInt(String(v || "0"), 10) || 0, 0), hi)).padStart(2, "0");
|
|
95
|
+
const resolveHour = (raw, mer) => {
|
|
96
|
+
let n = parseInt(String(raw || "0"), 10) || 0;
|
|
97
|
+
if (!twelveHour) {
|
|
98
|
+
const h = Math.min(Math.max(n, 0), 23);
|
|
99
|
+
return { h24: h, shown: String(h).padStart(2, "0"), mer };
|
|
100
|
+
}
|
|
101
|
+
if (n === 0) return { h24: 0, shown: "12", mer: "AM" };
|
|
102
|
+
if (n > 23) n = 23;
|
|
103
|
+
if (n > 12) return { h24: n, shown: String(n - 12).padStart(2, "0"), mer: "PM" };
|
|
104
|
+
return { h24: mer === "AM" ? n % 12 : n % 12 + 12, shown: String(n).padStart(2, "0"), mer };
|
|
105
|
+
};
|
|
106
|
+
const withTime = (d) => {
|
|
107
|
+
const { h24: h } = resolveHour(hourText, meridiem);
|
|
108
|
+
return `${d}T${String(h).padStart(2, "0")}:${clampPad(minuteText, 59)}`;
|
|
109
|
+
};
|
|
110
|
+
const apply = (iso) => {
|
|
111
|
+
setInvalid(false);
|
|
112
|
+
if (iso === (current ?? "")) setText(fmt(iso));
|
|
113
|
+
else commit(iso);
|
|
114
|
+
};
|
|
115
|
+
const resolve = (raw) => {
|
|
116
|
+
const t = raw.trim();
|
|
117
|
+
if (!t) {
|
|
118
|
+
setInvalid(false);
|
|
119
|
+
if (current) commit("");
|
|
120
|
+
else setText("");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const parsed = time ? parseDateTimeInput(t, resolvedLocale, { min: minD, max: maxD }) : parseDateInput(t, resolvedLocale, { min: minD, max: maxD });
|
|
124
|
+
if (!parsed) {
|
|
125
|
+
setInvalid(true);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const iso = time ? parsed.toString({ smallestUnit: "minute" }) : parsed.toString();
|
|
129
|
+
apply(iso);
|
|
130
|
+
};
|
|
131
|
+
const commitTime = (hRaw, mRaw, mer) => {
|
|
132
|
+
const { h24: h, shown } = resolveHour(hRaw, mer);
|
|
133
|
+
const mm = clampPad(mRaw, 59);
|
|
134
|
+
setHourText(shown);
|
|
135
|
+
setMinuteText(mm);
|
|
136
|
+
const base = dateISO || Temporal.Now.plainDateISO().toString();
|
|
137
|
+
commit(`${base}T${String(h).padStart(2, "0")}:${mm}`);
|
|
138
|
+
};
|
|
139
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
140
|
+
/* @__PURE__ */ jsx(
|
|
141
|
+
Input,
|
|
142
|
+
{
|
|
143
|
+
label,
|
|
144
|
+
hint: invalid ? `Unrecognized date. Try ${pattern}.` : hint,
|
|
145
|
+
status: invalid ? "critical" : status,
|
|
146
|
+
size,
|
|
147
|
+
round,
|
|
148
|
+
disabled,
|
|
149
|
+
clearable,
|
|
150
|
+
leading: icon ? /* @__PURE__ */ jsx(Icon, { shape: icon }) : void 0,
|
|
151
|
+
value: text,
|
|
152
|
+
placeholder: pattern,
|
|
153
|
+
inputMode: time ? "text" : "numeric",
|
|
154
|
+
autoComplete: "off",
|
|
155
|
+
onInput: (e) => {
|
|
156
|
+
setText(e.currentTarget.value);
|
|
157
|
+
if (invalid) setInvalid(false);
|
|
158
|
+
},
|
|
159
|
+
onChange: (e) => {
|
|
160
|
+
if (e.target !== e.currentTarget) return;
|
|
161
|
+
resolve(e.currentTarget.value);
|
|
162
|
+
},
|
|
163
|
+
className: cn(styles.dateField, className),
|
|
164
|
+
style,
|
|
165
|
+
trailing: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
166
|
+
/* @__PURE__ */ jsx(
|
|
167
|
+
Button,
|
|
168
|
+
{
|
|
169
|
+
priority: "tertiary",
|
|
170
|
+
size,
|
|
171
|
+
icon: "calendar-days",
|
|
172
|
+
"aria-label": "Open calendar",
|
|
173
|
+
"aria-haspopup": "dialog",
|
|
174
|
+
disabled
|
|
175
|
+
}
|
|
176
|
+
),
|
|
177
|
+
/* @__PURE__ */ jsx(
|
|
178
|
+
Menu,
|
|
179
|
+
{
|
|
180
|
+
open,
|
|
181
|
+
placement: "bottom-end",
|
|
182
|
+
className: styles.calendarMenu,
|
|
183
|
+
onStateChange: (_e, { next }) => setOpen(next),
|
|
184
|
+
children: /* @__PURE__ */ jsxs("div", { "data-menu-open": "", children: [
|
|
185
|
+
/* @__PURE__ */ jsx(
|
|
186
|
+
Calendar,
|
|
187
|
+
{
|
|
188
|
+
value: dateISO,
|
|
189
|
+
min,
|
|
190
|
+
max,
|
|
191
|
+
locale,
|
|
192
|
+
size,
|
|
193
|
+
disabled,
|
|
194
|
+
onStateChange: (_e, { next, reason }) => {
|
|
195
|
+
if (reason !== "user") return;
|
|
196
|
+
const d = next ?? "";
|
|
197
|
+
apply(time && d ? withTime(d) : d);
|
|
198
|
+
if (!time) setOpen(false);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
time && /* @__PURE__ */ jsxs("div", { className: styles.timeRow, children: [
|
|
203
|
+
/* @__PURE__ */ jsxs("div", { className: styles.time, role: "group", "aria-label": "Time", children: [
|
|
204
|
+
/* @__PURE__ */ jsx(Icon, { shape: "clock", "aria-hidden": "true", className: styles.clock }),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
Input,
|
|
207
|
+
{
|
|
208
|
+
size,
|
|
209
|
+
className: styles.timeField,
|
|
210
|
+
value: hourText,
|
|
211
|
+
placeholder: "HH",
|
|
212
|
+
type: "number",
|
|
213
|
+
inputMode: "numeric",
|
|
214
|
+
"aria-label": "Hours",
|
|
215
|
+
disabled,
|
|
216
|
+
onInput: (e) => setHourText(e.currentTarget.value.replace(/\D/g, "").slice(0, 2)),
|
|
217
|
+
onChange: (e) => commitTime(e.currentTarget.value, minuteText, meridiem)
|
|
218
|
+
}
|
|
219
|
+
),
|
|
220
|
+
/* @__PURE__ */ jsx("span", { className: styles.sep, "aria-hidden": "true", children: ":" }),
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
Input,
|
|
223
|
+
{
|
|
224
|
+
size,
|
|
225
|
+
className: styles.timeField,
|
|
226
|
+
value: minuteText,
|
|
227
|
+
placeholder: "MM",
|
|
228
|
+
type: "number",
|
|
229
|
+
inputMode: "numeric",
|
|
230
|
+
"aria-label": "Minutes",
|
|
231
|
+
disabled,
|
|
232
|
+
onInput: (e) => setMinuteText(e.currentTarget.value.replace(/\D/g, "").slice(0, 2)),
|
|
233
|
+
onChange: (e) => commitTime(hourText, e.currentTarget.value, meridiem)
|
|
234
|
+
}
|
|
235
|
+
),
|
|
236
|
+
twelveHour && /* @__PURE__ */ jsx(
|
|
237
|
+
Tabs,
|
|
238
|
+
{
|
|
239
|
+
className: styles.meridiem,
|
|
240
|
+
size,
|
|
241
|
+
priority: "primary",
|
|
242
|
+
"aria-label": "AM or PM",
|
|
243
|
+
options: [
|
|
244
|
+
{ value: "AM", label: "AM" },
|
|
245
|
+
{ value: "PM", label: "PM" }
|
|
246
|
+
],
|
|
247
|
+
value: meridiem,
|
|
248
|
+
onStateChange: (_e, { next }) => {
|
|
249
|
+
if (next === "AM" || next === "PM") commitTime(hourText, minuteText, next);
|
|
250
|
+
},
|
|
251
|
+
disabled
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
] }),
|
|
255
|
+
/* @__PURE__ */ jsx(
|
|
256
|
+
Button,
|
|
257
|
+
{
|
|
258
|
+
priority: "tertiary",
|
|
259
|
+
size,
|
|
260
|
+
icon: "check",
|
|
261
|
+
"aria-label": "Done",
|
|
262
|
+
onClick: () => setOpen(false)
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
] })
|
|
266
|
+
] })
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
] }),
|
|
270
|
+
...rest
|
|
271
|
+
}
|
|
272
|
+
),
|
|
273
|
+
name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current ?? "" }) : null
|
|
274
|
+
] });
|
|
275
|
+
};
|
|
276
|
+
export {
|
|
277
|
+
InputDate
|
|
278
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.calendarMenu{--menu-padding: 8px}.timeRow{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px;padding-top:8px;border-top:1px solid var(--border-3)}.time{display:flex;align-items:center;gap:4px}.clock{flex:none;color:var(--text-5);margin-inline-end:2px}.sep{color:var(--text-3);user-select:none}.timeField{width:2.25rem}.timeField[size=small]{width:2rem}.timeField[size=large]{width:2.5rem}.timeField::part(input){text-align:center;font-variant-numeric:tabular-nums;font-weight:500}.meridiem{margin-inline-start:4px;--tab-padding-x: 6px;--tabs-gap: 0px;--tab-padding-y: max(0px, calc((10px - var(--_pb)) / 2 - 2px))}.meridiem a-tab{font-weight:400}.dateField::part(input){text-overflow:ellipsis}
|
|
@@ -29,6 +29,10 @@ export interface MenuProps extends BaseProps {
|
|
|
29
29
|
* in response. Submenus are always uncontrolled regardless of this. See
|
|
30
30
|
* STATEFUL-COMPONENTS.md. */
|
|
31
31
|
open?: boolean;
|
|
32
|
+
/** Round the menu: the container softens to a 20px radius and its items go
|
|
33
|
+
* fully round. A `number` (px) or CSS length string tunes the container radius
|
|
34
|
+
* only — items stay full pills. */
|
|
35
|
+
round?: boolean | number | string;
|
|
32
36
|
/** Fired before the open state changes — on open, and on every dismiss (Esc,
|
|
33
37
|
* outside-click, scroll, selecting an item). `event` is the cancelable
|
|
34
38
|
* `statechange`; `detail.next`/`detail.prev` are the requested/previous open
|
|
@@ -70,4 +74,4 @@ export interface MenuProps extends BaseProps {
|
|
|
70
74
|
* </Menu>
|
|
71
75
|
* ```
|
|
72
76
|
*/
|
|
73
|
-
export declare const Menu: ({ placement, context, coord, nohover, offset, open, onStateChange, className, children, ...rest }: MenuProps) => any;
|
|
77
|
+
export declare const Menu: ({ placement, context, coord, nohover, offset, open, onStateChange, round, className, style, children, ...rest }: MenuProps) => any;
|
package/dist/components/Menu.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from "@antadesign/anta/jsx-runtime";
|
|
2
|
-
import { nativeStateChange } from "../anta_helpers";
|
|
2
|
+
import { nativeStateChange, roundStyle } from "../anta_helpers";
|
|
3
3
|
const Menu = ({
|
|
4
4
|
placement,
|
|
5
5
|
context,
|
|
@@ -8,7 +8,9 @@ const Menu = ({
|
|
|
8
8
|
offset,
|
|
9
9
|
open,
|
|
10
10
|
onStateChange,
|
|
11
|
+
round,
|
|
11
12
|
className,
|
|
13
|
+
style,
|
|
12
14
|
children,
|
|
13
15
|
...rest
|
|
14
16
|
}) => {
|
|
@@ -23,15 +25,17 @@ const Menu = ({
|
|
|
23
25
|
state: open === void 0 ? void 0 : open ? "open" : "closed",
|
|
24
26
|
onstatechange: onStateChange ? (e) => {
|
|
25
27
|
const { event, detail } = nativeStateChange(e);
|
|
26
|
-
if (detail)
|
|
28
|
+
if (detail && (detail.next === "open" || detail.next === "closed"))
|
|
27
29
|
onStateChange(event, {
|
|
28
30
|
next: detail.next === "open",
|
|
29
31
|
prev: detail.prev === "open"
|
|
30
32
|
});
|
|
31
33
|
} : void 0,
|
|
34
|
+
round: round ? "" : void 0,
|
|
32
35
|
role: "menu",
|
|
33
36
|
"aria-orientation": "vertical",
|
|
34
37
|
class: className,
|
|
38
|
+
style: roundStyle(round, "--menu-round", style),
|
|
35
39
|
...rest,
|
|
36
40
|
children
|
|
37
41
|
}
|
|
@@ -3,8 +3,14 @@ import type { IconShape } from '../elements/a-icon.shapes';
|
|
|
3
3
|
export interface MenuItemProps extends BaseProps {
|
|
4
4
|
/** Leading icon shape. */
|
|
5
5
|
icon?: IconShape;
|
|
6
|
-
/** The item's text.
|
|
7
|
-
|
|
6
|
+
/** The item's text. Usually a string, but any node is accepted — e.g. a filtered
|
|
7
|
+
* `Select` bolds the matched substring. Omit and pass `children` for richer
|
|
8
|
+
* content. */
|
|
9
|
+
label?: React.ReactNode;
|
|
10
|
+
/** Secondary text under the label — explanatory copy, like `RadioGroup`'s
|
|
11
|
+
* option `hint`. Requires `label` (it stacks in a column beneath it). Muted
|
|
12
|
+
* (`--text-3`) and tracks the row's `tone`. A string, or any node. */
|
|
13
|
+
hint?: React.ReactNode;
|
|
8
14
|
/** A trailing keyboard-shortcut hint, e.g. `"⌘E"`. */
|
|
9
15
|
kbd?: string;
|
|
10
16
|
/** A trailing icon. On a `submenu` item this **overrides** the default
|
|
@@ -13,10 +19,45 @@ export interface MenuItemProps extends BaseProps {
|
|
|
13
19
|
iconTrailing?: IconShape;
|
|
14
20
|
/** Disable the item: greyed out, not focusable for activation, no close. */
|
|
15
21
|
disabled?: boolean;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
22
|
+
/** Mark the item as selected. On a plain row (no `selectionIndicator`) this is
|
|
23
|
+
* a persistent background tint, the same resting fill a pressed row shows. On a
|
|
24
|
+
* checkable row (`selectionIndicator` set) it instead drives the leading
|
|
25
|
+
* `checkbox` / `radio` indicator and the row's `aria-checked`. */
|
|
26
|
+
selected?: boolean;
|
|
27
|
+
/** Turn the row into a checkable item, driven by `selected` (the row stays the
|
|
28
|
+
* control and carries `aria-checked`):
|
|
29
|
+
* - `'checkbox'` → `role="menuitemcheckbox"`, a leading passive `<a-checkbox>`
|
|
30
|
+
* (before `icon`); the tint is dropped (the box carries state).
|
|
31
|
+
* - `'radio'` → `role="menuitemradio"`, a leading passive `<a-radio>`; tint dropped.
|
|
32
|
+
* - `'check'` → `role="menuitemradio"`, a trailing check glyph on the selected
|
|
33
|
+
* row *and* the background tint (the canonical single-select look).
|
|
34
|
+
* Omit for a plain row (the default). */
|
|
35
|
+
selectionIndicator?: 'checkbox' | 'radio' | 'check';
|
|
36
|
+
/** Only meaningful with `selectionIndicator="checkbox"`: render the box in the
|
|
37
|
+
* mixed state (`aria-checked="mixed"`) — e.g. a "Select all" row when some but
|
|
38
|
+
* not all of its options are selected. */
|
|
39
|
+
indeterminate?: boolean;
|
|
40
|
+
/** Replace the built-in selection-indicator *visual* with your own node,
|
|
41
|
+
* rendered at the **leading** edge (where the checkbox / radio sit). Pair with
|
|
42
|
+
* `selectionIndicator` to keep the semantics — the row stays the control and
|
|
43
|
+
* carries `role` + `aria-checked`; only the drawn mark changes. Suppresses the
|
|
44
|
+
* built-in checkbox / radio and the trailing `check` glyph. The node is made
|
|
45
|
+
* passive (aria-hidden, no pointer events) so the row owns the click. */
|
|
46
|
+
indicator?: React.ReactNode;
|
|
47
|
+
/** Semantic tone — colors the label, icon, and hover/selected tint (and the
|
|
48
|
+
* `checkbox`/`radio` indicator, which adopts it). A named tone, or any literal
|
|
49
|
+
* CSS color (`'#ff1493'`, `'rebeccapurple'`) for a one-off custom tone whose
|
|
50
|
+
* hue + chroma are kept while the lightness is pinned to match the brand text.
|
|
51
|
+
* `critical` is the destructive action; `neutral` (the default) is the standard
|
|
52
|
+
* gray.
|
|
18
53
|
* @defaultValue neutral */
|
|
19
|
-
tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
|
|
54
|
+
tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
|
|
55
|
+
/** Like `tone`, but applied only while the row is `selected` — an unselected row
|
|
56
|
+
* stays neutral. The whole selected row (label, icon, tint, and the `checkbox` /
|
|
57
|
+
* `radio` indicator) takes the tone. Same value set as `tone`; on a selected row
|
|
58
|
+
* `toneSelected` wins over `tone` when both are set.
|
|
59
|
+
* @defaultValue neutral */
|
|
60
|
+
toneSelected?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
|
|
20
61
|
/** Marks this item as a submenu parent: adds the trailing chevron,
|
|
21
62
|
* `aria-haspopup="menu"`, and an `aria-expanded` baseline (kept in sync by
|
|
22
63
|
* the nested menu). Nest the flyout as a `<Menu>` child. */
|
|
@@ -32,7 +73,7 @@ export interface MenuItemProps extends BaseProps {
|
|
|
32
73
|
* `{ value, label }` detail. */
|
|
33
74
|
onSelect?: (event: any, detail: {
|
|
34
75
|
value?: string | number;
|
|
35
|
-
label?:
|
|
76
|
+
label?: React.ReactNode;
|
|
36
77
|
}) => void;
|
|
37
78
|
/** Item content. With `label` set, children are extra content — most
|
|
38
79
|
* notably the nested `<Menu>` for a submenu parent. */
|
|
@@ -58,4 +99,4 @@ export interface MenuItemProps extends BaseProps {
|
|
|
58
99
|
* </MenuItem>
|
|
59
100
|
* ```
|
|
60
101
|
*/
|
|
61
|
-
export declare const MenuItem: ({ icon, label, kbd, iconTrailing, disabled, tone, submenu, value, onSelect, className, children, ...rest }: MenuItemProps) => any;
|
|
102
|
+
export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, className, style, children, ...rest }: MenuItemProps) => any;
|