@antadesign/anta 0.3.10 → 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.
Files changed (37) hide show
  1. package/dist/components/Button.d.ts +4 -46
  2. package/dist/components/Button.js +7 -25
  3. package/dist/components/ButtonCopy.d.ts +45 -21
  4. package/dist/components/ButtonCopy.js +46 -4
  5. package/dist/components/Expander.js +1 -1
  6. package/dist/components/MenuItem.d.ts +2 -8
  7. package/dist/components/MenuItem.js +2 -14
  8. package/dist/components/MenuItemCopy.d.ts +23 -19
  9. package/dist/components/MenuItemCopy.js +38 -3
  10. package/dist/components/Select.d.ts +8 -0
  11. package/dist/components/Select.js +4 -0
  12. package/dist/components/SelectFaceted.d.ts +8 -0
  13. package/dist/components/SelectFaceted.js +4 -0
  14. package/dist/components/copy-props.d.ts +72 -0
  15. package/dist/components/copy-props.js +20 -0
  16. package/dist/elements/a-button.css +1 -1
  17. package/dist/elements/a-button.d.ts +0 -4
  18. package/dist/elements/a-button.js +7 -20
  19. package/dist/elements/a-copy.css +1 -0
  20. package/dist/elements/a-copy.d.ts +16 -0
  21. package/dist/elements/a-copy.js +148 -0
  22. package/dist/elements/a-expander.css +1 -1
  23. package/dist/elements/a-expander.js +20 -19
  24. package/dist/elements/a-menu-item.d.ts +8 -1
  25. package/dist/elements/a-menu-item.js +7 -11
  26. package/dist/elements/a-menu.d.ts +6 -2
  27. package/dist/elements/a-menu.js +35 -5
  28. package/dist/elements/copy-behavior.d.ts +31 -13
  29. package/dist/elements/copy-behavior.js +20 -18
  30. package/dist/elements/index.d.ts +1 -0
  31. package/dist/elements/index.js +3 -0
  32. package/dist/elements/key-activation.d.ts +28 -0
  33. package/dist/elements/key-activation.js +32 -0
  34. package/dist/general_types.d.ts +27 -37
  35. package/dist/index.d.ts +2 -1
  36. package/dist/jsx-runtime.d.ts +2 -1
  37. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Shared copy-to-clipboard behavior for `<a-button>` and `<a-menu-item>` — the
3
- * write half of the productized "copy button" / "copying menu item". The element
4
- * performs the clipboard write itself (rather than a UI-thread callback) and
5
- * reports the outcome with a `copydone` event; the JSX wrapper listens and
6
- * orchestrates the visual feedback (icon + tone swap). Nothing here touches the
2
+ * Copy-to-clipboard behavior for the `<a-copy>` element — the write half of the
3
+ * productized "copy button" / "copying menu item". `<a-copy>` (slotted inside a
4
+ * button or menu row) calls these on the host's activation: it performs the
5
+ * clipboard write itself (rather than a UI-thread callback) and reports the
6
+ * outcome with a `copydone` event; the JSX wrapper (`ButtonCopy` / `MenuItemCopy`)
7
+ * listens and orchestrates the visual feedback (icon + tone swap). Nothing here touches the
7
8
  * DOM — no light-DOM mutation, no host attributes, no ElementInternals.
8
9
  *
9
10
  * Events are dispatched non-bubbling (`bubbles: false`): the wrapper binds on the
@@ -11,23 +12,40 @@
11
12
  * nested copy row's `copydone` from flipping an ancestor copy row's feedback
