@antadesign/anta 0.3.16 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -224,18 +224,19 @@ To use the Antithesis fonts, register your own `@font-face` declarations and ove
224
224
 
225
225
  ## Browser support
226
226
 
227
- Anta targets evergreen browsers and ships **no baseline polyfills or feature detection**. Its floor is the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API), used by `<a-menu>` and `<a-tooltip>`:
227
+ Anta targets evergreen browsers and ships **no baseline polyfills**. Its floor is [custom-element states](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet), used throughout the components for their internal CSS state, alongside the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API):
228
228
 
229
229
  | Browser | Minimum version |
230
230
  | --- | --- |
231
- | Chrome / Edge | 114 (May 2023) |
232
- | Safari | 17 (Sep 2023) |
233
- | Firefox | 125 (Apr 2024) |
234
-
235
- This is roughly [Baseline 2024](https://web.dev/baseline). Anta also relies on
236
- relative OKLCH, `:has()`, `dvh`, cascade layers, and constructable shadow DOM.
237
- Older browsers can fail hard, including `showPopover()` throwing. Gate Anta on
238
- your own support matrix when you support older browsers.
231
+ | Chrome / Edge | 125 (May 2024) |
232
+ | Safari | 17.4 (Mar 2024) |
233
+ | Firefox | 126 (May 2024) |
234
+
235
+ This is [Baseline 2024](https://web.dev/baseline). Anta also relies on relative
236
+ OKLCH, `:has()`, `dvh`, cascade layers, and constructable shadow DOM. Older
237
+ browsers can fail hard, including `:state()` being unrecognized or
238
+ `showPopover()` throwing. Gate Anta on your own support matrix when you support
239
+ older browsers.
239
240
 
240
241
  Two features progressively enhance with fallbacks: `checkVisibility()` falls
241
242
  back to `getClientRects()`, and typed CSS `attr()` supports raw
@@ -73,18 +73,13 @@ export declare const NAMED_TONES: Set<string>;
73
73
  * derives the fill/text/border curve in oklch. Named tones return `base` unchanged.
74
74
  */
75
75
  export declare function toneStyle(tone: string | undefined, varName: string, base?: React.CSSProperties): React.CSSProperties | undefined;
76
- /**
77
- * Inline-style helper for a valued `round` — the polymorphic corner-radius prop
78
- * (`true` = fully round, a `number`/length = a custom radius). Mirrors `toneStyle`:
79
- * a custom value is handed to the element via `varName` (e.g. `--button-round`), so
80
- * the element's CSS resolves `border-radius: var(--{c}-round, <default>)`. This is
81
- * the portable path — the element's `attr(round type(<length>), …)` default only
82
- * resolves on newer engines, exactly like the tone-source `attr()`.
83
- *
84
- * A `number` becomes `<n>px`; a non-empty `string` is used verbatim (so `'1rem'` /
85
- * `'50%'` work). `true` / `false` / `undefined` add nothing (presence alone → the
86
- * element's default full-round). Returns `base` unchanged when there's no value.
87
- */
76
+ /** Converts a numeric or string length to CSS text. Numbers become pixels. */
77
+ export declare function cssLength(length: number | string | undefined): string | undefined;
78
+ /** Adds a CSS length to an inline custom property. Numbers become pixels; a
79
+ * non-empty string is used verbatim. Returns `base` unchanged when absent. */
80
+ export declare function lengthStyle(length: number | string | undefined, varName: string, base?: React.CSSProperties): React.CSSProperties | undefined;
81
+ /** Inline-style helper for a valued `round`. Its boolean form leaves the
82
+ * element's default full-round radius in place. */
88
83
  export declare function roundStyle(round: boolean | number | string | undefined, varName: string, base?: React.CSSProperties): React.CSSProperties | undefined;
89
84
  /**
90
85
  * Presence flag for a `round` attribute: `''` when a radius applies, else
@@ -40,10 +40,16 @@ const NAMED_TONES = /* @__PURE__ */ new Set([
40
40
  function toneStyle(tone, varName, base) {
41
41
  return tone != null && !NAMED_TONES.has(tone) ? { ...base, [varName]: tone } : base;
42
42
  }
43
+ function cssLength(length) {
44
+ if (typeof length === "number") return `${length}px`;
45
+ return length || void 0;
46
+ }
47
+ function lengthStyle(length, varName, base) {
48
+ const value = cssLength(length);
49
+ return value ? { ...base, [varName]: value } : base;
50
+ }
43
51
  function roundStyle(round, varName, base) {
44
- if (typeof round === "number") return { ...base, [varName]: `${round}px` };
45
- if (typeof round === "string" && round !== "") return { ...base, [varName]: round };
46
- return base;
52
+ return lengthStyle(typeof round === "number" || typeof round === "string" ? round : void 0, varName, base);
47
53
  }
48
54
  function roundAttr(round) {
49
55
  return round || round === 0 ? "" : void 0;
@@ -145,10 +151,12 @@ export {
145
151
  NAMED_TONES,
146
152
  SelectableChildElement,
147
153
  anchorRect,
154
+ cssLength,
148
155
  hasChildren,
149
156
  installDismissTrigger,
150
157
  isInsideOpenMenu,
151
158
  isMenuOpen,
159
+ lengthStyle,
152
160
  nativeStateChange,
153
161
  parseOpenState,
154
162
  roundAttr,
@@ -20,11 +20,14 @@ export type ButtonCopyProps = ButtonProps & {
20
20
  form?: never;
21
21
  iconTrailing?: never;
22
22
  } & CopyTarget & {
23
- /** Where the copy glyph sits relative to the label — or `'none'` to omit it,
24
- * in which case the feedback is a ghost of the label floating up (`toast`)
25
- * plus a brief success / failure tone flash.
23
+ /** Where the copy glyph sits relative to the label — or `'none'` to omit it.
24
+ * Without a glyph, a successful copy shows a small confirmation label near
25
+ * the pointer and leaves the button unchanged.
26
26
  * @defaultValue 'leading' */
27
27
  iconPlacement?: 'leading' | 'trailing' | 'none';
28
+ /** Text in the successful no-icon confirmation.
29
+ * @defaultValue Copied */
30
+ copiedLabel?: string;
28
31
  };
29
32
  /**
30
33
  * Copy button — a `Button` preset for copy-to-clipboard. Set `copy` for a literal
@@ -37,7 +40,7 @@ export type ButtonCopyProps = ButtonProps & {
37
40
  * copy glyph to a check (success) or ✕ (failure) and retones to `success` /
38
41
  * `critical` for ~2s (a re-render, no element state — see `useCopyFeedback`).
39
42
  * `iconPlacement` puts that glyph leading (default), trailing, or `'none'`; with
40
- * `'none'` the `<a-copy>` floats a ghost of the label upward instead.
43
+ * `'none'`, `<a-copy>` shows its `Copied` confirmation instead.
41
44
  *
42
45
  * With a `label` it's a labeled button; without one it's an icon-only copy button
43
46
  * (named "Copy" for assistive tech). Everything else is a normal `Button` prop —
@@ -48,9 +51,9 @@ export type ButtonCopyProps = ButtonProps & {
48
51
  * <ButtonCopy copy="npm i @antadesign/anta" label="Copy install" />
49
52
  * <ButtonCopy copy="https://anta.design" /> // icon-only
50
53
  * <ButtonCopy copy={code} label="Copy" iconPlacement="trailing" />
51
- * <ButtonCopy copy={code} label="Copy snippet" iconPlacement="none" /> // ghost feedback
54
+ * <ButtonCopy copy={code} label="Copy snippet" iconPlacement="none" copiedLabel="Copied" />
52
55
  * <ButtonCopy copyNode=".snippet" label="Copy block" priority="tertiary" />
53
56
  * <ButtonCopy copyUrl label="Copy link" /> // copies location.href
54
57
  * ```
55
58
  */
56
- export declare const ButtonCopy: ({ icon, iconPlacement, tone, onCopied, onCopyRequest, copy, copyNode, copyUrl, copyWithUrl, label, children, "aria-label": ariaLabel, ...rest }: ButtonCopyProps) => any;
59
+ export declare const ButtonCopy: ({ icon, iconPlacement, copiedLabel, tone, onCopied, onCopyRequest, copy, copyNode, copyUrl, copyWithUrl, label, children, "aria-label": ariaLabel, ...rest }: ButtonCopyProps) => any;
@@ -5,6 +5,7 @@ import { copyElementProps, isNodeCopy } from "./copy-props";
5
5
  const ButtonCopy = ({
6
6
  icon,
7
7
  iconPlacement = "leading",
8
+ copiedLabel,
8
9
  tone,
9
10
  onCopied,
10
11
  onCopyRequest,
@@ -28,15 +29,16 @@ const ButtonCopy = ({
28
29
  copyWithUrl,
29
30
  onCopyRequest,
30
31
  onCopied: handleCopied,
31
- // Ghost feedback only makes sense when there's no glyph to swap.
32
- toast: iconPlacement === "none"
32
+ // The top-layer confirmation only appears when there is no glyph to swap.
33
+ toast: iconPlacement === "none",
34
+ copiedLabel
33
35
  });
34
36
  return /* @__PURE__ */ jsxs(
35
37
  Button,
36
38
  {
37
39
  ...iconSlot,
38
40
  label,
39
- tone: shownTone,
41
+ tone: iconPlacement === "none" ? tone : shownTone,
40
42
  "aria-label": ariaLabel ?? (hasText ? void 0 : "Copy"),
41
43
  "data-copy-node-button": isNodeCopy({ copyNode }) ? "" : void 0,
42
44
  ...rest,
@@ -84,10 +84,11 @@ export interface InputTimeProps extends BaseProps, DOMEventHandlers {
84
84
  /**
85
85
  * `<InputTime>` — a segmented wall-clock time field. One boxed input (matching
86
86
  * the other inputs) with separate hour / minute / (12-hour) AM-PM sections that
87
- * behave as one control: each is a `role="spinbutton"` — ↑/↓ steps with wrap,
88
- * ←/→ moves between them, typing digits auto-advances, and AM/PM toggles with
89
- * ↑/↓ or `a`/`p`. Renders an `<a-input-time>` web component that owns the
90
- * segments, keyboard, and form value (via `ElementInternals`).
87
+ * behave as one control: each is a native text input — ↑/↓ steps with wrap,
88
+ * ←/→ crosses segments at caret boundaries, typing digits auto-advances, and
89
+ * AM/PM accepts its locale text or `a`/`p`. Renders an `<a-input-time>` web
90
+ * component that owns the segments, navigation, and form value (via
91
+ * `ElementInternals`).
91
92
  *
92
93
  * The clock and layout follow the locale (`en-US` → 12-hour, most others 24),
93
94
  * overridable with `hour12`. The value is a 24-hour `"HH:mm"` string; controlled
@@ -0,0 +1,89 @@
1
+ import type { BaseProps, DOMEventHandlers } from '../general_types';
2
+ /** A text marker displayed below the slider rail. Markers are labels only; they
3
+ * do not add dots or ticks to the rail. */
4
+ export interface SliderMarker {
5
+ value: number;
6
+ label: React.ReactNode;
7
+ }
8
+ /** Snapshot passed to `onValueChange` and `onValueCommit`. */
9
+ export interface SliderChangeAttrs {
10
+ value: number;
11
+ min: number;
12
+ max: number;
13
+ step: number;
14
+ name?: string;
15
+ }
16
+ export interface SliderProps extends BaseProps, DOMEventHandlers {
17
+ /** Visible field label, shown above the rail. A string supplies the slider's
18
+ * accessible name; give a rich label an explicit `aria-label`. */
19
+ label?: React.ReactNode;
20
+ /** Controlled value. Update it from `onValueChange`. */
21
+ value?: number;
22
+ /** Initial uncontrolled value.
23
+ * @defaultValue 0 */
24
+ defaultValue?: number;
25
+ /** Lowest permitted value.
26
+ * @defaultValue 0 */
27
+ min?: number;
28
+ /** Highest permitted value.
29
+ * @defaultValue 100 */
30
+ max?: number;
31
+ /** Smallest keyboard and drag increment.
32
+ * @defaultValue 1 */
33
+ step?: number;
34
+ /** Form field name. The current numeric value submits under this name. */
35
+ name?: string;
36
+ /** Disables pointer and keyboard interaction. */
37
+ disabled?: boolean;
38
+ /** Colour of the filled rail. Pass a named tone or a literal CSS colour for a
39
+ * one-off custom tone. The unfilled rail stays neutral.
40
+ * @defaultValue 'neutral' */
41
+ tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
42
+ /** Colour of the thumb stroke. Pass a named tone or a literal CSS colour for a
43
+ * one-off custom tone. Omit it to keep the thumb neutral.
44
+ * @defaultValue 'neutral' */
45
+ thumbTone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
46
+ /** Size variant. small=24px, medium=28px, large=32px tall.
47
+ * @defaultValue 'medium' */
48
+ size?: 'small' | 'medium' | 'large';
49
+ /** Thickness of both rail segments. Numbers use pixels; strings are CSS
50
+ * lengths. Keep it no larger than the thumb diameter.
51
+ * @defaultValue 2 */
52
+ trackSize?: number | string;
53
+ /** Diameter of the thumb. Numbers use pixels; strings are CSS lengths. Keep
54
+ * it at least as large as `trackSize`.
55
+ * @defaultValue 18 */
56
+ thumbSize?: number | string;
57
+ /** Fill the thumb with its resolved border colour. This follows `thumbTone`
58
+ * and interactive states.
59
+ * @defaultValue false */
60
+ thumbFill?: boolean;
61
+ /** Fully round the rail and thumb. Pass a number (px) or CSS length string
62
+ * for a shared custom radius. */
63
+ round?: boolean | number | string;
64
+ /** Where the live value appears. `end` puts it at the right edge of the label
65
+ * row. `inline` renders `Label: value`. `thumb` keeps it above the thumb.
66
+ * @defaultValue 'end' */
67
+ valueDisplay?: 'end' | 'inline' | 'thumb' | 'none';
68
+ /** Text inserted before the live numeric value, such as `$`. */
69
+ valuePrefix?: string;
70
+ /** Text inserted after the live numeric value, such as `%` or `°C`. */
71
+ valueSuffix?: string;
72
+ /** Compact text labels positioned below the rail. They do not add dots or
73
+ * ticks to the rail. */
74
+ markers?: SliderMarker[];
75
+ /** Track press behaviour. `drag-only` starts a relative grab from the current
76
+ * value; `jump` first moves to the press position.
77
+ * @defaultValue 'drag-only' */
78
+ trackClick?: 'drag-only' | 'jump';
79
+ /** Fires on every keyboard or pointer value change. */
80
+ onValueChange?: (event: any, attrs: SliderChangeAttrs) => void;
81
+ /** Fires after a drag ends and after each keyboard value change. */
82
+ onValueCommit?: (event: any, attrs: SliderChangeAttrs) => void;
83
+ }
84
+ /**
85
+ * Slider selects one numeric value from a range. Pressing anywhere on its rail
86
+ * starts a relative drag by default, so the thumb never jumps to the press point.
87
+ * Requires `@antadesign/anta/elements` on the client.
88
+ */
89
+ export declare const Slider: ({ label, value, defaultValue, min: minProp, max: maxProp, step: stepProp, name, disabled, tone, thumbTone, size, trackSize, thumbSize, thumbFill, round, valueDisplay, valuePrefix, valueSuffix, markers, trackClick, onInput, onChange, onValueChange, onValueCommit, className, style, children, tabIndex, ...rest }: SliderProps) => any;
@@ -0,0 +1,144 @@
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { toneStyle, roundStyle, roundAttr, lengthStyle, cssLength } from "../anta_helpers";
3
+ const number = (value, fallback) => Number.isFinite(value) ? value : fallback;
4
+ const isStringish = (node) => typeof node === "string" || typeof node === "number";
5
+ const bounds = (min, max) => {
6
+ const low = number(min, 0);
7
+ return { min: low, max: Math.max(low, number(max, 100)) };
8
+ };
9
+ const attrsOf = (event) => {
10
+ const element = event?.target ?? {};
11
+ return {
12
+ value: Number(element.value ?? 0),
13
+ min: Number(element.getAttribute?.("min") ?? 0),
14
+ max: Number(element.getAttribute?.("max") ?? 100),
15
+ step: Number(element.getAttribute?.("step") ?? 1),
16
+ name: element.getAttribute?.("name") ?? void 0
17
+ };
18
+ };
19
+ const Slider = ({
20
+ label,
21
+ value,
22
+ defaultValue,
23
+ min: minProp,
24
+ max: maxProp,
25
+ step: stepProp,
26
+ name,
27
+ disabled,
28
+ tone,
29
+ thumbTone,
30
+ size,
31
+ trackSize,
32
+ thumbSize,
33
+ thumbFill,
34
+ round,
35
+ valueDisplay = "end",
36
+ valuePrefix,
37
+ valueSuffix,
38
+ markers,
39
+ trackClick = "drag-only",
40
+ onInput,
41
+ onChange,
42
+ onValueChange,
43
+ onValueCommit,
44
+ className,
45
+ style,
46
+ children,
47
+ tabIndex,
48
+ ...rest
49
+ }) => {
50
+ const trackSizeValue = cssLength(trackSize);
51
+ const thumbSizeValue = cssLength(thumbSize);
52
+ const computedStyle = lengthStyle(
53
+ thumbSize,
54
+ "--anta-slider-thumb-size",
55
+ lengthStyle(
56
+ trackSize,
57
+ "--anta-slider-track-size",
58
+ roundStyle(
59
+ round,
60
+ "--slider-round",
61
+ toneStyle(thumbTone, "--slider-thumb-tone-source", toneStyle(tone, "--slider-tone-source", style))
62
+ )
63
+ )
64
+ );
65
+ const { min, max } = bounds(minProp, maxProp);
66
+ const step = number(stepProp, 1) > 0 ? number(stepProp, 1) : 1;
67
+ const explicitAriaLabel = rest["aria-label"];
68
+ const ariaLabel = (typeof explicitAriaLabel === "string" ? explicitAriaLabel : void 0) ?? (typeof label === "string" || typeof label === "number" ? String(label) : void 0);
69
+ const resolvedValueDisplay = valueDisplay === "inline" && label == null ? "end" : valueDisplay;
70
+ const markerPosition = (marker) => {
71
+ if (marker.value <= min) return "0%";
72
+ if (marker.value >= max) return "100%";
73
+ const ratio = (marker.value - min) / (max - min);
74
+ return `calc((100% - var(--anta-slider-thumb-size)) * ${ratio} + var(--anta-slider-thumb-size) / 2)`;
75
+ };
76
+ const markerAlignment = (marker) => marker.value <= min ? "start" : marker.value >= max ? "end" : void 0;
77
+ const markerStyle = (marker) => {
78
+ const markerStyle2 = {};
79
+ Object.assign(markerStyle2, { "--slider-marker-position": markerPosition(marker) });
80
+ return markerStyle2;
81
+ };
82
+ const handleInput = onInput || onValueChange ? (event) => {
83
+ const attrs = attrsOf(event);
84
+ onInput?.(event);
85
+ onValueChange?.(event, attrs);
86
+ } : void 0;
87
+ const handleChange = onChange || onValueCommit ? (event) => {
88
+ const attrs = attrsOf(event);
89
+ onChange?.(event);
90
+ onValueCommit?.(event, attrs);
91
+ } : void 0;
92
+ return /* @__PURE__ */ jsxs(
93
+ "a-slider",
94
+ {
95
+ value,
96
+ defaultvalue: defaultValue,
97
+ min,
98
+ max,
99
+ step,
100
+ name,
101
+ disabled: disabled ? "" : void 0,
102
+ tone: tone && tone !== "neutral" ? tone : void 0,
103
+ "thumb-tone": thumbTone && thumbTone !== "neutral" ? thumbTone : void 0,
104
+ size: size && size !== "medium" ? size : void 0,
105
+ "track-size": trackSizeValue,
106
+ "thumb-size": thumbSizeValue,
107
+ "thumb-fill": thumbFill ? "" : void 0,
108
+ round: roundAttr(round),
109
+ "track-click": trackClick === "jump" ? "jump" : void 0,
110
+ "value-display": resolvedValueDisplay === "end" ? void 0 : resolvedValueDisplay,
111
+ "value-prefix": valuePrefix,
112
+ "value-suffix": valueSuffix,
113
+ role: "slider",
114
+ "aria-label": ariaLabel,
115
+ tabIndex: disabled ? -1 : tabIndex ?? 0,
116
+ oninput: handleInput,
117
+ onchange: handleChange,
118
+ class: className,
119
+ style: computedStyle,
120
+ ...rest,
121
+ children: [
122
+ label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
123
+ markers?.map((marker, index) => {
124
+ const alignment = markerAlignment(marker);
125
+ return /* @__PURE__ */ jsx(
126
+ "span",
127
+ {
128
+ slot: "markers",
129
+ style: markerStyle(marker),
130
+ "data-slider-marker-start": alignment === "start" ? "" : void 0,
131
+ "data-slider-marker-end": alignment === "end" ? "" : void 0,
132
+ children: marker.label
133
+ },
134
+ `${marker.value}-${index}`
135
+ );
136
+ }),
137
+ children
138
+ ]
139
+ }
140
+ );
141
+ };
142
+ export {
143
+ Slider
144
+ };
@@ -51,6 +51,7 @@ type CopyElementInput = {
51
51
  onCopyRequest?: () => void;
52
52
  onCopied?: (ok: boolean) => void;
53
53
  toast?: boolean;
54
+ copiedLabel?: string;
54
55
  };
55
56
  /** Project the copy-target props onto `<a-copy>` attributes + event handlers.
56
57
  * Shared by `ButtonCopy` / `MenuItemCopy` so the mapping (and the cross-renderer
@@ -61,6 +62,7 @@ export declare function copyElementProps(p: CopyElementInput): {
61
62
  readonly 'copy-url': "" | undefined;
62
63
  readonly 'copy-with-url': "" | undefined;
63
64
  readonly toast: "" | undefined;
65
+ readonly 'copied-label': string | undefined;
64
66
  readonly oncopydone: ((e: any) => void) | undefined;
65
67
  readonly oncopyrequest: (() => void) | undefined;
66
68
  };
@@ -1,12 +1,13 @@
1
1
  import { nativeStateChange } from "../anta_helpers";
2
2
  function copyElementProps(p) {
3
- const { copy, copyNode, copyUrl, copyWithUrl, onCopyRequest, onCopied, toast } = p;
3
+ const { copy, copyNode, copyUrl, copyWithUrl, onCopyRequest, onCopied, toast, copiedLabel } = p;
4
4
  return {
5
5
  copy: copy != null ? copy : void 0,
6
6
  "copy-node": copyNode === true ? "" : typeof copyNode === "string" ? copyNode : void 0,
7
7
  "copy-url": copyUrl ? "" : void 0,
8
8
  "copy-with-url": copyWithUrl ? "" : void 0,
9
9
  toast: toast ? "" : void 0,
10
+ "copied-label": copiedLabel,
10
11
  oncopydone: onCopied ? (e) => onCopied(nativeStateChange(e).detail?.ok ?? false) : void 0,
11
12
  oncopyrequest: onCopyRequest ? () => onCopyRequest() : void 0
12
13
  };
@@ -9,7 +9,7 @@ export declare class ACopyElement extends HTMLElementBase {
9
9
  #private;
10
10
  connectedCallback(): void;
11
11
  /** Run the copy for the activated host. Called from the delegated click /
12
- * menuselect handlers; the ghost (if any) follows on `copydone`. */
12
+ * menuselect handlers; the confirmation follows a successful `copydone`. */
13
13
  activate(): void;
14
14
  disconnectedCallback(): void;
15
15
  }