@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.
- package/dist/components/Button.d.ts +5 -62
- package/dist/components/Button.js +7 -27
- package/dist/components/ButtonCopy.d.ts +43 -25
- package/dist/components/ButtonCopy.js +46 -4
- package/dist/components/Expander.js +1 -1
- package/dist/components/MenuItem.d.ts +2 -9
- package/dist/components/MenuItem.js +2 -16
- package/dist/components/MenuItemCopy.d.ts +22 -19
- package/dist/components/MenuItemCopy.js +38 -3
- package/dist/components/copy-props.d.ts +72 -0
- package/dist/components/copy-props.js +20 -0
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +0 -7
- package/dist/elements/a-button.js +0 -23
- package/dist/elements/a-copy.css +1 -0
- package/dist/elements/a-copy.d.ts +16 -0
- package/dist/elements/a-copy.js +148 -0
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.js +0 -18
- package/dist/elements/a-menu-item.d.ts +6 -6
- package/dist/elements/a-menu-item.js +0 -4
- package/dist/elements/a-menu.js +2 -3
- package/dist/elements/copy-behavior.d.ts +6 -5
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/index.js +3 -0
- package/dist/elements/key-activation.d.ts +5 -9
- package/dist/elements/key-activation.js +0 -1
- package/dist/general_types.d.ts +20 -32
- package/dist/index.d.ts +2 -1
- package/dist/jsx-runtime.d.ts +2 -1
- package/package.json +1 -1
|
@@ -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.
|
|
59
|
-
*
|
|
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
|
-
|
|
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
|
-
}
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
"aria-label": isIconOnly ?
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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).
|
|
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
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* (
|
|
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" />
|
|
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" />
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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",
|
|
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 |
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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).
|
|
18
|
-
*
|
|
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
|
-
*
|
|
22
|
-
*
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|