12
13
  * (the same point-to-point rule Anta's `statechange` follows).
13
14
  *
14
- * Three modes, chosen by attribute:
15
+ * Modes, chosen by attribute:
15
16
  * - `copy="<text>"` — copy the literal string on activation.
17
+ * - `copy-url` — copy the current page URL (`location.href`); no `copy` needed.
18
+ * - `copy-with-url` — prefix the copied `copy` string with `// URL: <href>`.
16
19
  * - `copy-node` / `copy-node="<selector>"` — copy a DOM node as rich text
17
20
  * (`text/html`) + plain text. Bare `copy-node` copies the nearest ancestor
18
21
  * marked `[data-copy-source]`; a value is a selector resolved with `closest()`
19
22
  * (an ancestor region). The copy control is stripped from the serialized
20
23
  * output (`[data-copy-node-button]`).
21
- * - `copy-lazy` — the content isn't known until the click. Activation dispatches
22
- * `copyrequest` whose `detail.provide(text)` the consumer calls with the value
23
- * (synchronously, or after an `await` — the browser's transient-activation
24
- * window still covers the write). Nothing lands in the DOM, and there's no
25
- * attribute round-trip to reset. Takes precedence over any `copy` value.
24
+ *
25
+ * ## Lazy content (off-UI-thread safe)
26
+ *
27
+ * When the content isn't known ahead of time, the element emits a `copyrequest`
28
+ * event on **pointerdown** (`emitCopyRequest`, fired whenever a `copy` attribute
29
+ * is present). The consumer answers by setting the `copy` attribute to the
30
+ * freshly-computed value; the **click** then reads that value and writes it. The
31
+ * pointerdown→click gap absorbs the round-trip, so this works even when the
32
+ * consumer's handler runs off the UI thread (e.g. a worker-rendered app): only a
33
+ * serializable string crosses the boundary via the normal reconcile — no
34
+ * callback, and the write stays inside the click's transient activation. This is
35
+ * why there is no `provide`-function handoff and no `copy-lazy` marker.
26
36
  *
27
37
  * Precedence when several are set (the discriminated-union prop types make this
28
- * unreachable from the wrappers, but hand-authored markup can): node → lazy →
29
- * literal `copy`.
38
+ * unreachable from the wrappers, but hand-authored markup can): node → url →
39
+ * literal `copy` (with the optional URL prefix).
40
+ */
41
+ /**
42
+ * Ask a lazy consumer to refresh the copy content, on pointerdown — dispatch a
43
+ * non-bubbling, payload-free `copyrequest`. The consumer answers by setting the
44
+ * `copy` attribute to the freshly-computed value before the click reads it (see
45
+ * the "Lazy content" note above). Fires only for string-copy controls; node/url
46
+ * modes resolve themselves at click time and need no request. No-op otherwise.
30
47
  */
