@compsych-ui-components/angular 7.0.20 → 7.0.21

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.
@@ -50,6 +50,15 @@ export class ButtonComponent {
50
50
  get hostFullWidth() {
51
51
  return this.fullWidth;
52
52
  }
53
+ /**
54
+ * Marks the host non-interactive while disabled or loading. The inner `<button>` sets
55
+ * `pointer-events: none` when disabled, so without this a click lands on the host wrapper
56
+ * instead — firing any `(click)` a consumer bound on `<compsych-button>`. Blocking pointer
57
+ * events at the host too makes the disabled state honour its "non-interactive" contract.
58
+ */
59
+ get hostNonInteractive() {
60
+ return this.disabled || this.loading;
61
+ }
53
62
  /** Pre-sanitized icon markup, recomputed on input change (avoids re-sanitizing every CD cycle). */
54
63
  leadingIconSvg = null;
55
64
  trailingIconSvg = null;
@@ -150,7 +159,7 @@ export class ButtonComponent {
150
159
  animation.onfinish = () => ripple.remove();
151
160
  }
152
161
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
153
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ButtonComponent, isStandalone: true, selector: "compsych-button", inputs: { label: "label", variant: "variant", size: "size", leadingIcon: "leadingIcon", trailingIcon: "trailingIcon", type: "type", ariaLabel: "ariaLabel", iconOnly: ["iconOnly", "iconOnly", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], loading: ["loading", "loading", booleanAttribute], fullWidth: ["fullWidth", "fullWidth", booleanAttribute] }, host: { properties: { "class.full-width": "this.hostFullWidth" } }, usesOnChanges: true, ngImport: i0, template: `
162
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ButtonComponent, isStandalone: true, selector: "compsych-button", inputs: { label: "label", variant: "variant", size: "size", leadingIcon: "leadingIcon", trailingIcon: "trailingIcon", type: "type", ariaLabel: "ariaLabel", iconOnly: ["iconOnly", "iconOnly", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], loading: ["loading", "loading", booleanAttribute], fullWidth: ["fullWidth", "fullWidth", booleanAttribute] }, host: { properties: { "class.full-width": "this.hostFullWidth", "class.non-interactive": "this.hostNonInteractive" } }, usesOnChanges: true, ngImport: i0, template: `
154
163
  <button
155
164
  [class]="buttonClasses"
156
165
  [type]="type"
@@ -172,7 +181,7 @@ export class ButtonComponent {
172
181
  <span class="icon" aria-hidden="true" [innerHTML]="trailingIconSvg"></span>
173
182
  }
174
183
  </button>
175
- `, isInline: true, styles: [":host{display:inline-flex}:host(.full-width){display:flex;width:100%}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none;transition:box-shadow .15s ease}.cmp-btn.full-width{width:100%}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn.filled:hover:not(:disabled),.cmp-btn.danger:hover:not(:disabled){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.cmp-btn:focus-visible{outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{pointer-events:none}.cmp-btn:disabled:not(.loading){cursor:not-allowed;background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface);border-color:transparent;box-shadow:none;opacity:.48}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.text:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){background-color:transparent}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){border-color:var(--outline-sys-outline-variant)}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;line-height:0;-webkit-user-select:none;user-select:none;width:16px;height:16px}.cmp-btn.large .icon{width:20px;height:20px}.cmp-btn.xlarge .icon{width:24px;height:24px}.icon svg{width:100%;height:100%}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:32px}.cmp-btn.icon-only.medium{width:40px}.cmp-btn.icon-only.large{width:48px}.cmp-btn.icon-only.xlarge{width:56px}.spinner{border:2px solid currentColor;border-right-color:transparent;border-radius:50%;box-sizing:border-box;animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.small{gap:var(--spacing-padding-sys-padding-4);height:32px;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}.cmp-btn.medium{gap:var(--spacing-padding-sys-padding-8);height:40px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking)}.cmp-btn.large{gap:var(--spacing-padding-sys-padding-8);height:48px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.xlarge{gap:var(--spacing-padding-sys-padding-12);height:56px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.elevated{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 2px 4px #00000014,0 4px 16px var(--transparent-neutral-sys-black-10)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText;background:ButtonFace}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
184
+ `, isInline: true, styles: [":host{display:inline-flex}:host(.full-width){display:flex;width:100%}:host(.non-interactive){pointer-events:none}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none;transition:box-shadow .15s ease}.cmp-btn.full-width{width:100%}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn.filled:hover:not(:disabled),.cmp-btn.danger:hover:not(:disabled){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.cmp-btn:focus-visible{outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{pointer-events:none}.cmp-btn:disabled:not(.loading){cursor:not-allowed;background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface);border-color:transparent;box-shadow:none;opacity:.48}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.text:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){background-color:transparent}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){border-color:var(--outline-sys-outline-variant)}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;line-height:0;-webkit-user-select:none;user-select:none;width:16px;height:16px}.cmp-btn.large .icon{width:20px;height:20px}.cmp-btn.xlarge .icon{width:24px;height:24px}.icon svg{width:100%;height:100%}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:32px}.cmp-btn.icon-only.medium{width:40px}.cmp-btn.icon-only.large{width:48px}.cmp-btn.icon-only.xlarge{width:56px}.spinner{border:2px solid currentColor;border-right-color:transparent;border-radius:50%;box-sizing:border-box;animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.small{gap:var(--spacing-padding-sys-padding-4);height:32px;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}.cmp-btn.medium{gap:var(--spacing-padding-sys-padding-8);height:40px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking)}.cmp-btn.large{gap:var(--spacing-padding-sys-padding-8);height:48px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.xlarge{gap:var(--spacing-padding-sys-padding-12);height:56px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.elevated{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 2px 4px #00000014,0 4px 16px var(--transparent-neutral-sys-black-10)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText;background:ButtonFace}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
176
185
  }
177
186
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
178
187
  type: Component,
@@ -198,7 +207,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
198
207
  <span class="icon" aria-hidden="true" [innerHTML]="trailingIconSvg"></span>
199
208
  }
