@antadesign/anta 0.2.2 → 0.3.1
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 +14 -0
- package/dist/anta_helpers.d.ts +39 -1
- package/dist/anta_helpers.js +30 -2
- package/dist/components/Button.d.ts +7 -4
- package/dist/components/Button.js +6 -11
- package/dist/components/Checkbox.d.ts +97 -0
- package/dist/components/Checkbox.js +77 -0
- package/dist/components/Expander.d.ts +74 -0
- package/dist/components/Expander.js +53 -0
- package/dist/components/Input.d.ts +159 -0
- package/dist/components/Input.js +150 -0
- package/dist/components/Menu.d.ts +73 -0
- package/dist/components/Menu.js +42 -0
- package/dist/components/MenuGroup.d.ts +24 -0
- package/dist/components/MenuGroup.js +19 -0
- package/dist/components/MenuItem.d.ts +61 -0
- package/dist/components/MenuItem.js +50 -0
- package/dist/components/MenuSeparator.d.ts +14 -0
- package/dist/components/MenuSeparator.js +7 -0
- package/dist/components/Progress.d.ts +12 -6
- package/dist/components/Progress.js +7 -4
- package/dist/components/Radio.d.ts +37 -0
- package/dist/components/Radio.js +33 -0
- package/dist/components/RadioGroup.d.ts +119 -0
- package/dist/components/RadioGroup.js +108 -0
- package/dist/components/Tag.d.ts +38 -5
- package/dist/components/Tag.js +9 -5
- package/dist/components/Text.d.ts +27 -12
- package/dist/components/Text.js +6 -3
- package/dist/components/Title.d.ts +10 -1
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-button.d.ts +56 -0
- package/dist/elements/a-button.js +13 -11
- package/dist/elements/a-checkbox.css +1 -0
- package/dist/elements/a-checkbox.d.ts +52 -0
- package/dist/elements/a-checkbox.js +130 -0
- package/dist/elements/a-expander.css +1 -0
- package/dist/elements/a-expander.d.ts +28 -0
- package/dist/elements/a-expander.js +237 -0
- package/dist/elements/a-icon.d.ts +14 -0
- package/dist/elements/a-icon.shapes.css +1 -1
- package/dist/elements/a-icon.shapes.d.ts +10 -1
- package/dist/elements/a-icon.shapes.js +11 -1
- package/dist/elements/a-input.css +1 -0
- package/dist/elements/a-input.d.ts +68 -0
- package/dist/elements/a-input.js +511 -0
- package/dist/elements/a-menu-group.css +1 -0
- package/dist/elements/a-menu-group.d.ts +13 -0
- package/dist/elements/a-menu-group.js +15 -0
- package/dist/elements/a-menu-item.css +1 -0
- package/dist/elements/a-menu-item.d.ts +47 -0
- package/dist/elements/a-menu-item.js +30 -0
- package/dist/elements/a-menu-separator.css +1 -0
- package/dist/elements/a-menu-separator.d.ts +13 -0
- package/dist/elements/a-menu-separator.js +15 -0
- package/dist/elements/a-menu.css +1 -0
- package/dist/elements/a-menu.d.ts +183 -0
- package/dist/elements/a-menu.js +763 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-progress.d.ts +12 -0
- package/dist/elements/a-progress.js +1 -0
- package/dist/elements/a-radio-group.css +1 -0
- package/dist/elements/a-radio-group.d.ts +33 -0
- package/dist/elements/a-radio-group.js +160 -0
- package/dist/elements/a-radio.css +1 -0
- package/dist/elements/a-radio.d.ts +14 -0
- package/dist/elements/a-radio.js +46 -0
- package/dist/elements/a-tag.css +1 -1
- package/dist/elements/a-text.css +1 -1
- package/dist/elements/a-text.d.ts +42 -3
- package/dist/elements/a-text.js +73 -33
- package/dist/elements/a-tooltip.d.ts +43 -11
- package/dist/elements/a-tooltip.js +46 -51
- package/dist/elements/index.d.ts +9 -0
- package/dist/elements/index.js +27 -0
- package/dist/general_types.d.ts +468 -15
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/jsx-runtime.d.ts +42 -7
- package/dist/jsx-runtime.js +14 -2
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { HTMLElementBase } from "../anta_helpers";
|
|
2
2
|
import "./a-button.css";
|
|
3
|
+
function installDocumentHandlers(doc) {
|
|
4
|
+
if (!doc || doc.hasKeyListenerForAButton) return;
|
|
5
|
+
doc.addEventListener("keydown", handleKeyDown, true);
|
|
6
|
+
doc.addEventListener("click", handleClick, true);
|
|
7
|
+
doc.hasKeyListenerForAButton = true;
|
|
8
|
+
}
|
|
3
9
|
class AButtonElement extends HTMLElementBase {
|
|
4
10
|
connectedCallback() {
|
|
5
|
-
|
|
6
|
-
document.addEventListener("keydown", handleKeyDown, true);
|
|
7
|
-
document.addEventListener("click", handleClick, true);
|
|
8
|
-
document.hasKeyListenerForAButton = true;
|
|
9
|
-
}
|
|
11
|
+
installDocumentHandlers(this.doc);
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
function findForm(el) {
|
|
13
15
|
const formId = el.getAttribute("form");
|
|
14
16
|
if (formId) {
|
|
15
|
-
return
|
|
17
|
+
return el.ownerDocument.getElementById(formId);
|
|
16
18
|
}
|
|
17
19
|
return el.closest("form");
|
|
18
20
|
}
|
|
19
21
|
function handleKeyDown(e) {
|
|
20
22
|
if (e.key !== "Enter" && e.key !== " ") return;
|
|
23
|
+
if (e.repeat) return;
|
|
21
24
|
const el = e.target.closest(
|
|
22
25
|
"a-button"
|
|
23
26
|
);
|
|
24
27
|
if (!el) return;
|
|
28
|
+
if (el.hasAttribute("disabled") || el.hasAttribute("loading")) return;
|
|
25
29
|
e.preventDefault();
|
|
26
30
|
el.click();
|
|
27
31
|
}
|
|
@@ -30,6 +34,7 @@ function handleClick(e) {
|
|
|
30
34
|
"a-button"
|
|
31
35
|
);
|
|
32
36
|
if (!el) return;
|
|
37
|
+
if (el.hasAttribute("disabled") || el.hasAttribute("loading")) return;
|
|
33
38
|
const customEvent = el.getAttribute("data-custom-event");
|
|
34
39
|
if (customEvent) {
|
|
35
40
|
el.dispatchEvent(
|
|
@@ -40,11 +45,7 @@ function handleClick(e) {
|
|
|
40
45
|
const form = findForm(el);
|
|
41
46
|
if (!form) return;
|
|
42
47
|
if (type === "submit") {
|
|
43
|
-
|
|
44
|
-
form.requestSubmit();
|
|
45
|
-
} else {
|
|
46
|
-
form.submit();
|
|
47
|
-
}
|
|
48
|
+
form.requestSubmit();
|
|
48
49
|
const formData = new FormData(form);
|
|
49
50
|
form.dispatchEvent(
|
|
50
51
|
new CustomEvent("submitdetailed", {
|
|
@@ -67,6 +68,7 @@ function handleClick(e) {
|
|
|
67
68
|
}
|
|
68
69
|
function register_a_button() {
|
|
69
70
|
if (typeof customElements === "undefined") return;
|
|
71
|
+
installDocumentHandlers(typeof document !== "undefined" ? document : void 0);
|
|
70
72
|
if (!customElements.get("a-button")) {
|
|
71
73
|
customElements.define("a-button", AButtonElement);
|
|
72
74
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer anta{a-checkbox{--checkbox-gap: 8px;--checkbox-bg: var(--bg-1);--checkbox-fill: #635b65;--checkbox-fill-hover: #534c57;--checkbox-fill-active: #49424c;--checkbox-border-neutral: #d4ced4;--checkbox-border: var(--checkbox-border-neutral);--checkbox-border-hover: #c1b9c1;--checkbox-border-active: #9f99a1;--checkbox-icon: #ffffff;--checkbox-icon-disabled: #c1b9c1;--checkbox-bg-disabled: color-mix(in oklch, #44374b 10%, transparent);--checkbox-border-disabled: color-mix(in oklch, #44374b 15%, transparent);--checkbox-mask-size: 12px;--checkbox-mask-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");--checkbox-mask-indeterminate: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");--checkbox-label-color: var(--text-2);--checkbox-label-color-disabled: var(--text-4);--checkbox-hint-color: var(--text-4);--checkbox-label-fs: 15px;--checkbox-label-lh: 20px;--checkbox-hint-fs: 13px;--checkbox-hint-lh: 16px;display:inline-grid;grid-template-columns:auto 1fr;align-items:center;column-gap:var(--checkbox-gap);vertical-align:middle;cursor:pointer;user-select:none;-webkit-user-drag:none;outline:none;color:var(--checkbox-label-color);font-family:var(--sans-serif);font-size:var(--checkbox-label-fs);line-height:var(--checkbox-label-lh);font-feature-settings:"ss02","ss05";&:before{content:"";grid-column:1;grid-row:1 / -1;align-self:center;box-sizing:border-box;inline-size:16px;block-size:16px;border-radius:4px;background:var(--checkbox-bg);border:1.5px solid var(--checkbox-border);transition:background-color .15s ease-out,border-color .15s ease-out}&:after{content:"";grid-column:1;grid-row:1 / -1;align-self:center;inline-size:16px;block-size:16px;background-color:transparent;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:var(--checkbox-mask-size);mask-size:var(--checkbox-mask-size)}@media(hover:hover)and (pointer:fine){&:not(:disabled):hover:before{border-color:var(--checkbox-border-hover);transition:background-color 75ms ease-in,border-color 75ms ease-in}&:not(:disabled):hover:is(:state(checked),:state(indeterminate)):before{background:var(--checkbox-fill-hover);border-color:var(--checkbox-fill-hover)}}&:not(:disabled):active:before{border-color:var(--checkbox-border-active);transition:background-color 50ms linear,border-color 50ms linear}&:not(:disabled):active:is(:state(checked),:state(indeterminate)):before{background:var(--checkbox-fill-active);border-color:var(--checkbox-fill-active)}&:is(:state(checked),:state(indeterminate)){&:before{background:var(--checkbox-fill);border-color:var(--checkbox-fill)}&:after{background-color:var(--checkbox-icon)}}&:state(checked):after{-webkit-mask-image:var(--checkbox-mask-check);mask-image:var(--checkbox-mask-check)}&:state(indeterminate):after{-webkit-mask-image:var(--checkbox-mask-indeterminate);mask-image:var(--checkbox-mask-indeterminate)}&:focus-visible:before{outline:1px solid var(--focus-ring);outline-offset:1px}&:disabled{cursor:not-allowed;color:var(--checkbox-label-color-disabled);&:before{background:var(--checkbox-bg-disabled);border-color:var(--checkbox-border-disabled);transition:none}&:is(:state(checked),:state(indeterminate)):after{background-color:var(--checkbox-icon-disabled)}a-checkbox-hint{color:var(--checkbox-label-color-disabled)}}&[size=small]{--checkbox-gap: 6px;--checkbox-label-fs: 13px;--checkbox-label-lh: 16px;--checkbox-hint-fs: 12px;--checkbox-hint-lh: 14px;--checkbox-mask-size: 10px;&:before,&:after{inline-size:14px;block-size:14px}}&[size=large]{--checkbox-label-fs: 17px;--checkbox-label-lh: 24px;--checkbox-hint-fs: 15px;--checkbox-hint-lh: 19px;--checkbox-mask-size: 14px;&:before,&:after{inline-size:18px;block-size:18px}}}a-checkbox[priority=secondary]{&:not(:disabled):is(:state(checked),:state(indeterminate)){&:before{background:var(--checkbox-bg);border-color:var(--checkbox-fill)}&:after{background-color:var(--checkbox-fill)}}@media(hover:hover)and (pointer:fine){&:not(:disabled):hover:is(:state(checked),:state(indeterminate)){&:before{background:var(--checkbox-bg);border-color:var(--checkbox-fill-hover)}&:after{background-color:var(--checkbox-fill-hover)}}}&:not(:disabled):active:is(:state(checked),:state(indeterminate)){&:before{background:var(--checkbox-bg);border-color:var(--checkbox-fill-active)}&:after{background-color:var(--checkbox-fill-active)}}}.dark a-checkbox{--checkbox-mask-size: 13px}.dark a-checkbox[size=small]{--checkbox-mask-size: 11px}.dark a-checkbox[size=large]{--checkbox-mask-size: 15px}.dark a-checkbox[priority=secondary]{--checkbox-mask-size: 14px;&[size=small]{--checkbox-mask-size: 12px}&[size=large]{--checkbox-mask-size: 16px}&:not(:disabled):is(:state(checked),:state(indeterminate)){&:before{border-color:var(--checkbox-fill-active)}&:after{background-color:var(--checkbox-fill-active)}}@media(hover:hover)and (pointer:fine){&:not(:disabled):hover:is(:state(checked),:state(indeterminate)){&:before{border-color:oklch(from var(--checkbox-fill-active) calc(l + .07) c h)}&:after{background-color:oklch(from var(--checkbox-fill-active) calc(l + .07) c h)}}}&:not(:disabled):active:is(:state(checked),:state(indeterminate)){&:before{border-color:oklch(from var(--checkbox-fill-active) calc(l + .12) c h)}&:after{background-color:oklch(from var(--checkbox-fill-active) calc(l + .12) c h)}}}a-checkbox-label,a-checkbox-hint{display:block;grid-column:2;min-width:0}a-checkbox-hint{color:var(--checkbox-hint-color);font-size:var(--checkbox-hint-fs);line-height:var(--checkbox-hint-lh);margin-block-start:2px}a-checkbox[tone=brand]{--checkbox-label-color: var(--text-2-brand);--checkbox-hint-color: var(--text-4-brand);--checkbox-fill: #5f4bc3;--checkbox-fill-hover: #503cb4;--checkbox-fill-active: #483493}a-checkbox[tone=neutral]{--checkbox-fill: #635b65;--checkbox-fill-hover: #534c57;--checkbox-fill-active: #49424c}a-checkbox[tone=info]{--checkbox-label-color: var(--text-2-info);--checkbox-hint-color: var(--text-4-info);--checkbox-fill: #1f6eb2;--checkbox-fill-hover: #1a5b93;--checkbox-fill-active: #175082}a-checkbox[tone=success]{--checkbox-label-color: var(--text-2-success);--checkbox-hint-color: var(--text-4-success);--checkbox-fill: #2a7e43;--checkbox-fill-hover: #226737;--checkbox-fill-active: #1f5c31}a-checkbox[tone=warning]{--checkbox-label-color: var(--text-2-warning);--checkbox-hint-color: var(--text-4-warning);--checkbox-fill: #c37416;--checkbox-fill-hover: #ae6613;--checkbox-fill-active: #995200}a-checkbox[tone=critical]{--checkbox-label-color: var(--text-2-critical);--checkbox-hint-color: var(--text-4-critical);--checkbox-fill: #c9302c;--checkbox-fill-hover: #b02120;--checkbox-fill-active: #a01c1c}a-checkbox[tone]:not([tone=""],[tone=neutral]){--_checkbox-border-base: color-mix(in oklch, var(--checkbox-fill) 70%, var(--checkbox-border-neutral));--checkbox-border: oklch(from var(--_checkbox-border-base) calc(l + .17) c h);--checkbox-border-hover: oklch(from var(--_checkbox-border-base) calc(l + .1) c h);--checkbox-border-active: var(--_checkbox-border-base);.dark &{--checkbox-border: var(--_checkbox-border-base);--checkbox-border-hover: oklch(from var(--_checkbox-border-base) calc(l + .1) c h);--checkbox-border-active: oklch(from var(--_checkbox-border-base) calc(l + .17) c h)}}a-checkbox[tone]:not([tone=""],[tone=brand],[tone=neutral],[tone=info],[tone=success],[tone=warning],[tone=critical]){--checkbox-tone-source: attr(tone type(<color>), transparent);--_tone-l-rest: .5;--_tone-l-hover: .45;--_tone-l-active: .4;--checkbox-fill: oklch(from var(--checkbox-tone-source) var(--_tone-l-rest) c h);--checkbox-fill-hover: oklch(from var(--checkbox-tone-source) var(--_tone-l-hover) c h);--checkbox-fill-active: oklch(from var(--checkbox-tone-source) var(--_tone-l-active) c h);--checkbox-label-color: oklch(from var(--checkbox-tone-source) .5 c h);--checkbox-hint-color: oklch(from var(--checkbox-tone-source) .5 c h / .7);.dark &{--_tone-l-rest: .45;--_tone-l-hover: .5;--_tone-l-active: .57;--checkbox-label-color: oklch(from var(--checkbox-tone-source) .8 c h);--checkbox-hint-color: oklch(from var(--checkbox-tone-source) .8 c h / .7)}}.dark a-checkbox{--checkbox-border-neutral: #49424c;--checkbox-border-hover: #635b65;--checkbox-border-active: #776e77;--checkbox-fill: #534c57;--checkbox-fill-hover: #635b65;--checkbox-fill-active: #938d96;--checkbox-icon-disabled: #635b65;--checkbox-bg-disabled: color-mix(in oklch, #e4d1ef 10%, transparent);--checkbox-border-disabled: color-mix(in oklch, #e4d1ef 15%, transparent)}.dark a-checkbox[tone=brand]{--checkbox-fill: #503cb4;--checkbox-fill-hover: #5f4bc3;--checkbox-fill-active: #7460d7}.dark a-checkbox[tone=neutral]{--checkbox-fill: #534c57;--checkbox-fill-hover: #635b65;--checkbox-fill-active: #938d96}.dark a-checkbox[tone=info]{--checkbox-fill: #1a5b93;--checkbox-fill-hover: #1f6eb2;--checkbox-fill-active: #2686d9}.dark a-checkbox[tone=success]{--checkbox-fill: #226737;--checkbox-fill-hover: #2a7e43;--checkbox-fill-active: #329550}.dark a-checkbox[tone=warning]{--checkbox-fill: #7f410b;--checkbox-fill-hover: #995200;--checkbox-fill-active: #ae6613}.dark a-checkbox[tone=critical]{--checkbox-fill: #b02120;--checkbox-fill-hover: #c9302c;--checkbox-fill-active: #de4545}}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HTMLElementBase } from "../anta_helpers";
|
|
2
|
+
import "./a-checkbox.css";
|
|
3
|
+
/**
|
|
4
|
+
* `<a-checkbox>` — interactive light-DOM checkbox. Visual state lives on
|
|
5
|
+
* `ElementInternals` (CSS keys off `:state(checked)` / `:state(indeterminate)`),
|
|
6
|
+
* never on host attributes. The element **publishes `aria-checked` itself**, via
|
|
7
|
+
* `ElementInternals` (off the DOM), so it stays live as the element self-toggles
|
|
8
|
+
* uncontrolled — the wrapper does NOT set `aria-checked`. Other ARIA (`role`,
|
|
9
|
+
* `aria-label`) is still the wrapper's / consumer's job.
|
|
10
|
+
*
|
|
11
|
+
* One ternary state — `checked` / `unchecked` / `indeterminate` — carried by two
|
|
12
|
+
* attributes: `state` is the **controlled** live value (the element reflects
|
|
13
|
+
* changes to it), `default-state` is the **uncontrolled** seed (read at connect /
|
|
14
|
+
* form-reset, and re-applied if it changes while the checkbox is still uncontrolled
|
|
15
|
+
* and un-toggled — matching a native `<input>`, whose `checked` attribute reflects
|
|
16
|
+
* until the control is "dirtied" by interaction; once toggled, later `default-state`
|
|
17
|
+
* changes are ignored). No "controlled" flag — the attribute name carries the intent
|
|
18
|
+
* (`controlled ≡ hasAttribute('state')`).
|
|
19
|
+
*
|
|
20
|
+
* Follows the shared state contract (see `STATEFUL-COMPONENTS.md`): on
|
|
21
|
+
* interaction the element fires a cancelable `statechange` *before* applying.
|
|
22
|
+
* Controlled callers re-assert `state` from their store; uncontrolled callers
|
|
23
|
+
* can veto a transition synchronously with `event.preventDefault()`. No
|
|
24
|
+
* optimistic self-paint.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ACheckboxElement extends HTMLElementBase {
|
|
27
|
+
static formAssociated: boolean;
|
|
28
|
+
static observedAttributes: string[];
|
|
29
|
+
private internals?;
|
|
30
|
+
private currentState;
|
|
31
|
+
private seeded;
|
|
32
|
+
private dirty;
|
|
33
|
+
private alive;
|
|
34
|
+
/** Current checked state as a boolean (native-`<input>`-like). `indeterminate`
|
|
35
|
+
* reads false here; check `.indeterminate` for the mixed state. */
|
|
36
|
+
get checked(): boolean;
|
|
37
|
+
/** Whether the checkbox is in the indeterminate ("mixed") state. */
|
|
38
|
+
get indeterminate(): boolean;
|
|
39
|
+
constructor();
|
|
40
|
+
connectedCallback(): void;
|
|
41
|
+
attributeChangedCallback(name: string): void;
|
|
42
|
+
private get isDisabled();
|
|
43
|
+
private get isControlled();
|
|
44
|
+
private seed;
|
|
45
|
+
private emitChange;
|
|
46
|
+
private toggle;
|
|
47
|
+
private paint;
|
|
48
|
+
formResetCallback(): void;
|
|
49
|
+
formStateRestoreCallback(state: string): void;
|
|
50
|
+
formDisabledCallback(): void;
|
|
51
|
+
}
|
|
52
|
+
export declare function register_a_checkbox(): void;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { HTMLElementBase } from "../anta_helpers";
|
|
2
|
+
import "./a-checkbox.css";
|
|
3
|
+
const parseState = (v) => v === "checked" || v === "indeterminate" ? v : "unchecked";
|
|
4
|
+
class ACheckboxElement extends HTMLElementBase {
|
|
5
|
+
static formAssociated = true;
|
|
6
|
+
static observedAttributes = ["state", "default-state", "value"];
|
|
7
|
+
internals;
|
|
8
|
+
currentState = "unchecked";
|
|
9
|
+
seeded = false;
|
|
10
|
+
// Set once the user toggles an uncontrolled checkbox — the native "dirty checked
|
|
11
|
+
// flag". After it's set, a changed `default-state` no longer re-seeds the display.
|
|
12
|
+
dirty = false;
|
|
13
|
+
// True after the first connect. Gates the native `change` event so it never
|
|
14
|
+
// fires for the initial attribute seed (only for real, post-connect transitions).
|
|
15
|
+
alive = false;
|
|
16
|
+
/** Current checked state as a boolean (native-`<input>`-like). `indeterminate`
|
|
17
|
+
* reads false here; check `.indeterminate` for the mixed state. */
|
|
18
|
+
get checked() {
|
|
19
|
+
return this.currentState === "checked";
|
|
20
|
+
}
|
|
21
|
+
/** Whether the checkbox is in the indeterminate ("mixed") state. */
|
|
22
|
+
get indeterminate() {
|
|
23
|
+
return this.currentState === "indeterminate";
|
|
24
|
+
}
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this.internals = this.attachInternals?.();
|
|
28
|
+
this.addEventListener("click", (e) => this.toggle(e));
|
|
29
|
+
this.addEventListener("keydown", (e) => {
|
|
30
|
+
if (e.key === " ") {
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
this.click();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
if (!this.seeded) {
|
|
38
|
+
this.seed();
|
|
39
|
+
this.seeded = true;
|
|
40
|
+
}
|
|
41
|
+
this.paint();
|
|
42
|
+
this.alive = true;
|
|
43
|
+
}
|
|
44
|
+
attributeChangedCallback(name) {
|
|
45
|
+
if (name === "state") {
|
|
46
|
+
const next = parseState(this.getAttribute("state"));
|
|
47
|
+
const changed = next !== this.currentState;
|
|
48
|
+
this.currentState = next;
|
|
49
|
+
this.paint();
|
|
50
|
+
if (changed && this.alive) this.emitChange();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (name === "default-state" && !this.isControlled && !this.dirty) this.seed();
|
|
54
|
+
this.paint();
|
|
55
|
+
}
|
|
56
|
+
// Matches the host `disabled` attribute and an ancestor `<fieldset disabled>`.
|
|
57
|
+
get isDisabled() {
|
|
58
|
+
return this.matches(":disabled");
|
|
59
|
+
}
|
|
60
|
+
// Controlled mode: the `state` attribute is present and owns the live value.
|
|
61
|
+
get isControlled() {
|
|
62
|
+
return this.hasAttribute("state");
|
|
63
|
+
}
|
|
64
|
+
seed() {
|
|
65
|
+
this.currentState = parseState(
|
|
66
|
+
this.getAttribute("state") ?? this.getAttribute("default-state")
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
// Native `change`, fired *after* a value transition applies (user toggle or a
|
|
70
|
+
// controlled `state` update) — the post-apply counterpart to the cancelable,
|
|
71
|
+
// pre-apply `statechange`. Bubbles like a native checkbox; not composed (the
|
|
72
|
+
// element is light-DOM, so it reaches a wrapping form / listener directly).
|
|
73
|
+
emitChange() {
|
|
74
|
+
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
75
|
+
}
|
|
76
|
+
toggle(_e) {
|
|
77
|
+
if (this.isDisabled) return;
|
|
78
|
+
const prev = this.currentState;
|
|
79
|
+
const next = prev === "checked" ? "unchecked" : "checked";
|
|
80
|
+
const ok = this.dispatchEvent(
|
|
81
|
+
new CustomEvent("statechange", {
|
|
82
|
+
cancelable: true,
|
|
83
|
+
bubbles: true,
|
|
84
|
+
composed: true,
|
|
85
|
+
detail: { next, prev }
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
if (this.isControlled) return;
|
|
89
|
+
if (ok) {
|
|
90
|
+
this.currentState = next;
|
|
91
|
+
this.dirty = true;
|
|
92
|
+
this.paint();
|
|
93
|
+
this.emitChange();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
paint() {
|
|
97
|
+
const i = this.internals;
|
|
98
|
+
if (!i) return;
|
|
99
|
+
i.states.delete("checked");
|
|
100
|
+
i.states.delete("indeterminate");
|
|
101
|
+
if (this.currentState === "indeterminate") i.states.add("indeterminate");
|
|
102
|
+
else if (this.currentState === "checked") i.states.add("checked");
|
|
103
|
+
i.ariaChecked = this.currentState === "indeterminate" ? "mixed" : this.currentState === "checked" ? "true" : "false";
|
|
104
|
+
i.setFormValue?.(
|
|
105
|
+
this.currentState === "checked" ? this.getAttribute("value") ?? "on" : null,
|
|
106
|
+
this.currentState
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
formResetCallback() {
|
|
110
|
+
this.seed();
|
|
111
|
+
this.paint();
|
|
112
|
+
}
|
|
113
|
+
formStateRestoreCallback(state) {
|
|
114
|
+
this.currentState = parseState(state);
|
|
115
|
+
this.paint();
|
|
116
|
+
}
|
|
117
|
+
formDisabledCallback() {
|
|
118
|
+
this.paint();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function register_a_checkbox() {
|
|
122
|
+
if (typeof customElements === "undefined") return;
|
|
123
|
+
if (!customElements.get("a-checkbox"))
|
|
124
|
+
customElements.define("a-checkbox", ACheckboxElement);
|
|
125
|
+
}
|
|
126
|
+
register_a_checkbox();
|
|
127
|
+
export {
|
|
128
|
+
ACheckboxElement,
|
|
129
|
+
register_a_checkbox
|
|
130
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer anta{a-expander{--expander-text: var(--text-2);--expander-text-hover: var(--text-1);--expander-bg-secondary: var(--bg-2);--expander-border-secondary: var(--border-5);--expander-bg-primary: var(--bg-4);--expander-border-primary: var(--border-4);--expander-bg: var(--expander-bg-secondary);--expander-border: var(--expander-border-secondary);--expander-bg-hover-lift: .01;--expander-bg-hover: oklch(from var(--expander-bg) calc(l + var(--expander-bg-hover-lift)) c h);--expander-gutter: 24px;--_expander-chevron: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'%3e%3cpath stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m9 18 6-6-6-6'/%3e%3c/svg%3e");display:block;width:100%;box-sizing:border-box;border:1px solid var(--expander-border);border-radius:4px;color:var(--expander-text);background:var(--expander-bg);transition:background-color .1s ease}a-expander:not(:defined) a-expander-details{display:none}a-expander:not(:defined)[state=open] a-expander-details,a-expander:not(:defined)[default-state=open] a-expander-details{display:block}a-expander:not(:defined) a-expander-summary{position:relative;padding:6px 4px 6px var(--expander-gutter)}a-expander:not(:defined) a-expander-summary:before{content:"";position:absolute;left:calc(var(--expander-gutter) - 16px - 2px);top:50%;width:16px;height:16px;transform:translateY(-50%);background-color:currentColor;-webkit-mask:var(--_expander-chevron) center / contain no-repeat;mask:var(--_expander-chevron) center / contain no-repeat;opacity:.6}a-expander-summary{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-decoration-line:var(--_summary-underline, none);text-decoration-style:dotted;text-decoration-color:color-mix(in oklch,currentColor 75%,transparent);text-decoration-thickness:1px;text-underline-offset:3px;transition:color .15s ease}a-expander-details{display:block;padding:2px 4px 6px var(--expander-gutter);line-height:20px}a-expander[priority=primary]{--expander-bg: var(--expander-bg-primary);--expander-border: var(--expander-border-primary)}a-expander[priority=tertiary]{--expander-bg: transparent;--expander-border: transparent}a-expander:not([disabled]):not(:state(open)):hover{background:var(--expander-bg-hover)}a-expander:not([disabled]):not(:state(open)):active{background:var(--expander-bg)}a-expander[priority=tertiary][outdent]{--expander-gutter: 0px}a-expander[tone=brand]{--expander-text: var(--text-2-brand);--expander-text-hover: var(--text-1-brand);--expander-bg-secondary: var(--bg-2-brand);--expander-border-secondary: var(--border-5-brand);--expander-bg-primary: var(--bg-4-brand);--expander-border-primary: var(--border-4-brand)}a-expander[tone=info]{--expander-text: var(--text-2-info);--expander-text-hover: var(--text-1-info);--expander-bg-secondary: var(--bg-2-info);--expander-border-secondary: var(--border-5-info);--expander-bg-primary: var(--bg-4-info);--expander-border-primary: var(--border-4-info)}a-expander[tone=success]{--expander-text: var(--text-2-success);--expander-text-hover: var(--text-1-success);--expander-bg-secondary: var(--bg-2-success);--expander-border-secondary: var(--border-5-success);--expander-bg-primary: var(--bg-4-success);--expander-border-primary: var(--border-4-success)}a-expander[tone=warning]{--expander-text: var(--text-2-warning);--expander-text-hover: var(--text-1-warning);--expander-bg-secondary: var(--bg-2-warning);--expander-border-secondary: var(--border-5-warning);--expander-bg-primary: var(--bg-4-warning);--expander-border-primary: var(--border-4-warning)}a-expander[tone=critical]{--expander-text: var(--text-2-critical);--expander-text-hover: var(--text-1-critical);--expander-bg-secondary: var(--bg-2-critical);--expander-border-secondary: var(--border-5-critical);--expander-bg-primary: var(--bg-4-critical);--expander-border-primary: var(--border-4-critical)}a-expander[tone]:not([tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--expander-tone-source: attr(tone type(<color>), currentColor);--expander-text: oklch(from var(--expander-tone-source) .41 .15 h);--expander-text-hover: oklch(from var(--expander-tone-source) .33 .15 h);--expander-bg-secondary: oklch(from var(--expander-tone-source) .99 .006 h);--expander-border-secondary: oklch(from var(--expander-tone-source) .93 .03 h);--expander-bg-primary: oklch(from var(--expander-tone-source) .955 .02 h);--expander-border-primary: oklch(from var(--expander-tone-source) .895 .045 h)}.dark a-expander[tone]:not([tone=neutral],[tone=brand],[tone=info],[tone=success],[tone=warning],[tone=critical]){--expander-text: oklch(from var(--expander-tone-source) .75 .11 h);--expander-text-hover: oklch(from var(--expander-tone-source) .82 .1 h);--expander-bg-secondary: oklch(from var(--expander-tone-source) .13 .02 h);--expander-border-secondary: oklch(from var(--expander-tone-source) .22 .05 h);--expander-bg-primary: oklch(from var(--expander-tone-source) .19 .04 h);--expander-border-primary: oklch(from var(--expander-tone-source) .26 .07 h)}a-expander[disabled],a-expander[disabled][tone]{--expander-text: #afa9b1}.dark a-expander[disabled],.dark a-expander[disabled][tone]{--expander-text: #635b65}}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HTMLElementBase } from '../anta_helpers';
|
|
2
|
+
import './a-expander.css';
|
|
3
|
+
export declare class AExpanderElement extends HTMLElementBase {
|
|
4
|
+
static observedAttributes: string[];
|
|
5
|
+
private summary;
|
|
6
|
+
private region;
|
|
7
|
+
private internals?;
|
|
8
|
+
constructor();
|
|
9
|
+
/** Controlled mode: the `state` attribute is present and owns the state. */
|
|
10
|
+
private get controlled();
|
|
11
|
+
/** The currently *applied* state (read from the shadow, the source of truth
|
|
12
|
+
* for what's painted). */
|
|
13
|
+
private get current();
|
|
14
|
+
connectedCallback(): void;
|
|
15
|
+
attributeChangedCallback(name: string): void;
|
|
16
|
+
private syncDisabled;
|
|
17
|
+
/** Apply state to the shadow internals (idempotent; reads the host, writes
|
|
18
|
+
* only the shadow). `aria-expanded` is both the a11y signal and the CSS
|
|
19
|
+
* state hook; `inert` keeps collapsed content out of the tab order and the
|
|
20
|
+
* accessibility tree. */
|
|
21
|
+
private applyState;
|
|
22
|
+
/** Compute the requested next state, announce it (cancelable, *before*
|
|
23
|
+
* applying), then — uncontrolled only, and only if not vetoed — apply it.
|
|
24
|
+
* Controlled: never self-apply; the consumer answers via the `state`
|
|
25
|
+
* attribute. See STATEFUL-COMPONENTS.md. */
|
|
26
|
+
private onSummaryClick;
|
|
27
|
+
}
|
|
28
|
+
export declare function register_a_expander(): void;
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { HTMLElementBase } from "../anta_helpers";
|
|
2
|
+
import "./a-expander.css";
|
|
3
|
+
const ANIM_MS = 200;
|
|
4
|
+
const SUMMARY_TYPE_SCALE = {
|
|
5
|
+
"1": [28, 32],
|
|
6
|
+
"2": [24, 28],
|
|
7
|
+
"3": [20, 24],
|
|
8
|
+
"4": [17, 20],
|
|
9
|
+
"5": [15, 20],
|
|
10
|
+
"6": [13, 16]
|
|
11
|
+
};
|
|
12
|
+
const SUMMARY_FONT_WEIGHT = 584.62;
|
|
13
|
+
const SUMMARY_LEVEL_RULES = Object.entries(SUMMARY_TYPE_SCALE).map(
|
|
14
|
+
([level, [size, line]]) => `:host([level="${level}"]) button { font-size: ${size}px; line-height: ${line}px; }`
|
|
15
|
+
).join("\n ");
|
|
16
|
+
const SHADOW_STYLE = `
|
|
17
|
+
:host { display: block; }
|
|
18
|
+
|
|
19
|
+
.header {
|
|
20
|
+
display: flex;
|
|
21
|
+
/* Stretch so the trigger button (and the actions row) fill the full host
|
|
22
|
+
height \u2014 the title's hit area is the whole header, not just the text. */
|
|
23
|
+
align-items: stretch;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
slot[name="actions"] { display: none; }
|
|
27
|
+
.header.has-actions slot[name="actions"] {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: 2px;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
margin-inline-start: 8px;
|
|
33
|
+
/* Inset from the right edge (the host has no padding of its own). */
|
|
34
|
+
margin-inline-end: 4px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
button {
|
|
38
|
+
appearance: none;
|
|
39
|
+
-webkit-appearance: none;
|
|
40
|
+
background: none;
|
|
41
|
+
border: none;
|
|
42
|
+
margin: 0;
|
|
43
|
+
/* The button is the full-bleed header: it spans the host's width (flex: 1)
|
|
44
|
+
and height (.header stretch). The title is inset by --expander-gutter
|
|
45
|
+
(the chevron lives absolutely inside that gutter, below \u2014 it never pushes
|
|
46
|
+
the title), so the title + the body share one inset value; 6px block
|
|
47
|
+
gives the header a touch more height. Restyle it edge-to-edge via
|
|
48
|
+
::part(summary). */
|
|
49
|
+
position: relative;
|
|
50
|
+
padding: 6px 4px 6px var(--expander-gutter);
|
|
51
|
+
flex: 1;
|
|
52
|
+
min-width: 0;
|
|
53
|
+
font: inherit;
|
|
54
|
+
color: inherit;
|
|
55
|
+
text-align: left;
|
|
56
|
+
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 0;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
user-select: none;
|
|
62
|
+
border-radius: 2px;
|
|
63
|
+
outline-offset: 4px;
|
|
64
|
+
font-size: ${SUMMARY_TYPE_SCALE["5"][0]}px;
|
|
65
|
+
line-height: ${SUMMARY_TYPE_SCALE["5"][1]}px;
|
|
66
|
+
font-weight: ${SUMMARY_FONT_WEIGHT};
|
|
67
|
+
letter-spacing: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
${SUMMARY_LEVEL_RULES}
|
|
71
|
+
|
|
72
|
+
/* The chevron sits absolutely INSIDE the gutter \u2014 out of flow, so it never
|
|
73
|
+
pushes the title. Its right edge lands 2px before the gutter (left = gutter
|
|
74
|
+
- 16px - 2px), leaving a 2px gap before the title; at the default 24px
|
|
75
|
+
gutter the chevron box is [6px, 22px], and on outdent (gutter 0) it
|
|
76
|
+
auto-hangs at -18px in the negative gutter. One var drives the title inset,
|
|
77
|
+
the body inset, and the chevron position. */
|
|
78
|
+
button::before {
|
|
79
|
+
content: '';
|
|
80
|
+
position: absolute;
|
|
81
|
+
left: calc(var(--expander-gutter) - 16px - 2px);
|
|
82
|
+
top: 50%;
|
|
83
|
+
width: 16px;
|
|
84
|
+
height: 16px;
|
|
85
|
+
background-color: currentColor;
|
|
86
|
+
-webkit-mask-image: var(--_expander-chevron);
|
|
87
|
+
mask-image: var(--_expander-chevron);
|
|
88
|
+
-webkit-mask-size: contain;
|
|
89
|
+
mask-size: contain;
|
|
90
|
+
-webkit-mask-repeat: no-repeat;
|
|
91
|
+
mask-repeat: no-repeat;
|
|
92
|
+
-webkit-mask-position: center;
|
|
93
|
+
mask-position: center;
|
|
94
|
+
opacity: 0.6;
|
|
95
|
+
/* translateY centers it; the open state adds rotate (same transform so the
|
|
96
|
+
transition animates only the rotation). */
|
|
97
|
+
transform: translateY(-50%);
|
|
98
|
+
transition: transform 150ms ease, opacity 150ms ease, background-color 150ms ease;
|
|
99
|
+
}
|
|
100
|
+
button:enabled:hover::before { opacity: 1; }
|
|
101
|
+
button[aria-expanded="true"]::before { transform: translateY(-50%) rotate(90deg); opacity: 1; }
|
|
102
|
+
|
|
103
|
+
button:enabled:hover { color: var(--expander-text-hover); }
|
|
104
|
+
:host([priority="tertiary"]) button:enabled:hover { --_summary-underline: underline; }
|
|
105
|
+
button:enabled:active { color: var(--expander-text); }
|
|
106
|
+
button:disabled { cursor: default; }
|
|
107
|
+
button:enabled:not([aria-expanded="true"]):active::before {
|
|
108
|
+
opacity: 0.6;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.region {
|
|
112
|
+
display: grid;
|
|
113
|
+
grid-template-rows: 0fr;
|
|
114
|
+
}
|
|
115
|
+
.header:has(button[aria-expanded="true"]) + .region { grid-template-rows: 1fr; }
|
|
116
|
+
|
|
117
|
+
/* Outdent flush correction: the host keeps its 1px (transparent) border for
|
|
118
|
+
cross-priority box stability, which insets the content-box by 1px. Pull the
|
|
119
|
+
header + region back by that 1px so the title and body land exactly on the
|
|
120
|
+
surrounding content (the chevron, absolutely positioned inside the header,
|
|
121
|
+
rides along). Logical start so it follows RTL. */
|
|
122
|
+
:host([priority="tertiary"][outdent]) .header,
|
|
123
|
+
:host([priority="tertiary"][outdent]) .region {
|
|
124
|
+
margin-inline-start: -1px;
|
|
125
|
+
}
|
|
126
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
127
|
+
.region { transition: grid-template-rows ${ANIM_MS}ms ease; }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[part="content"] { display: block; min-height: 0; overflow: clip; }
|
|
131
|
+
.header:has(button[aria-expanded="true"]) + .region [part="content"] {
|
|
132
|
+
overflow: visible;
|
|
133
|
+
transition: overflow 0s ${ANIM_MS}ms;
|
|
134
|
+
transition-behavior: allow-discrete;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
const parseState = (v) => v === "open" ? "open" : "closed";
|
|
138
|
+
class AExpanderElement extends HTMLElementBase {
|
|
139
|
+
static observedAttributes = ["state", "disabled"];
|
|
140
|
+
summary;
|
|
141
|
+
region;
|
|
142
|
+
// ElementInternals exposes the open/closed state as a custom state
|
|
143
|
+
// (`:state(open)`) for consumer styling — e.g. rotating a chevron that lives
|
|
144
|
+
// in `actions`, or matching an open header to a design. It's NOT a host
|
|
145
|
+
// attribute (declarative-DOM safe: no host mutation, no reconciliation
|
|
146
|
+
// churn), just element-internal state the browser exposes for CSS matching.
|
|
147
|
+
// Guarded for engines without attachInternals / CustomStateSet (no-op there).
|
|
148
|
+
internals;
|
|
149
|
+
constructor() {
|
|
150
|
+
super();
|
|
151
|
+
this.internals = typeof this.attachInternals === "function" ? this.attachInternals() : void 0;
|
|
152
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
153
|
+
const style = document.createElement("style");
|
|
154
|
+
style.textContent = SHADOW_STYLE;
|
|
155
|
+
this.summary = document.createElement("button");
|
|
156
|
+
this.summary.type = "button";
|
|
157
|
+
this.summary.setAttribute("aria-expanded", "false");
|
|
158
|
+
this.summary.setAttribute("part", "summary");
|
|
159
|
+
const titleSlot = document.createElement("slot");
|
|
160
|
+
titleSlot.name = "title";
|
|
161
|
+
this.summary.append(titleSlot);
|
|
162
|
+
this.summary.addEventListener("click", this.onSummaryClick);
|
|
163
|
+
const header = document.createElement("div");
|
|
164
|
+
header.className = "header";
|
|
165
|
+
const actionsSlot = document.createElement("slot");
|
|
166
|
+
actionsSlot.name = "actions";
|
|
167
|
+
actionsSlot.setAttribute("part", "actions");
|
|
168
|
+
actionsSlot.addEventListener("slotchange", () => {
|
|
169
|
+
header.classList.toggle("has-actions", actionsSlot.assignedElements().length > 0);
|
|
170
|
+
});
|
|
171
|
+
header.append(this.summary, actionsSlot);
|
|
172
|
+
this.region = document.createElement("div");
|
|
173
|
+
this.region.className = "region";
|
|
174
|
+
const content = document.createElement("slot");
|
|
175
|
+
content.setAttribute("part", "content");
|
|
176
|
+
this.region.append(content);
|
|
177
|
+
shadow.append(style, header, this.region);
|
|
178
|
+
}
|
|
179
|
+
/** Controlled mode: the `state` attribute is present and owns the state. */
|
|
180
|
+
get controlled() {
|
|
181
|
+
return this.hasAttribute("state");
|
|
182
|
+
}
|
|
183
|
+
/** The currently *applied* state (read from the shadow, the source of truth
|
|
184
|
+
* for what's painted). */
|
|
185
|
+
get current() {
|
|
186
|
+
return this.summary.getAttribute("aria-expanded") === "true" ? "open" : "closed";
|
|
187
|
+
}
|
|
188
|
+
connectedCallback() {
|
|
189
|
+
this.syncDisabled();
|
|
190
|
+
this.applyState(parseState(this.getAttribute(this.controlled ? "state" : "default-state")));
|
|
191
|
+
}
|
|
192
|
+
attributeChangedCallback(name) {
|
|
193
|
+
if (name === "disabled") this.syncDisabled();
|
|
194
|
+
else if (name === "state" && this.controlled) this.applyState(parseState(this.getAttribute("state")));
|
|
195
|
+
}
|
|
196
|
+
syncDisabled() {
|
|
197
|
+
this.summary.disabled = this.hasAttribute("disabled");
|
|
198
|
+
}
|
|
199
|
+
/** Apply state to the shadow internals (idempotent; reads the host, writes
|
|
200
|
+
* only the shadow). `aria-expanded` is both the a11y signal and the CSS
|
|
201
|
+
* state hook; `inert` keeps collapsed content out of the tab order and the
|
|
202
|
+
* accessibility tree. */
|
|
203
|
+
applyState(state) {
|
|
204
|
+
const open = state === "open";
|
|
205
|
+
this.summary.setAttribute("aria-expanded", open ? "true" : "false");
|
|
206
|
+
this.region.inert = !open;
|
|
207
|
+
try {
|
|
208
|
+
if (open) this.internals?.states.add("open");
|
|
209
|
+
else this.internals?.states.delete("open");
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** Compute the requested next state, announce it (cancelable, *before*
|
|
214
|
+
* applying), then — uncontrolled only, and only if not vetoed — apply it.
|
|
215
|
+
* Controlled: never self-apply; the consumer answers via the `state`
|
|
216
|
+
* attribute. See STATEFUL-COMPONENTS.md. */
|
|
217
|
+
onSummaryClick = () => {
|
|
218
|
+
const prev = this.current;
|
|
219
|
+
const next = prev === "open" ? "closed" : "open";
|
|
220
|
+
const ok = this.dispatchEvent(
|
|
221
|
+
new CustomEvent("statechange", { cancelable: true, detail: { next, prev } })
|
|
222
|
+
);
|
|
223
|
+
if (this.controlled) return;
|
|
224
|
+
if (ok) this.applyState(next);
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function register_a_expander() {
|
|
228
|
+
if (typeof customElements === "undefined") return;
|
|
229
|
+
if (!customElements.get("a-expander")) {
|
|
230
|
+
customElements.define("a-expander", AExpanderElement);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
register_a_expander();
|
|
234
|
+
export {
|
|
235
|
+
AExpanderElement,
|
|
236
|
+
register_a_expander
|
|
237
|
+
};
|
|
@@ -8,6 +8,20 @@ import './a-icon.shapes.css';
|
|
|
8
8
|
* CSS: the base rule (`a-icon.css`) sets up the mask compositing, and
|
|
9
9
|
* the per-shape rules (`a-icon.shapes.css`, generated) supply the
|
|
10
10
|
* `--icon` URL. Color follows `currentColor`.
|
|
11
|
+
*
|
|
12
|
+
* Sizing notes (`a-icon.css` ships comment-free):
|
|
13
|
+
* - The 16px default lives in a plain `--icon-size: 16px` declaration,
|
|
14
|
+
* never inside `attr()` — Safari has no typed `attr()` for `<length>`,
|
|
15
|
+
* so a default on the attr() path can't resolve there and the
|
|
16
|
+
* var-substituted `width` would collapse to 0 on an empty inline-block.
|
|
17
|
+
* - `<a-icon size="N">` maps to `--icon-size` via CSS Values 5 typed
|
|
18
|
+
* `attr()` (Chrome 133+ / Safari 18.2+) behind an `@supports` guard.
|
|
19
|
+
* Custom properties parse permissively, so without the guard the invalid
|
|
20
|
+
* `attr()` would only fail later, at `width: var(--icon-size)`
|
|
21
|
+
* substitution — collapsing the icon to 0×0 on unsupported engines
|
|
22
|
+
* instead of falling back to the 16px base. The `<Icon size={N}>` JSX
|
|
23
|
+
* wrapper sets `--icon-size` inline as a plain length and is the
|
|
24
|
+
* cross-browser path.
|
|
11
25
|
*/
|
|
12
26
|
export declare class AIconElement extends HTMLElementBase {
|
|
13
27
|
}
|