48
+ export declare function emitCopyRequest(el: HTMLElement): void;
31
49
  /**
32
50
  * Perform a copy on activation (click / Enter / Space / menu select). Returns
33
51
  * `true` if the element is a copy control (so the caller knows the copy path
@@ -37,32 +37,34 @@ function run(el, work) {
37
37
  el.dispatchEvent(new CustomEvent("copydone", { bubbles: false, detail: { ok } }));
38
38
  });
39
39
  }
40
+ function currentUrl() {
41
+ return typeof location !== "undefined" ? location.href : "";
42
+ }
43
+ function resolveText(el) {
44
+ if (el.hasAttribute("copy-url")) return currentUrl();
45
+ const base = el.getAttribute("copy") ?? "";
46
+ if (el.hasAttribute("copy-with-url")) return `// URL: ${currentUrl()}
47
+ ${base}`;
48
+ return base;
49
+ }
50
+ function isTextCopy(el) {
51
+ return el.hasAttribute("copy") || el.hasAttribute("copy-url");
52
+ }
53
+ function emitCopyRequest(el) {
54
+ if (!el.hasAttribute("copy")) return;
55
+ el.dispatchEvent(new CustomEvent("copyrequest", { bubbles: false }));
56
+ }
40
57
  function runCopy(el) {
41
58
  if (el.hasAttribute("copy-node")) {
42
59
  const target = resolveNodeTarget(el);
43
60
  run(el, target ? writeNode(target) : Promise.resolve(false));
44
61
  return true;
45
62
  }
46
- if (el.hasAttribute("copy-lazy")) {
47
- let done = false;
48
- el.dispatchEvent(
49
- new CustomEvent("copyrequest", {
50
- bubbles: false,
51
- detail: {
52
- provide(text) {
53
- if (done) return;
54
- done = true;
55
- run(el, writeText(String(text ?? "")));
56
- }
57
- }
58
- })
59
- );
60
- return true;
61
- }
62
- if (!el.hasAttribute("copy")) return false;
63
- run(el, writeText(el.getAttribute("copy") ?? ""));
63
+ if (!isTextCopy(el)) return false;
64
+ run(el, writeText(resolveText(el)));
64
65
  return true;
65
66
  }
66
67
  export {
68
+ emitCopyRequest,
67
69
  runCopy
68
70
  };
@@ -17,6 +17,7 @@ export { AProgressElement, register_a_progress } from './a-progress';
17
17
  export { ATextElement, register_a_text } from './a-text';
18
18
  export { AIconElement, register_a_icon } from './a-icon';
19
19
  export { AButtonElement, register_a_button } from './a-button';
20
+ export { ACopyElement, register_a_copy } from './a-copy';
20
21
  export { ACheckboxElement, register_a_checkbox } from './a-checkbox';
21
22
  export { AExpanderElement, register_a_expander } from './a-expander';
22
23
  export { ATooltipElement, register_a_tooltip } from './a-tooltip';
@@ -2,6 +2,7 @@ import { AProgressElement, register_a_progress } from "./a-progress";
2
2
  import { ATextElement, register_a_text } from "./a-text";
3
3
  import { AIconElement, register_a_icon } from "./a-icon";
4
4
  import { AButtonElement, register_a_button } from "./a-button";
5
+ import { ACopyElement, register_a_copy } from "./a-copy";
5
6
  import { ACheckboxElement, register_a_checkbox } from "./a-checkbox";
6
7
  import { AExpanderElement, register_a_expander } from "./a-expander";
7
8
  import { ATooltipElement, register_a_tooltip } from "./a-tooltip";
@@ -26,6 +27,7 @@ export {
26
27
  ACalendarElement,
27
28
  ACardElement,
28
29
  ACheckboxElement,
30
+ ACopyElement,
29
31
  ADialogElement,
30
32
  AExpanderElement,
31
33
  AIconElement,
@@ -47,6 +49,7 @@ export {
47
49
  register_a_calendar,
48
50
  register_a_card,
49
51
  register_a_checkbox,
52
+ register_a_copy,
50
53
  register_a_dialog,
51
54
  register_a_expander,
52
55
  register_a_icon,
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared "activate on key release" for Enter / Space, used by `<a-button>` and
3
+ * `<a-menu-item>` (via one delegated keydown/keyup pair per document).
4
+ *
5
+ * Activation fires on **keyup**, not keydown. This matches the native `<button>`
6
+ * (Space already activates on release), makes activation **cancelable** (move
7
+ * focus between press and release → no fire), and opens a **keydown→keyup gap**.
8
+ * A slotted `<a-copy>` uses that gap for its own lazy pre-request (it runs its
9
+ * own keydown listener); this module just synthesizes the click on release.
10
+ *
11
+ * keydown: `preventDefault()` (stop Space scrolling the page and a link's own
12
+ * native Enter→click) and arm the release. keyup: `activate(el)` only if the
13
+ * same element is still armed.
14
+ *
15
+ * Idempotency is the caller's job (each guards with its own per-document flag);
16
+ * this just wires one keydown/keyup pair.
17
+ */
18
+ export interface KeyActivation {
19
+ /** Keys that activate — `['Enter', ' ']` for buttons/menu items. */
20
+ keys: readonly string[];
21
+ /** Resolve the activatable element from the event target (e.g. `closest`). */
22
+ resolve(target: EventTarget | null): HTMLElement | null;
23
+ /** Blocked (disabled / loading) — swallow the key without activating. */
24
+ blocked?(el: HTMLElement): boolean;
25
+ /** Runs on keyup: the actual activation (a synthesized `click()`). */
26
+ activate(el: HTMLElement): void;
27
+ }
28
+ export declare function installKeyActivation(doc: Document, config: KeyActivation): void;
@@ -0,0 +1,32 @@
1
+ function installKeyActivation(doc, config) {
2
+ let armed = null;
3
+ doc.addEventListener(
4
+ "keydown",
5
+ (e) => {
6
+ if (e.repeat || !config.keys.includes(e.key)) return;
7
+ const el = config.resolve(e.target);
8
+ if (!el) return;
9
+ e.preventDefault();
10
+ armed = config.blocked?.(el) ? null : el;
11
+ },
12
+ true
13
+ );
14
+ doc.addEventListener(
15
+ "keyup",
16
+ (e) => {
17
+ if (!config.keys.includes(e.key)) return;
18
+ const el = config.resolve(e.target);
19
+ if (!el || el !== armed) {
20
+ armed = null;
21
+ return;
22
+ }
23
+ armed = null;
24
+ if (config.blocked?.(el)) return;
25
+ config.activate(el);
26
+ },
27
+ true
28
+ );
29
+ }
30
+ export {
31
+ installKeyActivation
32
+ };
@@ -727,26 +727,6 @@ export interface AMenuItemAttributes extends BaseAttributes {
727
727
  * a `MouseEvent`, carrying the modifier keys (e.g. `altKey`). All-lowercase so
728
728
  * React/Preact bind it to the CustomEvent. */
729
729
  onmenuselect?: (e: MouseEvent) => void;
730
- /** Text copied to the clipboard when the row is chosen (a "copying menu item").
731
- * The element performs the write itself on `menuselect`. */
732
- copy?: string;
733
- /** Copy a DOM node instead of a string (rich `text/html` + plain text). Bare
734
- * `copy-node` copies the nearest ancestor marked `[data-copy-source]`; a value
735
- * is a CSS selector resolved with `closest()`. Presence-based for the boolean
736
- * form. */
737
- 'copy-node'?: boolean | string;
738
- /** Lazy copy: with an empty `copy`, activation dispatches `copyrequest` and the
739
- * write completes once `copy` is set back (within the activation window).
740
- * Presence-based. */
741
- 'copy-lazy'?: boolean | '';
742
- /** Fired after a copy attempt (bubbling `copydone`, `detail: { ok }`).
743
- * All-lowercase so it binds in React *and* Preact. */
744
- oncopydone?: (e: CustomEvent<{
745
- ok: boolean;
746
- }>) => void;
747
- /** Fired on a lazy copy activation to request the content (bubbling
748
- * `copyrequest`). Answer by setting the `copy` attribute. All-lowercase. */
749
- oncopyrequest?: (e: CustomEvent) => void;
750
730
  }