200
209
  </button>
201
- `, styles: [":host{display:inline-flex}:host(.full-width){display:flex;width:100%}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none;transition:box-shadow .15s ease}.cmp-btn.full-width{width:100%}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn.filled:hover:not(:disabled),.cmp-btn.danger:hover:not(:disabled){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.cmp-btn:focus-visible{outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{pointer-events:none}.cmp-btn:disabled:not(.loading){cursor:not-allowed;background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface);border-color:transparent;box-shadow:none;opacity:.48}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.text:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){background-color:transparent}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){border-color:var(--outline-sys-outline-variant)}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;line-height:0;-webkit-user-select:none;user-select:none;width:16px;height:16px}.cmp-btn.large .icon{width:20px;height:20px}.cmp-btn.xlarge .icon{width:24px;height:24px}.icon svg{width:100%;height:100%}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:32px}.cmp-btn.icon-only.medium{width:40px}.cmp-btn.icon-only.large{width:48px}.cmp-btn.icon-only.xlarge{width:56px}.spinner{border:2px solid currentColor;border-right-color:transparent;border-radius:50%;box-sizing:border-box;animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.small{gap:var(--spacing-padding-sys-padding-4);height:32px;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}.cmp-btn.medium{gap:var(--spacing-padding-sys-padding-8);height:40px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking)}.cmp-btn.large{gap:var(--spacing-padding-sys-padding-8);height:48px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.xlarge{gap:var(--spacing-padding-sys-padding-12);height:56px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.elevated{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 2px 4px #00000014,0 4px 16px var(--transparent-neutral-sys-black-10)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText;background:ButtonFace}}\n"] }]
210
+ `, styles: [":host{display:inline-flex}:host(.full-width){display:flex;width:100%}:host(.non-interactive){pointer-events:none}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none;transition:box-shadow .15s ease}.cmp-btn.full-width{width:100%}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn.filled:hover:not(:disabled),.cmp-btn.danger:hover:not(:disabled){box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.cmp-btn:focus-visible{outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{pointer-events:none}.cmp-btn:disabled:not(.loading){cursor:not-allowed;background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface);border-color:transparent;box-shadow:none;opacity:.48}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.text:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){background-color:transparent}.cmp-btn.outlined:disabled:not(.loading),.cmp-btn.danger-outlined:disabled:not(.loading){border-color:var(--outline-sys-outline-variant)}.icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;line-height:0;-webkit-user-select:none;user-select:none;width:16px;height:16px}.cmp-btn.large .icon{width:20px;height:20px}.cmp-btn.xlarge .icon{width:24px;height:24px}.icon svg{width:100%;height:100%}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:32px}.cmp-btn.icon-only.medium{width:40px}.cmp-btn.icon-only.large{width:48px}.cmp-btn.icon-only.xlarge{width:56px}.spinner{border:2px solid currentColor;border-right-color:transparent;border-radius:50%;box-sizing:border-box;animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.small{gap:var(--spacing-padding-sys-padding-4);height:32px;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}.cmp-btn.medium{gap:var(--spacing-padding-sys-padding-8);height:40px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking)}.cmp-btn.large{gap:var(--spacing-padding-sys-padding-8);height:48px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.xlarge{gap:var(--spacing-padding-sys-padding-12);height:56px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.elevated{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 2px 4px #00000014,0 4px 16px var(--transparent-neutral-sys-black-10)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText;background:ButtonFace}}\n"] }]
202
211
  }], propDecorators: { label: [{
203
212
  type: Input
204
213
  }], variant: [{
@@ -228,5 +237,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
228
237
  }], hostFullWidth: [{
229
238
  type: HostBinding,
230
239
  args: ['class.full-width']
240
+ }], hostNonInteractive: [{
241
+ type: HostBinding,
242
+ args: ['class.non-interactive']
231
243
  }] } });
