@antadesign/anta 0.3.30 → 0.3.32
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 +4 -0
- package/dist/anta_helpers.js +10 -0
- package/dist/box-types.d.ts +12 -1
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +140 -66
- package/dist/components/Box.d.ts +18 -6
- package/dist/components/Box.js +4 -0
- package/dist/components/Expander.d.ts +17 -9
- package/dist/components/Expander.js +14 -1
- package/dist/components/InputDate.js +2 -0
- package/dist/components/MenuItem.d.ts +9 -7
- package/dist/components/MenuItem.js +21 -2
- package/dist/components/Text.js +2 -3
- package/dist/components/Title.d.ts +8 -2
- package/dist/components/Title.js +17 -6
- package/dist/elements/a-box.css +1 -1
- package/dist/elements/a-box.d.ts +2 -0
- package/dist/elements/a-box.js +74 -32
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +5 -2
- package/dist/elements/a-calendar.js +3 -1
- package/dist/elements/a-copy.js +23 -3
- package/dist/elements/a-dialog.js +2 -0
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.js +118 -39
- package/dist/elements/a-input-time.js +2 -1
- package/dist/elements/a-menu-item.css +1 -1
- package/dist/elements/a-menu.js +43 -3
- package/dist/elements/a-panel.css +1 -1
- package/dist/elements/a-panel.js +1 -1
- package/dist/elements/a-radio-group.js +2 -2
- package/dist/elements/a-slider.js +2 -2
- package/dist/elements/a-tabs.js +2 -2
- package/dist/elements/a-title.css +1 -1
- package/dist/elements/a-tooltip.js +1 -1
- package/dist/general_types.d.ts +22 -6
- package/dist/index.d.ts +1 -1
- package/dist/theme-antune.css +1 -1
- package/docs/changelog.md +64 -0
- package/docs/comparison.md +1 -1
- package/docs/components/box.md +81 -24
- package/docs/components/button.md +127 -381
- package/docs/components/expander.md +88 -47
- package/docs/components/menu.md +37 -15
- package/docs/components/panel.md +5 -6
- package/docs/components/select-faceted.md +25 -11
- package/docs/components/title.md +19 -0
- package/docs/credits.md +3 -0
- package/docs/packages/plot.md +234 -19
- package/package.json +2 -2
package/dist/components/Box.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export interface BoxProps extends BaseProps {
|
|
|
14
14
|
* pixels; a string is any CSS length or two-value gap (`'1rem'`,
|
|
15
15
|
* `'8px 16px'`). Applies while the Box is a flex or grid container. */
|
|
16
16
|
gap?: number | string;
|
|
17
|
+
/** CSS selector for descendants whose border boxes are included in
|
|
18
|
+
* `measurement.rects` when Box measures. Coordinates are relative to this
|
|
19
|
+
* Box's top-left border edge; matches are in document order. Box reads and
|
|
20
|
+
* compares matching rects for each measurement event. With `observe="scroll"`,
|
|
21
|
+
* use `throttle` when the selector matches many descendants; it also delays
|
|
22
|
+
* CSS state updates after the initial read. */
|
|
23
|
+
includeRectsFor?: string;
|
|
17
24
|
/** Inner spacing, matching CSS `padding`. Numbers are pixels; strings accept
|
|
18
25
|
* CSS shorthand, percentages, and custom properties. Omission adds no style. */
|
|
19
26
|
padding?: number | string;
|
|
@@ -31,9 +38,13 @@ export interface BoxProps extends BaseProps {
|
|
|
31
38
|
* overflow adds content observation; hidden edges and scroll add scroll reads.
|
|
32
39
|
* Without handlers or `fade`, omission stays idle. */
|
|
33
40
|
observe?: BoxObservation | readonly BoxObservation[];
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
41
|
+
/** Keeps measurement events and CSS clipping states current outside the
|
|
42
|
+
* viewport. Has no effect unless `observe` or `fade` selects measurement. */
|
|
43
|
+
observeOffscreen?: boolean;
|
|
44
|
+
/** Minimum interval between measurements, in milliseconds. The initial
|
|
45
|
+
* measurement runs immediately; a trailing read updates CSS states and
|
|
46
|
+
* delivers the latest event. Descendant rects are read only for emitted
|
|
47
|
+
* events. The `measurement` getter always reads fresh values.
|
|
37
48
|
* @defaultValue 0 */
|
|
38
49
|
throttle?: number;
|
|
39
50
|
/** Fades out every edge that currently hides clipped content, and drops the
|
|
@@ -43,8 +54,9 @@ export interface BoxProps extends BaseProps {
|
|
|
43
54
|
* length.
|
|
44
55
|
* @defaultValue 24 */
|
|
45
56
|
fadeSize?: number | string;
|
|
46
|
-
/** Fired when a selected measurement field changes. `detail` contains
|
|
47
|
-
* fields changed since the
|
|
57
|
+
/** Fired when a selected measurement field changes. `detail.changed` contains
|
|
58
|
+
* fields changed since the previous event; `detail.current` includes the full
|
|
59
|
+
* snapshot with matching rects. Rect changes alone do not trigger an event. */
|
|
48
60
|
onMeasureChange?: (event: CustomEvent<BoxMeasurementChange>, detail: BoxMeasurementChange) => void;
|
|
49
61
|
/** Fired after Box's browser and local rendering context changes. `detail`
|
|
50
62
|
* contains the changed fields and a full current snapshot. */
|
|
@@ -65,4 +77,4 @@ export interface BoxProps extends BaseProps {
|
|
|
65
77
|
* </Box>
|
|
66
78
|
* ```
|
|
67
79
|
*/
|
|
68
|
-
export declare const Box: ({ display, round, gap, padding, margin, observe, throttle, fade, fadeSize, onMeasureChange, onContextChange, className, style, children, ...rest }: BoxProps) => any;
|
|
80
|
+
export declare const Box: ({ display, round, gap, includeRectsFor, padding, margin, observe, observeOffscreen, throttle, fade, fadeSize, onMeasureChange, onContextChange, className, style, children, ...rest }: BoxProps) => any;
|
package/dist/components/Box.js
CHANGED
|
@@ -18,9 +18,11 @@ const Box = ({
|
|
|
18
18
|
display,
|
|
19
19
|
round,
|
|
20
20
|
gap,
|
|
21
|
+
includeRectsFor,
|
|
21
22
|
padding,
|
|
22
23
|
margin,
|
|
23
24
|
observe,
|
|
25
|
+
observeOffscreen,
|
|
24
26
|
throttle,
|
|
25
27
|
fade,
|
|
26
28
|
fadeSize,
|
|
@@ -42,9 +44,11 @@ const Box = ({
|
|
|
42
44
|
display: display === "block" ? void 0 : display,
|
|
43
45
|
round: roundAttr(round),
|
|
44
46
|
gap: gap != null ? "" : void 0,
|
|
47
|
+
"include-rects-for": includeRectsFor,
|
|
45
48
|
padding: padding != null ? "" : void 0,
|
|
46
49
|
margin: margin != null ? "" : void 0,
|
|
47
50
|
observe: observeAttr(observe, onMeasureChange, onContextChange),
|
|
51
|
+
"observe-offscreen": observeOffscreen ? "" : void 0,
|
|
48
52
|
throttle,
|
|
49
53
|
fade: fade ? "" : void 0,
|
|
50
54
|
"fade-size": fade && fadeSize != null ? cssLength(fadeSize) : void 0,
|
|
@@ -23,16 +23,24 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
|
|
|
23
23
|
* bare disclosure).
|
|
24
24
|
* @defaultValue 'secondary' */
|
|
25
25
|
priority?: "primary" | "secondary" | "tertiary";
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* container edge has to bound the chevron, so it's a no-op there. */
|
|
26
|
+
/** Align the title and body with surrounding content on a tertiary
|
|
27
|
+
* expander. With the default start indicator, the mark hangs in the
|
|
28
|
+
* gutter; an end indicator stays inside its edge. */
|
|
30
29
|
outdent?: boolean;
|
|
31
|
-
/** Header actions
|
|
32
|
-
*
|
|
33
|
-
* toggles, they're separately focusable, and screen readers see them
|
|
34
|
-
* as separate controls. */
|
|
30
|
+
/** Header actions rendered outside the toggle trigger. With an end
|
|
31
|
+
* indicator, actions sit between the title and indicator. */
|
|
35
32
|
actions?: React.ReactNode;
|
|
33
|
+
/** Built-in disclosure mark, a decorative node, or separate closed/open
|
|
34
|
+
* visuals. Other primitive values leave the indicator empty. Custom nodes
|
|
35
|
+
* are passive; use `actions` for controls.
|
|
36
|
+
* @defaultValue 'chevron' */
|
|
37
|
+
indicator?: "chevron" | "triangle" | "plus" | React.ReactNode | {
|
|
38
|
+
closed: React.ReactNode;
|
|
39
|
+
open: React.ReactNode;
|
|
40
|
+
};
|
|
41
|
+
/** Place the disclosure mark before the title or after the actions.
|
|
42
|
+
* @defaultValue 'start' */
|
|
43
|
+
indicatorPlacement?: "start" | "end";
|
|
36
44
|
/** Disables the header: not clickable or focusable, hover affordance
|
|
37
45
|
* off, text dimmed. The open state freezes as-is — disabling an open
|
|
38
46
|
* expander keeps it open. `actions` stay live; disable them
|
|
@@ -76,4 +84,4 @@ export interface ExpanderProps extends Omit<BaseProps, "title"> {
|
|
|
76
84
|
* giving real controlled semantics (a consumer can reject a toggle). See
|
|
77
85
|
* STATEFUL-COMPONENTS.md.
|
|
78
86
|
*/
|
|
79
|
-
export declare const Expander: ({ title, level, tone, priority, outdent, actions, disabled, round, open, defaultOpen, onStateChange, className, style, children, ...rest }: ExpanderProps) => any;
|
|
87
|
+
export declare const Expander: ({ title, level, tone, priority, outdent, actions, indicator, indicatorPlacement, disabled, round, open, defaultOpen, onStateChange, className, style, children, ...rest }: ExpanderProps) => any;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
2
|
import { nativeStateChange, neutralToneAttr, toneStyle, roundStyle, roundAttr } from "../anta_helpers";
|
|
3
3
|
const Expander = ({
|
|
4
4
|
title,
|
|
@@ -7,6 +7,8 @@ const Expander = ({
|
|
|
7
7
|
priority,
|
|
8
8
|
outdent,
|
|
9
9
|
actions,
|
|
10
|
+
indicator,
|
|
11
|
+
indicatorPlacement,
|
|
10
12
|
disabled,
|
|
11
13
|
round,
|
|
12
14
|
open,
|
|
@@ -18,6 +20,10 @@ const Expander = ({
|
|
|
18
20
|
...rest
|
|
19
21
|
}) => {
|
|
20
22
|
const controlled = open !== void 0;
|
|
23
|
+
const presetIndicator = typeof indicator === "string" && (indicator === "chevron" || indicator === "triangle" || indicator === "plus") ? indicator : void 0;
|
|
24
|
+
const pairedIndicator = indicator != null && typeof indicator === "object" && "closed" in indicator && "open" in indicator ? indicator : void 0;
|
|
25
|
+
const useCustomSlot = indicator != null && !presetIndicator && !pairedIndicator;
|
|
26
|
+
const customIndicator = typeof indicator === "object" ? indicator : null;
|
|
21
27
|
const computedStyle = roundStyle(round, "--expander-round", toneStyle(tone, "--expander-tone-source", style));
|
|
22
28
|
const titleNode = typeof title === "object" && title !== null ? /* @__PURE__ */ jsx("span", { slot: "title", style: { display: "contents" }, children: title }) : /* @__PURE__ */ jsx("a-expander-summary", { slot: "title", children: title });
|
|
23
29
|
return /* @__PURE__ */ jsxs(
|
|
@@ -31,6 +37,8 @@ const Expander = ({
|
|
|
31
37
|
outdent: outdent ? "" : void 0,
|
|
32
38
|
round: roundAttr(round),
|
|
33
39
|
disabled: disabled ? "" : void 0,
|
|
40
|
+
indicator: presetIndicator,
|
|
41
|
+
"indicator-placement": indicatorPlacement && indicatorPlacement !== "start" ? indicatorPlacement : void 0,
|
|
34
42
|
onstatechange: onStateChange ? (e) => {
|
|
35
43
|
const { event, detail, isOwn } = nativeStateChange(e);
|
|
36
44
|
if (isOwn && detail)
|
|
@@ -45,6 +53,11 @@ const Expander = ({
|
|
|
45
53
|
children: [
|
|
46
54
|
titleNode,
|
|
47
55
|
actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", children: actions }),
|
|
56
|
+
pairedIndicator && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx("span", { slot: "indicator", "data-when": "closed", "aria-hidden": "true", inert: true, children: pairedIndicator.closed }),
|
|
58
|
+
/* @__PURE__ */ jsx("span", { slot: "indicator", "data-when": "open", "aria-hidden": "true", inert: true, children: pairedIndicator.open })
|
|
59
|
+
] }),
|
|
60
|
+
useCustomSlot && /* @__PURE__ */ jsx("span", { slot: "indicator", "aria-hidden": "true", inert: true, children: customIndicator }),
|
|
48
61
|
/* @__PURE__ */ jsx("a-expander-details", { children })
|
|
49
62
|
]
|
|
50
63
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from "../jsx-runtime";
|
|
3
|
+
import { isModifiedNavigationKey } from "../anta_helpers";
|
|
3
4
|
import { Temporal } from "temporal-polyfill";
|
|
4
5
|
import {
|
|
5
6
|
parseISODate,
|
|
@@ -138,6 +139,7 @@ const InputDate = ({
|
|
|
138
139
|
},
|
|
139
140
|
onChange: (e) => resolve(e.currentTarget.value),
|
|
140
141
|
onKeyDown: (e) => {
|
|
142
|
+
if (isModifiedNavigationKey(e)) return;
|
|
141
143
|
if ((e.key === "ArrowDown" || e.key === "ArrowUp") && !disabled) {
|
|
142
144
|
e.preventDefault();
|
|
143
145
|
setFocusNonce((n) => n + 1);
|
|
@@ -28,19 +28,19 @@ export interface MenuItemCommonProps extends BaseProps {
|
|
|
28
28
|
/** Mark the item as selected. On a plain row (no `selectionIndicator`) this is
|
|
29
29
|
* a persistent background tint, the same resting fill a pressed row shows —
|
|
30
30
|
* also the way to flag the current page on a link item. On a checkable row
|
|
31
|
-
* (`selectionIndicator` set) it instead drives the
|
|
32
|
-
*
|
|
31
|
+
* (`selectionIndicator` set) it instead drives the indicator and the row's
|
|
32
|
+
* `aria-checked`. */
|
|
33
33
|
selected?: boolean;
|
|
34
|
-
/** Semantic tone — colors the label, icon,
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
/** Semantic tone — colors the label, icon, hover/selected tint, and selection
|
|
35
|
+
* indicator. A named tone, or any literal CSS color (`'#ff1493'`,
|
|
36
|
+
* `'rebeccapurple'`) for a one-off custom tone whose
|
|
37
37
|
* hue + chroma are kept while the lightness is pinned to match the brand text.
|
|
38
38
|
* `critical` is the destructive action; `neutral` (the default) is the standard
|
|
39
39
|
* gray.
|
|
40
40
|
* @defaultValue neutral */
|
|
41
41
|
tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
|
|
42
42
|
/** Apply `tone` to every row state, or only while the row is selected. In
|
|
43
|
-
* `selected` scope, an unselected row and its
|
|
43
|
+
* `selected` scope, an unselected row and its indicator stay
|
|
44
44
|
* neutral.
|
|
45
45
|
* @defaultValue 'all' */
|
|
46
46
|
toneScope?: ToneScope;
|
|
@@ -103,8 +103,10 @@ export type MenuItemActionMode = {
|
|
|
103
103
|
* - `'radio'` → `role="menuitemradio"`, a leading passive `<a-radio>`; tint dropped.
|
|
104
104
|
* - `'check'` → `role="menuitemradio"`, a trailing check glyph on the selected
|
|
105
105
|
* row *and* the background tint (the canonical single-select look).
|
|
106
|
+
* - `'switch'` → `role="menuitemcheckbox"`, a trailing passive `<a-switch>`;
|
|
107
|
+
* selecting the row keeps its menu open.
|
|
106
108
|
* Omit for a plain row (the default). */
|
|
107
|
-
selectionIndicator?: 'checkbox' | 'radio' | 'check';
|
|
109
|
+
selectionIndicator?: 'checkbox' | 'radio' | 'check' | 'switch';
|
|
108
110
|
/** Only meaningful with `selectionIndicator="checkbox"`: render the box in the
|
|
109
111
|
* mixed state (`aria-checked="mixed"`) — e.g. a "Select all" row when some but
|
|
110
112
|
* not all of its options are selected. */
|
|
@@ -93,12 +93,13 @@ const MenuItem = ({
|
|
|
93
93
|
...rest
|
|
94
94
|
}) => {
|
|
95
95
|
const leadingIcon = icon;
|
|
96
|
-
const checkable = selectionIndicator === "checkbox" || selectionIndicator === "radio" || selectionIndicator === "check";
|
|
97
|
-
const role = selectionIndicator === "checkbox" ? "menuitemcheckbox" : selectionIndicator === "radio" || selectionIndicator === "check" ? "menuitemradio" : "menuitem";
|
|
96
|
+
const checkable = selectionIndicator === "checkbox" || selectionIndicator === "radio" || selectionIndicator === "check" || selectionIndicator === "switch";
|
|
97
|
+
const role = selectionIndicator === "checkbox" || selectionIndicator === "switch" ? "menuitemcheckbox" : selectionIndicator === "radio" || selectionIndicator === "check" ? "menuitemradio" : "menuitem";
|
|
98
98
|
const ariaChecked = !checkable ? void 0 : selectionIndicator === "checkbox" && indeterminate ? "mixed" : selected ? "true" : "false";
|
|
99
99
|
const keepTint = selected && (selectionIndicator === void 0 || selectionIndicator === "check");
|
|
100
100
|
const effectiveTone = toneScope === "selected" && !selected ? void 0 : tone;
|
|
101
101
|
const toneAttr = neutralToneAttr(effectiveTone);
|
|
102
|
+
const switchBehavior = selectionIndicator === "switch" ? { "data-menu-open": "" } : {};
|
|
102
103
|
const declaredShortcuts = rest["aria-keyshortcuts"];
|
|
103
104
|
const keyShortcuts = kbd ? shortcutLabel(kbd) : void 0;
|
|
104
105
|
const kbdNode = kbd ? /* @__PURE__ */ jsx("kbd", { "aria-hidden": declaredShortcuts ?? keyShortcuts ? "true" : void 0, children: kbd }) : null;
|
|
@@ -149,6 +150,7 @@ const MenuItem = ({
|
|
|
149
150
|
onmenuselect: onSelect ? (e) => onSelect(e, { value, label }) : void 0,
|
|
150
151
|
class: className,
|
|
151
152
|
...rest,
|
|
153
|
+
...switchBehavior,
|
|
152
154
|
children: [
|
|
153
155
|
indicator != null ? (
|
|
154
156
|
// `min-height: 1lh` makes the wrapper one text line tall and centers the node
|
|
@@ -191,6 +193,23 @@ const MenuItem = ({
|
|
|
191
193
|
labelNode,
|
|
192
194
|
children,
|
|
193
195
|
kbdNode,
|
|
196
|
+
selectionIndicator === "switch" && indicator == null && /* @__PURE__ */ jsx(
|
|
197
|
+
"a-switch",
|
|
198
|
+
{
|
|
199
|
+
"data-menu-item-switch": "",
|
|
200
|
+
"aria-hidden": "true",
|
|
201
|
+
tabIndex: -1,
|
|
202
|
+
state: selected ? "checked" : "unchecked",
|
|
203
|
+
disabled: disabled ? "" : void 0,
|
|
204
|
+
size: "small",
|
|
205
|
+
tone: toneAttr,
|
|
206
|
+
style: toneStyle(
|
|
207
|
+
effectiveTone,
|
|
208
|
+
"--switch-tone-source",
|
|
209
|
+
toneStyle(effectiveTone, "--switch-off-tone-source")
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
),
|
|
194
213
|
(() => {
|
|
195
214
|
let trailing = submenu ? iconTrailing ?? "chevron-right" : iconTrailing;
|
|
196
215
|
if (!submenu && !iconTrailing && selectionIndicator === "check" && indicator == null)
|
package/dist/components/Text.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
|
-
import { neutralToneAttr, toneStyle } from "../anta_helpers";
|
|
2
|
+
import { lineClamp, neutralToneAttr, toneStyle } from "../anta_helpers";
|
|
3
3
|
import { Tooltip } from "./Tooltip";
|
|
4
4
|
const Text = ({ priority, tone, size, inline, truncate, expandable, collapsible, className, style, children, ...rest }) => {
|
|
5
|
-
const
|
|
6
|
-
const lineCount = n != null && n >= 1 ? n : null;
|
|
5
|
+
const lineCount = lineClamp(truncate);
|
|
7
6
|
const toneAttr = neutralToneAttr(tone);
|
|
8
7
|
const computedStyle = toneStyle(
|
|
9
8
|
toneAttr,
|
|
@@ -14,9 +14,15 @@ export interface TitleProps extends BaseProps {
|
|
|
14
14
|
* kept while lightness/chroma are pinned per priority in oklch.
|
|
15
15
|
* @defaultValue neutral */
|
|
16
16
|
tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
|
|
17
|
+
/** Truncate with a trailing ellipsis. `true` (or `1`) clamps to a
|
|
18
|
+
* single line; any integer ≥ 2 clamps to that many lines; `0` or a
|
|
19
|
+
* negative value means no truncation. A clipped JSX `Title` shows its
|
|
20
|
+
* text content in a tooltip by default. Nest a `<Tooltip>` to provide
|
|
21
|
+
* your own tooltip instead. */
|
|
22
|
+
truncate?: boolean | number;
|
|
17
23
|
}
|
|
18
24
|
/**
|
|
19
|
-
* Block-level heading with level 1-6, priority, and
|
|
25
|
+
* Block-level heading with level 1-6, priority, tone, and optional truncation.
|
|
20
26
|
*
|
|
21
27
|
* Renders an `<a-title>` styled tag (no JS, no shadow DOM) with
|
|
22
28
|
* `role="heading"` and `aria-level={level}` set by this wrapper for
|
|
@@ -57,4 +63,4 @@ export interface TitleProps extends BaseProps {
|
|
|
57
63
|
* </Title>
|
|
58
64
|
* ```
|
|
59
65
|
*/
|
|
60
|
-
export declare const Title: ({ level, priority, tone, className, style, children, ...rest }: TitleProps) => any;
|
|
66
|
+
export declare const Title: ({ level, priority, tone, truncate, className, style, children, ...rest }: TitleProps) => any;
|
package/dist/components/Title.js
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import { jsx } from "@antadesign/anta/jsx-runtime";
|
|
2
|
-
import { neutralToneAttr, toneStyle } from "../anta_helpers";
|
|
3
|
-
|
|
1
|
+
import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
|
|
2
|
+
import { lineClamp, neutralToneAttr, toneStyle } from "../anta_helpers";
|
|
3
|
+
import { Tooltip } from "./Tooltip";
|
|
4
|
+
const Title = ({ level = 2, priority, tone, truncate, className, style, children, ...rest }) => {
|
|
5
|
+
const lineCount = lineClamp(truncate);
|
|
4
6
|
const toneAttr = neutralToneAttr(tone);
|
|
5
|
-
|
|
7
|
+
const computedStyle = toneStyle(
|
|
8
|
+
toneAttr,
|
|
9
|
+
"--title-tone-source",
|
|
10
|
+
lineCount != null ? { ...style, ["--line-clamp"]: lineCount } : style
|
|
11
|
+
);
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
6
13
|
"a-title",
|
|
7
14
|
{
|
|
8
15
|
level: String(level),
|
|
9
16
|
priority,
|
|
10
17
|
tone: toneAttr,
|
|
18
|
+
truncate: lineCount != null ? String(lineCount) : void 0,
|
|
11
19
|
role: "heading",
|
|
12
20
|
"aria-level": level,
|
|
13
21
|
class: className,
|
|
14
|
-
style:
|
|
22
|
+
style: computedStyle,
|
|
15
23
|
...rest,
|
|
16
|
-
children
|
|
24
|
+
children: [
|
|
25
|
+
children,
|
|
26
|
+
lineCount != null && /* @__PURE__ */ jsx(Tooltip, { truncatedOnly: true, "data-anta-text-tooltip": "" })
|
|
27
|
+
]
|
|
17
28
|
}
|
|
18
29
|
);
|
|
19
30
|
};
|
package/dist/elements/a-box.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@layer anta.components{a-box{display:block;min-width:0}a-box[display=inline-block]{display:inline-block}a-box[display=flex]{display:flex}a-box[display=inline-flex]{display:inline-flex}a-box[display=grid]{display:grid}a-box[display=inline-grid]{display:inline-grid}a-box[gap]{gap:var(--box-gap, normal);@supports (border-radius: attr(probe type(<length>),1px)){--box-gap: attr(gap type(<length>))}}a-box[padding]{padding:var(--box-padding, 0px);@supports (padding: attr(padding type(*))){--box-padding: attr(padding type(*), 0px)}}a-box[margin]{margin:var(--box-margin, 0px);@supports (margin: attr(margin type(*))){--box-margin: attr(margin type(*), 0px)}}a-box[round]{border-radius:var(--box-round, 999px);@supports (border-radius: attr(probe type(<length>),1px)){--box-round: attr(round type(<length>))}}a-box[fade]{--box-fade-size: 24px;--box-fade-
|
|
1
|
+
@layer base,anta,components,utilities;@layer anta.reset,anta.components,anta.theme;@property --box-fade-start-x-strength{syntax: "<number>"; inherits: false; initial-value: 0;}@property --box-fade-end-x-strength{syntax: "<number>"; inherits: false; initial-value: 0;}@property --box-fade-start-y-strength{syntax: "<number>"; inherits: false; initial-value: 0;}@property --box-fade-end-y-strength{syntax: "<number>"; inherits: false; initial-value: 0;}@layer anta.components{a-box{display:block;min-width:0;position:relative}a-box[display=inline-block]{display:inline-block}a-box[display=flex]{display:flex}a-box[display=inline-flex]{display:inline-flex}a-box[display=grid]{display:grid}a-box[display=inline-grid]{display:inline-grid}a-box[gap]{gap:var(--box-gap, normal);@supports (border-radius: attr(probe type(<length>),1px)){--box-gap: attr(gap type(<length>))}}a-box[padding]{padding:var(--box-padding, 0px);@supports (padding: attr(padding type(*))){--box-padding: attr(padding type(*), 0px)}}a-box[margin]{margin:var(--box-margin, 0px);@supports (margin: attr(margin type(*))){--box-margin: attr(margin type(*), 0px)}}a-box[round]{border-radius:var(--box-round, 999px);@supports (border-radius: attr(probe type(<length>),1px)){--box-round: attr(round type(<length>))}}a-box[fade]{--box-fade-size: 24px;--box-fade-left-strength: var(--box-fade-start-x-strength);--box-fade-right-strength: var(--box-fade-end-x-strength);transition:--box-fade-start-x-strength .14s ease-out,--box-fade-end-x-strength .14s ease-out,--box-fade-start-y-strength .14s ease-out,--box-fade-end-y-strength .14s ease-out}a-box[fade]:state(fade-mask-active){mask-image:linear-gradient(#000,#000),linear-gradient(to right,rgb(0 0 0 / var(--box-fade-left-strength)),transparent var(--box-fade-size),transparent calc(100% - var(--box-fade-size)),rgb(0 0 0 / var(--box-fade-right-strength))),linear-gradient(to bottom,rgb(0 0 0 / var(--box-fade-start-y-strength)),transparent var(--box-fade-size),transparent calc(100% - var(--box-fade-size)),rgb(0 0 0 / var(--box-fade-end-y-strength)));mask-clip:border-box,padding-box,padding-box;mask-origin:border-box,padding-box,padding-box;mask-composite:exclude,add,add;mask-repeat:no-repeat;mask-size:100% 100%}a-box[fade][fade-size]{--box-fade-size: 24px;@supports (border-radius: attr(probe type(<length>),1px)){--box-fade-size: attr(fade-size type(<length>), 24px)}}a-box[fade]:dir(rtl){--box-fade-left-strength: var(--box-fade-end-x-strength);--box-fade-right-strength: var(--box-fade-start-x-strength)}a-box[fade]:state(hidden-end-x){--box-fade-end-x-strength: 1}a-box[fade]:state(hidden-start-x){--box-fade-start-x-strength: 1}a-box[fade]:state(hidden-end-y){--box-fade-end-y-strength: 1}a-box[fade]:state(hidden-start-y){--box-fade-start-y-strength: 1}@media(prefers-reduced-motion:reduce){a-box[fade]{transition:none}}}
|
package/dist/elements/a-box.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ import './a-box.css';
|
|
|
11
11
|
* measurement fields or presets, including `context` and `all`. `fade` keeps
|
|
12
12
|
* clipping states current without selecting event fields. A box with neither
|
|
13
13
|
* runs no observers at all, including the shared visibility observer.
|
|
14
|
+
* `observe-offscreen` opts out of the pause: measurement keeps running while
|
|
15
|
+
* the box is off screen, and the box skips the visibility observer entirely.
|
|
14
16
|
* The `measurement` / `context` / `isTruncated` getters still read on demand.
|
|
15
17
|
*
|
|
16
18
|
* Attributes rather than "is a listener attached" on purpose. A listener tally
|
package/dist/elements/a-box.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { HTMLElementBase } from "../anta_helpers";
|
|
2
|
-
import { throttle } from "es-toolkit";
|
|
2
|
+
import { isEqual, throttle } from "es-toolkit";
|
|
3
3
|
import { boxObservation } from "../box-observation";
|
|
4
4
|
import "./a-box.css";
|
|
5
5
|
const stores = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
const FADE_TRANSITION_MS = 140;
|
|
6
7
|
const MODE_CLASS = /(?:^|\s)(?:dark|light)(?:\s|$)/;
|
|
7
8
|
function hasModeClass(element) {
|
|
8
9
|
return element.classList.contains("dark") || element.classList.contains("light");
|
|
@@ -200,21 +201,13 @@ function deviceSnapshot(navigator) {
|
|
|
200
201
|
mobile: uaData.userAgentData?.mobile ?? /android|mobi|iphone|ipad|ipod/.test(source)
|
|
201
202
|
};
|
|
202
203
|
}
|
|
203
|
-
function equal(a, b) {
|
|
204
|
-
if (a === b) return true;
|
|
205
|
-
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
206
|
-
const left = a;
|
|
207
|
-
const right = b;
|
|
208
|
-
const keys = Object.keys(left);
|
|
209
|
-
return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
|
|
210
|
-
}
|
|
211
204
|
function same(a, b) {
|
|
212
|
-
return a !== void 0 && Object.keys(b).every((key) =>
|
|
205
|
+
return a !== void 0 && Object.keys(b).every((key) => isEqual(a[key], b[key]));
|
|
213
206
|
}
|
|
214
207
|
function changed(previous, current) {
|
|
215
208
|
if (!previous) return { ...current };
|
|
216
209
|
return Object.fromEntries(
|
|
217
|
-
Object.entries(current).filter(([key, value]) => !
|
|
210
|
+
Object.entries(current).filter(([key, value]) => !isEqual(previous[key], value))
|
|
218
211
|
);
|
|
219
212
|
}
|
|
220
213
|
function px(value) {
|
|
@@ -264,7 +257,7 @@ function rounded(value) {
|
|
|
264
257
|
return Math.round(value * 100) / 100;
|
|
265
258
|
}
|
|
266
259
|
class ABoxElement extends HTMLElementBase {
|
|
267
|
-
static observedAttributes = ["fade", "observe", "throttle"];
|
|
260
|
+
static observedAttributes = ["fade", "observe", "observe-offscreen", "throttle"];
|
|
268
261
|
#internals = this.attachInternals?.();
|
|
269
262
|
#store;
|
|
270
263
|
#resizeObserver;
|
|
@@ -276,9 +269,10 @@ class ABoxElement extends HTMLElementBase {
|
|
|
276
269
|
#measurementFields = /* @__PURE__ */ new Set();
|
|
277
270
|
#watchContent = false;
|
|
278
271
|
#watchScroll = false;
|
|
279
|
-
// Filtering
|
|
272
|
+
// Filtering compares against the last event, not the last read.
|
|
280
273
|
#reportedMeasurement;
|
|
281
274
|
#throttledReport;
|
|
275
|
+
#fadeMaskTimer;
|
|
282
276
|
#context;
|
|
283
277
|
#measuring = false;
|
|
284
278
|
#clips = false;
|
|
@@ -298,6 +292,7 @@ class ABoxElement extends HTMLElementBase {
|
|
|
298
292
|
this.#sync();
|
|
299
293
|
}
|
|
300
294
|
disconnectedCallback() {
|
|
295
|
+
this.#clearFadeMask();
|
|
301
296
|
this.#stopMeasuring();
|
|
302
297
|
this.#stopReportingContext();
|
|
303
298
|
this.#store?.unobserveVisibility(this);
|
|
@@ -311,8 +306,9 @@ class ABoxElement extends HTMLElementBase {
|
|
|
311
306
|
and the later connect would see the flags already set and skip it. */
|
|
312
307
|
attributeChangedCallback(name, previous, current) {
|
|
313
308
|
if (previous === current) return;
|
|
309
|
+
if (name === "fade" && current === null) this.#clearFadeMask();
|
|
314
310
|
if (!this.#store) return;
|
|
315
|
-
if (name === "fade" || name === "observe") this.#sync();
|
|
311
|
+
if (name === "fade" || name === "observe" || name === "observe-offscreen") this.#sync();
|
|
316
312
|
if (name === "throttle") this.#configureThrottle();
|
|
317
313
|
if (name === "observe" || name === "throttle") this.#queueMeasurement();
|
|
318
314
|
}
|
|
@@ -327,7 +323,7 @@ class ABoxElement extends HTMLElementBase {
|
|
|
327
323
|
}
|
|
328
324
|
/** Allows a nested `Tooltip truncatedOnly` to use Box's clipping decision. */
|
|
329
325
|
get isTruncated() {
|
|
330
|
-
const measurement = this.#readMeasurement();
|
|
326
|
+
const measurement = this.#readMeasurement(false);
|
|
331
327
|
return measurement.clippedX || measurement.clippedY;
|
|
332
328
|
}
|
|
333
329
|
/** Called by the per-window context cache. It is intentionally not a JSX prop. */
|
|
@@ -355,11 +351,12 @@ class ABoxElement extends HTMLElementBase {
|
|
|
355
351
|
this.#watchContent = this.hasAttribute("fade") || content;
|
|
356
352
|
this.#watchScroll = this.hasAttribute("fade") || scroll;
|
|
357
353
|
const wantsMeasurement = this.hasAttribute("fade") || fields.size > 0;
|
|
358
|
-
const
|
|
354
|
+
const offscreen = this.hasAttribute("observe-offscreen");
|
|
355
|
+
const measure = this.isConnected && wantsMeasurement && (offscreen || (this.#visible ?? this.hasAttribute("fade")));
|
|
359
356
|
if (measure) this.#startMeasuring();
|
|
360
357
|
else this.#stopMeasuring();
|
|
361
358
|
if (this.#measuring) this.#syncMeasurementSources();
|
|
362
|
-
if (this.isConnected && wantsMeasurement) this.#store?.observeVisibility(this);
|
|
359
|
+
if (this.isConnected && wantsMeasurement && !offscreen) this.#store?.observeVisibility(this);
|
|
363
360
|
else this.#store?.unobserveVisibility(this);
|
|
364
361
|
if (this.isConnected && wantsContext) this.#startReportingContext();
|
|
365
362
|
else this.#stopReportingContext();
|
|
@@ -391,7 +388,7 @@ class ABoxElement extends HTMLElementBase {
|
|
|
391
388
|
this.#resizeObserver = new this.view.ResizeObserver(this.#queueMeasurement);
|
|
392
389
|
this.#resizeObserver.observe(this);
|
|
393
390
|
this.doc.fonts?.addEventListener("loadingdone", this.#queueMeasurement);
|
|
394
|
-
this.#measurement = this.#readMeasurement();
|
|
391
|
+
this.#measurement = this.#readMeasurement(false);
|
|
395
392
|
this.#setMeasurementStates(this.#measurement);
|
|
396
393
|
this.#syncChildObservation();
|
|
397
394
|
this.#initialMeasurement = true;
|
|
@@ -488,7 +485,8 @@ class ABoxElement extends HTMLElementBase {
|
|
|
488
485
|
this.#frame = this.view.requestAnimationFrame(() => {
|
|
489
486
|
this.#frame = void 0;
|
|
490
487
|
if (!this.#measuring) return;
|
|
491
|
-
this.#
|
|
488
|
+
if (this.#throttledReport) this.#throttledReport();
|
|
489
|
+
else this.#reportMeasurement();
|
|
492
490
|
});
|
|
493
491
|
};
|
|
494
492
|
#queueContext = () => {
|
|
@@ -502,7 +500,30 @@ class ABoxElement extends HTMLElementBase {
|
|
|
502
500
|
this.#reportContext(initial);
|
|
503
501
|
});
|
|
504
502
|
};
|
|
505
|
-
#
|
|
503
|
+
#readRects(rect) {
|
|
504
|
+
let rects = [];
|
|
505
|
+
const selector = this.getAttribute("include-rects-for");
|
|
506
|
+
if (selector) {
|
|
507
|
+
try {
|
|
508
|
+
const boxRect = rect ?? this.getBoundingClientRect();
|
|
509
|
+
rects = Array.from(this.querySelectorAll(selector), (element) => {
|
|
510
|
+
const target = element.getBoundingClientRect();
|
|
511
|
+
return {
|
|
512
|
+
top: rounded(target.top - boxRect.top),
|
|
513
|
+
right: rounded(target.right - boxRect.left),
|
|
514
|
+
bottom: rounded(target.bottom - boxRect.top),
|
|
515
|
+
left: rounded(target.left - boxRect.left),
|
|
516
|
+
width: rounded(target.width),
|
|
517
|
+
height: rounded(target.height)
|
|
518
|
+
};
|
|
519
|
+
});
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (!(error instanceof this.view.DOMException) || error.name !== "SyntaxError") throw error;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return rects;
|
|
525
|
+
}
|
|
526
|
+
#readMeasurement(includeRects = true) {
|
|
506
527
|
const rect = this.getBoundingClientRect();
|
|
507
528
|
const clientWidth = this.clientWidth;
|
|
508
529
|
const clientHeight = this.clientHeight;
|
|
@@ -524,6 +545,7 @@ class ABoxElement extends HTMLElementBase {
|
|
|
524
545
|
return {
|
|
525
546
|
width: rounded(rect.width),
|
|
526
547
|
height: rounded(rect.height),
|
|
548
|
+
rects: includeRects ? this.#readRects(rect) : [],
|
|
527
549
|
clientWidth,
|
|
528
550
|
clientHeight,
|
|
529
551
|
scrollWidth,
|
|
@@ -559,7 +581,7 @@ class ABoxElement extends HTMLElementBase {
|
|
|
559
581
|
this.#throttledReport = void 0;
|
|
560
582
|
const interval = Number(this.getAttribute("throttle"));
|
|
561
583
|
if (this.#measuring && Number.isFinite(interval) && interval > 0) {
|
|
562
|
-
this.#throttledReport = throttle(this.#
|
|
584
|
+
this.#throttledReport = throttle(() => this.#reportMeasurement(), Math.min(interval, 2147483647));
|
|
563
585
|
}
|
|
564
586
|
}
|
|
565
587
|
#hasMeasurementChange(current) {
|
|
@@ -572,27 +594,22 @@ class ABoxElement extends HTMLElementBase {
|
|
|
572
594
|
return false;
|
|
573
595
|
}
|
|
574
596
|
#reportMeasurement() {
|
|
575
|
-
const current = this.#readMeasurement();
|
|
597
|
+
const current = this.#readMeasurement(false);
|
|
576
598
|
this.#measurement = current;
|
|
577
599
|
this.#setMeasurementStates(current);
|
|
578
600
|
this.#syncChildObservation();
|
|
579
601
|
if (!this.#hasMeasurementChange(current)) return;
|
|
580
|
-
|
|
581
|
-
else this.#emitMeasurement();
|
|
582
|
-
}
|
|
583
|
-
#emitMeasurement = () => {
|
|
584
|
-
const current = this.#measurement;
|
|
585
|
-
if (!this.#measuring || !current || !this.#hasMeasurementChange(current)) return;
|
|
602
|
+
const snapshot = { ...current, rects: this.#readRects() };
|
|
586
603
|
const initial = this.#initialMeasurement;
|
|
587
604
|
const previous = this.#reportedMeasurement;
|
|
588
605
|
this.#initialMeasurement = false;
|
|
589
|
-
this.#reportedMeasurement =
|
|
606
|
+
this.#reportedMeasurement = snapshot;
|
|
590
607
|
const detail = {
|
|
591
|
-
changed: initial ?
|
|
592
|
-
current
|
|
608
|
+
changed: changed(initial ? void 0 : previous, snapshot),
|
|
609
|
+
current: snapshot
|
|
593
610
|
};
|
|
594
611
|
this.dispatchEvent(new this.view.CustomEvent("measurechange", { detail }));
|
|
595
|
-
}
|
|
612
|
+
}
|
|
596
613
|
#reportContext(initial) {
|
|
597
614
|
const current = this.#readContext();
|
|
598
615
|
const previous = this.#context;
|
|
@@ -620,6 +637,31 @@ class ABoxElement extends HTMLElementBase {
|
|
|
620
637
|
this.#setState("hidden-end-x", measurement.hiddenEndX);
|
|
621
638
|
this.#setState("hidden-start-y", measurement.hiddenStartY);
|
|
622
639
|
this.#setState("hidden-end-y", measurement.hiddenEndY);
|
|
640
|
+
this.#syncFadeMask(measurement);
|
|
641
|
+
}
|
|
642
|
+
#syncFadeMask(measurement) {
|
|
643
|
+
const hidden = measurement.hiddenStartX || measurement.hiddenEndX || measurement.hiddenStartY || measurement.hiddenEndY;
|
|
644
|
+
if (hidden && this.hasAttribute("fade")) {
|
|
645
|
+
if (this.#fadeMaskTimer !== void 0) this.view.clearTimeout(this.#fadeMaskTimer);
|
|
646
|
+
this.#fadeMaskTimer = void 0;
|
|
647
|
+
this.#setState("fade-mask-active", true);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (!this.hasAttribute("fade")) {
|
|
651
|
+
this.#clearFadeMask();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (!this.#internals?.states?.has("fade-mask-active") || this.#fadeMaskTimer !== void 0) return;
|
|
655
|
+
if (this.view.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
656
|
+
this.#clearFadeMask();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.#fadeMaskTimer = this.view.setTimeout(() => this.#clearFadeMask(), FADE_TRANSITION_MS);
|
|
660
|
+
}
|
|
661
|
+
#clearFadeMask() {
|
|
662
|
+
if (this.#fadeMaskTimer !== void 0) this.view.clearTimeout(this.#fadeMaskTimer);
|
|
663
|
+
this.#fadeMaskTimer = void 0;
|
|
664
|
+
this.#setState("fade-mask-active", false);
|
|
623
665
|
}
|
|
624
666
|
#setState(name, active) {
|
|
625
667
|
if (active) this.#internals?.states?.add(name);
|