751
731
  /**
752
732
  * Attributes for the `<a-menu-group>` styled element. For the typed JSX
@@ -790,32 +770,42 @@ export interface AButtonAttributes extends BaseAttributes {
790
770
  form?: string;
791
771
  /** Custom event name dispatched (bubbling) on click. */
792
772
  'data-custom-event'?: string;
793
- /** Text copied to the clipboard on activation (the "copy button"). The element
794
- * performs the write itself and flashes a success / failure state. */
773
+ 'aria-disabled'?: 'true' | 'false' | boolean;
774
+ 'aria-busy'?: 'true' | 'false' | boolean;
775
+ }
776
+ /** `<a-copy>` — the copy-to-clipboard behavior, slotted inside an activatable
777
+ * control (`<a-button>`, `<a-menu-item>`, native `button`/`[role]`). It owns
778
+ * the whole feature; the base controls carry no copy knowledge. `ButtonCopy` /
779
+ * `MenuItemCopy` are the presets that drop it in. */
780
+ export interface ACopyAttributes extends BaseAttributes {
781
+ /** Text copied to the clipboard when the host control is activated. */
795
782
  copy?: string;
796
783
  /** Copy a DOM node instead of a string (rich `text/html` + plain text). Bare
797
784
  * `copy-node` copies the nearest ancestor marked `[data-copy-source]`; a value
798
- * is a CSS selector resolved with `closest()` (an ancestor region). Presence-
799
- * based for the boolean form. */
785
+ * is a CSS selector resolved with `closest()` (an ancestor region). The copy
786
+ * control is stripped from the serialized output. Presence-based for the
787
+ * boolean form. */
800
788
  'copy-node'?: boolean | string;
801
- /** Lazy copy: activation dispatches `copyrequest`; the consumer calls
802
- * `detail.provide(text)` with the content, which stays out of the DOM until
803
- * then. Takes precedence over `copy`. Presence-based. */
804
- 'copy-lazy'?: boolean | '';
789
+ /** Copy the current page URL (`location.href`) instead of a `copy` string; no
790
+ * `copy` needed. Presence-based. */
791
+ 'copy-url'?: boolean | '';
792
+ /** Prefix the copied `copy` string with `// URL: <href>`. Presence-based. */
793
+ 'copy-with-url'?: boolean | '';
794
+ /** Float a ghost of the host's content upward as visual feedback on a
795
+ * successful copy (rendered in the top layer so the host's overflow can't clip
796
+ * it). Presence-based. Opt-in — `ButtonCopy` sets it. */
797
+ toast?: boolean | '';
805
798
  /** Fired after a copy attempt (`copydone`, `detail: { ok }`, non-bubbling so a
806
- * nested copy row can't flip an ancestor's feedback). All-lowercase so it
799
+ * nested copy control can't flip an ancestor's feedback). All-lowercase so it
807
800
  * binds in React *and* Preact. */
808
801
  oncopydone?: (e: CustomEvent<{
809
802
  ok: boolean;
810
803
  }>) => void;
811
- /** Fired on a lazy copy activation (`copyrequest`, non-bubbling). Supply the
812
- * content by calling `detail.provide(text)` — synchronously or within the
813
- * click's activation window. All-lowercase. */
814
- oncopyrequest?: (e: CustomEvent<{
815
- provide: (text: string) => void;
816
- }>) => void;
817
- 'aria-disabled'?: 'true' | 'false' | boolean;
818
- 'aria-busy'?: 'true' | 'false' | boolean;
804
+ /** Fired on **pointerdown** / keydown for a string-copy control (`copyrequest`,
805
+ * non-bubbling). Answer by setting the `copy` attribute to the freshly-computed
806
+ * value; the activation then copies it. The gap lets an off-UI-thread handler
807
+ * set `copy` in time. All-lowercase. */
808
+ oncopyrequest?: (e: CustomEvent) => void;
819
809
  }