232
244
  //# sourceMappingURL=button.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/button/button.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n HostBinding,\r\n Input,\r\n OnChanges,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\n\r\n/** Visual style of a `compsych-button`. Controls color, border, and elevation. */\r\nexport type ButtonVariant =\r\n | 'filled'\r\n | 'tonal'\r\n | 'outlined'\r\n | 'elevated'\r\n | 'text'\r\n | 'danger'\r\n | 'danger-outlined';\r\n\r\n/** Button dimensions — controls height, padding, font size, and icon size. */\r\nexport type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge';\r\n\r\n/** Native button behaviour. `'submit'`/`'reset'` make the component a real form control. */\r\nexport type ButtonType = 'button' | 'submit' | 'reset';\r\n\r\n/** Icon pixel size per button size (per design: sm/md 16, lg 20, xl 24). */\r\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<ButtonSize, number> = {\r\n small: 16,\r\n medium: 16,\r\n large: 20,\r\n xlarge: 24,\r\n};\r\n\r\n/**\r\n * A themeable button that renders a native `<button>` element, so it is fully\r\n * form-submittable and keyboard/screen-reader accessible out of the box. Icons\r\n * are Lucide glyphs (matching the rest of the library); a button may carry a\r\n * leading icon, a trailing icon, or both.\r\n */\r\n@Component({\r\n selector: 'compsych-button',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <button\r\n [class]=\"buttonClasses\"\r\n [type]=\"type\"\r\n [disabled]=\"disabled || loading\"\r\n [attr.aria-busy]=\"loading ? 'true' : null\"\r\n [attr.aria-label]=\"computedAriaLabel\"\r\n (pointerdown)=\"onPointerDown($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n >\r\n @if (loading) {\r\n <span class=\"icon spinner\" aria-hidden=\"true\"></span>\r\n } @else if (leadingIcon) {\r\n <span class=\"icon\" aria-hidden=\"true\" [innerHTML]=\"leadingIconSvg\"></span>\r\n }\r\n @if (!iconOnly) {\r\n <span class=\"label\">{{ label }}<ng-content /></span>\r\n }\r\n @if (trailingIcon && !iconOnly) {\r\n <span class=\"icon\" aria-hidden=\"true\" [innerHTML]=\"trailingIconSvg\"></span>\r\n }\r\n </button>\r\n `,\r\n styleUrl: './button.css',\r\n})\r\nexport class ButtonComponent implements OnChanges {\r\n private readonly sanitizer = inject(DomSanitizer);\r\n\r\n /** Text label rendered on the button. */\r\n @Input() label = '';\r\n /** Visual style of the button. Defaults to `'filled'`. */\r\n @Input() variant: ButtonVariant = 'filled';\r\n /** Button dimensions — controls height, padding, font size, and icon size. Defaults to `'small'`. */\r\n @Input() size: ButtonSize = 'small';\r\n /** Lucide icon name shown before the label (e.g. `'house'`). Swapped for a spinner when loading. */\r\n @Input() leadingIcon?: string;\r\n /** Lucide icon name shown after the label (e.g. `'chevron-right'`). Can be combined with `leadingIcon`. */\r\n @Input() trailingIcon?: string;\r\n /** Native button type. Use `'submit'` inside a `<form>` to submit it. Defaults to `'button'`. */\r\n @Input() type: ButtonType = 'button';\r\n /**\r\n * Accessible name. Required for icon-only buttons since the icon is decorative.\r\n * When `iconOnly` or `loading` is set and this is omitted, `label` is used.\r\n */\r\n @Input() ariaLabel?: string;\r\n\r\n /** Hides the label and renders a square icon-only button. Uses `leadingIcon`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) iconOnly = false;\r\n /** Disables the button — non-interactive and rendered in a neutral grey. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /**\r\n * Loading state. Swaps the leading icon for a spinner and suppresses activation\r\n * (native `disabled` blocks click/keyboard), while keeping the label and exposing\r\n * `aria-busy=\"true\"`. Retains the variant's colours. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) loading = false;\r\n /** Stretches the button to the full width of its container. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) fullWidth = false;\r\n\r\n /** Mirrors `fullWidth` onto the host so it can stretch. */\r\n @HostBinding('class.full-width') get hostFullWidth(): boolean {\r\n return this.fullWidth;\r\n }\r\n\r\n /** Pre-sanitized icon markup, recomputed on input change (avoids re-sanitizing every CD cycle). */\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n protected trailingIconSvg: SafeHtml | null = null;\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n this.trailingIconSvg = this.trailingIcon ? this.buildIconSvg(this.trailingIcon) : null;\r\n }\r\n\r\n /** Space-separated class list applied to the inner native button. */\r\n get buttonClasses(): string {\r\n return [\r\n 'cmp-btn',\r\n this.variant,\r\n this.size,\r\n this.iconOnly ? 'icon-only' : '',\r\n this.loading ? 'loading' : '',\r\n this.fullWidth ? 'full-width' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n /** Resolves the accessible name. Icon-only buttons have no visible text, so they fall back to `label`. */\r\n get computedAriaLabel(): string | null {\r\n if (this.ariaLabel) {\r\n return this.ariaLabel;\r\n }\r\n // Loading keeps the visible label, so only icon-only needs an explicit name here.\r\n return this.iconOnly ? this.label || null : null;\r\n }\r\n\r\n /**\r\n * Looks up a Lucide icon by name and injects an explicit width/height so the SVG\r\n * renders at the correct size when injected via `[innerHTML]` (Angular's CSS\r\n * encapsulation does not reach dynamically injected content).\r\n */\r\n private buildIconSvg(name: string): SafeHtml {\r\n const size = ICON_SIZE_BY_BUTTON_SIZE[this.size];\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', `<svg width=\"${size}\" height=\"${size}\" `);\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /** M3 pressed ripple: pointer-down spawns a ripple at the pointer position. */\r\n onPointerDown(event: PointerEvent): void {\r\n if (this.disabled || this.loading || event.button !== 0) {\r\n return;\r\n }\r\n const host = event.currentTarget as HTMLElement;\r\n const rect = host.getBoundingClientRect();\r\n const x = event.clientX - rect.left;\r\n const y = event.clientY - rect.top;\r\n this.spawnRipple(host, x, y, this.farthestCornerDiameter(rect, x, y));\r\n }\r\n\r\n /** M3 pressed ripple: keyboard activation (Enter/Space) spawns a centered ripple. */\r\n onKeyDown(event: KeyboardEvent): void {\r\n if (this.disabled || this.loading || event.repeat) {\r\n return;\r\n }\r\n if (event.key !== 'Enter' && event.key !== ' ') {\r\n return;\r\n }\r\n const host = event.currentTarget as HTMLElement;\r\n const rect = host.getBoundingClientRect();\r\n this.spawnRipple(host, rect.width / 2, rect.height / 2, Math.hypot(rect.width, rect.height));\r\n }\r\n\r\n /** Diameter that reaches the farthest corner from (x, y), so the ripple always covers the button. */\r\n private farthestCornerDiameter(rect: DOMRect, x: number, y: number): number {\r\n const corners: Array<[number, number]> = [\r\n [0, 0],\r\n [rect.width, 0],\r\n [0, rect.height],\r\n [rect.width, rect.height],\r\n ];\r\n const maxRadius = Math.max(...corners.map(([cx, cy]) => Math.hypot(cx - x, cy - y)));\r\n return maxRadius * 2;\r\n }\r\n\r\n /**\r\n * Spawns an expanding ripple circle. Created imperatively with inline styles and\r\n * animated via the Web Animations API — Angular's emulated encapsulation does not\r\n * reach dynamically created nodes, so it cannot rely on the component stylesheet.\r\n */\r\n private spawnRipple(host: HTMLElement, x: number, y: number, diameter: number): void {\r\n if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {\r\n return;\r\n }\r\n const ripple = document.createElement('span');\r\n Object.assign(ripple.style, {\r\n position: 'absolute',\r\n borderRadius: '50%',\r\n backgroundColor: 'currentColor',\r\n pointerEvents: 'none',\r\n width: `${diameter}px`,\r\n height: `${diameter}px`,\r\n left: `${x - diameter / 2}px`,\r\n top: `${y - diameter / 2}px`,\r\n } as Partial<CSSStyleDeclaration>);\r\n host.appendChild(ripple);\r\n const animation = ripple.animate(\r\n [\r\n { transform: 'scale(0)', opacity: '0.16' },\r\n { transform: 'scale(1)', opacity: '0' },\r\n ],\r\n { duration: 450, easing: 'ease-out' },\r\n );\r\n animation.onfinish = () => ripple.remove();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/button/button.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n HostBinding,\r\n Input,\r\n OnChanges,\r\n booleanAttribute,\r\n inject,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\n\r\n/** Visual style of a `compsych-button`. Controls color, border, and elevation. */\r\nexport type ButtonVariant =\r\n | 'filled'\r\n | 'tonal'\r\n | 'outlined'\r\n | 'elevated'\r\n | 'text'\r\n | 'danger'\r\n | 'danger-outlined';\r\n\r\n/** Button dimensions — controls height, padding, font size, and icon size. */\r\nexport type ButtonSize = 'small' | 'medium' | 'large' | 'xlarge';\r\n\r\n/** Native button behaviour. `'submit'`/`'reset'` make the component a real form control. */\r\nexport type ButtonType = 'button' | 'submit' | 'reset';\r\n\r\n/** Icon pixel size per button size (per design: sm/md 16, lg 20, xl 24). */\r\nconst ICON_SIZE_BY_BUTTON_SIZE: Record<ButtonSize, number> = {\r\n small: 16,\r\n medium: 16,\r\n large: 20,\r\n xlarge: 24,\r\n};\r\n\r\n/**\r\n * A themeable button that renders a native `<button>` element, so it is fully\r\n * form-submittable and keyboard/screen-reader accessible out of the box. Icons\r\n * are Lucide glyphs (matching the rest of the library); a button may carry a\r\n * leading icon, a trailing icon, or both.\r\n */\r\n@Component({\r\n selector: 'compsych-button',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <button\r\n [class]=\"buttonClasses\"\r\n [type]=\"type\"\r\n [disabled]=\"disabled || loading\"\r\n [attr.aria-busy]=\"loading ? 'true' : null\"\r\n [attr.aria-label]=\"computedAriaLabel\"\r\n (pointerdown)=\"onPointerDown($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n >\r\n @if (loading) {\r\n <span class=\"icon spinner\" aria-hidden=\"true\"></span>\r\n } @else if (leadingIcon) {\r\n <span class=\"icon\" aria-hidden=\"true\" [innerHTML]=\"leadingIconSvg\"></span>\r\n }\r\n @if (!iconOnly) {\r\n <span class=\"label\">{{ label }}<ng-content /></span>\r\n }\r\n @if (trailingIcon && !iconOnly) {\r\n <span class=\"icon\" aria-hidden=\"true\" [innerHTML]=\"trailingIconSvg\"></span>\r\n }\r\n </button>\r\n `,\r\n styleUrl: './button.css',\r\n})\r\nexport class ButtonComponent implements OnChanges {\r\n private readonly sanitizer = inject(DomSanitizer);\r\n\r\n /** Text label rendered on the button. */\r\n @Input() label = '';\r\n /** Visual style of the button. Defaults to `'filled'`. */\r\n @Input() variant: ButtonVariant = 'filled';\r\n /** Button dimensions — controls height, padding, font size, and icon size. Defaults to `'small'`. */\r\n @Input() size: ButtonSize = 'small';\r\n /** Lucide icon name shown before the label (e.g. `'house'`). Swapped for a spinner when loading. */\r\n @Input() leadingIcon?: string;\r\n /** Lucide icon name shown after the label (e.g. `'chevron-right'`). Can be combined with `leadingIcon`. */\r\n @Input() trailingIcon?: string;\r\n /** Native button type. Use `'submit'` inside a `<form>` to submit it. Defaults to `'button'`. */\r\n @Input() type: ButtonType = 'button';\r\n /**\r\n * Accessible name. Required for icon-only buttons since the icon is decorative.\r\n * When `iconOnly` or `loading` is set and this is omitted, `label` is used.\r\n */\r\n @Input() ariaLabel?: string;\r\n\r\n /** Hides the label and renders a square icon-only button. Uses `leadingIcon`. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) iconOnly = false;\r\n /** Disables the button — non-interactive and rendered in a neutral grey. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) disabled = false;\r\n /**\r\n * Loading state. Swaps the leading icon for a spinner and suppresses activation\r\n * (native `disabled` blocks click/keyboard), while keeping the label and exposing\r\n * `aria-busy=\"true\"`. Retains the variant's colours. Supports boolean coercion.\r\n */\r\n @Input({ transform: booleanAttribute }) loading = false;\r\n /** Stretches the button to the full width of its container. Supports boolean coercion. */\r\n @Input({ transform: booleanAttribute }) fullWidth = false;\r\n\r\n /** Mirrors `fullWidth` onto the host so it can stretch. */\r\n @HostBinding('class.full-width') get hostFullWidth(): boolean {\r\n return this.fullWidth;\r\n }\r\n\r\n /**\r\n * Marks the host non-interactive while disabled or loading. The inner `<button>` sets\r\n * `pointer-events: none` when disabled, so without this a click lands on the host wrapper\r\n * instead — firing any `(click)` a consumer bound on `<compsych-button>`. Blocking pointer\r\n * events at the host too makes the disabled state honour its \"non-interactive\" contract.\r\n */\r\n @HostBinding('class.non-interactive') get hostNonInteractive(): boolean {\r\n return this.disabled || this.loading;\r\n }\r\n\r\n /** Pre-sanitized icon markup, recomputed on input change (avoids re-sanitizing every CD cycle). */\r\n protected leadingIconSvg: SafeHtml | null = null;\r\n protected trailingIconSvg: SafeHtml | null = null;\r\n\r\n ngOnChanges(): void {\r\n this.leadingIconSvg = this.leadingIcon ? this.buildIconSvg(this.leadingIcon) : null;\r\n this.trailingIconSvg = this.trailingIcon ? this.buildIconSvg(this.trailingIcon) : null;\r\n }\r\n\r\n /** Space-separated class list applied to the inner native button. */\r\n get buttonClasses(): string {\r\n return [\r\n 'cmp-btn',\r\n this.variant,\r\n this.size,\r\n this.iconOnly ? 'icon-only' : '',\r\n this.loading ? 'loading' : '',\r\n this.fullWidth ? 'full-width' : '',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n /** Resolves the accessible name. Icon-only buttons have no visible text, so they fall back to `label`. */\r\n get computedAriaLabel(): string | null {\r\n if (this.ariaLabel) {\r\n return this.ariaLabel;\r\n }\r\n // Loading keeps the visible label, so only icon-only needs an explicit name here.\r\n return this.iconOnly ? this.label || null : null;\r\n }\r\n\r\n /**\r\n * Looks up a Lucide icon by name and injects an explicit width/height so the SVG\r\n * renders at the correct size when injected via `[innerHTML]` (Angular's CSS\r\n * encapsulation does not reach dynamically injected content).\r\n */\r\n private buildIconSvg(name: string): SafeHtml {\r\n const size = ICON_SIZE_BY_BUTTON_SIZE[this.size];\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', `<svg width=\"${size}\" height=\"${size}\" `);\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n\r\n /** M3 pressed ripple: pointer-down spawns a ripple at the pointer position. */\r\n onPointerDown(event: PointerEvent): void {\r\n if (this.disabled || this.loading || event.button !== 0) {\r\n return;\r\n }\r\n const host = event.currentTarget as HTMLElement;\r\n const rect = host.getBoundingClientRect();\r\n const x = event.clientX - rect.left;\r\n const y = event.clientY - rect.top;\r\n this.spawnRipple(host, x, y, this.farthestCornerDiameter(rect, x, y));\r\n }\r\n\r\n /** M3 pressed ripple: keyboard activation (Enter/Space) spawns a centered ripple. */\r\n onKeyDown(event: KeyboardEvent): void {\r\n if (this.disabled || this.loading || event.repeat) {\r\n return;\r\n }\r\n if (event.key !== 'Enter' && event.key !== ' ') {\r\n return;\r\n }\r\n const host = event.currentTarget as HTMLElement;\r\n const rect = host.getBoundingClientRect();\r\n this.spawnRipple(host, rect.width / 2, rect.height / 2, Math.hypot(rect.width, rect.height));\r\n }\r\n\r\n /** Diameter that reaches the farthest corner from (x, y), so the ripple always covers the button. */\r\n private farthestCornerDiameter(rect: DOMRect, x: number, y: number): number {\r\n const corners: Array<[number, number]> = [\r\n [0, 0],\r\n [rect.width, 0],\r\n [0, rect.height],\r\n [rect.width, rect.height],\r\n ];\r\n const maxRadius = Math.max(...corners.map(([cx, cy]) => Math.hypot(cx - x, cy - y)));\r\n return maxRadius * 2;\r\n }\r\n\r\n /**\r\n * Spawns an expanding ripple circle. Created imperatively with inline styles and\r\n * animated via the Web Animations API — Angular's emulated encapsulation does not\r\n * reach dynamically created nodes, so it cannot rely on the component stylesheet.\r\n */\r\n private spawnRipple(host: HTMLElement, x: number, y: number, diameter: number): void {\r\n if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {\r\n return;\r\n }\r\n const ripple = document.createElement('span');\r\n Object.assign(ripple.style, {\r\n position: 'absolute',\r\n borderRadius: '50%',\r\n backgroundColor: 'currentColor',\r\n pointerEvents: 'none',\r\n width: `${diameter}px`,\r\n height: `${diameter}px`,\r\n left: `${x - diameter / 2}px`,\r\n top: `${y - diameter / 2}px`,\r\n } as Partial<CSSStyleDeclaration>);\r\n host.appendChild(ripple);\r\n const animation = ripple.animate(\r\n [\r\n { transform: 'scale(0)', opacity: '0.16' },\r\n { transform: 'scale(1)', opacity: '0' },\r\n ],\r\n { duration: 450, easing: 'ease-out' },\r\n );\r\n animation.onfinish = () => ripple.remove();\r\n }\r\n}\r\n"]}
