@antadesign/anta 0.3.13 → 0.3.15
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 +20 -2
- package/dist/anta_helpers.js +9 -0
- package/dist/components/Banner.d.ts +79 -0
- package/dist/components/Banner.js +69 -0
- package/dist/components/Select.d.ts +18 -0
- package/dist/components/Select.js +14 -3
- package/dist/components/Select.module.css +1 -1
- package/dist/components/SelectFaceted.js +2 -2
- package/dist/components/SelectFaceted.module.css +1 -1
- package/dist/components/Toaster.d.ts +48 -0
- package/dist/components/Toaster.js +56 -0
- package/dist/elements/a-banner.css +1 -0
- package/dist/elements/a-banner.d.ts +10 -0
- package/dist/elements/a-banner.js +136 -0
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.js +1 -0
- package/dist/elements/a-menu-item.js +6 -1
- package/dist/elements/a-menu.css +1 -1
- package/dist/elements/a-menu.js +2 -0
- package/dist/elements/a-toast.css +1 -0
- package/dist/elements/a-toast.d.ts +70 -0
- package/dist/elements/a-toast.js +283 -0
- package/dist/elements/a-toaster.css +1 -0
- package/dist/elements/a-toaster.d.ts +30 -0
- package/dist/elements/a-toaster.js +98 -0
- package/dist/elements/index.d.ts +3 -0
- package/dist/elements/index.js +9 -0
- package/dist/general_types.d.ts +79 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/jsx-runtime.d.ts +8 -1
- package/dist/jsx-runtime.js +7 -1
- package/dist/toaster.d.ts +99 -0
- package/dist/toaster.js +100 -0
- package/package.json +1 -1
package/dist/anta_helpers.d.ts
CHANGED
|
@@ -29,9 +29,17 @@ export declare function wrapLabel(kids: React.ReactNode, tag: string): React.Rea
|
|
|
29
29
|
* `currentTarget` is still live. (Read as `true` for the falsy-event guard so a
|
|
30
30
|
* missing event never spuriously reads as bubbled.)
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* The event a cross-renderer `statechange` handler receives: React wraps the native
|
|
34
|
+
* `CustomEvent` in a synthetic event (`.nativeEvent`); Preact passes it directly. `D`
|
|
35
|
+
* is the per-component `detail` vocabulary. Pair with `nativeStateChange` to unwrap.
|
|
36
|
+
* Exported so the stateful wrappers (`Banner`, and future ones) don't each re-declare
|
|
37
|
+
* this union — the CLAUDE.md "reuse shared wrapper helpers" rule.
|
|
38
|
+
*/
|
|
39
|
+
export type StateChangeEvent<D> = CustomEvent<D> | {
|
|
33
40
|
nativeEvent: CustomEvent<D>;
|
|
34
|
-
}
|
|
41
|
+
};
|
|
42
|
+
export declare function nativeStateChange<D>(e: StateChangeEvent<D>): {
|
|
35
43
|
event: CustomEvent<D>;
|
|
36
44
|
detail?: D;
|
|
37
45
|
isOwn: boolean;
|
|
@@ -46,6 +54,16 @@ export declare const ISOLATE_HINT: string;
|
|
|
46
54
|
* `'open'` is `'closed'`, so an absent / malformed attribute reads closed. Kept
|
|
47
55
|
* here so the two elements can't drift on how the token is parsed. */
|
|
48
56
|
export declare function parseOpenState(v: string | null): 'open' | 'closed';
|
|
57
|
+
/** Install a delegated "dismiss from an action" click handler: any element inside
|
|
58
|
+
* `host` carrying `[attr]` runs `onDismiss` on click, scoped to the nearest host
|
|
59
|
+
* of `host`'s own tag (so a control inside a nested same-tag element doesn't also
|
|
60
|
+
* dismiss the outer one). A read-only light-DOM walk — no host / tree mutation —
|
|
61
|
+
* that catches any activated control (native `<button>`, `<a>`, `<a-button>`,
|
|
62
|
+
* `<a-menu-item>`) with keyboard activation for free. Shared by `a-toast`
|
|
63
|
+
* (`data-toast-dismiss`) and `a-banner` (`data-banner-dismiss`), mirroring
|
|
64
|
+
* `a-dialog`'s `data-dialog-close` family; keep the one copy here so a future fix
|
|
65
|
+
* (e.g. skipping `[disabled]` triggers) lands for every element at once. */
|
|
66
|
+
export declare function installDismissTrigger(host: HTMLElement, attr: string, onDismiss: () => void): void;
|
|
49
67
|
/** The six named tones every toned component shares. Anything else is a literal
|
|
50
68
|
* CSS colour the element resolves through its `--{component}-tone-source` var. */
|
|
51
69
|
export declare const NAMED_TONES: Set<string>;
|
package/dist/anta_helpers.js
CHANGED
|
@@ -21,6 +21,14 @@ const ISOLATE_HINT = IS_MAC ? "\u2325+Click to select only this" : "Alt+Click to
|
|
|
21
21
|
function parseOpenState(v) {
|
|
22
22
|
return v === "open" ? "open" : "closed";
|
|
23
23
|
}
|
|
24
|
+
function installDismissTrigger(host, attr, onDismiss) {
|
|
25
|
+
host.addEventListener("click", (e) => {
|
|
26
|
+
const target = e.target;
|
|
27
|
+
if (!(target instanceof Element)) return;
|
|
28
|
+
const trigger = target.closest(`[${attr}]`);
|
|
29
|
+
if (trigger && trigger.closest(host.localName) === host) onDismiss();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
24
32
|
const NAMED_TONES = /* @__PURE__ */ new Set([
|
|
25
33
|
"brand",
|
|
26
34
|
"neutral",
|
|
@@ -138,6 +146,7 @@ export {
|
|
|
138
146
|
SelectableChildElement,
|
|
139
147
|
anchorRect,
|
|
140
148
|
hasChildren,
|
|
149
|
+
installDismissTrigger,
|
|
141
150
|
isInsideOpenMenu,
|
|
142
151
|
isMenuOpen,
|
|
143
152
|
nativeStateChange,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { BaseProps } from "../general_types";
|
|
2
|
+
/** Public props for the `<Banner>` message strip. `message` is the leading
|
|
3
|
+
* content, `children` sit between it and `actions`, all centered in one row. */
|
|
4
|
+
export interface BannerProps extends BaseProps {
|
|
5
|
+
/** Leading message — a string (rendered at the banner type scale) or any node.
|
|
6
|
+
* Centered in the bar, followed by `children` and then `actions`. */
|
|
7
|
+
message?: React.ReactNode;
|
|
8
|
+
/** Trailing controls (buttons, links) rendered as a compact row after the
|
|
9
|
+
* message and `children`. */
|
|
10
|
+
actions?: React.ReactNode;
|
|
11
|
+
/** Free content placed BETWEEN the message and the actions. */
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
/** Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`) for
|
|
14
|
+
* a one-off custom tone. Named tones re-point the surface, text, and border
|
|
15
|
+
* colour (used if you opt into a border); a custom color keeps its hue while
|
|
16
|
+
* lightness/chroma are pinned. `'neutral'` (the default) is the same as omitting it.
|
|
17
|
+
* @defaultValue 'neutral' */
|
|
18
|
+
tone?: "neutral" | "brand" | "info" | "success" | "warning" | "critical" | (string & {});
|
|
19
|
+
/** Rounded corners for a standalone, floating banner (`border-radius: 999px`,
|
|
20
|
+
* which clamps to a stadium) instead of the edge-to-edge strip. Still borderless
|
|
21
|
+
* by default. Pass a `number` (px) or a CSS length string for a custom radius. */
|
|
22
|
+
round?: boolean | number | string;
|
|
23
|
+
/** Whether the trailing ✕ dismiss button is present. `false` removes it — drive
|
|
24
|
+
* dismissal yourself (a controlled `dismissed`, or your own control) instead.
|
|
25
|
+
* It doesn't make the banner un-dismissible, it just drops the built-in ✕.
|
|
26
|
+
* @defaultValue true */
|
|
27
|
+
closable?: boolean;
|
|
28
|
+
/** ARIA role for the strip host. `'status'` (a polite live region, so the notice
|
|
29
|
+
* reaches assistive tech) by default; pass `'alert'` for an urgent notice that
|
|
30
|
+
* should interrupt, or a landmark role.
|
|
31
|
+
* @defaultValue 'status' */
|
|
32
|
+
role?: string;
|
|
33
|
+
/** Controlled dismissed state. When provided, the consumer owns visibility: the
|
|
34
|
+
* banner follows this prop only, and clicking ✕ *requests* dismissal (reject by
|
|
35
|
+
* not updating). **Requires `onDismiss`** — controlled mode never self-hides, so
|
|
36
|
+
* without a handler to set `dismissed` the ✕ can't close the banner. Leave
|
|
37
|
+
* undefined for uncontrolled. */
|
|
38
|
+
dismissed?: boolean;
|
|
39
|
+
/** Initial dismissed state for the uncontrolled case (read once on mount). */
|
|
40
|
+
defaultDismissed?: boolean;
|
|
41
|
+
/** Fired when the user dismisses the banner (clicks ✕). Uncontrolled, the banner
|
|
42
|
+
* hides itself and this just notifies. Controlled, it's how you accept the
|
|
43
|
+
* request — set `dismissed` to `true` (or ignore to reject); pair it with
|
|
44
|
+
* `dismissed`, or the banner can't be closed. */
|
|
45
|
+
onDismiss?: () => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `<Banner>` — a full-width, dismissible message strip with a bottom border.
|
|
49
|
+
*
|
|
50
|
+
* A close cousin of `<Card>` with a small, centered horizontal payload:
|
|
51
|
+
* `message` leads, `children` sit in the middle, `actions` trail, and (when
|
|
52
|
+
* `closable`) a 40px-wide, full-height ✕ fills the right edge without shifting the centered group.
|
|
53
|
+
*
|
|
54
|
+
* A pure, stateless pass-through to `<a-banner>`: the element owns dismissal and
|
|
55
|
+
* visibility, so the wrapper holds no state and grabs no ref — it maps props to
|
|
56
|
+
* attributes (safe wherever the host DOM is reconciled, incl. off the UI thread).
|
|
57
|
+
*
|
|
58
|
+
* Uncontrolled (`defaultDismissed`): the element owns visibility and the ✕
|
|
59
|
+
* self-dismisses. Controlled (`dismissed` + `onDismiss`): the wrapper emits
|
|
60
|
+
* `state="open"|"closed"`; the element treats the attribute as the source of
|
|
61
|
+
* truth and the ✕ only *requests* dismissal. The element uses Anta's shared
|
|
62
|
+
* open/closed state contract under the hood — the wrapper presents it as the
|
|
63
|
+
* banner-natural `dismissed` (closed = dismissed). See STATEFUL-COMPONENTS.md.
|
|
64
|
+
*
|
|
65
|
+
* Requires `@antadesign/anta/elements` to be imported (client-side only) to
|
|
66
|
+
* register the underlying custom element.
|
|
67
|
+
*
|
|
68
|
+
* @example Basic usage
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <Banner tone="info" message="A new release is available." actions={<Button size="small" label="Update" />} />
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example Controlled
|
|
74
|
+
* ```tsx
|
|
75
|
+
* const [hidden, setHidden] = useState(false)
|
|
76
|
+
* <Banner message="Saved." dismissed={hidden} onDismiss={() => setHidden(true)} />
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare const Banner: ({ message, actions, tone, round, closable, role, dismissed, defaultDismissed, onDismiss, className, style, children, ...rest }: BannerProps) => any;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
|
+
import { nativeStateChange, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
|
|
3
|
+
import { Button } from "./Button";
|
|
4
|
+
const Banner = ({
|
|
5
|
+
message,
|
|
6
|
+
actions,
|
|
7
|
+
tone,
|
|
8
|
+
round,
|
|
9
|
+
closable,
|
|
10
|
+
role = "status",
|
|
11
|
+
dismissed,
|
|
12
|
+
defaultDismissed,
|
|
13
|
+
onDismiss,
|
|
14
|
+
className,
|
|
15
|
+
style,
|
|
16
|
+
children,
|
|
17
|
+
...rest
|
|
18
|
+
}) => {
|
|
19
|
+
const controlled = dismissed != null;
|
|
20
|
+
const computedStyle = roundStyle(
|
|
21
|
+
round,
|
|
22
|
+
"--banner-round",
|
|
23
|
+
toneStyle(tone, "--banner-tone-source", style)
|
|
24
|
+
);
|
|
25
|
+
const messageNode = message == null ? null : typeof message === "string" || typeof message === "number" ? /* @__PURE__ */ jsx("a-banner-message", { slot: "message", children: message }) : /* @__PURE__ */ jsx("span", { slot: "message", style: { display: "contents" }, children: message });
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"a-banner",
|
|
28
|
+
{
|
|
29
|
+
state: controlled ? dismissed ? "closed" : "open" : void 0,
|
|
30
|
+
"default-state": !controlled ? defaultDismissed ? "closed" : "open" : void 0,
|
|
31
|
+
tone: tone && tone !== "neutral" ? tone : void 0,
|
|
32
|
+
round: roundAttr(round),
|
|
33
|
+
role,
|
|
34
|
+
onstatechange: onDismiss ? (e) => {
|
|
35
|
+
const { detail, isOwn } = nativeStateChange(e);
|
|
36
|
+
if (isOwn && detail?.next === "closed") onDismiss();
|
|
37
|
+
} : void 0,
|
|
38
|
+
class: className,
|
|
39
|
+
style: computedStyle,
|
|
40
|
+
...rest,
|
|
41
|
+
children: [
|
|
42
|
+
messageNode,
|
|
43
|
+
children,
|
|
44
|
+
actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", style: { display: "contents" }, children: actions }),
|
|
45
|
+
closable !== false && // A real <a-button> (light DOM → fully styled, keyboard-focusable) in the
|
|
46
|
+
// element's `close` slot. It fires the bubbling `dismissrequest` event via
|
|
47
|
+
// a-button's global listener, so dismissing works even without framework
|
|
48
|
+
// hydration; the element turns that into a dismiss request.
|
|
49
|
+
// CONTRACT: `data-custom-event` below MUST match `DISMISS_TRIGGER` in the
|
|
50
|
+
// element (src/elements/a-banner.ts). Duplicated, not shared — importing
|
|
51
|
+
// the element module here would self-register it and break the
|
|
52
|
+
// wrapper/element decoupling. Rename in both places.
|
|
53
|
+
/* @__PURE__ */ jsx("span", { slot: "close", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
|
|
54
|
+
Button,
|
|
55
|
+
{
|
|
56
|
+
priority: "quaternary",
|
|
57
|
+
icon: "x",
|
|
58
|
+
tone,
|
|
59
|
+
"aria-label": "Dismiss",
|
|
60
|
+
"data-custom-event": "dismissrequest"
|
|
61
|
+
}
|
|
62
|
+
) })
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
export {
|
|
68
|
+
Banner
|
|
69
|
+
};
|
|
@@ -227,6 +227,24 @@ export interface SelectCommonProps<V extends OptionValue = string> extends Omit<
|
|
|
227
227
|
* `indicator` (`'check'` / `'radio'`) or `selection="multiple"` for the
|
|
228
228
|
* semantics. Composes with `renderOption`. */
|
|
229
229
|
renderIndicator?: (state: OptionState<V>) => React.ReactNode;
|
|
230
|
+
/** `multiple` only: spell the picks out in the count summary — `3 selected:
|
|
231
|
+
* A, B, C` (labels comma-joined) in place of the bare `3 selected`. Applies
|
|
232
|
+
* to the multi-count case only: `All` stays `All`, a single pick stays its
|
|
233
|
+
* own label, and an empty selection stays the `placeholder`. The list flows
|
|
234
|
+
* into the read-only field, so it ellipsizes at the field's width when long
|
|
235
|
+
* (`3 selected: Engineering, Des… `). `renderSummary` overrides this. */
|
|
236
|
+
verbose?: boolean;
|
|
237
|
+
/** `multiple` only: build the trigger's selection summary text yourself,
|
|
238
|
+
* replacing the built-in "`All` / one label / `N selected`" logic. Receives
|
|
239
|
+
* the resolved selected options (`selected.length` is the count) and runs only
|
|
240
|
+
* while something is selected — an empty selection still shows the
|
|
241
|
+
* `placeholder`. Return a **string**: it flows into the default trigger's
|
|
242
|
+
* read-only field, so a long summary ellipsizes at the field's width just
|
|
243
|
+
* like a long value (`Engineering, Design, … `). Return `undefined` to fall
|
|
244
|
+
* back to the default for that case (e.g. customize only the count, keeping
|
|
245
|
+
* the single-label case built-in). For rich content (chips, multiple nodes)
|
|
246
|
+
* use `renderTrigger`, which replaces the whole field. */
|
|
247
|
+
renderSummary?: (selected: SelectOption<V>[]) => string | undefined;
|
|
230
248
|
/** Render your own trigger in place of the default field. Receives a
|
|
231
249
|
* `TriggerState` (`open` / `value` / `selected` / `disabled` / `icon`) to drive
|
|
232
250
|
* its look. **Return exactly one focusable element** (an Anta `Button`, say) —
|
|
@@ -62,6 +62,8 @@ const Select = (props) => {
|
|
|
62
62
|
clearLabel = "Clear",
|
|
63
63
|
renderOption,
|
|
64
64
|
renderIndicator,
|
|
65
|
+
verbose,
|
|
66
|
+
renderSummary,
|
|
65
67
|
renderTrigger,
|
|
66
68
|
renderEmpty,
|
|
67
69
|
className,
|
|
@@ -112,13 +114,22 @@ const Select = (props) => {
|
|
|
112
114
|
const selectedValues = Array.isArray(currentRaw) ? currentRaw : currentRaw != null ? [currentRaw] : [];
|
|
113
115
|
const isSelected = (v) => selectedValues.includes(v);
|
|
114
116
|
const selectedOptions = selectedValues.map((v) => byValue.get(v)).filter(Boolean);
|
|
117
|
+
const allEnabledValues = allLeaves.filter((l) => !l.disabled).map((l) => l.opt.value);
|
|
118
|
+
const everythingSelected = multiple && allEnabledValues.length > 1 && allEnabledValues.every((v) => selectedValues.includes(v));
|
|
119
|
+
const labelOf = (o) => o.label ?? String(o.value);
|
|
115
120
|
let display = "";
|
|
116
121
|
if (multiple) {
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
const custom = selectedOptions.length > 0 ? renderSummary?.(selectedOptions) : void 0;
|
|
123
|
+
if (custom != null && custom !== "") display = custom;
|
|
124
|
+
else if (everythingSelected) display = "All";
|
|
125
|
+
else if (selectedOptions.length === 1) display = labelOf(selectedOptions[0]);
|
|
126
|
+
else if (selectedOptions.length > 1) {
|
|
127
|
+
const count = `${selectedOptions.length} selected`;
|
|
128
|
+
display = verbose ? `${count}: ${selectedOptions.map(labelOf).join(", ")}` : count;
|
|
129
|
+
}
|
|
119
130
|
} else if (currentRaw != null) {
|
|
120
131
|
const o = byValue.get(currentRaw);
|
|
121
|
-
display = o ?
|
|
132
|
+
display = o ? labelOf(o) : "";
|
|
122
133
|
}
|
|
123
134
|
const choose = (o, e) => {
|
|
124
135
|
if (multiple) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.chevron{
|
|
1
|
+
.chevron{transition:transform .15s ease}.chevron.open{transform:rotate(180deg)}.filter{padding:4px 4px 0}.footer{padding:var(--menu-padding, 4px);border-top:1px solid var(--border-4)}
|
|
@@ -196,7 +196,7 @@ const SelectFaceted = (props) => {
|
|
|
196
196
|
const applied = current[facet.key] ?? "";
|
|
197
197
|
const draft = drafts[facet.key] ?? applied;
|
|
198
198
|
const apply = () => {
|
|
199
|
-
const next = draft.trim()
|
|
199
|
+
const next = draft.trim() === "" ? void 0 : draft;
|
|
200
200
|
if (next !== (applied || void 0)) setFacet(facet, next);
|
|
201
201
|
};
|
|
202
202
|
return (
|
|
@@ -312,7 +312,7 @@ const SelectFaceted = (props) => {
|
|
|
312
312
|
searchable && rootQuery.trim() ? renderFlatResults() : facets.map((facet) => /* @__PURE__ */ jsxs(MenuItem, { submenu: true, icon: facet.icon, label: facet.label, children: [
|
|
313
313
|
(() => {
|
|
314
314
|
const s = summaryOf(facet);
|
|
315
|
-
return s != null ? /* @__PURE__ */ jsx(Tag, { size: "small", tone: "brand", children: s }) : null;
|
|
315
|
+
return s != null ? /* @__PURE__ */ jsx(Tag, { size: "small", tone: "brand", children: /* @__PURE__ */ jsx("span", { className: styles.summary, children: s }) }) : null;
|
|
316
316
|
})(),
|
|
317
317
|
renderEditor(facet)
|
|
318
318
|
] }, facet.key)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.search{padding:var(--menu-padding, 4px)}.footer{padding:var(--menu-padding, 4px);border-top:1px solid var(--border-4)}
|
|
1
|
+
.search{padding:var(--menu-padding, 4px)}.summary{display:block;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.footer{padding:var(--menu-padding, 4px);border-top:1px solid var(--border-4)}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { BaseProps } from "../general_types";
|
|
2
|
+
import { type Toaster as ToasterStore } from "../toaster";
|
|
3
|
+
export interface ToasterProps extends BaseProps {
|
|
4
|
+
/** The store this region renders. Omit to bind the default store driven by
|
|
5
|
+
* `Toaster.manager`; pass a `createToaster()` for an isolated region. */
|
|
6
|
+
toaster?: ToasterStore;
|
|
7
|
+
/** Accessible label for the region landmark.
|
|
8
|
+
* @defaultValue 'Notifications' */
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `<Toaster>` — the mounted notification region. Keep one mounted somewhere in
|
|
13
|
+
* your app (a portal is fine); toasts are added imperatively through the store
|
|
14
|
+
* (`Toaster.manager`, or a `createToaster()` you pass as `toaster`). It
|
|
15
|
+
* subscribes to the store and renders each toast through the reconciler, so
|
|
16
|
+
* React/Preact owns the toast nodes.
|
|
17
|
+
*
|
|
18
|
+
* Announcement is opt-in **per toast** (`add(render, { politeness })`), not a
|
|
19
|
+
* blanket live region on the whole toaster.
|
|
20
|
+
*
|
|
21
|
+
* Requires `@antadesign/anta/elements` to be imported (client-side only) to
|
|
22
|
+
* register the underlying custom elements.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Toaster /> // mount once, keep it mounted
|
|
27
|
+
*
|
|
28
|
+
* // A bare string is auto-wrapped in a dismissible Banner:
|
|
29
|
+
* Toaster.manager.add(() => 'Saved', { placement: 'bottom-right' })
|
|
30
|
+
*
|
|
31
|
+
* // Or toast a Banner yourself and wire its dismiss to the toast:
|
|
32
|
+
* Toaster.manager.add((id) => (
|
|
33
|
+
* <Banner tone="success" message="Saved" onDismiss={() => Toaster.manager.dismiss(id)} />
|
|
34
|
+
* ))
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare const ToasterImpl: ({ toaster, label, className, style, children: _children, ...rest }: ToasterProps) => any;
|
|
38
|
+
/**
|
|
39
|
+
* The `<Toaster>` component, carrying the default toast store as a static:
|
|
40
|
+
* `Toaster.manager.add(render, opts)`. A function-with-a-static (like
|
|
41
|
+
* `Component.displayName`) — no class component, which would couple to a renderer.
|
|
42
|
+
*/
|
|
43
|
+
export declare const Toaster: typeof ToasterImpl & {
|
|
44
|
+
/** The default toast store, bound to a `<Toaster>` mounted with no `toaster`. */
|
|
45
|
+
readonly manager: ToasterStore;
|
|
46
|
+
};
|
|
47
|
+
export { createToaster } from "../toaster";
|
|
48
|
+
export type { Toaster as ToasterManager, ToastOptions, ToastPlacement, ToastContent, ToastRender, } from "../toaster";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx } from "@antadesign/anta/jsx-runtime";
|
|
2
|
+
import { useMemo, useSyncExternalStore } from "../jsx-runtime";
|
|
3
|
+
import { createToaster } from "../toaster";
|
|
4
|
+
import { Banner } from "./Banner";
|
|
5
|
+
const ToastItem = ({ entry, store }) => {
|
|
6
|
+
const content = useMemo(() => entry.render(entry.id), [entry.id, entry.rev]);
|
|
7
|
+
const isNode = typeof Node !== "undefined" && content instanceof Node;
|
|
8
|
+
const isText = typeof content === "string" || typeof content === "number";
|
|
9
|
+
const common = {
|
|
10
|
+
slot: entry.placement,
|
|
11
|
+
duration: entry.duration,
|
|
12
|
+
leaving: entry.leaving ? "" : void 0,
|
|
13
|
+
rev: entry.rev,
|
|
14
|
+
// Opt-in per-toast announcement. role=status/alert (not a bare aria-live) so
|
|
15
|
+
// it announces when inserted — a plain aria-live region must pre-exist before
|
|
16
|
+
// its content changes, which a freshly-added toast can't satisfy. Omitted (no
|
|
17
|
+
// announcement) unless requested.
|
|
18
|
+
role: entry.politeness === "assertive" ? "alert" : entry.politeness === "polite" ? "status" : void 0,
|
|
19
|
+
// Fires after the exit animation; that's when the entry actually leaves.
|
|
20
|
+
ondismiss: () => store.remove(entry.id)
|
|
21
|
+
};
|
|
22
|
+
if (isNode) return /* @__PURE__ */ jsx("a-toast", { ...common, content });
|
|
23
|
+
if (isText)
|
|
24
|
+
return /* @__PURE__ */ jsx("a-toast", { ...common, children: /* @__PURE__ */ jsx(
|
|
25
|
+
Banner,
|
|
26
|
+
{
|
|
27
|
+
round: true,
|
|
28
|
+
message: content,
|
|
29
|
+
dismissed: false,
|
|
30
|
+
onDismiss: () => store.dismiss(entry.id)
|
|
31
|
+
}
|
|
32
|
+
) });
|
|
33
|
+
return /* @__PURE__ */ jsx("a-toast", { ...common, children: content });
|
|
34
|
+
};
|
|
35
|
+
const ToasterImpl = ({
|
|
36
|
+
toaster,
|
|
37
|
+
label = "Notifications",
|
|
38
|
+
className,
|
|
39
|
+
style,
|
|
40
|
+
// The region's children come from the store, not from JSX.
|
|
41
|
+
children: _children,
|
|
42
|
+
...rest
|
|
43
|
+
}) => {
|
|
44
|
+
const store = toaster ?? getDefaultStore();
|
|
45
|
+
const entries = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
|
|
46
|
+
return /* @__PURE__ */ jsx("a-toaster", { role: "region", "aria-label": label, class: className, style, ...rest, children: entries.map((entry) => /* @__PURE__ */ jsx(ToastItem, { entry, store }, entry.id)) });
|
|
47
|
+
};
|
|
48
|
+
let defaultStore;
|
|
49
|
+
const getDefaultStore = () => defaultStore ??= createToaster();
|
|
50
|
+
const Toaster = ToasterImpl;
|
|
51
|
+
Object.defineProperty(Toaster, "manager", { get: getDefaultStore, enumerable: true });
|
|
52
|
+
import { createToaster as createToaster2 } from "../toaster";
|
|
53
|
+
export {
|
|
54
|
+
Toaster,
|
|
55
|
+
createToaster2 as createToaster
|
|
56
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer anta{a-banner{--banner-bg: var(--bg-4);--banner-border: var(--border-4);--banner-text: var(--text-2);display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 1ch;position:relative;box-sizing:border-box;width:100%;min-height:40px;padding:6px 52px;color:var(--banner-text);background:var(--banner-bg);border:0 solid var(--banner-border);border-radius:0;overflow:clip;overflow-clip-margin:3px;interpolate-size:allow-keywords;transition:opacity .16s ease,block-size .2s ease,padding-block .2s ease,border-block-width .2s ease,display .2s allow-discrete}a-banner[round]{--banner-round: attr(round type(<length>));border-radius:var(--banner-round, 999px)}a-banner [slot=close] a-button,a-banner a-button[slot=close]{height:100%;width:40px}a-banner-message{font-size:14px;line-height:20px;font-weight:475;text-align:center}a-banner[tone=brand]{--banner-bg: var(--bg-4-brand);--banner-border: var(--border-4-brand);--banner-text: var(--text-2-brand)}a-banner[tone=info]{--banner-bg: var(--bg-4-info);--banner-border: var(--border-4-info);--banner-text: var(--text-2-info)}a-banner[tone=success]{--banner-bg: var(--bg-4-success);--banner-border: var(--border-4-success);--banner-text: var(--text-2-success)}a-banner[tone=warning]{--banner-bg: var(--bg-4-warning);--banner-border: var(--border-4-warning);--banner-text: var(--text-2-warning)}a-banner[tone=critical]{--banner-bg: var(--bg-4-critical);--banner-border: var(--border-4-critical);--banner-text: var(--text-2-critical)}a-banner[tone]:not([tone=""],[tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--banner-tone-source: attr(tone type(<color>), currentColor);--banner-text: oklch(from var(--banner-tone-source) .5 .125 h);--banner-bg: oklch(from var(--banner-tone-source) .955 .02 h);--banner-border: oklch(from var(--banner-tone-source) .9 .04 h);.dark &{--banner-text: oklch(from var(--banner-tone-source) .77 .11 h);--banner-bg: oklch(from var(--banner-tone-source) .185 .05 h);--banner-border: oklch(from var(--banner-tone-source) .26 .06 h)}}a-banner:not(:defined){opacity:0}a-banner[state=closed],a-banner[default-state=closed],a-banner:state(closed){display:none;block-size:0;min-block-size:0;padding-block:0;border-block-width:0;opacity:0}@media(prefers-reduced-motion:reduce){a-banner{transition:none}}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HTMLElementBase } from '../anta_helpers';
|
|
2
|
+
import './a-banner.css';
|
|
3
|
+
export declare class ABannerElement extends HTMLElementBase {
|
|
4
|
+
#private;
|
|
5
|
+
static observedAttributes: string[];
|
|
6
|
+
constructor();
|
|
7
|
+
connectedCallback(): void;
|
|
8
|
+
attributeChangedCallback(name: string): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function register_a_banner(): void;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { HTMLElementBase, installDismissTrigger, parseOpenState } from "../anta_helpers";
|
|
2
|
+
import "./a-banner.css";
|
|
3
|
+
const DISMISS_TRIGGER = "dismissrequest";
|
|
4
|
+
const DISMISS_ATTR = "data-banner-dismiss";
|
|
5
|
+
const SHADOW_STYLE = `
|
|
6
|
+
slot[name="message"], slot:not([name]) { display: contents; }
|
|
7
|
+
|
|
8
|
+
slot[name="actions"] {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 4px;
|
|
12
|
+
flex-shrink: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
slot[name="close"] {
|
|
16
|
+
position: absolute;
|
|
17
|
+
inset-inline-end: 0;
|
|
18
|
+
inset-block: 0;
|
|
19
|
+
display: grid;
|
|
20
|
+
place-items: center;
|
|
21
|
+
opacity: 0.65;
|
|
22
|
+
transition: opacity 150ms ease;
|
|
23
|
+
}
|
|
24
|
+
slot[name="close"]:hover,
|
|
25
|
+
slot[name="close"]:focus-within {
|
|
26
|
+
opacity: 1;
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
const parseState = parseOpenState;
|
|
30
|
+
class ABannerElement extends HTMLElementBase {
|
|
31
|
+
static observedAttributes = ["state"];
|
|
32
|
+
// Visibility is held here as a custom state (`:state(open)` / `:state(closed)`),
|
|
33
|
+
// NOT a host attribute — declarative-DOM safe (no host mutation, no reconcile
|
|
34
|
+
// churn). This is the ONLY declarative-safe self-hide channel, so a runtime
|
|
35
|
+
// (uncontrolled) ✕ dismiss needs CustomStateSet + the CSS `:state()` selector
|
|
36
|
+
// (Chrome 90+, Safari 17.4+, Firefox 126+); below that the ✕ can't self-hide (an
|
|
37
|
+
// attribute fallback would violate the no-host-mutation rule). Controlled mode
|
|
38
|
+
// degrades everywhere — the `state="closed"` attribute the wrapper sets has the
|
|
39
|
+
// `[state="closed"]` CSS fallback, which needs no custom-state support.
|
|
40
|
+
#internals;
|
|
41
|
+
// `default-state` seeds the uncontrolled intent ONCE, on the first connect; a
|
|
42
|
+
// re-parent (disconnect → reconnect) then keeps the applied state (which lives
|
|
43
|
+
// off-DOM in #internals and survives the move) instead of re-seeding `open` over a
|
|
44
|
+
// banner the user already dismissed. Mirrors a-dialog's #seeded.
|
|
45
|
+
#seeded = false;
|
|
46
|
+
constructor() {
|
|
47
|
+
super();
|
|
48
|
+
this.#internals = typeof this.attachInternals === "function" ? this.attachInternals() : void 0;
|
|
49
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
50
|
+
const style = document.createElement("style");
|
|
51
|
+
style.textContent = SHADOW_STYLE;
|
|
52
|
+
const messageSlot = document.createElement("slot");
|
|
53
|
+
messageSlot.name = "message";
|
|
54
|
+
messageSlot.setAttribute("part", "message");
|
|
55
|
+
const contentSlot = document.createElement("slot");
|
|
56
|
+
contentSlot.setAttribute("part", "content");
|
|
57
|
+
const actionsSlot = document.createElement("slot");
|
|
58
|
+
actionsSlot.name = "actions";
|
|
59
|
+
actionsSlot.setAttribute("part", "actions");
|
|
60
|
+
const closeSlot = document.createElement("slot");
|
|
61
|
+
closeSlot.name = "close";
|
|
62
|
+
closeSlot.setAttribute("part", "close");
|
|
63
|
+
shadow.append(style, messageSlot, contentSlot, actionsSlot, closeSlot);
|
|
64
|
+
this.addEventListener(DISMISS_TRIGGER, (e) => {
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
this.#requestDismiss();
|
|
67
|
+
});
|
|
68
|
+
installDismissTrigger(this, DISMISS_ATTR, () => this.#requestDismiss());
|
|
69
|
+
}
|
|
70
|
+
/** Controlled mode: the `state` attribute is present and owns visibility. */
|
|
71
|
+
get #controlled() {
|
|
72
|
+
return this.hasAttribute("state");
|
|
73
|
+
}
|
|
74
|
+
/** The currently *applied* state (read from the custom state — the source of
|
|
75
|
+
* truth for what's painted). Reads open until the element has applied a state.
|
|
76
|
+
* Both `?.` guard engines with ElementInternals but no CustomStateSet (`.states`
|
|
77
|
+
* undefined), where reading it would otherwise throw inside the ✕ click. */
|
|
78
|
+
get #current() {
|
|
79
|
+
return this.#internals?.states?.has("closed") ? "closed" : "open";
|
|
80
|
+
}
|
|
81
|
+
connectedCallback() {
|
|
82
|
+
if (this.#controlled) {
|
|
83
|
+
this.#applyState(parseState(this.getAttribute("state")));
|
|
84
|
+
} else if (!this.#seeded) {
|
|
85
|
+
this.#applyState(this.#seedState());
|
|
86
|
+
this.#seeded = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** The uncontrolled initial state. A banner is shown by DEFAULT, so `open` unless
|
|
90
|
+
* `default-state="closed"` is explicit. (Unlike a-dialog / a-expander, whose
|
|
91
|
+
* absent default-state means closed — a banner is the inverse, so it can't share
|
|
92
|
+
* `parseState` here. A hand-authored `<a-banner>` with no attributes shows.) */
|
|
93
|
+
#seedState() {
|
|
94
|
+
return this.getAttribute("default-state") === "closed" ? "closed" : "open";
|
|
95
|
+
}
|
|
96
|
+
attributeChangedCallback(name) {
|
|
97
|
+
if (name === "state" && this.#controlled) this.#applyState(parseState(this.getAttribute("state")));
|
|
98
|
+
}
|
|
99
|
+
/** Mirror the state to `:state(open)` / `:state(closed)` (idempotent). The
|
|
100
|
+
* external sheet collapses the host on `:state(closed)`. Off-DOM only. */
|
|
101
|
+
#applyState(state) {
|
|
102
|
+
if (!this.#internals) return;
|
|
103
|
+
try {
|
|
104
|
+
if (state === "closed") {
|
|
105
|
+
this.#internals.states.add("closed");
|
|
106
|
+
this.#internals.states.delete("open");
|
|
107
|
+
} else {
|
|
108
|
+
this.#internals.states.add("open");
|
|
109
|
+
this.#internals.states.delete("closed");
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Announce a requested dismiss (cancelable, before applying), then — uncontrolled
|
|
115
|
+
* and un-vetoed — apply it. Controlled: never self-apply; the consumer answers
|
|
116
|
+
* via `state`. See STATEFUL-COMPONENTS.md. */
|
|
117
|
+
#requestDismiss() {
|
|
118
|
+
if (this.#current === "closed") return;
|
|
119
|
+
const ok = this.dispatchEvent(
|
|
120
|
+
new CustomEvent("statechange", { cancelable: true, detail: { next: "closed", prev: "open" } })
|
|
121
|
+
);
|
|
122
|
+
if (this.#controlled) return;
|
|
123
|
+
if (ok) this.#applyState("closed");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function register_a_banner() {
|
|
127
|
+
if (typeof customElements === "undefined") return;
|
|
128
|
+
if (!customElements.get("a-banner")) {
|
|
129
|
+
customElements.define("a-banner", ABannerElement);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
register_a_banner();
|
|
133
|
+
export {
|
|
134
|
+
ABannerElement,
|
|
135
|
+
register_a_banner
|
|
136
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer anta{a-input:not(:defined){display:grid;grid-template-columns:minmax(0,1fr);row-gap:4px}a-input:not(:defined)>[slot=leading],a-input:not(:defined)>[slot=trailing],a-input:not(:defined)>[slot=clear],a-input:not(:defined)>[slot=hint]{display:none!important}a-input:not(:defined)>[slot=label]{display:block;color:var(--input-label);font-family:var(--sans-serif);font-size:15px;line-height:20px;font-weight:500}a-input[size=small]:not(:defined)>[slot=label]{font-size:13px;line-height:16px}a-input[size=large]:not(:defined)>[slot=label]{font-size:17px;line-height:22px}a-input:not(:defined):after{content:attr(placeholder);display:block;min-height:28px;line-height:28px;padding-inline:7px;border-radius:4px;box-shadow:inset 0 0 0 .5px var(--input-border);color:var(--input-placeholder);font-family:var(--sans-serif);font-size:15px;font-weight:400;font-feature-settings:"ss02","ss05","tnum";font-variation-settings:"wdth" 100,"slnt" 0,"ital" 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}a-input[value]:not([value=""]):not([type=password]):not(:defined):after{content:attr(value);color:var(--input-text)}a-input[size=small]:not(:defined):after{min-height:24px;line-height:24px;font-size:13px}a-input[size=large]:not(:defined):after{min-height:32px;line-height:32px;font-size:17px}a-input[round]:not(:defined):after{border-radius:var(--input-round, 999px)}a-input{transition:opacity .28s ease-out 40ms}a-input:not(:defined){opacity:.5}@media(prefers-reduced-motion:reduce){a-input{transition:none}}a-input{--input-bg: var(--bg-1);--input-border: var(--border-2);--input-text: var(--text-1);--input-label: var(--text-3);--input-placeholder: var(--text-4);--input-adornment: var(--text-3);--input-hint: var(--text-3);width:100%}a-input[round]{--input-round: attr(round type(<length>), 999px)}a-input[tone]:not([tone=""]):not([status]){--input-tone-source: attr(tone type(<color>), var(--border-2));--input-border: oklch(from var(--input-tone-source) .7 calc(c*.8) h);.dark &{--input-border: oklch(from var(--input-tone-source) .5 calc(c*.8) h)}}a-input[status=critical]{--input-status-source: var(--anta-seed-critical);--input-bg: var(--bg-2-critical);--input-hint: var(--text-2-critical)}a-input[status=warning]{--input-status-source: var(--anta-seed-warning);--input-bg: var(--bg-2-warning);--input-hint: var(--text-2-warning)}a-input[status=success]{--input-status-source: var(--anta-seed-success);--input-bg: var(--bg-2-success);--input-hint: var(--text-2-success)}a-input[status=info]{--input-status-source: var(--anta-seed-info);--input-bg: var(--bg-2-info);--input-hint: var(--text-2-info)}a-input[status=brand]{--input-status-source: var(--anta-seed-brand);--input-bg: var(--bg-2-brand);--input-hint: var(--text-2-brand)}a-input[status]{--input-border: oklch(from var(--input-status-source) .58 c h)}.dark a-input[status]{--input-border: oklch(from var(--input-status-source) .52 c h)}a-input:disabled{--input-bg: #44374b12;--input-border: #44374b26;--input-text: var(--text-4);--input-label: var(--text-4);--input-hint: var(--text-5);cursor:not-allowed}a-input[status]:disabled{--input-bg: #44374b12;--input-border: #44374b26;--input-hint: var(--text-5)}.dark a-input:disabled{--input-bg: #e4d1ef0d;--input-border: #e4d1ef1f}.dark a-input[status]:disabled{--input-bg: #e4d1ef0d;--input-border: #e4d1ef1f;--input-hint: var(--text-5)}a-input[size=small] a-icon{--icon-size: 14px}a-input[size=large] a-icon{--icon-size: 18px}a-input [slot=hint]>a-icon{margin-block-start:1px}a-input[size=small] [slot=hint]>a-icon{margin-block-start:.5px}a-input[size=large] [slot=hint]>a-icon{margin-block-start:1.5px}}
|
|
1
|
+
@layer anta{a-input:not(:defined){display:grid;grid-template-columns:minmax(0,1fr);row-gap:4px}a-input:not(:defined)>[slot=leading],a-input:not(:defined)>[slot=trailing],a-input:not(:defined)>[slot=clear],a-input:not(:defined)>[slot=hint]{display:none!important}a-input:not(:defined)>[slot=label]{display:block;color:var(--input-label);font-family:var(--sans-serif);font-size:15px;line-height:20px;font-weight:500}a-input[size=small]:not(:defined)>[slot=label]{font-size:13px;line-height:16px}a-input[size=large]:not(:defined)>[slot=label]{font-size:17px;line-height:22px}a-input:not(:defined):after{content:attr(placeholder);display:block;min-height:28px;line-height:28px;padding-inline:7px;border-radius:4px;box-shadow:inset 0 0 0 .5px var(--input-border);color:var(--input-placeholder);font-family:var(--sans-serif);font-size:15px;font-weight:400;font-feature-settings:"ss02","ss05","tnum";font-variation-settings:"wdth" 100,"slnt" 0,"ital" 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}a-input[value]:not([value=""]):not([type=password]):not(:defined):after{content:attr(value);color:var(--input-text)}a-input[size=small]:not(:defined):after{min-height:24px;line-height:24px;font-size:13px}a-input[size=large]:not(:defined):after{min-height:32px;line-height:32px;font-size:17px}a-input[round]:not(:defined):after{border-radius:var(--input-round, 999px)}a-input{transition:opacity .28s ease-out 40ms}a-input:not(:defined){opacity:.5}@media(prefers-reduced-motion:reduce){a-input{transition:none}}a-input{--input-bg: var(--bg-1);--input-border: var(--border-2);--input-text: var(--text-1);--input-label: var(--text-3);--input-placeholder: var(--text-4);--input-adornment: var(--text-3);--input-hint: var(--text-3);--input-trailing-inset: 7px;width:100%}a-input:has([slot=trailing] :is(a-button,button)),a-input:has([slot=trailing]:is(a-button,button)){--input-trailing-inset: 0}a-input[round]{--input-round: attr(round type(<length>), 999px)}a-input[tone]:not([tone=""]):not([status]){--input-tone-source: attr(tone type(<color>), var(--border-2));--input-border: oklch(from var(--input-tone-source) .7 calc(c*.8) h);.dark &{--input-border: oklch(from var(--input-tone-source) .5 calc(c*.8) h)}}a-input[status=critical]{--input-status-source: var(--anta-seed-critical);--input-bg: var(--bg-2-critical);--input-hint: var(--text-2-critical)}a-input[status=warning]{--input-status-source: var(--anta-seed-warning);--input-bg: var(--bg-2-warning);--input-hint: var(--text-2-warning)}a-input[status=success]{--input-status-source: var(--anta-seed-success);--input-bg: var(--bg-2-success);--input-hint: var(--text-2-success)}a-input[status=info]{--input-status-source: var(--anta-seed-info);--input-bg: var(--bg-2-info);--input-hint: var(--text-2-info)}a-input[status=brand]{--input-status-source: var(--anta-seed-brand);--input-bg: var(--bg-2-brand);--input-hint: var(--text-2-brand)}a-input[status]{--input-border: oklch(from var(--input-status-source) .58 c h)}.dark a-input[status]{--input-border: oklch(from var(--input-status-source) .52 c h)}a-input:disabled{--input-bg: #44374b12;--input-border: #44374b26;--input-text: var(--text-4);--input-label: var(--text-4);--input-hint: var(--text-5);cursor:not-allowed}a-input[status]:disabled{--input-bg: #44374b12;--input-border: #44374b26;--input-hint: var(--text-5)}.dark a-input:disabled{--input-bg: #e4d1ef0d;--input-border: #e4d1ef1f}.dark a-input[status]:disabled{--input-bg: #e4d1ef0d;--input-border: #e4d1ef1f;--input-hint: var(--text-5)}a-input[size=small] a-icon{--icon-size: 14px}a-input[size=large] a-icon{--icon-size: 18px}a-input [slot=hint]>a-icon{margin-block-start:1px}a-input[size=small] [slot=hint]>a-icon{margin-block-start:.5px}a-input[size=large] [slot=hint]>a-icon{margin-block-start:1.5px}}
|
package/dist/elements/a-input.js
CHANGED
|
@@ -173,6 +173,7 @@ const SHADOW_STYLE = `
|
|
|
173
173
|
:host([readonly]) slot[name="clear"] { display: none; }
|
|
174
174
|
|
|
175
175
|
.field.has-leading slot[name="leading"] { margin-inline-start: 7px; }
|
|
176
|
+
.field.has-trailing slot[name="trailing"] { margin-inline-end: var(--input-trailing-inset, 7px); }
|
|
176
177
|
.field.has-leading input,
|
|
177
178
|
.field.has-leading textarea { padding-inline-start: 5px; }
|
|
178
179
|
:host([multiline]) .field.has-leading slot[name="leading"],
|
|
@@ -31,7 +31,12 @@ function ensureMenuItemKeyListener(doc) {
|
|
|
31
31
|
if (doc.hasKeyListenerForAMenuItem) return;
|
|
32
32
|
installKeyActivation(doc, {
|
|
33
33
|
keys: ["Enter", " "],
|
|
34
|
-
resolve: (t) =>
|
|
34
|
+
resolve: (t) => {
|
|
35
|
+
const el = t;
|
|
36
|
+
if (el?.closest?.('input, textarea, select, a-input, a-input-time, [contenteditable]:not([contenteditable="false"])'))
|
|
37
|
+
return null;
|
|
38
|
+
return el?.closest?.(MENU_ITEM_SELECTOR);
|
|
39
|
+
},
|
|
35
40
|
blocked: (el) => el.hasAttribute("disabled"),
|
|
36
41
|
activate: (el) => el.click()
|
|
37
42
|
});
|
package/dist/elements/a-menu.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer anta{a-menu:not(:defined){display:none}a-menu{--menu-bg: color-mix(in oklch, var(--bg-1) 90%, transparent);--menu-radius: var(--radius-md, 8px);--menu-border: 1px solid var(--border-3);--menu-shadow: 0 2px 6px color-mix(in oklch, var(--text-1) 8%, transparent), 0 8px 24px color-mix(in oklch, var(--text-1) 10%, transparent);--menu-padding: 4px;--menu-min-width: 88px;--menu-backdrop-filter: blur(20px)}a-menu[round]{--menu-radius: attr(round type(<length>), 20px)}.dark a-menu{--menu-bg: color-mix(in oklch, var(--bg-1) 78%, transparent);--menu-shadow: 0 2px 8px color-mix(in oklch, black 50%, transparent), 0 12px 32px color-mix(in oklch, black 40%, transparent), inset 0 0 0 1px color-mix(in oklch, white 8%, transparent)}}
|
|
1
|
+
@layer anta{a-menu:not(:defined){display:none}a-menu{--menu-bg: color-mix(in oklch, var(--bg-1) 90%, transparent);--menu-radius: var(--radius-md, 8px);--menu-border: 1px solid var(--border-3);--menu-shadow: 0 2px 6px color-mix(in oklch, var(--text-1) 8%, transparent), 0 8px 24px color-mix(in oklch, var(--text-1) 10%, transparent);--menu-padding: 4px;--menu-min-width: 88px;--menu-backdrop-filter: blur(20px);--menu-width-duration: .16s}@media(prefers-reduced-motion:reduce){a-menu{--menu-width-duration: 0}}a-menu[round]{--menu-radius: attr(round type(<length>), 20px)}.dark a-menu{--menu-bg: color-mix(in oklch, var(--bg-1) 78%, transparent);--menu-shadow: 0 2px 8px color-mix(in oklch, black 50%, transparent), 0 12px 32px color-mix(in oklch, black 40%, transparent), inset 0 0 0 1px color-mix(in oklch, white 8%, transparent)}}
|
package/dist/elements/a-menu.js
CHANGED
|
@@ -216,6 +216,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
216
216
|
transition:
|
|
217
217
|
opacity 140ms ease-out,
|
|
218
218
|
translate 140ms ease-out,
|
|
219
|
+
width var(--menu-width-duration, 160ms) ease,
|
|
219
220
|
display 140ms allow-discrete,
|
|
220
221
|
overlay 140ms allow-discrete;
|
|
221
222
|
}
|
|
@@ -227,6 +228,7 @@ class AMenuElement extends HTMLElementBase {
|
|
|
227
228
|
transition:
|
|
228
229
|
opacity 100ms ease-out,
|
|
229
230
|
translate 100ms ease-out,
|
|
231
|
+
width var(--menu-width-duration, 160ms) ease,
|
|
230
232
|
display 100ms allow-discrete,
|
|
231
233
|
overlay 100ms allow-discrete;
|
|
232
234
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@property --toast-remaining{syntax: "<number>"; inherits: true; initial-value: 1;}@layer anta{a-toast{--toast-dur: .22s}a-toast:not(:defined){display:none}}
|