@antadesign/anta 0.3.11 → 0.3.12

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.
@@ -55,10 +55,8 @@ export type ContentMode = {
55
55
  iconTrailing?: IconShape;
56
56
  };
57
57
  /** Submit axis — anchors (href) don't carry form-submission props; buttons
58
- * don't carry anchor props. A link (`href`) also can't be a copy control, so the
59
- * copy props live only on the non-link branch (via `CopyMode`) and are `never`
60
- * on the link branch — a `<ButtonCopy href=…>` or `<Button href=… copy=…>` is a
61
- * type error, not a silently-ignored attribute. */
58
+ * don't carry anchor props. (Copy is no longer a Button concern — `ButtonCopy`
59
+ * composes a `<a-copy>` child; see `copy-props.ts`.) */
62
60
  export type SubmitMode = {
63
61
  /** Renders as `<a role="button">` instead of `<a-button>`. */
64
62
  href: string;
@@ -72,13 +70,7 @@ export type SubmitMode = {
72
70
  ping?: string;
73
71
  type?: never;
74
72
  form?: never;
75
- copy?: never;
76
- copyNode?: never;
77
- copyUrl?: never;
78
- copyWithUrl?: never;
79
- onCopyRequest?: never;
80
- onCopied?: never;
81
- } | ({
73
+ } | {
82
74
  href?: never;
83
75
  target?: never;
84
76
  rel?: never;
@@ -88,7 +80,7 @@ export type SubmitMode = {
88
80
  type?: 'button' | 'submit' | 'reset';
89
81
  /** Form id when the button isn't a descendant of its form. */
90
82
  form?: string;
91
- } & CopyMode);
83
+ };
92
84
  /** Priority axis — `underline` only on `tertiary` / `quaternary`,
93
85
  * `paddingless` only on `quaternary`. */
94
86
  export type PriorityMode = {
@@ -109,55 +101,6 @@ export type PriorityMode = {
109
101
  /** Drops outer padding to zero. */
110
102
  paddingless?: boolean;
111
103
  };
112
- /** Copy axis — turns the button into a copy control that writes to the clipboard
113
- * on click and flashes a success / failure state (see `<a-button>`'s copy
114
- * behavior). **Exactly one** of `copy`, `copyNode`, or `copyUrl` may be set;
115
- * the union makes the others `never` in each mode. For the batteries-included
116
- * preset, use `ButtonCopy`. */
117
- export type CopyMode = {
118
- copy?: never;
119
- copyNode?: never;
120
- copyUrl?: never;
121
- copyWithUrl?: never;
122
- onCopied?: never;
123
- onCopyRequest?: never;
124
- } | {
125
- /** Text copied to the clipboard on click. */
126
- copy: string;
127
- /** Prefix the copied text with `// URL: <current page URL>`. */
128
- copyWithUrl?: boolean;
129
- /** Compute the copy content lazily. Fires on **pointerdown**; update `copy`
130
- * (a state change) here and the click copies the latest value. The
131
- * pointerdown→click gap lets the update land even when this handler runs
132
- * off the UI thread (a worker-rendered app) — only the serializable `copy`
133
- * string crosses, via the normal re-render. */
134
- onCopyRequest?: () => void;
135
- copyNode?: never;
136
- copyUrl?: never;
137
- /** Fires after the copy attempt with whether it succeeded. */
138
- onCopied?: (ok: boolean) => void;
139
- } | {
140
- /** Copy a DOM node as rich text instead of a string. `true` copies the
141
- * nearest ancestor marked `data-copy-source`; a string is a CSS selector
142
- * for an ancestor region (`closest`). The copy control is stripped from
143
- * the copied output. */
144
- copyNode: boolean | string;
145
- copy?: never;
146
- copyUrl?: never;
147
- copyWithUrl?: never;
148
- onCopyRequest?: never;
149
- /** Fires after the copy attempt with whether it succeeded. */
150
- onCopied?: (ok: boolean) => void;
151
- } | {
152
- /** Copy the current page URL (`location.href`). */
153
- copyUrl: true;
154
- copy?: never;
155
- copyNode?: never;
156
- copyWithUrl?: never;
157
- onCopyRequest?: never;
158
- /** Fires after the copy attempt with whether it succeeded. */
159
- onCopied?: (ok: boolean) => void;
160
- };
161
104
  export type ButtonProps = BaseButtonProps & PriorityMode & ContentMode & SubmitMode & BaseProps;
162
105
  /**
163
106
  * Action button.
@@ -179,4 +122,4 @@ export type ButtonProps = BaseButtonProps & PriorityMode & ContentMode & SubmitM
179
122
  * <Button href="/docs" target="_blank" label="Read the docs" />
180
123
  * ```
181
124
  */
182
- export declare const Button: ({ priority, tone, underline, icon, iconTrailing, paddingless, label, size, loading, disabled, selected, round, href, type, form, copy, copyNode, copyUrl, copyWithUrl, onCopied, onCopyRequest, className, style, children, ...rest }: ButtonProps) => any;
125
+ export declare const Button: ({ priority, tone, underline, icon, iconTrailing, paddingless, label, size, loading, disabled, selected, round, href, type, form, className, style, children, ...rest }: ButtonProps) => any;
@@ -1,5 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { toneStyle, roundStyle, roundAttr, wrapLabel, nativeStateChange } from "../anta_helpers";
2
+ import { toneStyle, roundStyle, roundAttr, wrapLabel } from "../anta_helpers";
3
3
  const Button = ({
4
4
  priority,
5
5
  tone,
@@ -16,22 +16,14 @@ const Button = ({
16
16
  href,
17
17
  type,
18
18
  form,
19
- copy,
20
- copyNode,
21
- copyUrl,
22
- copyWithUrl,
23
- onCopied,
24
- onCopyRequest,
25
19
  className,
26
20
  style,
27
21
  children,
28
22
  ...rest
29
23
  }) => {
30
- const isCopy = copy != null || copyNode != null || copyUrl === true;
31
24
  const toneAttr = tone || void 0;
32
25
  const computedStyle = roundStyle(round, "--button-round", toneStyle(toneAttr, "--button-tone-source", style));
33
- const leadingIcon = icon ?? (isCopy ? "copy" : void 0);
34
- const isIconOnly = leadingIcon != null && label == null && children == null && iconTrailing == null;
26
+ const isIconOnly = icon != null && label == null && children == null && iconTrailing == null;
35
27
  const sharedAttrs = {
36
28
  // `<a-button>` is a custom element with no implicit ARIA role, so AT would
37
29
  // announce it as a generic clickable — and `aria-pressed` below is only
@@ -61,27 +53,15 @@ const Button = ({
61
53
  "aria-disabled": disabled || loading ? "true" : void 0,
62
54
  "aria-busy": loading ? "true" : void 0,
63
55
  "aria-pressed": selected ? "true" : void 0,
64
- // Icon-only buttons get an accessible name: "Copy" for a copy control (its
65
- // glyph carries no text), else the icon shape. Consumer's own `aria-label`
66
- // (via ...rest) wins by spread order.
67
- "aria-label": isIconOnly ? isCopy ? "Copy" : icon : void 0,
68
- // Copy behavior — the element reads these and performs the write itself.
69
- copy: copy != null ? copy : void 0,
70
- "copy-node": copyNode === true ? "" : typeof copyNode === "string" ? copyNode : void 0,
71
- "copy-url": copyUrl ? "" : void 0,
72
- "copy-with-url": copyWithUrl ? "" : void 0,
73
- // Marks the control so `copy-node` serialization strips it from the copied
74
- // node (a copy button inside the copied region shouldn't paste itself).
75
- "data-copy-node-button": copyNode != null && copyNode !== false ? "" : void 0,
76
- oncopydone: onCopied ? (e) => onCopied(nativeStateChange(e).detail?.ok ?? false) : void 0,
77
- // Fired on pointerdown for a lazy copy — the consumer updates `copy` here
78
- // (a state change) and the click copies the latest value. No payload crosses.
79
- oncopyrequest: onCopyRequest ? () => onCopyRequest() : void 0,
56
+ // Icon-only buttons get an accessible name from the icon shape. Consumer's
57
+ // own `aria-label` (via ...rest) wins by spread order — that's the channel
58
+ // ButtonCopy uses to name an icon-only copy button "Copy".
59
+ "aria-label": isIconOnly ? icon : void 0,
80
60
  class: className,
81
61
  style: computedStyle
82
62
  };
83
63
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
84
- leadingIcon && /* @__PURE__ */ jsx("a-icon", { shape: leadingIcon, "aria-hidden": "true" }),
64
+ icon && /* @__PURE__ */ jsx("a-icon", { shape: icon, "aria-hidden": "true" }),
85
65
  label != null && /* @__PURE__ */ jsx("a-button-label", { children: label }),
86
66
  wrapLabel(children, "a-button-label"),
87
67
  iconTrailing && /* @__PURE__ */ jsx("a-icon", { shape: iconTrailing, "aria-hidden": "true" })
@@ -1,38 +1,56 @@
1
1
  import { type ButtonProps } from './Button';
2
- /** `ButtonProps` narrowed so a copy target is required — a copy button must copy
3
- * something. Intersecting with the target union drops the "no copy prop" member
4
- * of `CopyMode` (its `copy?: never` makes each combination `never`). */
5
- export type ButtonCopyProps = ButtonProps & ({
6
- copy: string;
7
- } | {
8
- copyNode: boolean | string;
9
- } | {
10
- copyUrl: true;
11
- });
2
+ import { type CopyTarget } from './copy-props';
3
+ /** A copy button is a plain action control, so `ButtonCopy` keeps the `Button`
4
+ * axes that fit — tone / size / priority styling, `disabled` / `loading`, a
5
+ * leading `icon` + `label` — and pins the ones that don't to `never`: `selected`
6
+ * (it isn't a toggle), the whole submit/link axis (`href` / `target` / `rel` /
7
+ * `download` / `ping` / `type` / `form` — a copy button neither navigates nor
8
+ * submits), and the public `iconTrailing` (the glyph's side is `iconPlacement`'s
9
+ * job). Plus a required copy target and the placement knob. (Pinning to `never`
10
+ * keeps the intersection expanding fully in the docs props table, which `Omit`
11
+ * wouldn't.) */
12
+ export type ButtonCopyProps = ButtonProps & {
13
+ selected?: never;
14
+ href?: never;
15
+ target?: never;
16
+ rel?: never;
17
+ download?: never;
18
+ ping?: never;
19
+ type?: never;
20
+ form?: never;
21
+ iconTrailing?: never;
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.
26
+ * @defaultValue 'leading' */
27
+ iconPlacement?: 'leading' | 'trailing' | 'none';
28
+ };
12
29
  /**
13
30
  * Copy button — a `Button` preset for copy-to-clipboard. Set `copy` for a literal
14
31
  * string, `copyNode` to copy a DOM region, or `copyUrl` to copy the current page
15
32
  * URL. For content computed on demand, keep `copy` reactive and refresh it in
16
- * `onCopyRequest` (fired on pointerdown). The `<a-button>` element performs the
17
- * write and reports the result; this wrapper flips the leading icon to a check
18
- * (success) or ✕ (failure) and retones to `success` / `critical` for ~2s — a JSX
19
- * re-render, no element state (see `useCopyFeedback`).
33
+ * `onCopyRequest` (fired on pointerdown / keydown).
20
34
  *
21
- * With a `label` it's a labeled button; without one it's an icon-only copy button.
22
- * Everything else is a normal `Button` prop — `tone`, `priority`, `size`, `icon`
23
- * (the resting glyph), `onCopied`.
35
+ * It composes a plain `<Button>` with a slotted `<a-copy>` child that performs
36
+ * the write — the button itself carries no copy behavior. This wrapper flips the
37
+ * copy glyph to a check (success) or ✕ (failure) and retones to `success` /
38
+ * `critical` for ~2s (a re-render, no element state — see `useCopyFeedback`).
39
+ * `iconPlacement` puts that glyph leading (default), trailing, or `'none'`; with
40
+ * `'none'` the `<a-copy>` floats a ghost of the label upward instead.
41
+ *
42
+ * With a `label` it's a labeled button; without one it's an icon-only copy button
43
+ * (named "Copy" for assistive tech). Everything else is a normal `Button` prop —
44
+ * `tone`, `priority`, `size`, `icon` (the resting glyph), `onCopied`.
24
45
  *
25
46
  * @example
26
47
  * ```tsx
27
48
  * <ButtonCopy copy="npm i @antadesign/anta" label="Copy install" />
28
- * <ButtonCopy copy="https://anta.design" /> // icon-only
49
+ * <ButtonCopy copy="https://anta.design" /> // icon-only
50
+ * <ButtonCopy copy={code} label="Copy" iconPlacement="trailing" />
51
+ * <ButtonCopy copy={code} label="Copy snippet" iconPlacement="none" /> // ghost feedback
29
52
  * <ButtonCopy copyNode=".snippet" label="Copy block" priority="tertiary" />
30
- * <ButtonCopy copyUrl label="Copy link" /> // copies location.href
31
- * <ButtonCopy copy={code} copyWithUrl label="Copy snippet" /> // code + source URL
32
- *
33
- * // Lazy: compute on pointerdown, copied on the click that follows.
34
- * const [report, setReport] = useState('')
35
- * <ButtonCopy copy={report} onCopyRequest={() => setReport(buildReport())} label="Copy report" />
53
+ * <ButtonCopy copyUrl label="Copy link" /> // copies location.href
36
54
  * ```
37
55
  */
38
- export declare const ButtonCopy: ({ icon, tone, onCopied, ...rest }: ButtonCopyProps) => any;
56
+ export declare const ButtonCopy: ({ icon, iconPlacement, tone, onCopied, onCopyRequest, copy, copyNode, copyUrl, copyWithUrl, label, children, "aria-label": ariaLabel, ...rest }: ButtonCopyProps) => any;
@@ -1,9 +1,51 @@
1
- import { jsx } from "@antadesign/anta/jsx-runtime";
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { Button } from "./Button";
3
3
  import { useCopyFeedback } from "../anta_helpers";
4
- const ButtonCopy = ({ icon, tone, onCopied, ...rest }) => {
5
- const { shownIcon, shownTone, handleCopied } = useCopyFeedback(icon, tone, onCopied);
6
- return /* @__PURE__ */ jsx(Button, { icon: shownIcon, tone: shownTone, onCopied: handleCopied, ...rest });
4
+ import { copyElementProps, isNodeCopy } from "./copy-props";
5
+ const ButtonCopy = ({
6
+ icon,
7
+ iconPlacement = "leading",
8
+ tone,
9
+ onCopied,
10
+ onCopyRequest,
11
+ copy,
12
+ copyNode,
13
+ copyUrl,
14
+ copyWithUrl,
15
+ label,
16
+ children,
17
+ "aria-label": ariaLabel,
18
+ ...rest
19
+ }) => {
20
+ const restingIcon = iconPlacement === "none" ? void 0 : icon ?? "copy";
21
+ const { shownIcon, shownTone, handleCopied } = useCopyFeedback(restingIcon, tone, onCopied);
22
+ const iconSlot = iconPlacement === "trailing" ? { iconTrailing: shownIcon } : iconPlacement === "none" ? {} : { icon: shownIcon };
23
+ const hasText = label != null || children != null;
24
+ const copyAttrs = copyElementProps({
25
+ copy,
26
+ copyNode,
27
+ copyUrl,
28
+ copyWithUrl,
29
+ onCopyRequest,
30
+ onCopied: handleCopied,
31
+ // Ghost feedback only makes sense when there's no glyph to swap.
32
+ toast: iconPlacement === "none"
33
+ });
34
+ return /* @__PURE__ */ jsxs(
35
+ Button,
36
+ {
37
+ ...iconSlot,
38
+ label,
39
+ tone: shownTone,
40
+ "aria-label": ariaLabel ?? (hasText ? void 0 : "Copy"),
41
+ "data-copy-node-button": isNodeCopy({ copyNode }) ? "" : void 0,
42
+ ...rest,
43
+ children: [
44
+ children,
45
+ /* @__PURE__ */ jsx("a-copy", { ...copyAttrs })
46
+ ]
47
+ }
48
+ );
7
49
  };
8
50
  export {
9
51
  ButtonCopy
@@ -44,7 +44,7 @@ const Expander = ({
44
44
  ...rest,
45
45
  children: [
46
46
  titleNode,
47
- actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", style: { display: "contents" }, children: actions }),
47
+ actions != null && /* @__PURE__ */ jsx("span", { slot: "actions", children: actions }),
48
48
  /* @__PURE__ */ jsx("a-expander-details", { children })
49
49
  ]
50
50
  }
@@ -1,6 +1,5 @@
1
1
  import type { BaseProps } from '../general_types';
2
2
  import type { IconShape } from '../elements/a-icon.shapes';
3
- import type { CopyMode } from './Button';
4
3
  /** Props shared by every menu item, link or not. */
5
4
  export interface MenuItemCommonProps extends BaseProps {
6
5
  /** Leading icon shape. */
@@ -84,12 +83,6 @@ export type MenuItemLinkMode = {
84
83
  selectionIndicator?: never;
85
84
  indeterminate?: never;
86
85
  indicator?: never;
87
- copy?: never;
88
- copyNode?: never;
89
- copyUrl?: never;
90
- copyWithUrl?: never;
91
- onCopied?: never;
92
- onCopyRequest?: never;
93
86
  };
94
87
  export type MenuItemActionMode = {
95
88
  href?: never;
@@ -121,7 +114,7 @@ export type MenuItemActionMode = {
121
114
  * passive (aria-hidden, no pointer events) so the row owns the click. */
122
115
  indicator?: React.ReactNode;
123
116
  };
124
- export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | (MenuItemActionMode & CopyMode));
117
+ export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | MenuItemActionMode);
125
118
  /**
126
119
  * MenuItem — a single selectable row inside a `Menu`. Composes a leading
127
120
  * `icon`, a `label` (or `children`), an optional trailing `kbd` hint, and an
@@ -142,4 +135,4 @@ export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | (MenuItemA
142
135
  * </MenuItem>
143
136
  * ```
144
137
  */
145
- export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, href, target, rel, download, ping, copy, copyNode, copyUrl, copyWithUrl, onCopied, onCopyRequest, role: roleOverride, className, style, children, ...rest }: MenuItemProps) => any;
138
+ export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, href, target, rel, download, ping, role: roleOverride, className, style, children, ...rest }: MenuItemProps) => any;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { toneStyle, nativeStateChange } from "../anta_helpers";
2
+ import { toneStyle } from "../anta_helpers";
3
3
  const MenuItem = ({
4
4
  icon,
5
5
  label,
@@ -21,20 +21,13 @@ const MenuItem = ({
21
21
  rel,
22
22
  download,
23
23
  ping,
24
- copy,
25
- copyNode,
26
- copyUrl,
27
- copyWithUrl,
28
- onCopied,
29
- onCopyRequest,
30
24
  role: roleOverride,
31
25
  className,
32
26
  style,
33
27
  children,
34
28
  ...rest
35
29
  }) => {
36
- const isCopy = copy != null || copyNode != null || copyUrl === true;
37
- const leadingIcon = icon ?? (isCopy ? "copy" : void 0);
30
+ const leadingIcon = icon;
38
31
  const checkable = selectionIndicator === "checkbox" || selectionIndicator === "radio" || selectionIndicator === "check";
39
32
  const role = selectionIndicator === "checkbox" ? "menuitemcheckbox" : selectionIndicator === "radio" || selectionIndicator === "check" ? "menuitemradio" : "menuitem";
40
33
  const ariaChecked = !checkable ? void 0 : selectionIndicator === "checkbox" && indeterminate ? "mixed" : selected ? "true" : "false";
@@ -84,13 +77,6 @@ const MenuItem = ({
84
77
  "aria-haspopup": submenu ? "menu" : void 0,
85
78
  "aria-disabled": disabled ? "true" : void 0,
86
79
  onmenuselect: onSelect ? (e) => onSelect(e, { value, label }) : void 0,
87
- copy: copy != null ? copy : void 0,
88
- "copy-node": copyNode === true ? "" : typeof copyNode === "string" ? copyNode : void 0,
89
- "copy-url": copyUrl ? "" : void 0,
90
- "copy-with-url": copyWithUrl ? "" : void 0,
91
- "data-copy-node-button": copyNode != null && copyNode !== false ? "" : void 0,
92
- oncopydone: onCopied ? (e) => onCopied(nativeStateChange(e).detail?.ok ?? false) : void 0,
93
- oncopyrequest: onCopyRequest ? () => onCopyRequest() : void 0,
94
80
  class: className,
95
81
  ...rest,
96
82
  children: [
@@ -1,34 +1,37 @@
1
1
  import { type MenuItemCommonProps, type MenuItemActionMode } from './MenuItem';
2
- import type { CopyMode } from './Button';
3
- /** The action-mode menu-item props plus a required copy target (a copying row
4
- * must copy something). The target intersection drops `CopyMode`'s "no copy
5
- * prop" member. */
6
- export type MenuItemCopyProps = MenuItemCommonProps & MenuItemActionMode & CopyMode & ({
7
- copy: string;
8
- } | {
9
- copyNode: boolean | string;
10
- } | {
11
- copyUrl: true;
12
- });
2
+ import { type CopyTarget } from './copy-props';
3
+ /** Action-mode menu-item props plus a required copy target (a copying row must
4
+ * copy something). The action-mode props that don't fit a copy row are pinned to
5
+ * `never`: `submenu` (a copy row has no flyout) and the selection-indicator axis
6
+ * (`selectionIndicator` / `indeterminate` / `indicator` — a copy row isn't a
7
+ * checkable option). Pinning (not `Omit`) keeps the intersection expanding fully
8
+ * in the docs props table. */
9
+ export type MenuItemCopyProps = MenuItemCommonProps & MenuItemActionMode & {
10
+ submenu?: never;
11
+ selectionIndicator?: never;
12
+ indeterminate?: never;
13
+ indicator?: never;
14
+ } & CopyTarget;
13
15
  /**
14
16
  * Copying menu item — a `MenuItem` preset for copy-to-clipboard rows inside a
15
17
  * `Menu`. Set `copy` for a literal string, `copyNode` to copy a DOM region, or
16
18
  * `copyUrl` to copy the current page URL. For content computed on demand, keep
17
- * `copy` reactive and refresh it in `onCopyRequest` (fired on pointerdown). The
18
- * `<a-menu-item>` element performs the write when the row is chosen; this wrapper
19
+ * `copy` reactive and refresh it in `onCopyRequest` (fired on pointerdown / keydown).
20
+ *
21
+ * It composes a plain `<MenuItem>` with a slotted `<a-copy>` child that performs
22
+ * the write when the row is chosen — the item itself carries no copy behavior.
23
+ * The row is kept open (`data-menu-open`) so the feedback shows: this wrapper
19
24
  * flips the leading icon to a check / ✕ and retones for ~2s (see `useCopyFeedback`).
20
25
  *
21
- * The menu closes on select by default, so the feedback shows only when the row
22
- * is kept open (`data-menu-open`). Defaults `label` to "Copy" and the leading
23
- * `icon` to `copy`; override either. Other props pass through — `tone`, `kbd`,
24
- * `onCopied`.
26
+ * Defaults `label` to "Copy" and the leading `icon` to `copy`; override either.
27
+ * Other props pass through — `tone`, `kbd`, `onCopied`.
25
28
  *
26
29
  * @example
27
30
  * ```tsx
28
31
  * <Menu>
29
32
  * <MenuItemCopy copy={shareUrl} label="Copy link" kbd="⌘C" />
30
- * <MenuItemCopy copyNode=".invoice" label="Copy invoice" data-menu-open />
33
+ * <MenuItemCopy copyNode=".invoice" label="Copy invoice" />
31
34
  * </Menu>
32
35
  * ```
33
36
  */
34
- export declare const MenuItemCopy: ({ label, icon, tone, onCopied, ...rest }: MenuItemCopyProps) => any;
37
+ export declare const MenuItemCopy: ({ label, icon, tone, onCopied, onCopyRequest, copy, copyNode, copyUrl, copyWithUrl, children, ...rest }: MenuItemCopyProps) => any;
@@ -1,9 +1,44 @@
1
- import { jsx } from "@antadesign/anta/jsx-runtime";
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { MenuItem } from "./MenuItem";
3
3
  import { useCopyFeedback } from "../anta_helpers";
4
- const MenuItemCopy = ({ label = "Copy", icon = "copy", tone, onCopied, ...rest }) => {
4
+ import { copyElementProps, isNodeCopy } from "./copy-props";
5
+ const MenuItemCopy = ({
6
+ label = "Copy",
7
+ icon = "copy",
8
+ tone,
9
+ onCopied,
10
+ onCopyRequest,
11
+ copy,
12
+ copyNode,
13
+ copyUrl,
14
+ copyWithUrl,
15
+ children,
16
+ ...rest
17
+ }) => {
5
18
  const { shownIcon, shownTone, handleCopied } = useCopyFeedback(icon, tone, onCopied);
6
- return /* @__PURE__ */ jsx(MenuItem, { label, icon: shownIcon, tone: shownTone, onCopied: handleCopied, ...rest });
19
+ const copyAttrs = copyElementProps({
20
+ copy,
21
+ copyNode,
22
+ copyUrl,
23
+ copyWithUrl,
24
+ onCopyRequest,
25
+ onCopied: handleCopied
26
+ });
27
+ return /* @__PURE__ */ jsxs(
28
+ MenuItem,
29
+ {
30
+ label,
31
+ icon: shownIcon,
32
+ tone: shownTone,
33
+ "data-menu-open": "",
34
+ "data-copy-node-button": isNodeCopy({ copyNode }) ? "" : void 0,
35
+ ...rest,
36
+ children: [
37
+ children,
38
+ /* @__PURE__ */ jsx("a-copy", { ...copyAttrs })
39
+ ]
40
+ }
41
+ );
7
42
  };
8
43
  export {
9
44
  MenuItemCopy
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The copy target for a copy preset (`ButtonCopy` / `MenuItemCopy`) — exactly
3
+ * one of a literal string, a DOM region, or the page URL. The write itself is
4
+ * performed by the slotted `<a-copy>` element; these props map to its attributes
5
+ * via `copyElementProps`. `copyWithUrl` / `onCopyRequest` are only meaningful for
6
+ * the string target, so the union pins them `never` elsewhere.
7
+ */
8
+ export type CopyTarget = {
9
+ /** Text copied to the clipboard on activation. */
10
+ copy: string;
11
+ /** Prefix the copied text with `// URL: <current page URL>`. */
12
+ copyWithUrl?: boolean;
13
+ /** Compute the copy content lazily. Fires on pointerdown / keydown; update
14
+ * `copy` (a state change) here and the activation copies the latest value.
15
+ * The gap lets the update land even off the UI thread — only the
16
+ * serializable `copy` string crosses. */
17
+ onCopyRequest?: () => void;
18
+ /** Fires after the copy attempt with whether it succeeded. */
19
+ onCopied?: (ok: boolean) => void;
20
+ copyNode?: never;
21
+ copyUrl?: never;
22
+ } | {
23
+ /** Copy a DOM node as rich text (`text/html`) + plain text. `true` copies
24
+ * the nearest ancestor marked `data-copy-source`; a string is a CSS
25
+ * selector for an ancestor region (`closest`). The copy control is stripped
26
+ * from the copied output. */
27
+ copyNode: boolean | string;
28
+ /** Fires after the copy attempt with whether it succeeded. */
29
+ onCopied?: (ok: boolean) => void;
30
+ copy?: never;
31
+ copyUrl?: never;
32
+ copyWithUrl?: never;
33
+ onCopyRequest?: never;
34
+ } | {
35
+ /** Copy the current page URL (`location.href`). */
36
+ copyUrl: true;
37
+ /** Fires after the copy attempt with whether it succeeded. */
38
+ onCopied?: (ok: boolean) => void;
39
+ copy?: never;
40
+ copyNode?: never;
41
+ copyWithUrl?: never;
42
+ onCopyRequest?: never;
43
+ };
44
+ /** Everything `copyElementProps` reads: the target plus the resolved `onCopied`
45
+ * (the preset passes its feedback-wrapped handler) and the `toast` opt-in. */
46
+ type CopyElementInput = {
47
+ copy?: string;
48
+ copyNode?: boolean | string;
49
+ copyUrl?: true;
50
+ copyWithUrl?: boolean;
51
+ onCopyRequest?: () => void;
52
+ onCopied?: (ok: boolean) => void;
53
+ toast?: boolean;
54
+ };
55
+ /** Project the copy-target props onto `<a-copy>` attributes + event handlers.
56
+ * Shared by `ButtonCopy` / `MenuItemCopy` so the mapping (and the cross-renderer
57
+ * `copydone` detail unwrap) lives in one place. */
58
+ export declare function copyElementProps(p: CopyElementInput): {
59
+ readonly copy: string | undefined;
60
+ readonly 'copy-node': string | undefined;
61
+ readonly 'copy-url': "" | undefined;
62
+ readonly 'copy-with-url': "" | undefined;
63
+ readonly toast: "" | undefined;
64
+ readonly oncopydone: ((e: any) => void) | undefined;
65
+ readonly oncopyrequest: (() => void) | undefined;
66
+ };
67
+ /** True when these props copy a DOM node — the host control must then be marked
68
+ * `data-copy-node-button` so the serializer strips it from the copied region. */
69
+ export declare function isNodeCopy(p: {
70
+ copyNode?: boolean | string;
71
+ }): boolean;
72
+ export {};
@@ -0,0 +1,20 @@
1
+ import { nativeStateChange } from "../anta_helpers";
2
+ function copyElementProps(p) {
3
+ const { copy, copyNode, copyUrl, copyWithUrl, onCopyRequest, onCopied, toast } = p;
4
+ return {
5
+ copy: copy != null ? copy : void 0,
6
+ "copy-node": copyNode === true ? "" : typeof copyNode === "string" ? copyNode : void 0,
7
+ "copy-url": copyUrl ? "" : void 0,
8
+ "copy-with-url": copyWithUrl ? "" : void 0,
9
+ toast: toast ? "" : void 0,
10
+ oncopydone: onCopied ? (e) => onCopied(nativeStateChange(e).detail?.ok ?? false) : void 0,
11
+ oncopyrequest: onCopyRequest ? () => onCopyRequest() : void 0
12
+ };
13
+ }
14
+ function isNodeCopy(p) {
15
+ return p.copyNode != null && p.copyNode !== false;
16
+ }
17
+ export {
18
+ copyElementProps,
19
+ isNodeCopy
20
+ };