@@ -46,6 +46,13 @@ export declare class ButtonComponent implements OnChanges {
46
46
  fullWidth: boolean;
47
47
  /** Mirrors `fullWidth` onto the host so it can stretch. */
48
48
  get hostFullWidth(): boolean;
49
+ /**
50
+ * Marks the host non-interactive while disabled or loading. The inner `<button>` sets
51
+ * `pointer-events: none` when disabled, so without this a click lands on the host wrapper
52
+ * instead — firing any `(click)` a consumer bound on `<compsych-button>`. Blocking pointer
53
+ * events at the host too makes the disabled state honour its "non-interactive" contract.
54
+ */
55
+ get hostNonInteractive(): boolean;
49
56
  /** Pre-sanitized icon markup, recomputed on input change (avoids re-sanitizing every CD cycle). */
50
57
  protected leadingIconSvg: SafeHtml | null;
51
58
  protected trailingIconSvg: SafeHtml | null;
@@ -138,9 +138,9 @@ export declare class CardComponent {
138
138
  */
139
139
  readonly effectiveHeadingLevel: import("@angular/core").Signal<CardHeadingLevel | undefined>;
140
140
  /** Base Card Item plain-icon glyph size by `size` (48 md, 32 sm, 24 xs). */
141
- readonly basicIconSize: import("@angular/core").Signal<32 | 24 | 48>;
141
+ readonly basicIconSize: import("@angular/core").Signal<24 | 32 | 48>;
142
142
  /** Base Card Item arrow glyph size by `size` (24 md, 20 sm/xs). */
143
- readonly basicArrowSize: import("@angular/core").Signal<24 | 20>;
143
+ readonly basicArrowSize: import("@angular/core").Signal<20 | 24>;
144
144
  /** CSS `url(...)` for the media layer, or null when no image is set. */
145
145
  readonly mediaUrl: import("@angular/core").Signal<string | null>;
146
146
  /** Padding resolved per variant; `basic` honors the `padding` input. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compsych-ui-components/angular",
3
- "version": "7.0.20",
3
+ "version": "7.0.21",
4
4
  "description": "Angular UI components for compsych",
5
5
  "publishConfig": {
6
6
  "access": "public",