820
810
  /**
821
811
  * Attributes for the `<a-radio>` custom element — one option in a radio set.
package/dist/index.d.ts CHANGED
@@ -27,9 +27,10 @@ export type { TagProps } from './components/Tag';
27
27
  export { Icon } from './components/Icon';
28
28
  export type { IconProps } from './components/Icon';
29
29
  export { Button } from './components/Button';
30
- export type { ButtonProps, BaseButtonProps, ContentMode, SubmitMode, PriorityMode, CopyMode, } from './components/Button';
30
+ export type { ButtonProps, BaseButtonProps, ContentMode, SubmitMode, PriorityMode, } from './components/Button';
31
31
  export { ButtonCopy } from './components/ButtonCopy';
32
32
  export type { ButtonCopyProps } from './components/ButtonCopy';
33
+ export type { CopyTarget } from './components/copy-props';
33
34
  export { ICON_SHAPES, ICON_SYNONYMS } from './elements/a-icon.shapes';
34
35
  export { Tooltip } from './components/Tooltip';
35
36
  export type { TooltipProps } from './components/Tooltip';
@@ -41,7 +41,7 @@ export declare function useMemo<T>(factory: () => T, deps: unknown[]): T;
41
41
  export declare function jsx(type: ComponentType, props: Record<string, unknown> | null, key?: string | number): unknown;
42
42
  export declare function jsxs(type: ComponentType, props: Record<string, unknown> | null, key?: string | number): unknown;
43
43
  export { _Fragment as Fragment };
44
- import type { AProgressAttributes, ATextAttributes, ATitleAttributes, ATagAttributes, AExpanderAttributes, AIconAttributes, AButtonAttributes, ACheckboxAttributes, ATooltipAttributes, AInputAttributes, AInputTimeAttributes, ACalendarAttributes, ARadioAttributes, ARadioGroupAttributes, AMenuAttributes, AMenuItemAttributes, AMenuGroupAttributes, ATabsAttributes, ATabAttributes, ATabpanelAttributes, ADialogAttributes, ACardAttributes, BaseAttributes } from './general_types';
44
+ import type { AProgressAttributes, ATextAttributes, ATitleAttributes, ATagAttributes, AExpanderAttributes, AIconAttributes, AButtonAttributes, ACopyAttributes, ACheckboxAttributes, ATooltipAttributes, AInputAttributes, AInputTimeAttributes, ACalendarAttributes, ARadioAttributes, ARadioGroupAttributes, AMenuAttributes, AMenuItemAttributes, AMenuGroupAttributes, ATabsAttributes, ATabAttributes, ATabpanelAttributes, ADialogAttributes, ACardAttributes, BaseAttributes } from './general_types';
45
45
  export declare namespace JSX {
46
46
  interface IntrinsicElements extends React.JSX.IntrinsicElements, AntaIntrinsicElements {
47
47
  }
@@ -65,6 +65,7 @@ export interface AntaIntrinsicElements {
65
65
  'a-icon': AIconAttributes;
66
66
  'a-button': AButtonAttributes;
67
67
  'a-button-label': BaseAttributes;
68
+ 'a-copy': ACopyAttributes;
68
69
  'a-checkbox': ACheckboxAttributes;
69
70
  'a-checkbox-label': BaseAttributes;
70
71
  'a-checkbox-hint': BaseAttributes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antadesign/anta",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",