@awc-ui/core 1.0.0-beta.5 → 1.0.0-beta.7
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/cjs/md-button_6.cjs.entry.js +17 -2
- package/dist/cjs/md-button_6.cjs.entry.js.map +1 -1
- package/dist/cjs/md-segmented-button-set.cjs.entry.js +20 -2
- package/dist/cjs/md-segmented-button-set.cjs.entry.js.map +1 -1
- package/dist/collection/components/md-button/md-button.js +17 -2
- package/dist/collection/components/md-button/md-button.js.map +1 -1
- package/dist/collection/components/md-segmented-button-set/md-segmented-button-set.js +20 -2
- package/dist/collection/components/md-segmented-button-set/md-segmented-button-set.js.map +1 -1
- package/dist/components/md-button.js +1 -1
- package/dist/components/md-date-picker.js +1 -1
- package/dist/components/md-dialog.js +1 -1
- package/dist/components/md-multi-select.js +1 -1
- package/dist/components/md-segmented-button-set.js +1 -1
- package/dist/components/md-segmented-button-set.js.map +1 -1
- package/dist/components/md-snackbar.js +1 -1
- package/dist/components/md-step.js +1 -1
- package/dist/components/md-stepper.js +1 -1
- package/dist/components/md-time-picker.js +1 -1
- package/dist/components/{p-C1RN8YA1.js → p-BTgac9TA.js} +2 -2
- package/dist/components/p-BTgac9TA.js.map +1 -0
- package/dist/components-csr/md-button.js +1 -1
- package/dist/components-csr/md-date-picker.js +1 -1
- package/dist/components-csr/md-dialog.js +1 -1
- package/dist/components-csr/md-multi-select.js +1 -1
- package/dist/components-csr/md-segmented-button-set.js +1 -1
- package/dist/components-csr/md-segmented-button-set.js.map +1 -1
- package/dist/components-csr/md-snackbar.js +1 -1
- package/dist/components-csr/md-step.js +1 -1
- package/dist/components-csr/md-stepper.js +1 -1
- package/dist/components-csr/md-time-picker.js +1 -1
- package/dist/components-csr/{p-GG1ZMEng.js → p-QF3B4ODr.js} +2 -2
- package/dist/components-csr/p-QF3B4ODr.js.map +1 -0
- package/dist/esm/md-button_6.entry.js +17 -2
- package/dist/esm/md-button_6.entry.js.map +1 -1
- package/dist/esm/md-segmented-button-set.entry.js +20 -2
- package/dist/esm/md-segmented-button-set.entry.js.map +1 -1
- package/dist/md3/md3.esm.js +1 -1
- package/dist/md3/p-91df971f.entry.js +2 -0
- package/dist/md3/p-91df971f.entry.js.map +1 -0
- package/dist/md3/p-e661754b.entry.js +2 -0
- package/dist/md3/p-e661754b.entry.js.map +1 -0
- package/dist/types/components/md-segmented-button-set/md-segmented-button-set.d.ts +16 -0
- package/hydrate/index.js +37 -4
- package/hydrate/index.mjs +37 -4
- package/package.json +2 -2
- package/dist/components/p-C1RN8YA1.js.map +0 -1
- package/dist/components-csr/p-GG1ZMEng.js.map +0 -1
- package/dist/md3/p-56ea09a4.entry.js +0 -2
- package/dist/md3/p-56ea09a4.entry.js.map +0 -1
- package/dist/md3/p-6b841f69.entry.js +0 -2
- package/dist/md3/p-6b841f69.entry.js.map +0 -1
package/hydrate/index.js
CHANGED
|
@@ -21446,7 +21446,22 @@ class MdButton {
|
|
|
21446
21446
|
before. window.open remains for a programmatic click() on a button
|
|
21447
21447
|
whose href is safe but whose anchor has not rendered yet. */
|
|
21448
21448
|
const href = sanitizeHref(this.href);
|
|
21449
|
-
if (href
|
|
21449
|
+
if (!href)
|
|
21450
|
+
return;
|
|
21451
|
+
const anchor = this.el.shadowRoot?.querySelector('.md-button__anchor');
|
|
21452
|
+
/* Did this click actually pass THROUGH the anchor? composedPath sees
|
|
21453
|
+
across the shadow boundary, so a real pointer click — on the label, an
|
|
21454
|
+
icon, or the anchor's stretched ::after over the padding — is
|
|
21455
|
+
recognised and left alone: the browser is already navigating.
|
|
21456
|
+
|
|
21457
|
+
A PROGRAMMATIC el.click() targets the host and never reaches the
|
|
21458
|
+
anchor, so nothing would happen at all. A native <a>.click() does
|
|
21459
|
+
navigate, and this component behaved that way before it grew a real
|
|
21460
|
+
anchor — including keyboard activation, which routes through
|
|
21461
|
+
el.click() in handleKeyDown. Falling back to window.open keeps both
|
|
21462
|
+
working, and the composedPath check is what stops a real click from
|
|
21463
|
+
navigating twice. */
|
|
21464
|
+
if (!anchor || !e.composedPath().includes(anchor)) {
|
|
21450
21465
|
window.open(href, this.target, SAFE_WINDOW_FEATURES);
|
|
21451
21466
|
}
|
|
21452
21467
|
return;
|
|
@@ -21511,7 +21526,7 @@ class MdButton {
|
|
|
21511
21526
|
context. Blanket noreferrer would also strip the Referer on ordinary
|
|
21512
21527
|
same-tab navigation, quietly breaking referrer-based analytics. */
|
|
21513
21528
|
const opensNewContext = !!this.target && this.target !== '_self';
|
|
21514
|
-
return (hAsync(Host, { key: '
|
|
21529
|
+
return (hAsync(Host, { key: 'be127994ceadc697fd589c22af7d7ecc3bc0908d', class: {
|
|
21515
21530
|
'md-button': true,
|
|
21516
21531
|
[`md-button--${this.variant}`]: true,
|
|
21517
21532
|
[`md-button--${this.size}`]: true,
|
|
@@ -42742,6 +42757,24 @@ class MdSegmentedButtonSet {
|
|
|
42742
42757
|
this.multiselect = false;
|
|
42743
42758
|
/** Density: 0 = 40px, -1 = 36px, -2 = 32px, -3 = 28px, -4 = 24px */
|
|
42744
42759
|
this.density = 0;
|
|
42760
|
+
/**
|
|
42761
|
+
* Re-assign positions whenever the slotted segments change.
|
|
42762
|
+
*
|
|
42763
|
+
* `componentDidLoad` alone is not enough, and the gap is visible rather than
|
|
42764
|
+
* theoretical. A segment defaults to `segmentIndex: 0` of `segmentTotal: 1`,
|
|
42765
|
+
* which makes it BOTH first and last — so an unsynced segment renders fully
|
|
42766
|
+
* rounded on every edge. Replace a set's children after it has loaded and
|
|
42767
|
+
* every segment in it becomes a lone pill: the joined bar visibly falls apart,
|
|
42768
|
+
* and nothing about it looks like a stale index.
|
|
42769
|
+
*
|
|
42770
|
+
* Any consumer that renders segments dynamically hits this — a set whose
|
|
42771
|
+
* options come from state, a framework re-render, or `replaceChildren()`. The
|
|
42772
|
+
* showcase dock rebuilds its controls that way and shows the bug on the first
|
|
42773
|
+
* re-render after navigation.
|
|
42774
|
+
*/
|
|
42775
|
+
this.handleSlotChange = () => {
|
|
42776
|
+
this.syncSegments();
|
|
42777
|
+
};
|
|
42745
42778
|
}
|
|
42746
42779
|
onPropsChange() {
|
|
42747
42780
|
this.syncSegments();
|
|
@@ -42788,10 +42821,10 @@ class MdSegmentedButtonSet {
|
|
|
42788
42821
|
}
|
|
42789
42822
|
}
|
|
42790
42823
|
render() {
|
|
42791
|
-
return (hAsync(Host, { key: '
|
|
42824
|
+
return (hAsync(Host, { key: '2a8bf244149352d69dfb03dd32ef0dcc24487dc4', class: {
|
|
42792
42825
|
'md-segmented-button-set': true,
|
|
42793
42826
|
[`md-segmented-button-set--density-${Math.abs(this.density)}`]: this.density !== 0,
|
|
42794
|
-
}, role: this.multiselect ? 'group' : 'radiogroup' }, hAsync("slot", { key: '
|
|
42827
|
+
}, role: this.multiselect ? 'group' : 'radiogroup' }, hAsync("slot", { key: '2d6cfc8e5a79bf9dc7d4cf92d0a087b9401bff46', onSlotchange: this.handleSlotChange })));
|
|
42795
42828
|
}
|
|
42796
42829
|
get el() { return getElement(this); }
|
|
42797
42830
|
static get watchers() { return {
|
package/hydrate/index.mjs
CHANGED
|
@@ -21444,7 +21444,22 @@ class MdButton {
|
|
|
21444
21444
|
before. window.open remains for a programmatic click() on a button
|
|
21445
21445
|
whose href is safe but whose anchor has not rendered yet. */
|
|
21446
21446
|
const href = sanitizeHref(this.href);
|
|
21447
|
-
if (href
|
|
21447
|
+
if (!href)
|
|
21448
|
+
return;
|
|
21449
|
+
const anchor = this.el.shadowRoot?.querySelector('.md-button__anchor');
|
|
21450
|
+
/* Did this click actually pass THROUGH the anchor? composedPath sees
|
|
21451
|
+
across the shadow boundary, so a real pointer click — on the label, an
|
|
21452
|
+
icon, or the anchor's stretched ::after over the padding — is
|
|
21453
|
+
recognised and left alone: the browser is already navigating.
|
|
21454
|
+
|
|
21455
|
+
A PROGRAMMATIC el.click() targets the host and never reaches the
|
|
21456
|
+
anchor, so nothing would happen at all. A native <a>.click() does
|
|
21457
|
+
navigate, and this component behaved that way before it grew a real
|
|
21458
|
+
anchor — including keyboard activation, which routes through
|
|
21459
|
+
el.click() in handleKeyDown. Falling back to window.open keeps both
|
|
21460
|
+
working, and the composedPath check is what stops a real click from
|
|
21461
|
+
navigating twice. */
|
|
21462
|
+
if (!anchor || !e.composedPath().includes(anchor)) {
|
|
21448
21463
|
window.open(href, this.target, SAFE_WINDOW_FEATURES);
|
|
21449
21464
|
}
|
|
21450
21465
|
return;
|
|
@@ -21509,7 +21524,7 @@ class MdButton {
|
|
|
21509
21524
|
context. Blanket noreferrer would also strip the Referer on ordinary
|
|
21510
21525
|
same-tab navigation, quietly breaking referrer-based analytics. */
|
|
21511
21526
|
const opensNewContext = !!this.target && this.target !== '_self';
|
|
21512
|
-
return (hAsync(Host, { key: '
|
|
21527
|
+
return (hAsync(Host, { key: 'be127994ceadc697fd589c22af7d7ecc3bc0908d', class: {
|
|
21513
21528
|
'md-button': true,
|
|
21514
21529
|
[`md-button--${this.variant}`]: true,
|
|
21515
21530
|
[`md-button--${this.size}`]: true,
|
|
@@ -42740,6 +42755,24 @@ class MdSegmentedButtonSet {
|
|
|
42740
42755
|
this.multiselect = false;
|
|
42741
42756
|
/** Density: 0 = 40px, -1 = 36px, -2 = 32px, -3 = 28px, -4 = 24px */
|
|
42742
42757
|
this.density = 0;
|
|
42758
|
+
/**
|
|
42759
|
+
* Re-assign positions whenever the slotted segments change.
|
|
42760
|
+
*
|
|
42761
|
+
* `componentDidLoad` alone is not enough, and the gap is visible rather than
|
|
42762
|
+
* theoretical. A segment defaults to `segmentIndex: 0` of `segmentTotal: 1`,
|
|
42763
|
+
* which makes it BOTH first and last — so an unsynced segment renders fully
|
|
42764
|
+
* rounded on every edge. Replace a set's children after it has loaded and
|
|
42765
|
+
* every segment in it becomes a lone pill: the joined bar visibly falls apart,
|
|
42766
|
+
* and nothing about it looks like a stale index.
|
|
42767
|
+
*
|
|
42768
|
+
* Any consumer that renders segments dynamically hits this — a set whose
|
|
42769
|
+
* options come from state, a framework re-render, or `replaceChildren()`. The
|
|
42770
|
+
* showcase dock rebuilds its controls that way and shows the bug on the first
|
|
42771
|
+
* re-render after navigation.
|
|
42772
|
+
*/
|
|
42773
|
+
this.handleSlotChange = () => {
|
|
42774
|
+
this.syncSegments();
|
|
42775
|
+
};
|
|
42743
42776
|
}
|
|
42744
42777
|
onPropsChange() {
|
|
42745
42778
|
this.syncSegments();
|
|
@@ -42786,10 +42819,10 @@ class MdSegmentedButtonSet {
|
|
|
42786
42819
|
}
|
|
42787
42820
|
}
|
|
42788
42821
|
render() {
|
|
42789
|
-
return (hAsync(Host, { key: '
|
|
42822
|
+
return (hAsync(Host, { key: '2a8bf244149352d69dfb03dd32ef0dcc24487dc4', class: {
|
|
42790
42823
|
'md-segmented-button-set': true,
|
|
42791
42824
|
[`md-segmented-button-set--density-${Math.abs(this.density)}`]: this.density !== 0,
|
|
42792
|
-
}, role: this.multiselect ? 'group' : 'radiogroup' }, hAsync("slot", { key: '
|
|
42825
|
+
}, role: this.multiselect ? 'group' : 'radiogroup' }, hAsync("slot", { key: '2d6cfc8e5a79bf9dc7d4cf92d0a087b9401bff46', onSlotchange: this.handleSlotChange })));
|
|
42793
42826
|
}
|
|
42794
42827
|
get el() { return getElement(this); }
|
|
42795
42828
|
static get watchers() { return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awc-ui/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "Material Design 3 Web Components built with StencilJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"material-design",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"puppeteer": "^24.0.0",
|
|
114
114
|
"size-limit": "^11.1.6",
|
|
115
115
|
"typescript": "^5.4.0",
|
|
116
|
-
"@awc-ui/tokens": "1.0.0-beta.
|
|
116
|
+
"@awc-ui/tokens": "1.0.0-beta.7"
|
|
117
117
|
},
|
|
118
118
|
"license": "MIT",
|
|
119
119
|
"author": "AWC UI",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["mdButtonCss","MdButton","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","type","variant","size","shape","disabled","softDisabled","ripple","loading","icon","trailingIcon","href","target","toggle","selected","shapeMorph","mirrorIcon","suppressExpandIconFlip","fullWidth","connectedLeft","connectedRight","value","groupTabindex","roleOverride","density","hasSlottedLeadingIcon","hasSlottedTrailingIcon","pressed","handleClick","e","isDisabled","preventDefault","nextSelected","evt","mdClick","emit","originalEvent","defaultPrevented","mdChange","sanitizeHref","el","shadowRoot","querySelector","window","open","SAFE_WINDOW_FEATURES","internals","form","requestSubmit","reset","handleKeyDown","key","triggerRipple","click","setTimeout","handlePointerDown","handlePointerUp","componentDidLoad","shadow","leadingSlot","trailingSlot","addEventListener","assignedElements","length","render","isEffectivelyDisabled","hasLeadingIcon","hasTrailingIcon","linkHref","opensNewContext","h","Host","class","role","String","undefined","tabindex","onClick","onKeyDown","onPointerDown","onPointerUp","onPointerLeave","onPointerCancel","part","rel","SAFE_LINK_REL","renderContent","Fragment","name","LazyLoadingIndicator"],"sources":["src/components/md-button/md-button.css?tag=md-button&encapsulation=shadow","src/components/md-button/md-button.tsx"],"sourcesContent":["/* ==========================================================\n md-button — Material Design 3 Common Button\n Spec: https://m3.material.io/components/buttons/specs\n API: https://developer.android.com/reference/kotlin/\n androidx/compose/material3/ButtonDefaults\n Supports M3 + M3 Expressive (size, shape)\n\n PUBLIC CSS CUSTOM PROPERTIES (override per-instance):\n --md-button-container-color Container background\n --md-button-label-color Label + icon text color\n --md-button-icon-color Icon-only color override\n --md-button-icon-size Icon dimensions\n --md-button-container-shape Border-radius override\n --md-button-outline-color Outlined border color\n --md-button-outline-width Outlined border width\n --md-button-width Fixed inline-size (default: auto)\n --md-button-min-width Minimum inline-size (default: per-size touch target)\n --md-button-max-width Maximum inline-size (default: none)\n --md-button-height Fixed block-size (default: auto)\n --md-button-min-height Minimum block-size (default: per-size touch target)\n\n CSS PARTS:\n state-layer State-layer overlay\n icon Leading icon (fallback)\n trailing-icon Trailing icon (fallback)\n label Label text wrapper\n loading Loading spinner\n\n SLOTS:\n leading-icon Custom leading icon (replaces icon prop)\n trailing-icon Custom trailing icon (replaces trailingIcon prop)\n\n RTL:\n Layout uses CSS logical properties throughout, so leading/\n trailing icons swap visual sides automatically in `dir=\"rtl\"`.\n Set the `mirror-icon` attribute on buttons that use directional\n glyphs (arrows, chevrons) so the icon path itself flips.\n ========================================================== */\n\n:host {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n --_container-elevation: var(--md-sys-elevation-0, none);\n --_icon-size: var(--md-button-icon-size, 18px);\n\n /* Sizing — defaults come from each size class via --_size-* tokens.\n Public --md-button-{width,min-width,max-width,height,min-height}\n custom properties override per-instance for variable / full-width\n buttons without sacrificing the spec touch-target on resize. */\n --_width: var(--md-button-width, auto);\n /* Min-width tightens with density too (floored at 40px) so short-label\n buttons narrow, not just content-wide ones. Unchanged at density 0. */\n --_min-width: var(\n --md-button-min-width,\n max(40px, calc(var(--_size-min-width, 48px) + var(--md-sys-density-scale, 0) * 4px))\n );\n --_max-width: var(--md-button-max-width, none);\n --_height: var(--md-button-height, auto);\n /* Global density: on top of the size scale, each `data-density` rung (0…-4)\n trims 4px off the touch target, floored at 24px so small buttons stay\n usable. density-scale is 0 by default so the per-size height is unchanged. */\n --_min-height: var(\n --md-button-min-height,\n max(24px, calc(var(--_size-min-height, 32px) + var(--md-sys-density-scale, 0) * 4px))\n );\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n cursor: pointer;\n user-select: none;\n text-decoration: none;\n border: none;\n /* Transparent resting outline; focus-visible swaps outline-color in instantly.\n Deliberately NOT transitioned — on hydration the host's pre-upgrade UA outline\n (style:none, color:currentColor) animates to solid/transparent, which renders a\n dark ring that fades out on every button. Most visible when many mount at once\n (e.g. the 42 day cells in md-date-picker). Instant focus ring matches M3. */\n outline: 3px solid transparent;\n outline-offset: 2px;\n box-sizing: border-box;\n inline-size: var(--_width);\n block-size: var(--_height);\n min-inline-size: var(--_min-width);\n min-block-size: var(--_min-height);\n max-inline-size: var(--_max-width);\n background-color: var(--_container-color);\n color: var(--_label-color);\n box-shadow: var(--_container-elevation);\n --_shape-round: 20px;\n transition:\n box-shadow var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n background-color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n border-color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n border-radius var(--md-sys-motion-duration-medium4, 400ms) var(--md-sys-motion-easing-emphasized-decelerate, cubic-bezier(0.05, 0.7, 0.1, 1)),\n color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n -webkit-tap-highlight-color: transparent;\n vertical-align: middle;\n}\n\n/* ==========================================================\n 1. SIZE\n XS (32px) · SM (40px, default) · MD (48px) · LG (56px) · XL (96px)\n ========================================================== */\n/* Icon<->label gap tapers with density like the rest of the button: the\n container height and icon already shrink per rung, so a frozen gap made\n compact buttons read as loosely spaced. Each tier halves across the range\n (rate = base/8 per rung), floored at half its base. */\n:host(.md-button--xs) {\n --_icon-size: var(--md-button-icon-size, max(10px, calc(20px + var(--md-sys-density-scale, 0) * 2.5px)));\n --_shape-round: 16px;\n --_size-min-height: 32px;\n --_size-min-width: 48px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(6px, calc(12px + var(--md-sys-density-scale, 0) * 0.75px));\n gap: max(2px, calc(4px + var(--md-sys-density-scale, 0) * 0.5px));\n font-family: var(--md-sys-typescale-label-medium-font-family, Roboto, sans-serif);\n font-size: max(10px, calc(12px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-label-medium-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-label-medium-letter-spacing, 0.5px);\n line-height: max(14px, calc(16px + var(--md-sys-density-scale, 0) * 0.5px));\n}\n\n:host(.md-button--sm) {\n --_icon-size: var(--md-button-icon-size, max(10px, calc(20px + var(--md-sys-density-scale, 0) * 2.5px)));\n --_shape-round: 20px;\n --_size-min-height: 40px;\n --_size-min-width: 58px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(8px, calc(16px + var(--md-sys-density-scale, 0) * 1px));\n gap: max(4px, calc(8px + var(--md-sys-density-scale, 0) * 1px));\n font-family: var(--md-sys-typescale-label-large-font-family, Roboto, sans-serif);\n font-size: max(12px, calc(14px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-label-large-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-label-large-letter-spacing, 0.1px);\n line-height: max(16px, calc(20px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--md) {\n --_icon-size: var(--md-button-icon-size, max(12px, calc(24px + var(--md-sys-density-scale, 0) * 3px)));\n --_shape-round: 28px;\n --_size-min-height: 56px;\n --_size-min-width: 72px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(12px, calc(24px + var(--md-sys-density-scale, 0) * 1.5px));\n gap: max(4px, calc(8px + var(--md-sys-density-scale, 0) * 1px));\n font-family: var(--md-sys-typescale-title-small-font-family, Roboto, sans-serif);\n font-size: max(12px, calc(14px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-title-small-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-title-small-letter-spacing, 0.1px);\n line-height: max(16px, calc(20px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--lg) {\n --_icon-size: var(--md-button-icon-size, max(16px, calc(32px + var(--md-sys-density-scale, 0) * 4px)));\n --_shape-round: 48px;\n --_size-min-height: 96px;\n --_size-min-width: 88px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(24px, calc(48px + var(--md-sys-density-scale, 0) * 3px));\n gap: max(6px, calc(12px + var(--md-sys-density-scale, 0) * 1.5px));\n font-family: var(--md-sys-typescale-title-medium-font-family, Roboto, sans-serif);\n font-size: max(13px, calc(16px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-title-medium-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-title-medium-letter-spacing, 0.15px);\n line-height: max(18px, calc(24px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--xl) {\n --_icon-size: var(--md-button-icon-size, max(20px, calc(40px + var(--md-sys-density-scale, 0) * 5px)));\n --_shape-round: 68px;\n --_size-min-height: 136px;\n --_size-min-width: 120px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(32px, calc(64px + var(--md-sys-density-scale, 0) * 4px));\n gap: max(8px, calc(16px + var(--md-sys-density-scale, 0) * 2px));\n font-family: var(--md-sys-typescale-headline-small-font-family, Roboto, sans-serif);\n font-size: max(18px, calc(24px + var(--md-sys-density-scale, 0) * 1px));\n font-weight: var(--md-sys-typescale-headline-small-font-weight, 400);\n letter-spacing: var(--md-sys-typescale-headline-small-letter-spacing, 0px);\n line-height: max(24px, calc(32px + var(--md-sys-density-scale, 0) * 2px));\n}\n\n/* ==========================================================\n 1b. FULL WIDTH — block-level, fills inline axis\n `full-width` switches the host from inline-flex to flex and stretches\n inline-size to 100% of the containing block. The size's\n --_size-min-height (touch target) is still in force, so the button\n keeps its spec block-size unless --md-button-height overrides it.\n --md-button-width still wins if explicitly set.\n ========================================================== */\n:host(.md-button--full-width) {\n display: flex;\n --_width: var(--md-button-width, 100%);\n}\n\n/* ==========================================================\n 2. SHAPE\n Round (default) · Square (scaled corner radius)\n Round uses --_shape-round (height/2 per size) instead of\n corner-full (9999px) so the browser can smoothly interpolate\n border-radius transitions between resting and morphed states.\n --md-button-container-shape overrides both.\n ========================================================== */\n/* The RESTING radius is named (--_shape-rest) so the morph states below can be\n expressed as a blend back toward it. --_morph-to carries the morphed target\n and defaults to the resting value, so a button with no active morph state\n resolves to exactly its resting radius:\n\n calc(REST + FLAG * (MORPH - REST)) FLAG 1 -> MORPH, FLAG 0 -> REST\n\n That is what makes `data-shape-morph=\"off\"` / `shape-morph=\"off\"` freeze the\n shape (§13 in tokens.css) without a re-render and without :host-context,\n which Firefox does not implement. */\n:host(.md-button--round) {\n --_shape-rest: var(--md-button-container-shape, var(--_shape-round));\n border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest)));\n}\n\n:host(.md-button--square.md-button--xs) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-small, 8px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--sm) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-medium, 12px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--md) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-medium, 12px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--lg) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-large, 16px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--xl) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-extra-large, 28px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n\n/* ==========================================================\n 2b. SHAPE MORPHING — M3 Expressive\n Uses direct border-radius overrides for reliable transitions.\n\n Order: selected rules FIRST, pressed rules LAST so pressed\n always wins while the pointer is down.\n\n Asymmetric timing (M3 motion tokens):\n Release / selected : medium4 (400ms) + emphasized-decelerate\n Press-in : short4 (200ms) + standard\n ========================================================== */\n\n/* ── Selected shape — round resting → square tokens when selected ── */\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--xs) { --_morph-to: var(--md-sys-shape-corner-small, 8px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--sm) { --_morph-to: var(--md-sys-shape-corner-medium, 12px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--md) { --_morph-to: var(--md-sys-shape-corner-medium, 12px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--lg) { --_morph-to: var(--md-sys-shape-corner-large, 16px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--xl) { --_morph-to: var(--md-sys-shape-corner-extra-large, 28px); }\n\n/* ── Selected shape — square resting → round when selected ── */\n:host(.md-button--shape-morph.md-button--square.md-button--selected) {\n --_morph-to: var(--md-button-container-shape, var(--_shape-round));\n}\n\n/* ── Pressed shape — both round & square morph to more square, overrides selected ── */\n:host(.md-button--pressed.md-button--xs) {\n --_morph-to: var(--md-sys-shape-corner-extra-small, 4px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--sm) {\n --_morph-to: var(--md-sys-shape-corner-small, 8px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--md) {\n --_morph-to: var(--md-sys-shape-corner-small, 8px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--lg) {\n --_morph-to: var(--md-sys-shape-corner-medium, 12px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--xl) {\n --_morph-to: var(--md-sys-shape-corner-large, 16px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* ==========================================================\n 2c. CONNECTED GROUP — per-corner border-radius\n Set by md-button-group when variant=\"connected\".\n shapeMorph is disabled for connected children so these rules\n only need to beat the base shape rules (section 2, specificity 0-1-0).\n ========================================================== */\n\n:host(.md-button--connected-left.md-button--xs),\n:host(.md-button--connected-right.md-button--xs) { --_c-inner: 4px; }\n:host(.md-button--connected-left.md-button--sm),\n:host(.md-button--connected-right.md-button--sm) { --_c-inner: 8px; }\n:host(.md-button--connected-left.md-button--md),\n:host(.md-button--connected-right.md-button--md) { --_c-inner: 8px; }\n:host(.md-button--connected-left.md-button--lg),\n:host(.md-button--connected-right.md-button--lg) { --_c-inner: 16px; }\n:host(.md-button--connected-left.md-button--xl),\n:host(.md-button--connected-right.md-button--xl) { --_c-inner: 20px; }\n\n/* Middle button — all corners inner */\n:host(.md-button--connected-left.md-button--connected-right) {\n border-radius: var(--_c-inner);\n}\n\n/* `connected-left` / `connected-right` reflect DOM-order siblings (set by\n md-button-group when a previous / next sibling exists), so the radii\n here must use logical corner properties to flip correctly under\n `dir=\"rtl\"`. The outer pill always lives on the free inline-start\n (first child) or inline-end (last child) edge. */\n\n/* First button (round) — pill on inline-start edge, inner on inline-end edge */\n:host(.md-button--round.md-button--connected-right:not(.md-button--connected-left)) {\n border-start-start-radius: var(--_shape-round);\n border-end-start-radius: var(--_shape-round);\n border-start-end-radius: var(--_c-inner);\n border-end-end-radius: var(--_c-inner);\n}\n\n/* Last button (round) — inner on inline-start edge, pill on inline-end edge */\n:host(.md-button--round.md-button--connected-left:not(.md-button--connected-right)) {\n border-start-start-radius: var(--_c-inner);\n border-end-start-radius: var(--_c-inner);\n border-start-end-radius: var(--_shape-round);\n border-end-end-radius: var(--_shape-round);\n}\n\n/* Square first / last — outer equals inner */\n:host(.md-button--square.md-button--connected-right:not(.md-button--connected-left)),\n:host(.md-button--square.md-button--connected-left:not(.md-button--connected-right)) {\n border-radius: var(--_c-inner);\n}\n\n/* ==========================================================\n 3. ICON PADDING\n An icon does NOT change the button's insets. The M3 Expressive size sheet\n specifies the SAME padding on both sides whether or not a leading icon is\n present (12/12, 16/16, 24/24, 48/48, 64/64) — the third number on those\n drawings is the icon-to-label gap, not a reduced leading inset. The size's\n base padding therefore applies symmetrically and there is nothing to override.\n ========================================================== */\n\n/* ==========================================================\n 4. COLOR VARIANTS\n --md-button-container-color and --md-button-label-color\n override the variant defaults when set.\n ========================================================== */\n\n/* Filled */\n:host(.md-button--filled) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-primary));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-primary));\n --_state-layer-color: var(--md-sys-color-on-primary);\n}\n:host(.md-button--filled:hover) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--filled:active) { --_container-elevation: var(--md-sys-elevation-0, none); }\n:host(.md-button--filled:focus-visible) { --_container-elevation: var(--md-sys-elevation-0, none); }\n\n/* Outlined */\n:host(.md-button--outlined) {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n border: var(--md-button-outline-width, 1px) solid var(--md-button-outline-color, var(--md-sys-color-outline));\n}\n/* Variant padding follows the SAME density rule as the base size rules above:\n slope = base/16 (25% shrink across the four rungs) and a floor of base/2.\n These overrides previously decayed at 52-133% per the old tuning — text sm\n lost 133% of its inset and pinned to its floor by rung -2 — so outlined and\n text shrank far faster than the button did. Past rung -2 that INVERTED the\n variants: an outlined button, wider than filled at density 0, became\n narrower than it, and a row of mixed-variant buttons went visibly ragged.\n Density-0 values are unchanged; only the decay was corrected. */\n:host(.md-button--outlined.md-button--xs) { padding: 0 max(5.5px, calc(11px + var(--md-sys-density-scale, 0) * 0.6875px)); }\n:host(.md-button--outlined.md-button--sm) { padding: 0 max(11.5px, calc(23px + var(--md-sys-density-scale, 0) * 1.4375px)); }\n:host(.md-button--outlined.md-button--md) { padding: 0 max(13.5px, calc(27px + var(--md-sys-density-scale, 0) * 1.6875px)); }\n:host(.md-button--outlined.md-button--lg) { padding: 0 max(15.5px, calc(31px + var(--md-sys-density-scale, 0) * 1.9375px)); }\n:host(.md-button--outlined.md-button--xl) { padding: 0 max(23.5px, calc(47px + var(--md-sys-density-scale, 0) * 2.9375px)); }\n:host(.md-button--outlined:hover:not(.md-button--disabled)) {\n border-color: var(--md-button-outline-color, var(--md-sys-color-primary));\n}\n:host(.md-button--outlined:focus-visible) {\n border-color: var(--md-button-outline-color, var(--md-sys-color-primary));\n}\n\n/* Text */\n:host(.md-button--text) {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n}\n/* Text buttons intentionally carry the SAME horizontal padding as every other\n variant (the base size rules above); there is deliberately no override here.\n They used to sit far tighter — a text button's inset was a third of a\n filled one's and pinned to its floor at tight densities (4px by rung -2).\n The container is invisible at rest, but the hover/press state layer is not:\n it paints the padding box, so a text button in a toggle strip beside a\n tonal or filled one showed a cramped pill hugging its label while its\n neighbour's was roomy, and the mismatch grew with density. Sharing the base\n padding keeps every state layer in a strip the same shape, and keeps a\n button's width stable if its variant changes to signal selection. */\n\n/* Elevated */\n:host(.md-button--elevated) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-low));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n --_container-elevation: var(--md-sys-elevation-1);\n}\n:host(.md-button--elevated:hover) { --_container-elevation: var(--md-sys-elevation-2); }\n:host(.md-button--elevated:focus-visible) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--elevated:active) { --_container-elevation: var(--md-sys-elevation-1); }\n\n/* Tonal */\n:host(.md-button--tonal) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-secondary-container));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-secondary-container));\n --_state-layer-color: var(--md-sys-color-on-secondary-container);\n}\n:host(.md-button--tonal:hover) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--tonal:focus-visible) { --_container-elevation: var(--md-sys-elevation-0, none); }\n:host(.md-button--tonal:active) { --_container-elevation: var(--md-sys-elevation-0, none); }\n\n/* ==========================================================\n 4b. TOGGLE SELECTED / UNSELECTED — MD3 spec\n When toggle=true, the unselected state is muted; selected\n shows the full variant color. Mirrors md-icon-button pattern.\n ========================================================== */\n\n/* The toggle-state rules below all carry `:not(.md-button--disabled)` so\n they drop out when the host becomes disabled. Without it, their higher\n specificity (4 classes vs the disabled rules' 2–3) would override the\n muted disabled palette and the button would still look enabled. */\n\n/* ── Filled toggle ── */\n:host(.md-button--filled.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n}\n\n/* ── Tonal toggle ── */\n:host(.md-button--tonal.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n}\n\n/* ── Outlined toggle ── */\n:host(.md-button--outlined.md-button--toggle.md-button--selected:not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-secondary-container, #E8DEF8));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-secondary-container, #1E192B));\n --_state-layer-color: var(--md-sys-color-on-secondary-container, #1E192B);\n border-color: transparent;\n}\n\n/* ── Elevated toggle ── */\n:host(.md-button--elevated.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n --_container-elevation: var(--md-sys-elevation-0, none);\n}\n\n/* ── Text toggle ── */\n:host(.md-button--text.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n}\n\n/* ==========================================================\n 5. STATE LAYER\n ========================================================== */\n.md-button__state-layer {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-color: var(--_state-layer-color);\n opacity: 0;\n transition: opacity var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n pointer-events: none;\n}\n:host(:hover) .md-button__state-layer {\n opacity: var(--md-sys-state-hover-state-layer-opacity, 0.08);\n}\n:host(:active:not(.md-button--disabled)) .md-button__state-layer {\n opacity: var(--md-sys-state-pressed-state-layer-opacity, 0.12);\n transition-duration: var(--md-sys-motion-duration-short1, 50ms);\n transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate, cubic-bezier(0.05, 0.7, 0.1, 1));\n}\n:host(:focus-visible) .md-button__state-layer {\n opacity: var(--md-sys-state-focus-state-layer-opacity, 0.12);\n}\n\n/* ==========================================================\n 6. FOCUS RING\n `z-index: 1` raises the focused button above its siblings so the\n outline is never clipped by a neighbor — important inside\n md-button-group, where connected groups have only 2px between\n buttons and standard groups animate neighbor padding on press.\n ========================================================== */\n/* Connected children inside md-button-group sit only 2px apart, so a\n 3px-outset focus ring naturally extends across the neighbor. The\n `z-index: 1` raise lets it draw *on top* of the neighbor cleanly so\n the ring keeps its full 2px breathing room — same visual treatment\n as a stand-alone button. */\n:host(:focus-visible) {\n outline-color: var(--md-sys-color-secondary);\n z-index: 1;\n}\n\n/* ==========================================================\n 7. DISABLED\n ========================================================== */\n:host(.md-button--disabled) {\n cursor: default;\n pointer-events: none;\n --_container-elevation: var(--md-sys-elevation-0, none) !important;\n transition: none;\n}\n\n:host([soft-disabled]) {\n cursor: default;\n pointer-events: auto;\n}\n:host([soft-disabled]) .md-button__state-layer { display: none; }\n\n:host(.md-button--filled.md-button--disabled),\n:host(.md-button--tonal.md-button--disabled) {\n --_container-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n}\n:host(.md-button--elevated.md-button--disabled) {\n --_container-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n --_container-elevation: var(--md-sys-elevation-0, none);\n}\n\n:host(.md-button--disabled) {\n --_label-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity, 0.38) * 100%), transparent);\n}\n\n:host(.md-button--outlined.md-button--disabled) {\n --_container-color: transparent;\n border-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n}\n\n:host(.md-button--text.md-button--disabled) {\n --_container-color: transparent;\n}\n\n:host(.md-button--disabled:hover) .md-button__state-layer,\n:host(.md-button--disabled:active) .md-button__state-layer,\n:host(.md-button--disabled:focus-visible) .md-button__state-layer {\n opacity: 0;\n}\n\n/* ==========================================================\n 8. ICON (prop-based fallback inside slot)\n ========================================================== */\n.md-button__icon,\n.md-button__trailing-icon {\n color: var(--md-button-icon-color, inherit);\n font-size: var(--_icon-size, 18px);\n width: var(--_icon-size, 18px);\n height: var(--_icon-size, 18px);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n/* ==========================================================\n 9. NAMED ICON SLOTS\n Slots use display:contents so they don't create a box;\n slotted content participates directly in flex layout.\n ========================================================== */\nslot[name=\"leading-icon\"],\nslot[name=\"trailing-icon\"] {\n display: contents;\n}\n\n::slotted([slot=\"leading-icon\"]),\n::slotted([slot=\"trailing-icon\"]) {\n color: var(--md-button-icon-color, inherit);\n width: var(--_icon-size, 18px);\n height: var(--_icon-size, 18px);\n flex-shrink: 0;\n}\n\n/* ----------------------------------------------------------\n Trailing-icon rotation when this button is a popover anchor.\n\n md-menu (and any other popup component using the same ARIA\n contract) sets aria-expanded=\"true\" on its anchor element\n while the popup is open. Rotating the trailing dropdown\n indicator 180° on that signal flips arrow_drop_down to\n point up, matching MD3 select-trigger behavior, with no\n wiring needed on the consumer side.\n\n Honors prefers-reduced-motion via short2 motion duration.\n ---------------------------------------------------------- */\n.md-button__trailing-icon,\n::slotted([slot=\"trailing-icon\"]) {\n transition: transform var(--md-sys-motion-duration-short2, 150ms)\n var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n:host([aria-expanded=\"true\"]:not([suppress-expand-icon-flip])) .md-button__trailing-icon,\n:host([aria-expanded=\"true\"]:not([suppress-expand-icon-flip])) ::slotted([slot=\"trailing-icon\"]) {\n transform: rotate(180deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n\n .md-button__state-layer {\n transition: none;\n }\n\n .md-button__trailing-icon,\n ::slotted([slot=\"trailing-icon\"]) {\n transition: none;\n }\n}\n\n/* ==========================================================\n 10. LABEL\n ========================================================== */\n.md-button__label {\n display: flex;\n align-items: center;\n white-space: nowrap;\n}\n\n/* ==========================================================\n 11. LOADING SPINNER (scales with icon size)\n ========================================================== */\n.md-button__loading {\n /* A FRACTION of the icon it stands in for. Two constraints pull against each\n other: at full --_icon-size the ring reads heavier than the glyph (a bare\n stroke has none of the internal padding a Material glyph carries), but at\n 1em it was pinned to the label and stopped scaling per tier -- sm and md\n share a 14px label, so a 40px and a 56px button drew the same 14px ring\n despite 20px vs 24px icons. 0.7 keeps it visibly lighter than a glyph while\n tracking the tier, and inherits the icon's density taper. */\n /* 10px floor: the icon token itself bottoms out at 10px on the small tiers,\n and 0.7 of that is a 7px ring — too small to read as a spinner at all. */\n --_loading-size: var(--md-button-loading-size, max(10px, calc(var(--_icon-size, 18px) * 0.7)));\n\n /* Hand the size straight to md-loading-indicator's PUBLIC hooks. These\n replace the child's whole default expression, so the button's own taper\n (and its 10px floor) survives instead of hitting the child's 32px floor.\n Not --_size / --_shape-size: those are the child's private vars and\n writing them from out here is the handoff trap. currentColor keeps the\n indicator matching each variant's label, which a token would not. */\n --md-loading-indicator-size: var(--_loading-size);\n --md-loading-indicator-shape-size: var(--_loading-size);\n --md-loading-indicator-color: currentColor;\n flex-shrink: 0;\n /* A wrapper now, so the hooks above reach a SLOTTED replacement too. It has\n to lay out like the indicator it replaced — an inline span would add a text\n baseline gap next to the label. */\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Whatever the consumer slots in gets the same box the default occupies. */\n.md-button__loading ::slotted(*) {\n flex-shrink: 0;\n}\n\n/* ==========================================================\n 12. MATERIAL SYMBOLS (default icon font)\n ========================================================== */\n.material-symbols-outlined {\n font-family: var(--md-sys-icon-font-family, 'Material Symbols Outlined', 'Material Icons');\n font-weight: normal;\n font-style: normal;\n font-size: var(--_icon-size, 18px);\n line-height: 1;\n letter-spacing: normal;\n text-transform: none;\n display: inline-block;\n white-space: nowrap;\n overflow-wrap: normal;\n direction: ltr;\n font-feature-settings: 'liga';\n -webkit-font-smoothing: antialiased;\n}\n\n/* ==========================================================\n 13. RTL — opt-in directional-icon mirroring\n\n Buttons that show directional Material Symbols (arrow_forward,\n chevron_right, send, reply…) should set `mirror-icon` so the\n glyph itself flips alongside the leading/trailing slot swap that\n the logical-property layout already performs. Non-directional\n icons (add, search, favorite…) leave `mirror-icon` unset so the\n glyph stays semantically correct.\n\n Two rules cover both placements of `dir=\"rtl\"`:\n (a) on the host itself — `:host([dir=\"rtl\"])`\n (b) on any ancestor element — `:host-context([dir=\"rtl\"])`\n ========================================================== */\n:host([mirror-icon][dir=\"rtl\"]) .md-button__icon,\n:host([mirror-icon][dir=\"rtl\"]) .md-button__trailing-icon,\n:host([mirror-icon][dir=\"rtl\"]) ::slotted([slot=\"leading-icon\"]),\n:host([mirror-icon][dir=\"rtl\"]) ::slotted([slot=\"trailing-icon\"]) {\n transform: scaleX(-1);\n}\n\n:host-context([dir=\"rtl\"]):host([mirror-icon]) .md-button__icon,\n:host-context([dir=\"rtl\"]):host([mirror-icon]) .md-button__trailing-icon,\n:host-context([dir=\"rtl\"]):host([mirror-icon]) ::slotted([slot=\"leading-icon\"]),\n:host-context([dir=\"rtl\"]):host([mirror-icon]) ::slotted([slot=\"trailing-icon\"]) {\n transform: scaleX(-1);\n}\n\n/* ==========================================================\n DENSITY (local override)\n Maps the local `density` prop onto the same --md-sys-density-scale signal a\n global `data-density` ancestor sets. Declared on :host, so a local rung wins\n over the inherited global one. Every density-sensitive value in this file\n derives from that single signal by calc — never add a fixed per-rung ladder.\n ========================================================== */\n:host([density='-1']) { --md-sys-density-scale: -1; }\n:host([density='-2']) { --md-sys-density-scale: -2; }\n:host([density='-3']) { --md-sys-density-scale: -3; }\n:host([density='-4']) { --md-sys-density-scale: -4; }\n\n/* ==========================================================\n LINK MODE — the anchor rendered when `href` is set\n\n The anchor wraps the button's innards so the LABEL gives it its accessible\n name, and it carries the tab stop (the host drops role/tabindex in this\n mode). It reproduces the host's own flex row rather than sitting inside it,\n so a link button lays out identically to a plain one.\n\n `::after` stretches the hit area over the host's padding: the anchor itself\n only fills the CONTENT box, so without it a click on the padding — a good\n third of a large button — would miss the link and lose native activation.\n The host is already `position: relative`, so the inset resolves to it.\n ========================================================== */\n.md-button__anchor {\n display: flex;\n align-items: center;\n justify-content: center;\n /* Not inherited by default; picking up the host's computed value keeps the\n icon/label spacing identical to a non-link button at every density. */\n gap: inherit;\n inline-size: 100%;\n block-size: 100%;\n min-inline-size: 0;\n /* A button is not underlined blue text. */\n color: inherit;\n font: inherit;\n text-decoration: none;\n outline: none;\n}\n\n.md-button__anchor::after {\n content: '';\n position: absolute;\n inset: 0;\n /* Behind the state layer and ripple so neither is occluded. */\n z-index: 0;\n}\n\n/* The focus ring belongs to the whole button, not the inner content box. */\n:host(:has(.md-button__anchor:focus-visible)) {\n outline: var(--md-sys-state-focus-ring-width, 3px) solid\n var(--md-button-focus-ring-color, var(--md-sys-color-secondary, #625B71));\n outline-offset: var(--md-sys-state-focus-ring-offset, 2px);\n}\n\n/* ==========================================================\n HIDDEN\n `[hidden]` is a UA-stylesheet rule, and every `:host` display\n declaration above is an AUTHOR rule — author origin beats UA, so without\n this guard `el.hidden = true` left the component fully laid out.\n `!important` because state rules in this file set `display` at equal or\n higher specificity than `:host([hidden])`.\n ========================================================== */\n:host([hidden]) {\n display: none !important;\n}\n","import { Component, Fragment, Host, h, Prop, State, Event, EventEmitter, Element, AttachInternals } from '@stencil/core';\nimport { LazyLoadingIndicator } from '../../utils/lazy-loading-indicator';\nimport { triggerRipple } from '../../utils/ripple';\nimport { sanitizeHref, SAFE_LINK_REL, SAFE_WINDOW_FEATURES } from '../../utils/url';\n\n/**\n * Detail payload emitted by `md-button`'s `mdClick` event when the user\n * activates the button (mouse, touch, or keyboard).\n *\n * The event is **cancelable** — calling `event.preventDefault()` from a\n * listener stops the button's default side effects (toggle-state flip\n * and `href` navigation) but does NOT stop the underlying DOM click\n * event. This is the recommended hook for SPA routing and \"are you\n * sure?\" confirmation flows.\n */\nexport interface MdButtonClickDetail {\n /** The button's `value` prop (useful when grouped via `md-button-group`). */\n value: string;\n /** Whether toggle mode is enabled. */\n toggle: boolean;\n /**\n * Selected state the button WILL transition to if the click is not\n * cancelled. In non-toggle mode this is identical to `selected`.\n */\n selected: boolean;\n /** Navigation target (empty if the button is not a link). */\n href: string;\n /** Browsing context (`_self`, `_blank`, …). */\n target: string;\n /** The original DOM event that triggered the activation. */\n originalEvent: MouseEvent | KeyboardEvent;\n}\n\n/**\n * Detail payload emitted by `md-button`'s `mdChange` event when toggle\n * mode flips the selected state in response to a user activation.\n */\nexport interface MdButtonChangeDetail {\n /** New selected state. */\n selected: boolean;\n /** The button's `value` prop. */\n value: string;\n}\n\n@Component({\n tag: 'md-button',\n styleUrl: 'md-button.css',\n shadow: true,\n formAssociated: true,\n})\nexport class MdButton {\n @Element() el!: HTMLElement;\n\n /**\n * Form-association handle. Used to submit/reset the associated form when\n * `type` is `submit`/`reset` — works across shadow boundaries, unlike a\n * native `<button>` rendered in the shadow root (which can't see the\n * light-DOM `<form>` it lives in).\n */\n @AttachInternals() internals!: ElementInternals;\n\n /**\n * Activation behavior, mirroring the native `<button type>`:\n * - `button` (default): no implicit form action.\n * - `submit`: submits the associated form (`requestSubmit`, so the form's\n * `submit` event fires and constraint validation runs).\n * - `reset`: resets the associated form.\n */\n @Prop({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'button';\n\n @Prop() variant: 'filled' | 'outlined' | 'text' | 'elevated' | 'tonal' = 'filled';\n @Prop() size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'sm';\n @Prop() shape: 'round' | 'square' = 'round';\n @Prop({ reflect: true }) disabled: boolean = false;\n @Prop({ reflect: true, attribute: 'soft-disabled' }) softDisabled: boolean = false;\n @Prop() ripple: boolean = true;\n @Prop() loading: boolean = false;\n @Prop() icon: string = '';\n @Prop() trailingIcon: string = '';\n @Prop() href: string = '';\n @Prop() target: string = '_self';\n\n /** Enable toggle behavior (selected/unselected) */\n @Prop() toggle: boolean = false;\n\n /** Current selected state (toggle mode) */\n @Prop({ mutable: true }) selected: boolean = false;\n\n /** Enable M3 Expressive shape morphing on press and toggle */\n @Prop() shapeMorph: boolean = true;\n\n /**\n * Mirror leading and trailing icons horizontally when the button is\n * rendered in a right-to-left context. Set this on buttons that use\n * **directional** Material Symbols (arrows, chevrons, send, reply…)\n * — non-directional icons (add, search, favorite…) should leave it\n * `false` so the glyph stays semantically correct.\n */\n @Prop({ reflect: true, attribute: 'mirror-icon' }) mirrorIcon: boolean = false;\n\n /**\n * When true, suppresses the default 180° trailing-icon rotation while\n * `aria-expanded=\"true\"`. Use when the consumer swaps the glyph (e.g.\n * docked `md-date-picker` month/year triggers use `chevron_right`).\n */\n @Prop({ reflect: true, attribute: 'suppress-expand-icon-flip' })\n suppressExpandIconFlip: boolean = false;\n\n /**\n * Stretch the button to fill its inline-axis container.\n *\n * Switches the host from `inline-flex` to `flex` and sets\n * `inline-size: 100%` so the button consumes the full width of its\n * parent — the standard pattern for primary CTAs in forms, modals,\n * and bottom sheets. The size's `min-block-size` (touch target) is\n * preserved.\n *\n * For arbitrary fixed widths or heights, set the\n * `--md-button-width`, `--md-button-height`, `--md-button-min-width`,\n * `--md-button-min-height`, or `--md-button-max-width` CSS custom\n * properties on the host instead.\n */\n @Prop({ reflect: true, attribute: 'full-width' }) fullWidth: boolean = false;\n\n /** @internal Set by md-button-group — left side is connected to a neighbor */\n @Prop({ reflect: true }) connectedLeft: boolean = false;\n\n /** @internal Set by md-button-group — right side is connected to a neighbor */\n @Prop({ reflect: true }) connectedRight: boolean = false;\n\n /** @internal Value for button-group association */\n @Prop() value: string = '';\n\n /**\n * @internal Roving-tabindex override set by `md-button-group`. When the\n * button is a child of a group, the group sets this to `0` on the\n * currently-active button and `-1` on the rest, so Tab enters the group\n * once instead of stepping through every button. Outside a group, this\n * stays `null` and the button uses its normal tabindex behavior.\n */\n @Prop({ mutable: true, reflect: false }) groupTabindex: number | null = null;\n\n /**\n * Override the default `role=\"button\"`. Used by composite widgets such as\n * `md-date-picker` day cells (`role=\"gridcell\"`). Leave empty for `button`.\n */\n @Prop({ attribute: 'role-override' }) roleOverride: string = '';\n\n /**\n * Local density rung. Drives the same `--md-sys-density-scale` signal that a\n * global `data-density` ancestor sets, so a local value simply overrides the\n * inherited one. 0 = default, -4 = ultra-compact.\n */\n @Prop({ reflect: true }) density: 0 | -1 | -2 | -3 | -4 = 0;\n\n /**\n * Fires every time the user activates the button (mouse click, touch,\n * or `Enter`/`Space` while focused). The event is **cancelable** and\n * the detail payload describes what *would* happen: the post-click\n * toggle state, the navigation `href`, and so on.\n *\n * Calling `event.preventDefault()` from a listener suppresses the\n * default side effects (toggle flip + `href` navigation) but lets\n * the underlying DOM click bubble. This is the hook to use for SPA\n * routing, \"are you sure?\" prompts, async confirmation, etc.\n *\n * The event bubbles and is composed, so listeners outside the\n * shadow tree (the typical case) receive it.\n *\n * ```ts\n * document.querySelector('md-button')!.addEventListener('mdClick', (e) => {\n * const { href, selected } = (e as CustomEvent).detail;\n * if (!confirm('Continue?')) e.preventDefault();\n * });\n * ```\n */\n @Event({ cancelable: true, bubbles: true, composed: true })\n mdClick!: EventEmitter<MdButtonClickDetail>;\n\n /**\n * Fires when toggle mode flips the `selected` state in response to a\n * user activation. Not cancelable — the state change has already\n * happened. Pair with `mdClick` (cancelable) when you need a veto.\n *\n * Bubbles and is composed.\n */\n @Event({ bubbles: true, composed: true })\n mdChange!: EventEmitter<MdButtonChangeDetail>;\n\n @State() private hasSlottedLeadingIcon = false;\n @State() private hasSlottedTrailingIcon = false;\n @State() private pressed = false;\n\n private get isDisabled() {\n return this.disabled || this.softDisabled || this.loading;\n }\n\n componentDidLoad() {\n const shadow = this.el.shadowRoot;\n if (!shadow) return;\n\n const leadingSlot = shadow.querySelector('slot[name=\"leading-icon\"]') as HTMLSlotElement | null;\n const trailingSlot = shadow.querySelector('slot[name=\"trailing-icon\"]') as HTMLSlotElement | null;\n\n leadingSlot?.addEventListener('slotchange', () => {\n this.hasSlottedLeadingIcon = leadingSlot.assignedElements().length > 0;\n });\n trailingSlot?.addEventListener('slotchange', () => {\n this.hasSlottedTrailingIcon = trailingSlot.assignedElements().length > 0;\n });\n }\n\n private handleClick = (e: MouseEvent) => {\n if (this.isDisabled) { e.preventDefault(); return; }\n\n // Compute the post-click toggle state up front so the detail\n // payload describes what *would* happen — listeners can then\n // call preventDefault on the CustomEvent to veto the change.\n const nextSelected = this.toggle ? !this.selected : this.selected;\n\n const evt = this.mdClick.emit({\n value: this.value,\n toggle: this.toggle,\n selected: nextSelected,\n href: this.href,\n target: this.target,\n originalEvent: e,\n });\n\n if (evt.defaultPrevented) {\n // Suppress browser's default link navigation as well so the\n // host stays consistent. We don't flip selected and don't\n // open the href.\n e.preventDefault();\n return;\n }\n\n if (this.toggle && nextSelected !== this.selected) {\n this.selected = nextSelected;\n this.mdChange.emit({ selected: nextSelected, value: this.value });\n }\n\n /* Setting `href` makes this a link rather than a form control, so the\n branch is taken on the raw prop — an unsafe URL must not silently fall\n through to submitting the form. Only the navigation itself is refused. */\n if (this.href) {\n /* A safe href renders a REAL <a> in the shadow root, and the browser\n owns activation from there — so do nothing here. That is what makes\n middle-click, cmd/ctrl-click and \"copy link address\" work, and it is\n why cancelling mdClick above (which calls preventDefault on this very\n event) still vetoes navigation.\n\n The fallback below covers only the case where NO anchor exists: an\n href that failed sanitising renders none, so there is nothing to\n navigate and nothing to fall back to — the navigation is refused, as\n before. window.open remains for a programmatic click() on a button\n whose href is safe but whose anchor has not rendered yet. */\n const href = sanitizeHref(this.href);\n if (href && !this.el.shadowRoot?.querySelector('.md-button__anchor')) {\n window.open(href, this.target, SAFE_WINDOW_FEATURES);\n }\n return;\n }\n\n // Implicit form action, mirroring a native submit/reset button. Uses\n // ElementInternals so the associated <form> is found across the shadow\n // boundary. requestSubmit() (not submit()) so the form's submit event\n // fires and built-in constraint validation runs.\n if (this.type === 'submit') {\n this.internals.form?.requestSubmit();\n } else if (this.type === 'reset') {\n this.internals.form?.reset();\n }\n };\n\n private handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (!this.isDisabled) {\n this.pressed = true;\n triggerRipple(this.el);\n this.el.click();\n setTimeout(() => { this.pressed = false; }, 150);\n }\n }\n };\n\n private handlePointerDown = () => {\n if (!this.isDisabled) this.pressed = true;\n };\n\n private handlePointerUp = () => {\n this.pressed = false;\n };\n\n render() {\n const isEffectivelyDisabled = this.isDisabled;\n const hasLeadingIcon = !!this.icon;\n const hasTrailingIcon = !!this.trailingIcon;\n /* A component that accepts `href` must produce a real link. Sanitising\n here (not just at click time) means an unsafe URL renders NO anchor at\n all rather than an inert one. Disabled link-buttons also drop the\n anchor: there is no disabled state for <a>, and a focusable link that\n refuses to navigate is worse than no link. */\n const linkHref =\n this.href && !isEffectivelyDisabled ? sanitizeHref(this.href) : '';\n /* rel only where it means something: a target that opens a NEW browsing\n context. Blanket noreferrer would also strip the Referer on ordinary\n same-tab navigation, quietly breaking referrer-based analytics. */\n const opensNewContext = !!this.target && this.target !== '_self';\n\n return (\n <Host\n class={{\n 'md-button': true,\n [`md-button--${this.variant}`]: true,\n [`md-button--${this.size}`]: true,\n [`md-button--${this.shape}`]: true,\n 'md-button--with-leading-icon': hasLeadingIcon || this.hasSlottedLeadingIcon,\n 'md-button--with-trailing-icon': hasTrailingIcon || this.hasSlottedTrailingIcon,\n 'md-button--disabled': this.disabled || this.softDisabled,\n 'md-button--loading': this.loading,\n 'md-button--toggle': this.toggle,\n 'md-button--selected': this.selected,\n 'md-button--shape-morph': this.shapeMorph,\n 'md-button--pressed': this.shapeMorph && this.pressed,\n 'md-button--connected-left': this.connectedLeft,\n 'md-button--connected-right': this.connectedRight,\n 'md-button--full-width': this.fullWidth,\n }}\n role={this.roleOverride || (linkHref ? null : 'button')}\n aria-disabled={isEffectivelyDisabled ? 'true' : 'false'}\n aria-pressed={this.toggle ? String(this.selected) : undefined}\n tabindex={\n /* When a real anchor is rendered IT is the focusable element, so the\n host must not be a tab stop as well — otherwise every link button\n costs two presses of Tab and screen readers meet a button wrapping\n a link. */\n linkHref\n ? null\n : this.disabled\n ? '-1'\n : (this.groupTabindex !== null ? String(this.groupTabindex) : '0')\n }\n onClick={this.handleClick}\n onKeyDown={this.handleKeyDown}\n onPointerDown={this.handlePointerDown}\n onPointerUp={this.handlePointerUp}\n onPointerLeave={this.handlePointerUp}\n onPointerCancel={this.handlePointerUp}\n >\n {linkHref ? (\n <a\n class=\"md-button__anchor\"\n part=\"anchor\"\n role=\"link\"\n href={linkHref}\n target={this.target || undefined}\n rel={opensNewContext ? SAFE_LINK_REL : undefined}\n tabindex={this.groupTabindex !== null ? String(this.groupTabindex) : '0'}\n aria-disabled={isEffectivelyDisabled ? 'true' : null}\n >\n {this.renderContent(isEffectivelyDisabled, hasLeadingIcon, hasTrailingIcon)}\n </a>\n ) : (\n this.renderContent(isEffectivelyDisabled, hasLeadingIcon, hasTrailingIcon)\n )}\n </Host>\n );\n }\n\n /** Button innards, rendered either directly in the host or inside the\n * anchor when `href` makes this a link. */\n private renderContent(\n isEffectivelyDisabled: boolean,\n hasLeadingIcon: boolean,\n hasTrailingIcon: boolean,\n ) {\n return (\n <Fragment>\n {this.ripple && <md-ripple disabled={isEffectivelyDisabled}></md-ripple>}\n <span class=\"md-button__state-layer\" part=\"state-layer\" aria-hidden=\"true\"></span>\n <slot name=\"leading-icon\">\n {hasLeadingIcon && <span class=\"md-button__icon material-symbols-outlined\" part=\"icon\">{this.icon}</span>}\n </slot>\n <span class=\"md-button__label\" part=\"label\"><slot /></span>\n <slot name=\"trailing-icon\">\n {hasTrailingIcon && <span class=\"md-button__trailing-icon material-symbols-outlined\" part=\"trailing-icon\">{this.trailingIcon}</span>}\n </slot>\n {this.loading && (\n /* md-loading-indicator, not a hand-rolled ring: never ship a custom\n loading affordance where a library component exists. It is\n md-loading-indicator rather than md-progress-indicator because the\n latter clamps its circular variant to a 24px M3 minimum, while a\n button spinner runs 10-28px -- every tier but xl would clamp up and\n lose the density taper. Same choice md-select made for its trigger.\n\n The `loader` slot lets a consumer swap it (e.g. for a circular\n md-progress-indicator). The size hooks sit on this WRAPPER so they\n cascade to whatever ends up inside it, default or slotted. */\n <span class=\"md-button__loading\" part=\"loading\" aria-hidden=\"true\">\n <slot name=\"loader\">\n <LazyLoadingIndicator class=\"md-button__loading-default\" />\n </slot>\n </span>\n )}\n </Fragment>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["mdButtonCss","MdButton","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","type","variant","size","shape","disabled","softDisabled","ripple","loading","icon","trailingIcon","href","target","toggle","selected","shapeMorph","mirrorIcon","suppressExpandIconFlip","fullWidth","connectedLeft","connectedRight","value","groupTabindex","roleOverride","density","hasSlottedLeadingIcon","hasSlottedTrailingIcon","pressed","handleClick","e","isDisabled","preventDefault","nextSelected","evt","mdClick","emit","originalEvent","defaultPrevented","mdChange","sanitizeHref","el","shadowRoot","querySelector","window","open","SAFE_WINDOW_FEATURES","internals","form","requestSubmit","reset","handleKeyDown","key","triggerRipple","click","setTimeout","handlePointerDown","handlePointerUp","componentDidLoad","shadow","leadingSlot","trailingSlot","addEventListener","assignedElements","length","render","isEffectivelyDisabled","hasLeadingIcon","hasTrailingIcon","linkHref","opensNewContext","h","Host","class","role","String","undefined","tabindex","onClick","onKeyDown","onPointerDown","onPointerUp","onPointerLeave","onPointerCancel","part","rel","SAFE_LINK_REL","renderContent","Fragment","name","LazyLoadingIndicator"],"sources":["src/components/md-button/md-button.css?tag=md-button&encapsulation=shadow","src/components/md-button/md-button.tsx"],"sourcesContent":["/* ==========================================================\n md-button — Material Design 3 Common Button\n Spec: https://m3.material.io/components/buttons/specs\n API: https://developer.android.com/reference/kotlin/\n androidx/compose/material3/ButtonDefaults\n Supports M3 + M3 Expressive (size, shape)\n\n PUBLIC CSS CUSTOM PROPERTIES (override per-instance):\n --md-button-container-color Container background\n --md-button-label-color Label + icon text color\n --md-button-icon-color Icon-only color override\n --md-button-icon-size Icon dimensions\n --md-button-container-shape Border-radius override\n --md-button-outline-color Outlined border color\n --md-button-outline-width Outlined border width\n --md-button-width Fixed inline-size (default: auto)\n --md-button-min-width Minimum inline-size (default: per-size touch target)\n --md-button-max-width Maximum inline-size (default: none)\n --md-button-height Fixed block-size (default: auto)\n --md-button-min-height Minimum block-size (default: per-size touch target)\n\n CSS PARTS:\n state-layer State-layer overlay\n icon Leading icon (fallback)\n trailing-icon Trailing icon (fallback)\n label Label text wrapper\n loading Loading spinner\n\n SLOTS:\n leading-icon Custom leading icon (replaces icon prop)\n trailing-icon Custom trailing icon (replaces trailingIcon prop)\n\n RTL:\n Layout uses CSS logical properties throughout, so leading/\n trailing icons swap visual sides automatically in `dir=\"rtl\"`.\n Set the `mirror-icon` attribute on buttons that use directional\n glyphs (arrows, chevrons) so the icon path itself flips.\n ========================================================== */\n\n:host {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n --_container-elevation: var(--md-sys-elevation-0, none);\n --_icon-size: var(--md-button-icon-size, 18px);\n\n /* Sizing — defaults come from each size class via --_size-* tokens.\n Public --md-button-{width,min-width,max-width,height,min-height}\n custom properties override per-instance for variable / full-width\n buttons without sacrificing the spec touch-target on resize. */\n --_width: var(--md-button-width, auto);\n /* Min-width tightens with density too (floored at 40px) so short-label\n buttons narrow, not just content-wide ones. Unchanged at density 0. */\n --_min-width: var(\n --md-button-min-width,\n max(40px, calc(var(--_size-min-width, 48px) + var(--md-sys-density-scale, 0) * 4px))\n );\n --_max-width: var(--md-button-max-width, none);\n --_height: var(--md-button-height, auto);\n /* Global density: on top of the size scale, each `data-density` rung (0…-4)\n trims 4px off the touch target, floored at 24px so small buttons stay\n usable. density-scale is 0 by default so the per-size height is unchanged. */\n --_min-height: var(\n --md-button-min-height,\n max(24px, calc(var(--_size-min-height, 32px) + var(--md-sys-density-scale, 0) * 4px))\n );\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n cursor: pointer;\n user-select: none;\n text-decoration: none;\n border: none;\n /* Transparent resting outline; focus-visible swaps outline-color in instantly.\n Deliberately NOT transitioned — on hydration the host's pre-upgrade UA outline\n (style:none, color:currentColor) animates to solid/transparent, which renders a\n dark ring that fades out on every button. Most visible when many mount at once\n (e.g. the 42 day cells in md-date-picker). Instant focus ring matches M3. */\n outline: 3px solid transparent;\n outline-offset: 2px;\n box-sizing: border-box;\n inline-size: var(--_width);\n block-size: var(--_height);\n min-inline-size: var(--_min-width);\n min-block-size: var(--_min-height);\n max-inline-size: var(--_max-width);\n background-color: var(--_container-color);\n color: var(--_label-color);\n box-shadow: var(--_container-elevation);\n --_shape-round: 20px;\n transition:\n box-shadow var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n background-color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n border-color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1)),\n border-radius var(--md-sys-motion-duration-medium4, 400ms) var(--md-sys-motion-easing-emphasized-decelerate, cubic-bezier(0.05, 0.7, 0.1, 1)),\n color var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n -webkit-tap-highlight-color: transparent;\n vertical-align: middle;\n}\n\n/* ==========================================================\n 1. SIZE\n XS (32px) · SM (40px, default) · MD (48px) · LG (56px) · XL (96px)\n ========================================================== */\n/* Icon<->label gap tapers with density like the rest of the button: the\n container height and icon already shrink per rung, so a frozen gap made\n compact buttons read as loosely spaced. Each tier halves across the range\n (rate = base/8 per rung), floored at half its base. */\n:host(.md-button--xs) {\n --_icon-size: var(--md-button-icon-size, max(10px, calc(20px + var(--md-sys-density-scale, 0) * 2.5px)));\n --_shape-round: 16px;\n --_size-min-height: 32px;\n --_size-min-width: 48px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(6px, calc(12px + var(--md-sys-density-scale, 0) * 0.75px));\n gap: max(2px, calc(4px + var(--md-sys-density-scale, 0) * 0.5px));\n font-family: var(--md-sys-typescale-label-medium-font-family, Roboto, sans-serif);\n font-size: max(10px, calc(12px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-label-medium-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-label-medium-letter-spacing, 0.5px);\n line-height: max(14px, calc(16px + var(--md-sys-density-scale, 0) * 0.5px));\n}\n\n:host(.md-button--sm) {\n --_icon-size: var(--md-button-icon-size, max(10px, calc(20px + var(--md-sys-density-scale, 0) * 2.5px)));\n --_shape-round: 20px;\n --_size-min-height: 40px;\n --_size-min-width: 58px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(8px, calc(16px + var(--md-sys-density-scale, 0) * 1px));\n gap: max(4px, calc(8px + var(--md-sys-density-scale, 0) * 1px));\n font-family: var(--md-sys-typescale-label-large-font-family, Roboto, sans-serif);\n font-size: max(12px, calc(14px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-label-large-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-label-large-letter-spacing, 0.1px);\n line-height: max(16px, calc(20px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--md) {\n --_icon-size: var(--md-button-icon-size, max(12px, calc(24px + var(--md-sys-density-scale, 0) * 3px)));\n --_shape-round: 28px;\n --_size-min-height: 56px;\n --_size-min-width: 72px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(12px, calc(24px + var(--md-sys-density-scale, 0) * 1.5px));\n gap: max(4px, calc(8px + var(--md-sys-density-scale, 0) * 1px));\n font-family: var(--md-sys-typescale-title-small-font-family, Roboto, sans-serif);\n font-size: max(12px, calc(14px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-title-small-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-title-small-letter-spacing, 0.1px);\n line-height: max(16px, calc(20px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--lg) {\n --_icon-size: var(--md-button-icon-size, max(16px, calc(32px + var(--md-sys-density-scale, 0) * 4px)));\n --_shape-round: 48px;\n --_size-min-height: 96px;\n --_size-min-width: 88px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(24px, calc(48px + var(--md-sys-density-scale, 0) * 3px));\n gap: max(6px, calc(12px + var(--md-sys-density-scale, 0) * 1.5px));\n font-family: var(--md-sys-typescale-title-medium-font-family, Roboto, sans-serif);\n font-size: max(13px, calc(16px + var(--md-sys-density-scale, 0) * 0.5px));\n font-weight: var(--md-sys-typescale-title-medium-font-weight, 500);\n letter-spacing: var(--md-sys-typescale-title-medium-letter-spacing, 0.15px);\n line-height: max(18px, calc(24px + var(--md-sys-density-scale, 0) * 1px));\n}\n\n:host(.md-button--xl) {\n --_icon-size: var(--md-button-icon-size, max(20px, calc(40px + var(--md-sys-density-scale, 0) * 5px)));\n --_shape-round: 68px;\n --_size-min-height: 136px;\n --_size-min-width: 120px;\n /* 25% across the density range, floored at half. At 50% the inset shrank\n faster than the button did — a -4 sm button was still 24px tall but had\n only 8px of side padding, which read crammed. */\n padding: 0 max(32px, calc(64px + var(--md-sys-density-scale, 0) * 4px));\n gap: max(8px, calc(16px + var(--md-sys-density-scale, 0) * 2px));\n font-family: var(--md-sys-typescale-headline-small-font-family, Roboto, sans-serif);\n font-size: max(18px, calc(24px + var(--md-sys-density-scale, 0) * 1px));\n font-weight: var(--md-sys-typescale-headline-small-font-weight, 400);\n letter-spacing: var(--md-sys-typescale-headline-small-letter-spacing, 0px);\n line-height: max(24px, calc(32px + var(--md-sys-density-scale, 0) * 2px));\n}\n\n/* ==========================================================\n 1b. FULL WIDTH — block-level, fills inline axis\n `full-width` switches the host from inline-flex to flex and stretches\n inline-size to 100% of the containing block. The size's\n --_size-min-height (touch target) is still in force, so the button\n keeps its spec block-size unless --md-button-height overrides it.\n --md-button-width still wins if explicitly set.\n ========================================================== */\n:host(.md-button--full-width) {\n display: flex;\n --_width: var(--md-button-width, 100%);\n}\n\n/* ==========================================================\n 2. SHAPE\n Round (default) · Square (scaled corner radius)\n Round uses --_shape-round (height/2 per size) instead of\n corner-full (9999px) so the browser can smoothly interpolate\n border-radius transitions between resting and morphed states.\n --md-button-container-shape overrides both.\n ========================================================== */\n/* The RESTING radius is named (--_shape-rest) so the morph states below can be\n expressed as a blend back toward it. --_morph-to carries the morphed target\n and defaults to the resting value, so a button with no active morph state\n resolves to exactly its resting radius:\n\n calc(REST + FLAG * (MORPH - REST)) FLAG 1 -> MORPH, FLAG 0 -> REST\n\n That is what makes `data-shape-morph=\"off\"` / `shape-morph=\"off\"` freeze the\n shape (§13 in tokens.css) without a re-render and without :host-context,\n which Firefox does not implement. */\n:host(.md-button--round) {\n --_shape-rest: var(--md-button-container-shape, var(--_shape-round));\n border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest)));\n}\n\n:host(.md-button--square.md-button--xs) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-small, 8px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--sm) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-medium, 12px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--md) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-medium, 12px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--lg) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-large, 16px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n:host(.md-button--square.md-button--xl) { --_shape-rest: var(--md-button-container-shape, var(--md-sys-shape-corner-extra-large, 28px)); border-radius: calc(var(--_shape-rest) + var(--md-sys-shape-morph-enabled, 1) * (var(--_morph-to, var(--_shape-rest)) - var(--_shape-rest))); }\n\n/* ==========================================================\n 2b. SHAPE MORPHING — M3 Expressive\n Uses direct border-radius overrides for reliable transitions.\n\n Order: selected rules FIRST, pressed rules LAST so pressed\n always wins while the pointer is down.\n\n Asymmetric timing (M3 motion tokens):\n Release / selected : medium4 (400ms) + emphasized-decelerate\n Press-in : short4 (200ms) + standard\n ========================================================== */\n\n/* ── Selected shape — round resting → square tokens when selected ── */\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--xs) { --_morph-to: var(--md-sys-shape-corner-small, 8px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--sm) { --_morph-to: var(--md-sys-shape-corner-medium, 12px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--md) { --_morph-to: var(--md-sys-shape-corner-medium, 12px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--lg) { --_morph-to: var(--md-sys-shape-corner-large, 16px); }\n:host(.md-button--shape-morph.md-button--round.md-button--selected.md-button--xl) { --_morph-to: var(--md-sys-shape-corner-extra-large, 28px); }\n\n/* ── Selected shape — square resting → round when selected ── */\n:host(.md-button--shape-morph.md-button--square.md-button--selected) {\n --_morph-to: var(--md-button-container-shape, var(--_shape-round));\n}\n\n/* ── Pressed shape — both round & square morph to more square, overrides selected ── */\n:host(.md-button--pressed.md-button--xs) {\n --_morph-to: var(--md-sys-shape-corner-extra-small, 4px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--sm) {\n --_morph-to: var(--md-sys-shape-corner-small, 8px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--md) {\n --_morph-to: var(--md-sys-shape-corner-small, 8px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--lg) {\n --_morph-to: var(--md-sys-shape-corner-medium, 12px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n:host(.md-button--pressed.md-button--xl) {\n --_morph-to: var(--md-sys-shape-corner-large, 16px);\n transition-duration: var(--md-sys-motion-duration-short4, 200ms);\n transition-timing-function: var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* ==========================================================\n 2c. CONNECTED GROUP — per-corner border-radius\n Set by md-button-group when variant=\"connected\".\n shapeMorph is disabled for connected children so these rules\n only need to beat the base shape rules (section 2, specificity 0-1-0).\n ========================================================== */\n\n:host(.md-button--connected-left.md-button--xs),\n:host(.md-button--connected-right.md-button--xs) { --_c-inner: 4px; }\n:host(.md-button--connected-left.md-button--sm),\n:host(.md-button--connected-right.md-button--sm) { --_c-inner: 8px; }\n:host(.md-button--connected-left.md-button--md),\n:host(.md-button--connected-right.md-button--md) { --_c-inner: 8px; }\n:host(.md-button--connected-left.md-button--lg),\n:host(.md-button--connected-right.md-button--lg) { --_c-inner: 16px; }\n:host(.md-button--connected-left.md-button--xl),\n:host(.md-button--connected-right.md-button--xl) { --_c-inner: 20px; }\n\n/* Middle button — all corners inner */\n:host(.md-button--connected-left.md-button--connected-right) {\n border-radius: var(--_c-inner);\n}\n\n/* `connected-left` / `connected-right` reflect DOM-order siblings (set by\n md-button-group when a previous / next sibling exists), so the radii\n here must use logical corner properties to flip correctly under\n `dir=\"rtl\"`. The outer pill always lives on the free inline-start\n (first child) or inline-end (last child) edge. */\n\n/* First button (round) — pill on inline-start edge, inner on inline-end edge */\n:host(.md-button--round.md-button--connected-right:not(.md-button--connected-left)) {\n border-start-start-radius: var(--_shape-round);\n border-end-start-radius: var(--_shape-round);\n border-start-end-radius: var(--_c-inner);\n border-end-end-radius: var(--_c-inner);\n}\n\n/* Last button (round) — inner on inline-start edge, pill on inline-end edge */\n:host(.md-button--round.md-button--connected-left:not(.md-button--connected-right)) {\n border-start-start-radius: var(--_c-inner);\n border-end-start-radius: var(--_c-inner);\n border-start-end-radius: var(--_shape-round);\n border-end-end-radius: var(--_shape-round);\n}\n\n/* Square first / last — outer equals inner */\n:host(.md-button--square.md-button--connected-right:not(.md-button--connected-left)),\n:host(.md-button--square.md-button--connected-left:not(.md-button--connected-right)) {\n border-radius: var(--_c-inner);\n}\n\n/* ==========================================================\n 3. ICON PADDING\n An icon does NOT change the button's insets. The M3 Expressive size sheet\n specifies the SAME padding on both sides whether or not a leading icon is\n present (12/12, 16/16, 24/24, 48/48, 64/64) — the third number on those\n drawings is the icon-to-label gap, not a reduced leading inset. The size's\n base padding therefore applies symmetrically and there is nothing to override.\n ========================================================== */\n\n/* ==========================================================\n 4. COLOR VARIANTS\n --md-button-container-color and --md-button-label-color\n override the variant defaults when set.\n ========================================================== */\n\n/* Filled */\n:host(.md-button--filled) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-primary));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-primary));\n --_state-layer-color: var(--md-sys-color-on-primary);\n}\n:host(.md-button--filled:hover) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--filled:active) { --_container-elevation: var(--md-sys-elevation-0, none); }\n:host(.md-button--filled:focus-visible) { --_container-elevation: var(--md-sys-elevation-0, none); }\n\n/* Outlined */\n:host(.md-button--outlined) {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n border: var(--md-button-outline-width, 1px) solid var(--md-button-outline-color, var(--md-sys-color-outline));\n}\n/* Variant padding follows the SAME density rule as the base size rules above:\n slope = base/16 (25% shrink across the four rungs) and a floor of base/2.\n These overrides previously decayed at 52-133% per the old tuning — text sm\n lost 133% of its inset and pinned to its floor by rung -2 — so outlined and\n text shrank far faster than the button did. Past rung -2 that INVERTED the\n variants: an outlined button, wider than filled at density 0, became\n narrower than it, and a row of mixed-variant buttons went visibly ragged.\n Density-0 values are unchanged; only the decay was corrected. */\n:host(.md-button--outlined.md-button--xs) { padding: 0 max(5.5px, calc(11px + var(--md-sys-density-scale, 0) * 0.6875px)); }\n:host(.md-button--outlined.md-button--sm) { padding: 0 max(11.5px, calc(23px + var(--md-sys-density-scale, 0) * 1.4375px)); }\n:host(.md-button--outlined.md-button--md) { padding: 0 max(13.5px, calc(27px + var(--md-sys-density-scale, 0) * 1.6875px)); }\n:host(.md-button--outlined.md-button--lg) { padding: 0 max(15.5px, calc(31px + var(--md-sys-density-scale, 0) * 1.9375px)); }\n:host(.md-button--outlined.md-button--xl) { padding: 0 max(23.5px, calc(47px + var(--md-sys-density-scale, 0) * 2.9375px)); }\n:host(.md-button--outlined:hover:not(.md-button--disabled)) {\n border-color: var(--md-button-outline-color, var(--md-sys-color-primary));\n}\n:host(.md-button--outlined:focus-visible) {\n border-color: var(--md-button-outline-color, var(--md-sys-color-primary));\n}\n\n/* Text */\n:host(.md-button--text) {\n --_container-color: var(--md-button-container-color, transparent);\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n}\n/* Text buttons intentionally carry the SAME horizontal padding as every other\n variant (the base size rules above); there is deliberately no override here.\n They used to sit far tighter — a text button's inset was a third of a\n filled one's and pinned to its floor at tight densities (4px by rung -2).\n The container is invisible at rest, but the hover/press state layer is not:\n it paints the padding box, so a text button in a toggle strip beside a\n tonal or filled one showed a cramped pill hugging its label while its\n neighbour's was roomy, and the mismatch grew with density. Sharing the base\n padding keeps every state layer in a strip the same shape, and keeps a\n button's width stable if its variant changes to signal selection. */\n\n/* Elevated */\n:host(.md-button--elevated) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-low));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n --_container-elevation: var(--md-sys-elevation-1);\n}\n:host(.md-button--elevated:hover) { --_container-elevation: var(--md-sys-elevation-2); }\n:host(.md-button--elevated:focus-visible) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--elevated:active) { --_container-elevation: var(--md-sys-elevation-1); }\n\n/* Tonal */\n:host(.md-button--tonal) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-secondary-container));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-secondary-container));\n --_state-layer-color: var(--md-sys-color-on-secondary-container);\n}\n:host(.md-button--tonal:hover) { --_container-elevation: var(--md-sys-elevation-1); }\n:host(.md-button--tonal:focus-visible) { --_container-elevation: var(--md-sys-elevation-0, none); }\n:host(.md-button--tonal:active) { --_container-elevation: var(--md-sys-elevation-0, none); }\n\n/* ==========================================================\n 4b. TOGGLE SELECTED / UNSELECTED — MD3 spec\n When toggle=true, the unselected state is muted; selected\n shows the full variant color. Mirrors md-icon-button pattern.\n ========================================================== */\n\n/* The toggle-state rules below all carry `:not(.md-button--disabled)` so\n they drop out when the host becomes disabled. Without it, their higher\n specificity (4 classes vs the disabled rules' 2–3) would override the\n muted disabled palette and the button would still look enabled. */\n\n/* ── Filled toggle ── */\n:host(.md-button--filled.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-primary));\n --_state-layer-color: var(--md-sys-color-primary);\n}\n\n/* ── Tonal toggle ── */\n:host(.md-button--tonal.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n}\n\n/* ── Outlined toggle ── */\n:host(.md-button--outlined.md-button--toggle.md-button--selected:not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-secondary-container, #E8DEF8));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-secondary-container, #1E192B));\n --_state-layer-color: var(--md-sys-color-on-secondary-container, #1E192B);\n border-color: transparent;\n}\n\n/* ── Elevated toggle ── */\n:host(.md-button--elevated.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_container-color: var(--md-button-container-color, var(--md-sys-color-surface-container-highest, #E6E0E9));\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n --_container-elevation: var(--md-sys-elevation-0, none);\n}\n\n/* ── Text toggle ── */\n:host(.md-button--text.md-button--toggle:not(.md-button--selected):not(.md-button--disabled)) {\n --_label-color: var(--md-button-label-color, var(--md-sys-color-on-surface-variant, #49454F));\n --_state-layer-color: var(--md-sys-color-on-surface-variant, #49454F);\n}\n\n/* ==========================================================\n 5. STATE LAYER\n ========================================================== */\n.md-button__state-layer {\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-color: var(--_state-layer-color);\n opacity: 0;\n transition: opacity var(--md-sys-motion-duration-short2, 100ms) var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n pointer-events: none;\n}\n:host(:hover) .md-button__state-layer {\n opacity: var(--md-sys-state-hover-state-layer-opacity, 0.08);\n}\n:host(:active:not(.md-button--disabled)) .md-button__state-layer {\n opacity: var(--md-sys-state-pressed-state-layer-opacity, 0.12);\n transition-duration: var(--md-sys-motion-duration-short1, 50ms);\n transition-timing-function: var(--md-sys-motion-easing-emphasized-decelerate, cubic-bezier(0.05, 0.7, 0.1, 1));\n}\n:host(:focus-visible) .md-button__state-layer {\n opacity: var(--md-sys-state-focus-state-layer-opacity, 0.12);\n}\n\n/* ==========================================================\n 6. FOCUS RING\n `z-index: 1` raises the focused button above its siblings so the\n outline is never clipped by a neighbor — important inside\n md-button-group, where connected groups have only 2px between\n buttons and standard groups animate neighbor padding on press.\n ========================================================== */\n/* Connected children inside md-button-group sit only 2px apart, so a\n 3px-outset focus ring naturally extends across the neighbor. The\n `z-index: 1` raise lets it draw *on top* of the neighbor cleanly so\n the ring keeps its full 2px breathing room — same visual treatment\n as a stand-alone button. */\n:host(:focus-visible) {\n outline-color: var(--md-sys-color-secondary);\n z-index: 1;\n}\n\n/* ==========================================================\n 7. DISABLED\n ========================================================== */\n:host(.md-button--disabled) {\n cursor: default;\n pointer-events: none;\n --_container-elevation: var(--md-sys-elevation-0, none) !important;\n transition: none;\n}\n\n:host([soft-disabled]) {\n cursor: default;\n pointer-events: auto;\n}\n:host([soft-disabled]) .md-button__state-layer { display: none; }\n\n:host(.md-button--filled.md-button--disabled),\n:host(.md-button--tonal.md-button--disabled) {\n --_container-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n}\n:host(.md-button--elevated.md-button--disabled) {\n --_container-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n --_container-elevation: var(--md-sys-elevation-0, none);\n}\n\n:host(.md-button--disabled) {\n --_label-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity, 0.38) * 100%), transparent);\n}\n\n:host(.md-button--outlined.md-button--disabled) {\n --_container-color: transparent;\n border-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity, 0.12) * 100%), transparent);\n}\n\n:host(.md-button--text.md-button--disabled) {\n --_container-color: transparent;\n}\n\n:host(.md-button--disabled:hover) .md-button__state-layer,\n:host(.md-button--disabled:active) .md-button__state-layer,\n:host(.md-button--disabled:focus-visible) .md-button__state-layer {\n opacity: 0;\n}\n\n/* ==========================================================\n 8. ICON (prop-based fallback inside slot)\n ========================================================== */\n.md-button__icon,\n.md-button__trailing-icon {\n color: var(--md-button-icon-color, inherit);\n font-size: var(--_icon-size, 18px);\n width: var(--_icon-size, 18px);\n height: var(--_icon-size, 18px);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n/* ==========================================================\n 9. NAMED ICON SLOTS\n Slots use display:contents so they don't create a box;\n slotted content participates directly in flex layout.\n ========================================================== */\nslot[name=\"leading-icon\"],\nslot[name=\"trailing-icon\"] {\n display: contents;\n}\n\n::slotted([slot=\"leading-icon\"]),\n::slotted([slot=\"trailing-icon\"]) {\n color: var(--md-button-icon-color, inherit);\n width: var(--_icon-size, 18px);\n height: var(--_icon-size, 18px);\n flex-shrink: 0;\n}\n\n/* ----------------------------------------------------------\n Trailing-icon rotation when this button is a popover anchor.\n\n md-menu (and any other popup component using the same ARIA\n contract) sets aria-expanded=\"true\" on its anchor element\n while the popup is open. Rotating the trailing dropdown\n indicator 180° on that signal flips arrow_drop_down to\n point up, matching MD3 select-trigger behavior, with no\n wiring needed on the consumer side.\n\n Honors prefers-reduced-motion via short2 motion duration.\n ---------------------------------------------------------- */\n.md-button__trailing-icon,\n::slotted([slot=\"trailing-icon\"]) {\n transition: transform var(--md-sys-motion-duration-short2, 150ms)\n var(--md-sys-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n:host([aria-expanded=\"true\"]:not([suppress-expand-icon-flip])) .md-button__trailing-icon,\n:host([aria-expanded=\"true\"]:not([suppress-expand-icon-flip])) ::slotted([slot=\"trailing-icon\"]) {\n transform: rotate(180deg);\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n\n .md-button__state-layer {\n transition: none;\n }\n\n .md-button__trailing-icon,\n ::slotted([slot=\"trailing-icon\"]) {\n transition: none;\n }\n}\n\n/* ==========================================================\n 10. LABEL\n ========================================================== */\n.md-button__label {\n display: flex;\n align-items: center;\n white-space: nowrap;\n}\n\n/* ==========================================================\n 11. LOADING SPINNER (scales with icon size)\n ========================================================== */\n.md-button__loading {\n /* A FRACTION of the icon it stands in for. Two constraints pull against each\n other: at full --_icon-size the ring reads heavier than the glyph (a bare\n stroke has none of the internal padding a Material glyph carries), but at\n 1em it was pinned to the label and stopped scaling per tier -- sm and md\n share a 14px label, so a 40px and a 56px button drew the same 14px ring\n despite 20px vs 24px icons. 0.7 keeps it visibly lighter than a glyph while\n tracking the tier, and inherits the icon's density taper. */\n /* 10px floor: the icon token itself bottoms out at 10px on the small tiers,\n and 0.7 of that is a 7px ring — too small to read as a spinner at all. */\n --_loading-size: var(--md-button-loading-size, max(10px, calc(var(--_icon-size, 18px) * 0.7)));\n\n /* Hand the size straight to md-loading-indicator's PUBLIC hooks. These\n replace the child's whole default expression, so the button's own taper\n (and its 10px floor) survives instead of hitting the child's 32px floor.\n Not --_size / --_shape-size: those are the child's private vars and\n writing them from out here is the handoff trap. currentColor keeps the\n indicator matching each variant's label, which a token would not. */\n --md-loading-indicator-size: var(--_loading-size);\n --md-loading-indicator-shape-size: var(--_loading-size);\n --md-loading-indicator-color: currentColor;\n flex-shrink: 0;\n /* A wrapper now, so the hooks above reach a SLOTTED replacement too. It has\n to lay out like the indicator it replaced — an inline span would add a text\n baseline gap next to the label. */\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n/* Whatever the consumer slots in gets the same box the default occupies. */\n.md-button__loading ::slotted(*) {\n flex-shrink: 0;\n}\n\n/* ==========================================================\n 12. MATERIAL SYMBOLS (default icon font)\n ========================================================== */\n.material-symbols-outlined {\n font-family: var(--md-sys-icon-font-family, 'Material Symbols Outlined', 'Material Icons');\n font-weight: normal;\n font-style: normal;\n font-size: var(--_icon-size, 18px);\n line-height: 1;\n letter-spacing: normal;\n text-transform: none;\n display: inline-block;\n white-space: nowrap;\n overflow-wrap: normal;\n direction: ltr;\n font-feature-settings: 'liga';\n -webkit-font-smoothing: antialiased;\n}\n\n/* ==========================================================\n 13. RTL — opt-in directional-icon mirroring\n\n Buttons that show directional Material Symbols (arrow_forward,\n chevron_right, send, reply…) should set `mirror-icon` so the\n glyph itself flips alongside the leading/trailing slot swap that\n the logical-property layout already performs. Non-directional\n icons (add, search, favorite…) leave `mirror-icon` unset so the\n glyph stays semantically correct.\n\n Two rules cover both placements of `dir=\"rtl\"`:\n (a) on the host itself — `:host([dir=\"rtl\"])`\n (b) on any ancestor element — `:host-context([dir=\"rtl\"])`\n ========================================================== */\n:host([mirror-icon][dir=\"rtl\"]) .md-button__icon,\n:host([mirror-icon][dir=\"rtl\"]) .md-button__trailing-icon,\n:host([mirror-icon][dir=\"rtl\"]) ::slotted([slot=\"leading-icon\"]),\n:host([mirror-icon][dir=\"rtl\"]) ::slotted([slot=\"trailing-icon\"]) {\n transform: scaleX(-1);\n}\n\n:host-context([dir=\"rtl\"]):host([mirror-icon]) .md-button__icon,\n:host-context([dir=\"rtl\"]):host([mirror-icon]) .md-button__trailing-icon,\n:host-context([dir=\"rtl\"]):host([mirror-icon]) ::slotted([slot=\"leading-icon\"]),\n:host-context([dir=\"rtl\"]):host([mirror-icon]) ::slotted([slot=\"trailing-icon\"]) {\n transform: scaleX(-1);\n}\n\n/* ==========================================================\n DENSITY (local override)\n Maps the local `density` prop onto the same --md-sys-density-scale signal a\n global `data-density` ancestor sets. Declared on :host, so a local rung wins\n over the inherited global one. Every density-sensitive value in this file\n derives from that single signal by calc — never add a fixed per-rung ladder.\n ========================================================== */\n:host([density='-1']) { --md-sys-density-scale: -1; }\n:host([density='-2']) { --md-sys-density-scale: -2; }\n:host([density='-3']) { --md-sys-density-scale: -3; }\n:host([density='-4']) { --md-sys-density-scale: -4; }\n\n/* ==========================================================\n LINK MODE — the anchor rendered when `href` is set\n\n The anchor wraps the button's innards so the LABEL gives it its accessible\n name, and it carries the tab stop (the host drops role/tabindex in this\n mode). It reproduces the host's own flex row rather than sitting inside it,\n so a link button lays out identically to a plain one.\n\n `::after` stretches the hit area over the host's padding: the anchor itself\n only fills the CONTENT box, so without it a click on the padding — a good\n third of a large button — would miss the link and lose native activation.\n The host is already `position: relative`, so the inset resolves to it.\n ========================================================== */\n.md-button__anchor {\n display: flex;\n align-items: center;\n justify-content: center;\n /* Not inherited by default; picking up the host's computed value keeps the\n icon/label spacing identical to a non-link button at every density. */\n gap: inherit;\n inline-size: 100%;\n block-size: 100%;\n min-inline-size: 0;\n /* A button is not underlined blue text. */\n color: inherit;\n font: inherit;\n text-decoration: none;\n outline: none;\n}\n\n.md-button__anchor::after {\n content: '';\n position: absolute;\n inset: 0;\n /* Behind the state layer and ripple so neither is occluded. */\n z-index: 0;\n}\n\n/* The focus ring belongs to the whole button, not the inner content box. */\n:host(:has(.md-button__anchor:focus-visible)) {\n outline: var(--md-sys-state-focus-ring-width, 3px) solid\n var(--md-button-focus-ring-color, var(--md-sys-color-secondary, #625B71));\n outline-offset: var(--md-sys-state-focus-ring-offset, 2px);\n}\n\n/* ==========================================================\n HIDDEN\n `[hidden]` is a UA-stylesheet rule, and every `:host` display\n declaration above is an AUTHOR rule — author origin beats UA, so without\n this guard `el.hidden = true` left the component fully laid out.\n `!important` because state rules in this file set `display` at equal or\n higher specificity than `:host([hidden])`.\n ========================================================== */\n:host([hidden]) {\n display: none !important;\n}\n","import { Component, Fragment, Host, h, Prop, State, Event, EventEmitter, Element, AttachInternals } from '@stencil/core';\nimport { LazyLoadingIndicator } from '../../utils/lazy-loading-indicator';\nimport { triggerRipple } from '../../utils/ripple';\nimport { sanitizeHref, SAFE_LINK_REL, SAFE_WINDOW_FEATURES } from '../../utils/url';\n\n/**\n * Detail payload emitted by `md-button`'s `mdClick` event when the user\n * activates the button (mouse, touch, or keyboard).\n *\n * The event is **cancelable** — calling `event.preventDefault()` from a\n * listener stops the button's default side effects (toggle-state flip\n * and `href` navigation) but does NOT stop the underlying DOM click\n * event. This is the recommended hook for SPA routing and \"are you\n * sure?\" confirmation flows.\n */\nexport interface MdButtonClickDetail {\n /** The button's `value` prop (useful when grouped via `md-button-group`). */\n value: string;\n /** Whether toggle mode is enabled. */\n toggle: boolean;\n /**\n * Selected state the button WILL transition to if the click is not\n * cancelled. In non-toggle mode this is identical to `selected`.\n */\n selected: boolean;\n /** Navigation target (empty if the button is not a link). */\n href: string;\n /** Browsing context (`_self`, `_blank`, …). */\n target: string;\n /** The original DOM event that triggered the activation. */\n originalEvent: MouseEvent | KeyboardEvent;\n}\n\n/**\n * Detail payload emitted by `md-button`'s `mdChange` event when toggle\n * mode flips the selected state in response to a user activation.\n */\nexport interface MdButtonChangeDetail {\n /** New selected state. */\n selected: boolean;\n /** The button's `value` prop. */\n value: string;\n}\n\n@Component({\n tag: 'md-button',\n styleUrl: 'md-button.css',\n shadow: true,\n formAssociated: true,\n})\nexport class MdButton {\n @Element() el!: HTMLElement;\n\n /**\n * Form-association handle. Used to submit/reset the associated form when\n * `type` is `submit`/`reset` — works across shadow boundaries, unlike a\n * native `<button>` rendered in the shadow root (which can't see the\n * light-DOM `<form>` it lives in).\n */\n @AttachInternals() internals!: ElementInternals;\n\n /**\n * Activation behavior, mirroring the native `<button type>`:\n * - `button` (default): no implicit form action.\n * - `submit`: submits the associated form (`requestSubmit`, so the form's\n * `submit` event fires and constraint validation runs).\n * - `reset`: resets the associated form.\n */\n @Prop({ reflect: true }) type: 'button' | 'submit' | 'reset' = 'button';\n\n @Prop() variant: 'filled' | 'outlined' | 'text' | 'elevated' | 'tonal' = 'filled';\n @Prop() size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'sm';\n @Prop() shape: 'round' | 'square' = 'round';\n @Prop({ reflect: true }) disabled: boolean = false;\n @Prop({ reflect: true, attribute: 'soft-disabled' }) softDisabled: boolean = false;\n @Prop() ripple: boolean = true;\n @Prop() loading: boolean = false;\n @Prop() icon: string = '';\n @Prop() trailingIcon: string = '';\n @Prop() href: string = '';\n @Prop() target: string = '_self';\n\n /** Enable toggle behavior (selected/unselected) */\n @Prop() toggle: boolean = false;\n\n /** Current selected state (toggle mode) */\n @Prop({ mutable: true }) selected: boolean = false;\n\n /** Enable M3 Expressive shape morphing on press and toggle */\n @Prop() shapeMorph: boolean = true;\n\n /**\n * Mirror leading and trailing icons horizontally when the button is\n * rendered in a right-to-left context. Set this on buttons that use\n * **directional** Material Symbols (arrows, chevrons, send, reply…)\n * — non-directional icons (add, search, favorite…) should leave it\n * `false` so the glyph stays semantically correct.\n */\n @Prop({ reflect: true, attribute: 'mirror-icon' }) mirrorIcon: boolean = false;\n\n /**\n * When true, suppresses the default 180° trailing-icon rotation while\n * `aria-expanded=\"true\"`. Use when the consumer swaps the glyph (e.g.\n * docked `md-date-picker` month/year triggers use `chevron_right`).\n */\n @Prop({ reflect: true, attribute: 'suppress-expand-icon-flip' })\n suppressExpandIconFlip: boolean = false;\n\n /**\n * Stretch the button to fill its inline-axis container.\n *\n * Switches the host from `inline-flex` to `flex` and sets\n * `inline-size: 100%` so the button consumes the full width of its\n * parent — the standard pattern for primary CTAs in forms, modals,\n * and bottom sheets. The size's `min-block-size` (touch target) is\n * preserved.\n *\n * For arbitrary fixed widths or heights, set the\n * `--md-button-width`, `--md-button-height`, `--md-button-min-width`,\n * `--md-button-min-height`, or `--md-button-max-width` CSS custom\n * properties on the host instead.\n */\n @Prop({ reflect: true, attribute: 'full-width' }) fullWidth: boolean = false;\n\n /** @internal Set by md-button-group — left side is connected to a neighbor */\n @Prop({ reflect: true }) connectedLeft: boolean = false;\n\n /** @internal Set by md-button-group — right side is connected to a neighbor */\n @Prop({ reflect: true }) connectedRight: boolean = false;\n\n /** @internal Value for button-group association */\n @Prop() value: string = '';\n\n /**\n * @internal Roving-tabindex override set by `md-button-group`. When the\n * button is a child of a group, the group sets this to `0` on the\n * currently-active button and `-1` on the rest, so Tab enters the group\n * once instead of stepping through every button. Outside a group, this\n * stays `null` and the button uses its normal tabindex behavior.\n */\n @Prop({ mutable: true, reflect: false }) groupTabindex: number | null = null;\n\n /**\n * Override the default `role=\"button\"`. Used by composite widgets such as\n * `md-date-picker` day cells (`role=\"gridcell\"`). Leave empty for `button`.\n */\n @Prop({ attribute: 'role-override' }) roleOverride: string = '';\n\n /**\n * Local density rung. Drives the same `--md-sys-density-scale` signal that a\n * global `data-density` ancestor sets, so a local value simply overrides the\n * inherited one. 0 = default, -4 = ultra-compact.\n */\n @Prop({ reflect: true }) density: 0 | -1 | -2 | -3 | -4 = 0;\n\n /**\n * Fires every time the user activates the button (mouse click, touch,\n * or `Enter`/`Space` while focused). The event is **cancelable** and\n * the detail payload describes what *would* happen: the post-click\n * toggle state, the navigation `href`, and so on.\n *\n * Calling `event.preventDefault()` from a listener suppresses the\n * default side effects (toggle flip + `href` navigation) but lets\n * the underlying DOM click bubble. This is the hook to use for SPA\n * routing, \"are you sure?\" prompts, async confirmation, etc.\n *\n * The event bubbles and is composed, so listeners outside the\n * shadow tree (the typical case) receive it.\n *\n * ```ts\n * document.querySelector('md-button')!.addEventListener('mdClick', (e) => {\n * const { href, selected } = (e as CustomEvent).detail;\n * if (!confirm('Continue?')) e.preventDefault();\n * });\n * ```\n */\n @Event({ cancelable: true, bubbles: true, composed: true })\n mdClick!: EventEmitter<MdButtonClickDetail>;\n\n /**\n * Fires when toggle mode flips the `selected` state in response to a\n * user activation. Not cancelable — the state change has already\n * happened. Pair with `mdClick` (cancelable) when you need a veto.\n *\n * Bubbles and is composed.\n */\n @Event({ bubbles: true, composed: true })\n mdChange!: EventEmitter<MdButtonChangeDetail>;\n\n @State() private hasSlottedLeadingIcon = false;\n @State() private hasSlottedTrailingIcon = false;\n @State() private pressed = false;\n\n private get isDisabled() {\n return this.disabled || this.softDisabled || this.loading;\n }\n\n componentDidLoad() {\n const shadow = this.el.shadowRoot;\n if (!shadow) return;\n\n const leadingSlot = shadow.querySelector('slot[name=\"leading-icon\"]') as HTMLSlotElement | null;\n const trailingSlot = shadow.querySelector('slot[name=\"trailing-icon\"]') as HTMLSlotElement | null;\n\n leadingSlot?.addEventListener('slotchange', () => {\n this.hasSlottedLeadingIcon = leadingSlot.assignedElements().length > 0;\n });\n trailingSlot?.addEventListener('slotchange', () => {\n this.hasSlottedTrailingIcon = trailingSlot.assignedElements().length > 0;\n });\n }\n\n private handleClick = (e: MouseEvent) => {\n if (this.isDisabled) { e.preventDefault(); return; }\n\n // Compute the post-click toggle state up front so the detail\n // payload describes what *would* happen — listeners can then\n // call preventDefault on the CustomEvent to veto the change.\n const nextSelected = this.toggle ? !this.selected : this.selected;\n\n const evt = this.mdClick.emit({\n value: this.value,\n toggle: this.toggle,\n selected: nextSelected,\n href: this.href,\n target: this.target,\n originalEvent: e,\n });\n\n if (evt.defaultPrevented) {\n // Suppress browser's default link navigation as well so the\n // host stays consistent. We don't flip selected and don't\n // open the href.\n e.preventDefault();\n return;\n }\n\n if (this.toggle && nextSelected !== this.selected) {\n this.selected = nextSelected;\n this.mdChange.emit({ selected: nextSelected, value: this.value });\n }\n\n /* Setting `href` makes this a link rather than a form control, so the\n branch is taken on the raw prop — an unsafe URL must not silently fall\n through to submitting the form. Only the navigation itself is refused. */\n if (this.href) {\n /* A safe href renders a REAL <a> in the shadow root, and the browser\n owns activation from there — so do nothing here. That is what makes\n middle-click, cmd/ctrl-click and \"copy link address\" work, and it is\n why cancelling mdClick above (which calls preventDefault on this very\n event) still vetoes navigation.\n\n The fallback below covers only the case where NO anchor exists: an\n href that failed sanitising renders none, so there is nothing to\n navigate and nothing to fall back to — the navigation is refused, as\n before. window.open remains for a programmatic click() on a button\n whose href is safe but whose anchor has not rendered yet. */\n const href = sanitizeHref(this.href);\n if (href && !this.el.shadowRoot?.querySelector('.md-button__anchor')) {\n window.open(href, this.target, SAFE_WINDOW_FEATURES);\n }\n return;\n }\n\n // Implicit form action, mirroring a native submit/reset button. Uses\n // ElementInternals so the associated <form> is found across the shadow\n // boundary. requestSubmit() (not submit()) so the form's submit event\n // fires and built-in constraint validation runs.\n if (this.type === 'submit') {\n this.internals.form?.requestSubmit();\n } else if (this.type === 'reset') {\n this.internals.form?.reset();\n }\n };\n\n private handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (!this.isDisabled) {\n this.pressed = true;\n triggerRipple(this.el);\n this.el.click();\n setTimeout(() => { this.pressed = false; }, 150);\n }\n }\n };\n\n private handlePointerDown = () => {\n if (!this.isDisabled) this.pressed = true;\n };\n\n private handlePointerUp = () => {\n this.pressed = false;\n };\n\n render() {\n const isEffectivelyDisabled = this.isDisabled;\n const hasLeadingIcon = !!this.icon;\n const hasTrailingIcon = !!this.trailingIcon;\n /* A component that accepts `href` must produce a real link. Sanitising\n here (not just at click time) means an unsafe URL renders NO anchor at\n all rather than an inert one. Disabled link-buttons also drop the\n anchor: there is no disabled state for <a>, and a focusable link that\n refuses to navigate is worse than no link. */\n const linkHref =\n this.href && !isEffectivelyDisabled ? sanitizeHref(this.href) : '';\n /* rel only where it means something: a target that opens a NEW browsing\n context. Blanket noreferrer would also strip the Referer on ordinary\n same-tab navigation, quietly breaking referrer-based analytics. */\n const opensNewContext = !!this.target && this.target !== '_self';\n\n return (\n <Host\n class={{\n 'md-button': true,\n [`md-button--${this.variant}`]: true,\n [`md-button--${this.size}`]: true,\n [`md-button--${this.shape}`]: true,\n 'md-button--with-leading-icon': hasLeadingIcon || this.hasSlottedLeadingIcon,\n 'md-button--with-trailing-icon': hasTrailingIcon || this.hasSlottedTrailingIcon,\n 'md-button--disabled': this.disabled || this.softDisabled,\n 'md-button--loading': this.loading,\n 'md-button--toggle': this.toggle,\n 'md-button--selected': this.selected,\n 'md-button--shape-morph': this.shapeMorph,\n 'md-button--pressed': this.shapeMorph && this.pressed,\n 'md-button--connected-left': this.connectedLeft,\n 'md-button--connected-right': this.connectedRight,\n 'md-button--full-width': this.fullWidth,\n }}\n role={this.roleOverride || (linkHref ? null : 'button')}\n aria-disabled={isEffectivelyDisabled ? 'true' : 'false'}\n aria-pressed={this.toggle ? String(this.selected) : undefined}\n tabindex={\n /* When a real anchor is rendered IT is the focusable element, so the\n host must not be a tab stop as well — otherwise every link button\n costs two presses of Tab and screen readers meet a button wrapping\n a link. */\n linkHref\n ? null\n : this.disabled\n ? '-1'\n : (this.groupTabindex !== null ? String(this.groupTabindex) : '0')\n }\n onClick={this.handleClick}\n onKeyDown={this.handleKeyDown}\n onPointerDown={this.handlePointerDown}\n onPointerUp={this.handlePointerUp}\n onPointerLeave={this.handlePointerUp}\n onPointerCancel={this.handlePointerUp}\n >\n {linkHref ? (\n <a\n class=\"md-button__anchor\"\n part=\"anchor\"\n role=\"link\"\n href={linkHref}\n target={this.target || undefined}\n rel={opensNewContext ? SAFE_LINK_REL : undefined}\n tabindex={this.groupTabindex !== null ? String(this.groupTabindex) : '0'}\n aria-disabled={isEffectivelyDisabled ? 'true' : null}\n >\n {this.renderContent(isEffectivelyDisabled, hasLeadingIcon, hasTrailingIcon)}\n </a>\n ) : (\n this.renderContent(isEffectivelyDisabled, hasLeadingIcon, hasTrailingIcon)\n )}\n </Host>\n );\n }\n\n /** Button innards, rendered either directly in the host or inside the\n * anchor when `href` makes this a link. */\n private renderContent(\n isEffectivelyDisabled: boolean,\n hasLeadingIcon: boolean,\n hasTrailingIcon: boolean,\n ) {\n return (\n <Fragment>\n {this.ripple && <md-ripple disabled={isEffectivelyDisabled}></md-ripple>}\n <span class=\"md-button__state-layer\" part=\"state-layer\" aria-hidden=\"true\"></span>\n <slot name=\"leading-icon\">\n {hasLeadingIcon && <span class=\"md-button__icon material-symbols-outlined\" part=\"icon\">{this.icon}</span>}\n </slot>\n <span class=\"md-button__label\" part=\"label\"><slot /></span>\n <slot name=\"trailing-icon\">\n {hasTrailingIcon && <span class=\"md-button__trailing-icon material-symbols-outlined\" part=\"trailing-icon\">{this.trailingIcon}</span>}\n </slot>\n {this.loading && (\n /* md-loading-indicator, not a hand-rolled ring: never ship a custom\n loading affordance where a library component exists. It is\n md-loading-indicator rather than md-progress-indicator because the\n latter clamps its circular variant to a 24px M3 minimum, while a\n button spinner runs 10-28px -- every tier but xl would clamp up and\n lose the density taper. Same choice md-select made for its trigger.\n\n The `loader` slot lets a consumer swap it (e.g. for a circular\n md-progress-indicator). The size hooks sit on this WRAPPER so they\n cascade to whatever ends up inside it, default or slotted. */\n <span class=\"md-button__loading\" part=\"loading\" aria-hidden=\"true\">\n <slot name=\"loader\">\n <LazyLoadingIndicator class=\"md-button__loading-default\" />\n </slot>\n </span>\n )}\n </Fragment>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as t,c as e,h as s,H as o,g as n}from"./p-C5Hg-PUP.js";const i=()=>`:host{display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;box-sizing:border-box;vertical-align:middle}@media (prefers-reduced-motion: reduce){:host{transition:none}}@media (forced-colors: active){:host{forced-color-adjust:none}}:host([hidden]){display:none !important}`;const r=class{constructor(s){t(this,s);this.mdChange=e(this,"mdChange",7);this.multiselect=false;this.density=0}onPropsChange(){this.syncSegments()}componentDidLoad(){this.syncSegments()}handleSegmentClick(t){t.stopPropagation();const e=this.getSegments();const s=t.target;if(!s)return;const{selected:o}=t.detail;if(this.multiselect){s.selected=o}else{for(const t of e){t.selected=t===s}}this.mdChange.emit(e.filter((t=>t.selected)).map((t=>t.value)))}getSegments(){return Array.from(this.el.querySelectorAll("md-segmented-button"))}syncSegments(){const t=this.getSegments();const e=t.length;for(let s=0;s<e;s++){const o=t[s];o.segmentIndex=s;o.segmentTotal=e;o.segmentDensity=this.density;o.segmentMultiselect=this.multiselect}}render(){return s(o,{key:"3910452445a90d64e6d54a1eb1c200ccea857093",class:{"md-segmented-button-set":true,[`md-segmented-button-set--density-${Math.abs(this.density)}`]:this.density!==0},role:this.multiselect?"group":"radiogroup"},s("slot",{key:"b5c88b4eaaac8dbde890492afea86d1f801f1dc7"}))}get el(){return n(this)}static get watchers(){return{multiselect:[{onPropsChange:0}],density:[{onPropsChange:0}]}}};r.style=i();export{r as md_segmented_button_set};
|
|
2
|
-
//# sourceMappingURL=p-56ea09a4.entry.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["mdSegmentedButtonSetCss","MdSegmentedButtonSet","constructor","hostRef","this","multiselect","density","onPropsChange","syncSegments","componentDidLoad","handleSegmentClick","event","stopPropagation","segments","getSegments","clicked","target","selected","detail","seg","mdChange","emit","filter","s","map","value","Array","from","el","querySelectorAll","total","length","i","segmentIndex","segmentTotal","segmentDensity","segmentMultiselect","render","h","Host","key","class","Math","abs","role"],"sources":["src/components/md-segmented-button-set/md-segmented-button-set.css?tag=md-segmented-button-set&encapsulation=shadow","src/components/md-segmented-button-set/md-segmented-button-set.tsx"],"sourcesContent":["/* ==========================================================\n md-segmented-button-set — Container for segmented buttons\n Spec: https://m3.material.io/components/segmented-buttons/specs\n ========================================================== */\n\n:host {\n display: inline-grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n box-sizing: border-box;\n vertical-align: middle;\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n transition: none;\n }\n}\n\n@media (forced-colors: active) {\n :host {\n forced-color-adjust: none;\n }\n}\n\n/* ==========================================================\n HIDDEN\n `[hidden]` is a UA-stylesheet rule, and every `:host` display\n declaration above is an AUTHOR rule — author origin beats UA, so without\n this guard `el.hidden = true` left the component fully laid out.\n `!important` because state rules in this file set `display` at equal or\n higher specificity than `:host([hidden])`.\n ========================================================== */\n:host([hidden]) {\n display: none !important;\n}\n","import { Component, Host, h, Prop, Element, Event, EventEmitter, Listen, Watch } from '@stencil/core';\nimport { SegmentElement } from '../../utils/types';\n\n@Component({\n tag: 'md-segmented-button-set',\n styleUrl: 'md-segmented-button-set.css',\n shadow: true,\n})\nexport class MdSegmentedButtonSet {\n @Element() el!: HTMLElement;\n\n /** Single-select or multi-select mode */\n @Prop() multiselect: boolean = false;\n\n /** Density: 0 = 40px, -1 = 36px, -2 = 32px, -3 = 28px, -4 = 24px */\n @Prop({ reflect: true }) density: 0 | -1 | -2 | -3 | -4 = 0;\n\n /** Fires when the set of selected segment values changes */\n @Event() mdChange: EventEmitter<string[]>;\n\n @Watch('multiselect')\n @Watch('density')\n onPropsChange() {\n this.syncSegments();\n }\n\n componentDidLoad() {\n this.syncSegments();\n }\n\n @Listen('mdSegmentClick')\n handleSegmentClick(event: CustomEvent<{ value: string; selected: boolean }>) {\n event.stopPropagation();\n const segments = this.getSegments();\n // Match by element identity, not by `.value`. The child segment\n // emits `mdSegmentClick` from its own host, and the event re-targets\n // to that <md-segmented-button> as it crosses the shadow boundary\n // into this set. Matching by value would break whenever segments\n // share the default value of '' (which is the common case when\n // consumers omit the `value` prop — every segment would toggle\n // together because every `'' === ''` is true).\n const clicked = event.target as SegmentElement | null;\n if (!clicked) return;\n\n const { selected } = event.detail;\n\n if (this.multiselect) {\n clicked.selected = selected;\n } else {\n for (const seg of segments) {\n seg.selected = seg === clicked;\n }\n }\n\n this.mdChange.emit(\n segments.filter(s => s.selected).map(s => s.value),\n );\n }\n\n private getSegments(): SegmentElement[] {\n return Array.from(this.el.querySelectorAll('md-segmented-button')) as unknown as SegmentElement[];\n }\n\n private syncSegments() {\n const segments = this.getSegments();\n const total = segments.length;\n\n for (let i = 0; i < total; i++) {\n const seg = segments[i];\n seg.segmentIndex = i;\n seg.segmentTotal = total;\n seg.segmentDensity = this.density;\n seg.segmentMultiselect = this.multiselect;\n }\n }\n\n render() {\n return (\n <Host\n class={{\n 'md-segmented-button-set': true,\n [`md-segmented-button-set--density-${Math.abs(this.density)}`]: this.density !== 0,\n }}\n role={this.multiselect ? 'group' : 'radiogroup'}\n >\n <slot></slot>\n </Host>\n );\n }\n}\n"],"mappings":"gEAAA,MAAMA,EAA0B,IAAM,2R,MCQzBC,EAAoB,MALjC,WAAAC,CAAAC,G,6CASUC,KAAAC,YAAuB,MAGND,KAAAE,QAAiC,CA0E3D,CAnEC,aAAAC,GACEH,KAAKI,c,CAGP,gBAAAC,GACEL,KAAKI,c,CAIP,kBAAAE,CAAmBC,GACjBA,EAAMC,kBACN,MAAMC,EAAWT,KAAKU,cAQtB,MAAMC,EAAUJ,EAAMK,OACtB,IAAKD,EAAS,OAEd,MAAME,SAAEA,GAAaN,EAAMO,OAE3B,GAAId,KAAKC,YAAa,CACpBU,EAAQE,SAAWA,C,KACd,CACL,IAAK,MAAME,KAAON,EAAU,CAC1BM,EAAIF,SAAWE,IAAQJ,C,EAI3BX,KAAKgB,SAASC,KACZR,EAASS,QAAOC,GAAKA,EAAEN,WAAUO,KAAID,GAAKA,EAAEE,Q,CAIxC,WAAAX,GACN,OAAOY,MAAMC,KAAKvB,KAAKwB,GAAGC,iBAAiB,uB,CAGrC,YAAArB,GACN,MAAMK,EAAWT,KAAKU,cACtB,MAAMgB,EAAQjB,EAASkB,OAEvB,IAAK,IAAIC,EAAI,EAAGA,EAAIF,EAAOE,IAAK,CAC9B,MAAMb,EAAMN,EAASmB,GACrBb,EAAIc,aAAeD,EACnBb,EAAIe,aAAeJ,EACnBX,EAAIgB,eAAiB/B,KAAKE,QAC1Ba,EAAIiB,mBAAqBhC,KAAKC,W,EAIlC,MAAAgC,GACE,OACEC,EAACC,EAAI,CAAAC,IAAA,2CACHC,MAAO,CACL,0BAA2B,KAC3B,CAAC,oCAAoCC,KAAKC,IAAIvC,KAAKE,YAAaF,KAAKE,UAAY,GAEnFsC,KAAMxC,KAAKC,YAAc,QAAU,cAEnCiC,EAAA,QAAAE,IAAA,6C","ignoreList":[]}
|