@skra/ui 0.2.0
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/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +39 -0
- package/consumer-guide/README.md +84 -0
- package/consumer-guide/accessible-names.md +80 -0
- package/consumer-guide/forms-and-binding.md +149 -0
- package/consumer-guide/layout.md +86 -0
- package/consumer-guide/overlays.md +100 -0
- package/consumer-guide/setup-and-gotchas.md +103 -0
- package/consumer-guide/theming-and-encapsulation.md +115 -0
- package/consumer-guide/wrapping-patterns.md +90 -0
- package/fesm2022/skra-ui-accordion.mjs +333 -0
- package/fesm2022/skra-ui-accordion.mjs.map +1 -0
- package/fesm2022/skra-ui-alert.mjs +110 -0
- package/fesm2022/skra-ui-alert.mjs.map +1 -0
- package/fesm2022/skra-ui-avatar.mjs +83 -0
- package/fesm2022/skra-ui-avatar.mjs.map +1 -0
- package/fesm2022/skra-ui-badge.mjs +91 -0
- package/fesm2022/skra-ui-badge.mjs.map +1 -0
- package/fesm2022/skra-ui-button.mjs +67 -0
- package/fesm2022/skra-ui-button.mjs.map +1 -0
- package/fesm2022/skra-ui-checkbox.mjs +98 -0
- package/fesm2022/skra-ui-checkbox.mjs.map +1 -0
- package/fesm2022/skra-ui-chip.mjs +82 -0
- package/fesm2022/skra-ui-chip.mjs.map +1 -0
- package/fesm2022/skra-ui-combobox.mjs +335 -0
- package/fesm2022/skra-ui-combobox.mjs.map +1 -0
- package/fesm2022/skra-ui-core.mjs +529 -0
- package/fesm2022/skra-ui-core.mjs.map +1 -0
- package/fesm2022/skra-ui-counter.mjs +53 -0
- package/fesm2022/skra-ui-counter.mjs.map +1 -0
- package/fesm2022/skra-ui-datepicker.mjs +867 -0
- package/fesm2022/skra-ui-datepicker.mjs.map +1 -0
- package/fesm2022/skra-ui-dialog.mjs +133 -0
- package/fesm2022/skra-ui-dialog.mjs.map +1 -0
- package/fesm2022/skra-ui-field.mjs +293 -0
- package/fesm2022/skra-ui-field.mjs.map +1 -0
- package/fesm2022/skra-ui-icon.mjs +54 -0
- package/fesm2022/skra-ui-icon.mjs.map +1 -0
- package/fesm2022/skra-ui-input.mjs +42 -0
- package/fesm2022/skra-ui-input.mjs.map +1 -0
- package/fesm2022/skra-ui-kbd.mjs +44 -0
- package/fesm2022/skra-ui-kbd.mjs.map +1 -0
- package/fesm2022/skra-ui-layout.mjs +119 -0
- package/fesm2022/skra-ui-layout.mjs.map +1 -0
- package/fesm2022/skra-ui-link.mjs +29 -0
- package/fesm2022/skra-ui-link.mjs.map +1 -0
- package/fesm2022/skra-ui-loading-indicator.mjs +24 -0
- package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -0
- package/fesm2022/skra-ui-menu.mjs +334 -0
- package/fesm2022/skra-ui-menu.mjs.map +1 -0
- package/fesm2022/skra-ui-multi-select.mjs +179 -0
- package/fesm2022/skra-ui-multi-select.mjs.map +1 -0
- package/fesm2022/skra-ui-native-select.mjs +123 -0
- package/fesm2022/skra-ui-native-select.mjs.map +1 -0
- package/fesm2022/skra-ui-popover.mjs +158 -0
- package/fesm2022/skra-ui-popover.mjs.map +1 -0
- package/fesm2022/skra-ui-progress.mjs +57 -0
- package/fesm2022/skra-ui-progress.mjs.map +1 -0
- package/fesm2022/skra-ui-radio.mjs +92 -0
- package/fesm2022/skra-ui-radio.mjs.map +1 -0
- package/fesm2022/skra-ui-rating-display.mjs +81 -0
- package/fesm2022/skra-ui-rating-display.mjs.map +1 -0
- package/fesm2022/skra-ui-select.mjs +151 -0
- package/fesm2022/skra-ui-select.mjs.map +1 -0
- package/fesm2022/skra-ui-skeleton.mjs +65 -0
- package/fesm2022/skra-ui-skeleton.mjs.map +1 -0
- package/fesm2022/skra-ui-star-rating.mjs +110 -0
- package/fesm2022/skra-ui-star-rating.mjs.map +1 -0
- package/fesm2022/skra-ui-status-dot.mjs +61 -0
- package/fesm2022/skra-ui-status-dot.mjs.map +1 -0
- package/fesm2022/skra-ui-switch.mjs +94 -0
- package/fesm2022/skra-ui-switch.mjs.map +1 -0
- package/fesm2022/skra-ui-tabs.mjs +356 -0
- package/fesm2022/skra-ui-tabs.mjs.map +1 -0
- package/fesm2022/skra-ui-textarea.mjs +81 -0
- package/fesm2022/skra-ui-textarea.mjs.map +1 -0
- package/fesm2022/skra-ui-toast.mjs +208 -0
- package/fesm2022/skra-ui-toast.mjs.map +1 -0
- package/fesm2022/skra-ui-tooltip.mjs +252 -0
- package/fesm2022/skra-ui-tooltip.mjs.map +1 -0
- package/fesm2022/skra-ui-typography.mjs +35 -0
- package/fesm2022/skra-ui-typography.mjs.map +1 -0
- package/fesm2022/skra-ui.mjs +4 -0
- package/fesm2022/skra-ui.mjs.map +1 -0
- package/package.json +175 -0
- package/types/skra-ui-accordion.d.ts +196 -0
- package/types/skra-ui-accordion.d.ts.map +1 -0
- package/types/skra-ui-alert.d.ts +81 -0
- package/types/skra-ui-alert.d.ts.map +1 -0
- package/types/skra-ui-avatar.d.ts +71 -0
- package/types/skra-ui-avatar.d.ts.map +1 -0
- package/types/skra-ui-badge.d.ts +73 -0
- package/types/skra-ui-badge.d.ts.map +1 -0
- package/types/skra-ui-button.d.ts +45 -0
- package/types/skra-ui-button.d.ts.map +1 -0
- package/types/skra-ui-checkbox.d.ts +52 -0
- package/types/skra-ui-checkbox.d.ts.map +1 -0
- package/types/skra-ui-chip.d.ts +60 -0
- package/types/skra-ui-chip.d.ts.map +1 -0
- package/types/skra-ui-combobox.d.ts +95 -0
- package/types/skra-ui-combobox.d.ts.map +1 -0
- package/types/skra-ui-core.d.ts +314 -0
- package/types/skra-ui-core.d.ts.map +1 -0
- package/types/skra-ui-counter.d.ts +33 -0
- package/types/skra-ui-counter.d.ts.map +1 -0
- package/types/skra-ui-datepicker.d.ts +164 -0
- package/types/skra-ui-datepicker.d.ts.map +1 -0
- package/types/skra-ui-dialog.d.ts +59 -0
- package/types/skra-ui-dialog.d.ts.map +1 -0
- package/types/skra-ui-field.d.ts +181 -0
- package/types/skra-ui-field.d.ts.map +1 -0
- package/types/skra-ui-icon.d.ts +38 -0
- package/types/skra-ui-icon.d.ts.map +1 -0
- package/types/skra-ui-input.d.ts +22 -0
- package/types/skra-ui-input.d.ts.map +1 -0
- package/types/skra-ui-kbd.d.ts +33 -0
- package/types/skra-ui-kbd.d.ts.map +1 -0
- package/types/skra-ui-layout.d.ts +86 -0
- package/types/skra-ui-layout.d.ts.map +1 -0
- package/types/skra-ui-link.d.ts +20 -0
- package/types/skra-ui-link.d.ts.map +1 -0
- package/types/skra-ui-loading-indicator.d.ts +9 -0
- package/types/skra-ui-loading-indicator.d.ts.map +1 -0
- package/types/skra-ui-menu.d.ts +116 -0
- package/types/skra-ui-menu.d.ts.map +1 -0
- package/types/skra-ui-multi-select.d.ts +47 -0
- package/types/skra-ui-multi-select.d.ts.map +1 -0
- package/types/skra-ui-native-select.d.ts +74 -0
- package/types/skra-ui-native-select.d.ts.map +1 -0
- package/types/skra-ui-popover.d.ts +57 -0
- package/types/skra-ui-popover.d.ts.map +1 -0
- package/types/skra-ui-progress.d.ts +41 -0
- package/types/skra-ui-progress.d.ts.map +1 -0
- package/types/skra-ui-radio.d.ts +54 -0
- package/types/skra-ui-radio.d.ts.map +1 -0
- package/types/skra-ui-rating-display.d.ts +56 -0
- package/types/skra-ui-rating-display.d.ts.map +1 -0
- package/types/skra-ui-select.d.ts +43 -0
- package/types/skra-ui-select.d.ts.map +1 -0
- package/types/skra-ui-skeleton.d.ts +42 -0
- package/types/skra-ui-skeleton.d.ts.map +1 -0
- package/types/skra-ui-star-rating.d.ts +71 -0
- package/types/skra-ui-star-rating.d.ts.map +1 -0
- package/types/skra-ui-status-dot.d.ts +62 -0
- package/types/skra-ui-status-dot.d.ts.map +1 -0
- package/types/skra-ui-switch.d.ts +54 -0
- package/types/skra-ui-switch.d.ts.map +1 -0
- package/types/skra-ui-tabs.d.ts +191 -0
- package/types/skra-ui-tabs.d.ts.map +1 -0
- package/types/skra-ui-textarea.d.ts +55 -0
- package/types/skra-ui-textarea.d.ts.map +1 -0
- package/types/skra-ui-toast.d.ts +140 -0
- package/types/skra-ui-toast.d.ts.map +1 -0
- package/types/skra-ui-tooltip.d.ts +90 -0
- package/types/skra-ui-tooltip.d.ts.map +1 -0
- package/types/skra-ui-typography.d.ts +17 -0
- package/types/skra-ui-typography.d.ts.map +1 -0
- package/types/skra-ui.d.ts +3 -0
- package/types/skra-ui.d.ts.map +1 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `<skra-avatar>` — a compact graphical representation of a person/entity: a photo with a
|
|
6
|
+
* graceful fallback to projected **initials** or an **icon** when the image is absent or fails
|
|
7
|
+
* to load. First resident of `libs/ui/data-display` (the identity/media-display family). See
|
|
8
|
+
* `adr/specs/component-avatar-spec.md`.
|
|
9
|
+
*
|
|
10
|
+
* **Element, not a decorated native (ADR 012):** there is no native `<avatar>`, and a bare
|
|
11
|
+
* `<img>` has nowhere to render the text/icon fallback — so Forge owns a container that
|
|
12
|
+
* conditionally renders an internal `<img>` OR the projected fallback. NOT a Web Component
|
|
13
|
+
* (ADR 000).
|
|
14
|
+
*
|
|
15
|
+
* **Image + fallback (the defining value-add):** Forge owns the internal `<img>` (rendered from
|
|
16
|
+
* the `src` *data* input — value not look, ADR 037, agnostic) so it can observe `(error)`/`(load)`
|
|
17
|
+
* and swap to the projected fallback. The swap is internal presentational logic, NOT an ADR 028
|
|
18
|
+
* `loading` operation (no `aria-busy`, no `[skraLoadingIndicator]`). SSR-safe: pure signals +
|
|
19
|
+
* template; on the server the `<img>` renders, the client swaps on error.
|
|
20
|
+
*
|
|
21
|
+
* **Non-interactive (Badge precedent):** no focusable node, no role, no keyboard, no outputs →
|
|
22
|
+
* State Surface all-N/A. A clickable avatar is `button[skraButton]`/`a[skraLink]` wrapping
|
|
23
|
+
* `<skra-avatar>` (composition), not a variant.
|
|
24
|
+
*
|
|
25
|
+
* **Accessible name (ADR 033 Path 3 — graphic host, the SkraIcon/ADR 034 §3.4 contract):** the
|
|
26
|
+
* inner content is **always decorative** — the internal `<img>` is `alt=""` and the fallback
|
|
27
|
+
* wrapper is `aria-hidden` — and the host carries **NO default ARIA**. The consumer declares
|
|
28
|
+
* semantics explicitly, exactly as for `<skra-icon>`:
|
|
29
|
+
* - decorative (Mode A, the ~90% case — avatar beside a visible name) → `aria-hidden="true"`;
|
|
30
|
+
* - meaningful (Mode B — standalone) → **`role="img"` + `aria-label`/`aria-labelledby` TOGETHER**.
|
|
31
|
+
* `role="img"` is REQUIRED: `aria-label` on the roleless custom-element host is ARIA-prohibited
|
|
32
|
+
* (axe `aria-prohibited-attr`) and conveys no name. Forge does NOT auto-set the role (parity
|
|
33
|
+
* with SkraIcon — host owns no default ARIA).
|
|
34
|
+
* NO `ariaLabel` input (ADR 033 boundary #8), NO `name`/`alt` input (no initials derivation —
|
|
35
|
+
* agnostic; the name survives image failure by living on the host, not on the swapped-out img).
|
|
36
|
+
*
|
|
37
|
+
* **Look is skin-owned:** circle-vs-square is the `radius` comp token (a skin with
|
|
38
|
+
* `sys.radius.full: 0` — e.g. brutal — ships square avatars automatically). NO `shape`/`color`/
|
|
39
|
+
* `variant` look-inputs (skin philosophy). NO `intent`/`emphasis` (a neutral identity graphic —
|
|
40
|
+
* status colour is the sibling StatusDot's job, SRP).
|
|
41
|
+
*/
|
|
42
|
+
class SkraAvatar {
|
|
43
|
+
/**
|
|
44
|
+
* Image URL — a data input (ADR 037; agnostic). Absent or failed-to-load → the projected
|
|
45
|
+
* fallback (initials / icon) shows.
|
|
46
|
+
*/
|
|
47
|
+
src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : /* istanbul ignore next */ []));
|
|
48
|
+
/** Variant Surface Size axis (ADR 035) — the square box dimension + initials scale. */
|
|
49
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
50
|
+
/**
|
|
51
|
+
* The `src` value that failed to load, if any. Keyed by URL (not a boolean) so that changing
|
|
52
|
+
* `src` to a new URL re-attempts the image instead of staying on the fallback.
|
|
53
|
+
*/
|
|
54
|
+
failedSrc = signal(null, ...(ngDevMode ? [{ debugName: "failedSrc" }] : /* istanbul ignore next */ []));
|
|
55
|
+
/** Whether the internal `<img>` should render (a `src` is set and has not failed). */
|
|
56
|
+
showImage = computed(() => {
|
|
57
|
+
const current = this.src();
|
|
58
|
+
return !!current && this.failedSrc() !== current;
|
|
59
|
+
}, ...(ngDevMode ? [{ debugName: "showImage" }] : /* istanbul ignore next */ []));
|
|
60
|
+
/** The `<img>` failed to load → fall back to the projected content. */
|
|
61
|
+
onImageError() {
|
|
62
|
+
this.failedSrc.set(this.src() ?? null);
|
|
63
|
+
}
|
|
64
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
65
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraAvatar, isStandalone: true, selector: "skra-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-size": "size()", "attr.data-skra-state": "showImage() ? \"image\" : \"fallback\"" } }, ngImport: i0, template: "@if (showImage()) {\n<!-- alt=\"\" \u2014 the image is always decorative; the accessible name lives on the host\n (consumer native aria-label / adjacent text). ADR 033 Path 3. -->\n<img class=\"skra-avatar-image\" [src]=\"src()\" alt=\"\" (error)=\"onImageError()\" />\n} @else {\n<!-- Fallback (initials / icon) is a decorative visual stand-in \u2014 aria-hidden so it never\n competes as a visible accessible name (the name is host-level). ADR 034. -->\n<span class=\"skra-avatar-fallback\" aria-hidden=\"true\">\n <ng-content></ng-content>\n</span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;margin:0;overflow:hidden;inline-size:var(--skra-comp-avatar-size-md);block-size:var(--skra-comp-avatar-size-md);font-size:var(--skra-comp-avatar-font-size-md);font-family:var(--skra-sys-typography-role-label-medium-font-family);font-weight:var(--skra-sys-typography-role-label-medium-font-weight);letter-spacing:var(--skra-sys-typography-role-label-medium-letter-spacing);line-height:1;-webkit-user-select:none;user-select:none;background:var(--skra-comp-avatar-background);color:var(--skra-comp-avatar-foreground);border-color:var(--skra-comp-avatar-border-color);border-width:var(--skra-comp-avatar-border-width);border-style:var(--skra-comp-avatar-border-style);border-radius:var(--skra-comp-avatar-radius);padding-block:var(--skra-comp-avatar-padding-block);padding-inline:var(--skra-comp-avatar-padding-inline);box-shadow:var(--skra-comp-avatar-shadow)}:host([data-skra-size=sm]){inline-size:var(--skra-comp-avatar-size-sm);block-size:var(--skra-comp-avatar-size-sm);font-size:var(--skra-comp-avatar-font-size-sm)}:host([data-skra-size=lg]){inline-size:var(--skra-comp-avatar-size-lg);block-size:var(--skra-comp-avatar-size-lg);font-size:var(--skra-comp-avatar-font-size-lg)}.skra-avatar-image{display:block;inline-size:100%;block-size:100%;object-fit:cover;border-radius:inherit}.skra-avatar-fallback{display:inline-flex;align-items:center;justify-content:center;--skra-comp-icon-size: var(--skra-comp-avatar-icon-size)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
66
|
+
}
|
|
67
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraAvatar, decorators: [{
|
|
68
|
+
type: Component,
|
|
69
|
+
args: [{ selector: 'skra-avatar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
70
|
+
// Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])
|
|
71
|
+
'[attr.data-skra-size]': 'size()',
|
|
72
|
+
// Presentational state for SCSS (image painted vs placeholder/fallback). Not an ADR 027
|
|
73
|
+
// State — it is presentational, like a Smart-Slot flag (§3.6 exempt).
|
|
74
|
+
'[attr.data-skra-state]': 'showImage() ? "image" : "fallback"',
|
|
75
|
+
}, template: "@if (showImage()) {\n<!-- alt=\"\" \u2014 the image is always decorative; the accessible name lives on the host\n (consumer native aria-label / adjacent text). ADR 033 Path 3. -->\n<img class=\"skra-avatar-image\" [src]=\"src()\" alt=\"\" (error)=\"onImageError()\" />\n} @else {\n<!-- Fallback (initials / icon) is a decorative visual stand-in \u2014 aria-hidden so it never\n competes as a visible accessible name (the name is host-level). ADR 034. -->\n<span class=\"skra-avatar-fallback\" aria-hidden=\"true\">\n <ng-content></ng-content>\n</span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;margin:0;overflow:hidden;inline-size:var(--skra-comp-avatar-size-md);block-size:var(--skra-comp-avatar-size-md);font-size:var(--skra-comp-avatar-font-size-md);font-family:var(--skra-sys-typography-role-label-medium-font-family);font-weight:var(--skra-sys-typography-role-label-medium-font-weight);letter-spacing:var(--skra-sys-typography-role-label-medium-letter-spacing);line-height:1;-webkit-user-select:none;user-select:none;background:var(--skra-comp-avatar-background);color:var(--skra-comp-avatar-foreground);border-color:var(--skra-comp-avatar-border-color);border-width:var(--skra-comp-avatar-border-width);border-style:var(--skra-comp-avatar-border-style);border-radius:var(--skra-comp-avatar-radius);padding-block:var(--skra-comp-avatar-padding-block);padding-inline:var(--skra-comp-avatar-padding-inline);box-shadow:var(--skra-comp-avatar-shadow)}:host([data-skra-size=sm]){inline-size:var(--skra-comp-avatar-size-sm);block-size:var(--skra-comp-avatar-size-sm);font-size:var(--skra-comp-avatar-font-size-sm)}:host([data-skra-size=lg]){inline-size:var(--skra-comp-avatar-size-lg);block-size:var(--skra-comp-avatar-size-lg);font-size:var(--skra-comp-avatar-font-size-lg)}.skra-avatar-image{display:block;inline-size:100%;block-size:100%;object-fit:cover;border-radius:inherit}.skra-avatar-fallback{display:inline-flex;align-items:center;justify-content:center;--skra-comp-icon-size: var(--skra-comp-avatar-icon-size)}\n"] }]
|
|
76
|
+
}], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generated bundle index. Do not edit.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
export { SkraAvatar };
|
|
83
|
+
//# sourceMappingURL=skra-ui-avatar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-avatar.mjs","sources":["../../../../libs/ui/avatar/src/lib/avatar/avatar.ts","../../../../libs/ui/avatar/src/lib/avatar/avatar.html","../../../../libs/ui/avatar/src/skra-ui-avatar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n signal,\n} from '@angular/core';\n\n/**\n * AvatarSize — the Variant Surface Size axis (ADR 035 §3.1, downward subset `sm|md|lg`).\n * `xs`/`xl` are reserved (ADR 035 §3.2) and NOT in MVP — adding them later is purely additive\n * (new enum values + token rows, zero migration). Closed enum; extension requires an ADR 035\n * amendment. See `adr/specs/component-avatar-spec.md` OQ 1.\n */\nexport type AvatarSize = 'sm' | 'md' | 'lg';\n\n/**\n * `<skra-avatar>` — a compact graphical representation of a person/entity: a photo with a\n * graceful fallback to projected **initials** or an **icon** when the image is absent or fails\n * to load. First resident of `libs/ui/data-display` (the identity/media-display family). See\n * `adr/specs/component-avatar-spec.md`.\n *\n * **Element, not a decorated native (ADR 012):** there is no native `<avatar>`, and a bare\n * `<img>` has nowhere to render the text/icon fallback — so Forge owns a container that\n * conditionally renders an internal `<img>` OR the projected fallback. NOT a Web Component\n * (ADR 000).\n *\n * **Image + fallback (the defining value-add):** Forge owns the internal `<img>` (rendered from\n * the `src` *data* input — value not look, ADR 037, agnostic) so it can observe `(error)`/`(load)`\n * and swap to the projected fallback. The swap is internal presentational logic, NOT an ADR 028\n * `loading` operation (no `aria-busy`, no `[skraLoadingIndicator]`). SSR-safe: pure signals +\n * template; on the server the `<img>` renders, the client swaps on error.\n *\n * **Non-interactive (Badge precedent):** no focusable node, no role, no keyboard, no outputs →\n * State Surface all-N/A. A clickable avatar is `button[skraButton]`/`a[skraLink]` wrapping\n * `<skra-avatar>` (composition), not a variant.\n *\n * **Accessible name (ADR 033 Path 3 — graphic host, the SkraIcon/ADR 034 §3.4 contract):** the\n * inner content is **always decorative** — the internal `<img>` is `alt=\"\"` and the fallback\n * wrapper is `aria-hidden` — and the host carries **NO default ARIA**. The consumer declares\n * semantics explicitly, exactly as for `<skra-icon>`:\n * - decorative (Mode A, the ~90% case — avatar beside a visible name) → `aria-hidden=\"true\"`;\n * - meaningful (Mode B — standalone) → **`role=\"img\"` + `aria-label`/`aria-labelledby` TOGETHER**.\n * `role=\"img\"` is REQUIRED: `aria-label` on the roleless custom-element host is ARIA-prohibited\n * (axe `aria-prohibited-attr`) and conveys no name. Forge does NOT auto-set the role (parity\n * with SkraIcon — host owns no default ARIA).\n * NO `ariaLabel` input (ADR 033 boundary #8), NO `name`/`alt` input (no initials derivation —\n * agnostic; the name survives image failure by living on the host, not on the swapped-out img).\n *\n * **Look is skin-owned:** circle-vs-square is the `radius` comp token (a skin with\n * `sys.radius.full: 0` — e.g. brutal — ships square avatars automatically). NO `shape`/`color`/\n * `variant` look-inputs (skin philosophy). NO `intent`/`emphasis` (a neutral identity graphic —\n * status colour is the sibling StatusDot's job, SRP).\n */\n@Component({\n selector: 'skra-avatar',\n standalone: true,\n templateUrl: './avatar.html',\n styleUrl: './avatar.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])\n '[attr.data-skra-size]': 'size()',\n // Presentational state for SCSS (image painted vs placeholder/fallback). Not an ADR 027\n // State — it is presentational, like a Smart-Slot flag (§3.6 exempt).\n '[attr.data-skra-state]': 'showImage() ? \"image\" : \"fallback\"',\n },\n})\nexport class SkraAvatar {\n /**\n * Image URL — a data input (ADR 037; agnostic). Absent or failed-to-load → the projected\n * fallback (initials / icon) shows.\n */\n readonly src = input<string>();\n\n /** Variant Surface Size axis (ADR 035) — the square box dimension + initials scale. */\n readonly size = input<AvatarSize>('md');\n\n /**\n * The `src` value that failed to load, if any. Keyed by URL (not a boolean) so that changing\n * `src` to a new URL re-attempts the image instead of staying on the fallback.\n */\n private readonly failedSrc = signal<string | null>(null);\n\n /** Whether the internal `<img>` should render (a `src` is set and has not failed). */\n readonly showImage = computed(() => {\n const current = this.src();\n return !!current && this.failedSrc() !== current;\n });\n\n /** The `<img>` failed to load → fall back to the projected content. */\n protected onImageError(): void {\n this.failedSrc.set(this.src() ?? null);\n }\n}\n","@if (showImage()) {\n<!-- alt=\"\" — the image is always decorative; the accessible name lives on the host\n (consumer native aria-label / adjacent text). ADR 033 Path 3. -->\n<img class=\"skra-avatar-image\" [src]=\"src()\" alt=\"\" (error)=\"onImageError()\" />\n} @else {\n<!-- Fallback (initials / icon) is a decorative visual stand-in — aria-hidden so it never\n competes as a visible accessible name (the name is host-level). ADR 034. -->\n<span class=\"skra-avatar-fallback\" aria-hidden=\"true\">\n <ng-content></ng-content>\n</span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCG;MAgBU,UAAU,CAAA;AACrB;;;AAGG;IACM,GAAG,GAAG,KAAK,CAAA,IAAA,SAAA,GAAA,CAAA,SAAA,EAAA,EAAA,SAAA,EAAA,KAAA,EAAA,CAAA,8BAAA,EAAA,CAAA,CAAU;;AAGrB,IAAA,IAAI,GAAG,KAAK,CAAa,IAAI,2EAAC;AAEvC;;;AAGG;AACc,IAAA,SAAS,GAAG,MAAM,CAAgB,IAAI,gFAAC;;AAG/C,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AACjC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE;QAC1B,OAAO,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,EAAE,KAAK,OAAO;AAClD,IAAA,CAAC,gFAAC;;IAGQ,YAAY,GAAA;AACpB,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;IACxC;uGAzBW,UAAU,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAV,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAU,ubCtEvB,ujBAWA,EAAA,MAAA,EAAA,CAAA,ihDAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FD2Da,UAAU,EAAA,UAAA,EAAA,CAAA;kBAftB,SAAS;+BACE,aAAa,EAAA,UAAA,EACX,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;;AAEJ,wBAAA,uBAAuB,EAAE,QAAQ;;;AAGjC,wBAAA,wBAAwB,EAAE,oCAAoC;AAC/D,qBAAA,EAAA,QAAA,EAAA,ujBAAA,EAAA,MAAA,EAAA,CAAA,ihDAAA,CAAA,EAAA;;;AEpEH;;AAEG;;;;"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Directive, input, contentChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Marker directive for the OPTIONAL leading icon/dot slot of `<skra-badge>`.
|
|
6
|
+
* Detected via `contentChild` (ADR 020 Smart Slots v2.0) to toggle the icon
|
|
7
|
+
* wrapper without emitting an empty DOM node. Feedback-local (not the button
|
|
8
|
+
* lib's `[skra-start]`) to avoid a cross-lib marker dependency, mirroring
|
|
9
|
+
* `[skra-alert-icon]`.
|
|
10
|
+
*
|
|
11
|
+
* The icon is decorative — the consumer sets `aria-hidden` on its content (ADR
|
|
12
|
+
* 034). The badge's visible text label still carries the meaning (WCAG 1.4.1):
|
|
13
|
+
* a leading dot enables the `● Active` pattern but never substitutes for the word.
|
|
14
|
+
*/
|
|
15
|
+
class SkraBadgeIcon {
|
|
16
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraBadgeIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
17
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraBadgeIcon, isStandalone: true, selector: "[skra-badge-icon]", ngImport: i0 });
|
|
18
|
+
}
|
|
19
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraBadgeIcon, decorators: [{
|
|
20
|
+
type: Directive,
|
|
21
|
+
args: [{
|
|
22
|
+
selector: '[skra-badge-icon]',
|
|
23
|
+
standalone: true,
|
|
24
|
+
}]
|
|
25
|
+
}] });
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `<skra-badge>` — a small, inline, non-interactive status indicator (Feedback/Status
|
|
29
|
+
* family, ADR 0052). Carries semantic importance (intent) as a short label or count —
|
|
30
|
+
* e.g. a status lozenge (`Active`/`Failed`) in a table, or a notification count.
|
|
31
|
+
*
|
|
32
|
+
* SRP-distinct from `<skra-counter>` (form value-bound character counter, `libs/ui/forms`)
|
|
33
|
+
* and from the closable `Tag/Chip` (interactive/dismissible — a different SRP, Wave 2).
|
|
34
|
+
*
|
|
35
|
+
* Non-interactive: no focusable node, no widget role, no keyboard, no outputs. The host is
|
|
36
|
+
* a presentational surface; severity is a Variant Surface axis (ADR 035), not a State (ADR 027).
|
|
37
|
+
*
|
|
38
|
+
* Announcement (ADR 0052): the `live` input maps to the implicit-live ARIA role
|
|
39
|
+
* (`status`/`alert`) — which carries `aria-live` + `aria-atomic` implicitly. NO explicit
|
|
40
|
+
* `aria-live` is set (that would be redundant; the `skra-alert`/`skra-error` pattern).
|
|
41
|
+
* When `live ≠ off`, the content must be self-describing (`3 unread`, not a bare `3`) — the
|
|
42
|
+
* role announces the whole badge atomically.
|
|
43
|
+
*
|
|
44
|
+
* Positioning (ADR 022): the badge owns no outer position. Overlaying it on a host's corner
|
|
45
|
+
* (a notification badge) is a consumer composition, not an atom responsibility.
|
|
46
|
+
*/
|
|
47
|
+
class SkraBadge {
|
|
48
|
+
// --- Variant Surface (ADR 035) ---
|
|
49
|
+
intent = input('neutral', ...(ngDevMode ? [{ debugName: "intent" }] : /* istanbul ignore next */ []));
|
|
50
|
+
emphasis = input('medium', ...(ngDevMode ? [{ debugName: "emphasis" }] : /* istanbul ignore next */ []));
|
|
51
|
+
// --- Announcement (ADR 0052) ---
|
|
52
|
+
live = input('off', ...(ngDevMode ? [{ debugName: "live" }] : /* istanbul ignore next */ []));
|
|
53
|
+
// --- Smart Slots v2.0 (ADR 020) ---
|
|
54
|
+
iconSlot = contentChild(SkraBadgeIcon, ...(ngDevMode ? [{ debugName: "iconSlot" }] : /* istanbul ignore next */ []));
|
|
55
|
+
hasIcon = computed(() => !!this.iconSlot(), ...(ngDevMode ? [{ debugName: "hasIcon" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/**
|
|
57
|
+
* Map `live` politeness to the implicit-live ARIA role (ADR 0052). The role
|
|
58
|
+
* carries `aria-live` + `aria-atomic` implicitly; `off` → no live region.
|
|
59
|
+
*/
|
|
60
|
+
liveRole = computed(() => {
|
|
61
|
+
switch (this.live()) {
|
|
62
|
+
case 'assertive':
|
|
63
|
+
return 'alert';
|
|
64
|
+
case 'polite':
|
|
65
|
+
return 'status';
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}, ...(ngDevMode ? [{ debugName: "liveRole" }] : /* istanbul ignore next */ []));
|
|
70
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
71
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraBadge, isStandalone: true, selector: "skra-badge", inputs: { intent: { classPropertyName: "intent", publicName: "intent", isSignal: true, isRequired: false, transformFunction: null }, emphasis: { classPropertyName: "emphasis", publicName: "emphasis", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-intent": "intent()", "attr.data-skra-emphasis": "emphasis()", "attr.role": "liveRole()", "attr.data-skra-has-icon": "hasIcon() ? \"\" : null" } }, queries: [{ propertyName: "iconSlot", first: true, predicate: SkraBadgeIcon, descendants: true, isSignal: true }], ngImport: i0, template: "@if (hasIcon()) {\n<span class=\"skra-badge-icon\">\n <ng-content select=\"[skra-badge-icon]\"></ng-content>\n</span>\n}\n\n<ng-content></ng-content>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;box-sizing:border-box;margin:0;white-space:nowrap;gap:var(--skra-comp-badge-icon-gap);font-family:var(--skra-sys-typography-role-label-small-font-family);font-size:var(--skra-sys-typography-role-label-small-font-size);font-weight:var(--skra-sys-typography-role-label-small-font-weight);line-height:var(--skra-sys-typography-role-label-small-line-height);letter-spacing:var(--skra-sys-typography-role-label-small-letter-spacing);background:var(--skra-comp-badge-background);color:var(--skra-comp-badge-foreground);border-color:var(--skra-comp-badge-border-color);border-width:var(--skra-comp-badge-border-width);border-style:var(--skra-comp-badge-border-style);border-radius:var(--skra-comp-badge-radius);padding-block:var(--skra-comp-badge-padding-block);padding-inline:var(--skra-comp-badge-padding-inline);box-shadow:var(--skra-comp-badge-shadow)}:host([data-skra-emphasis=high]){--skra-comp-badge-border-width: 0}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-neutral-medium, var(--skra-sys-color-neutral-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-neutral-medium, var(--skra-sys-color-neutral-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-neutral-medium, var(--skra-sys-color-neutral-border) )}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-info-medium, var(--skra-sys-color-info-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-info-medium, var(--skra-sys-color-info-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-info-medium, var(--skra-sys-color-info-border) )}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-success-medium, var(--skra-sys-color-success-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-success-medium, var(--skra-sys-color-success-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-success-medium, var(--skra-sys-color-success-border) )}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-warning-medium, var(--skra-sys-color-warning-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-warning-medium, var(--skra-sys-color-warning-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-warning-medium, var(--skra-sys-color-warning-border) )}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-danger-medium, var(--skra-sys-color-danger-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-danger-medium, var(--skra-sys-color-danger-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-danger-medium, var(--skra-sys-color-danger-border) )}.skra-badge-icon{display:inline-flex;flex-shrink:0;align-items:center;block-size:1lh;--skra-comp-icon-size: var(--skra-comp-badge-icon-size)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
72
|
+
}
|
|
73
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraBadge, decorators: [{
|
|
74
|
+
type: Component,
|
|
75
|
+
args: [{ selector: 'skra-badge', standalone: true, imports: [SkraBadgeIcon], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
76
|
+
// Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])
|
|
77
|
+
'[attr.data-skra-intent]': 'intent()',
|
|
78
|
+
'[attr.data-skra-emphasis]': 'emphasis()',
|
|
79
|
+
// Announcement role (ADR 0052) — implicit-live role ONLY; never an explicit aria-live.
|
|
80
|
+
'[attr.role]': 'liveRole()',
|
|
81
|
+
// Smart Slot layout reflection (ADR 020 v2.0 — slot-presence, State-Surface-exempt §3.6)
|
|
82
|
+
'[attr.data-skra-has-icon]': 'hasIcon() ? "" : null',
|
|
83
|
+
}, template: "@if (hasIcon()) {\n<span class=\"skra-badge-icon\">\n <ng-content select=\"[skra-badge-icon]\"></ng-content>\n</span>\n}\n\n<ng-content></ng-content>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;box-sizing:border-box;margin:0;white-space:nowrap;gap:var(--skra-comp-badge-icon-gap);font-family:var(--skra-sys-typography-role-label-small-font-family);font-size:var(--skra-sys-typography-role-label-small-font-size);font-weight:var(--skra-sys-typography-role-label-small-font-weight);line-height:var(--skra-sys-typography-role-label-small-line-height);letter-spacing:var(--skra-sys-typography-role-label-small-letter-spacing);background:var(--skra-comp-badge-background);color:var(--skra-comp-badge-foreground);border-color:var(--skra-comp-badge-border-color);border-width:var(--skra-comp-badge-border-width);border-style:var(--skra-comp-badge-border-style);border-radius:var(--skra-comp-badge-radius);padding-block:var(--skra-comp-badge-padding-block);padding-inline:var(--skra-comp-badge-padding-inline);box-shadow:var(--skra-comp-badge-shadow)}:host([data-skra-emphasis=high]){--skra-comp-badge-border-width: 0}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-neutral-medium, var(--skra-sys-color-neutral-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-neutral-medium, var(--skra-sys-color-neutral-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-neutral-medium, var(--skra-sys-color-neutral-border) )}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-info-medium, var(--skra-sys-color-info-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-info-medium, var(--skra-sys-color-info-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-info-medium, var(--skra-sys-color-info-border) )}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-success-medium, var(--skra-sys-color-success-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-success-medium, var(--skra-sys-color-success-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-success-medium, var(--skra-sys-color-success-border) )}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-warning-medium, var(--skra-sys-color-warning-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-warning-medium, var(--skra-sys-color-warning-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-warning-medium, var(--skra-sys-color-warning-border) )}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-badge-background: var( --skra-comp-badge-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-badge-background: var( --skra-comp-badge-background-danger-medium, var(--skra-sys-color-danger-soft) );--skra-comp-badge-foreground: var( --skra-comp-badge-foreground-danger-medium, var(--skra-sys-color-danger-strong) );--skra-comp-badge-border-color: var( --skra-comp-badge-border-color-danger-medium, var(--skra-sys-color-danger-border) )}.skra-badge-icon{display:inline-flex;flex-shrink:0;align-items:center;block-size:1lh;--skra-comp-icon-size: var(--skra-comp-badge-icon-size)}\n"] }]
|
|
84
|
+
}], propDecorators: { intent: [{ type: i0.Input, args: [{ isSignal: true, alias: "intent", required: false }] }], emphasis: [{ type: i0.Input, args: [{ isSignal: true, alias: "emphasis", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], iconSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraBadgeIcon), { isSignal: true }] }] } });
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Generated bundle index. Do not edit.
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
export { SkraBadge, SkraBadgeIcon };
|
|
91
|
+
//# sourceMappingURL=skra-ui-badge.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-badge.mjs","sources":["../../../../libs/ui/badge/src/lib/badge.directives.ts","../../../../libs/ui/badge/src/lib/badge/badge.ts","../../../../libs/ui/badge/src/lib/badge/badge.html","../../../../libs/ui/badge/src/skra-ui-badge.ts"],"sourcesContent":["import { Directive } from '@angular/core';\n\n/**\n * Marker directive for the OPTIONAL leading icon/dot slot of `<skra-badge>`.\n * Detected via `contentChild` (ADR 020 Smart Slots v2.0) to toggle the icon\n * wrapper without emitting an empty DOM node. Feedback-local (not the button\n * lib's `[skra-start]`) to avoid a cross-lib marker dependency, mirroring\n * `[skra-alert-icon]`.\n *\n * The icon is decorative — the consumer sets `aria-hidden` on its content (ADR\n * 034). The badge's visible text label still carries the meaning (WCAG 1.4.1):\n * a leading dot enables the `● Active` pattern but never substitutes for the word.\n */\n@Directive({\n selector: '[skra-badge-icon]',\n standalone: true,\n})\nexport class SkraBadgeIcon {}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n contentChild,\n input,\n} from '@angular/core';\nimport { SkraBadgeIcon } from '../badge.directives';\n\n/**\n * BadgeIntent — semantic feedback meaning (ADR 035 §3.1, downward subset).\n * Excludes the action intents `primary`/`secondary` (meaningless for a status\n * indicator). Closed enum; extension requires an ADR 035 amendment.\n */\nexport type BadgeIntent = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\n\n/**\n * BadgeEmphasis — visual loudness within the chosen intent (ADR 035 §3.1, subset).\n * `high` = filled chip, `medium` = soft/tinted lozenge. `low` deferred.\n */\nexport type BadgeEmphasis = 'high' | 'medium';\n\n/**\n * BadgeLive — screen-reader announcement politeness (ADR 0052). Maps to the\n * implicit-live ARIA role; politeness is a property of USE (static status vs a\n * live notification count), NOT of intent. Default `off` (the static-status case).\n */\nexport type BadgeLive = 'off' | 'polite' | 'assertive';\n\n/**\n * `<skra-badge>` — a small, inline, non-interactive status indicator (Feedback/Status\n * family, ADR 0052). Carries semantic importance (intent) as a short label or count —\n * e.g. a status lozenge (`Active`/`Failed`) in a table, or a notification count.\n *\n * SRP-distinct from `<skra-counter>` (form value-bound character counter, `libs/ui/forms`)\n * and from the closable `Tag/Chip` (interactive/dismissible — a different SRP, Wave 2).\n *\n * Non-interactive: no focusable node, no widget role, no keyboard, no outputs. The host is\n * a presentational surface; severity is a Variant Surface axis (ADR 035), not a State (ADR 027).\n *\n * Announcement (ADR 0052): the `live` input maps to the implicit-live ARIA role\n * (`status`/`alert`) — which carries `aria-live` + `aria-atomic` implicitly. NO explicit\n * `aria-live` is set (that would be redundant; the `skra-alert`/`skra-error` pattern).\n * When `live ≠ off`, the content must be self-describing (`3 unread`, not a bare `3`) — the\n * role announces the whole badge atomically.\n *\n * Positioning (ADR 022): the badge owns no outer position. Overlaying it on a host's corner\n * (a notification badge) is a consumer composition, not an atom responsibility.\n */\n@Component({\n selector: 'skra-badge',\n standalone: true,\n imports: [SkraBadgeIcon],\n templateUrl: './badge.html',\n styleUrl: './badge.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // Variant Surface reflection (ADR 035 §3.5; ADR 021 — never [style.--var])\n '[attr.data-skra-intent]': 'intent()',\n '[attr.data-skra-emphasis]': 'emphasis()',\n\n // Announcement role (ADR 0052) — implicit-live role ONLY; never an explicit aria-live.\n '[attr.role]': 'liveRole()',\n\n // Smart Slot layout reflection (ADR 020 v2.0 — slot-presence, State-Surface-exempt §3.6)\n '[attr.data-skra-has-icon]': 'hasIcon() ? \"\" : null',\n },\n})\nexport class SkraBadge {\n // --- Variant Surface (ADR 035) ---\n readonly intent = input<BadgeIntent>('neutral');\n readonly emphasis = input<BadgeEmphasis>('medium');\n\n // --- Announcement (ADR 0052) ---\n readonly live = input<BadgeLive>('off');\n\n // --- Smart Slots v2.0 (ADR 020) ---\n readonly iconSlot = contentChild(SkraBadgeIcon);\n readonly hasIcon = computed(() => !!this.iconSlot());\n\n /**\n * Map `live` politeness to the implicit-live ARIA role (ADR 0052). The role\n * carries `aria-live` + `aria-atomic` implicitly; `off` → no live region.\n */\n readonly liveRole = computed<'alert' | 'status' | null>(() => {\n switch (this.live()) {\n case 'assertive':\n return 'alert';\n case 'polite':\n return 'status';\n default:\n return null;\n }\n });\n}\n","@if (hasIcon()) {\n<span class=\"skra-badge-icon\">\n <ng-content select=\"[skra-badge-icon]\"></ng-content>\n</span>\n}\n\n<ng-content></ng-content>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Directive, input, booleanAttribute, contentChild, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { SkraLoadingIndicator } from '@skra/ui/loading-indicator';
|
|
4
|
+
|
|
5
|
+
class SkraButtonStart {
|
|
6
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButtonStart, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
7
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraButtonStart, isStandalone: true, selector: "[skra-start]", ngImport: i0 });
|
|
8
|
+
}
|
|
9
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButtonStart, decorators: [{
|
|
10
|
+
type: Directive,
|
|
11
|
+
args: [{
|
|
12
|
+
selector: '[skra-start]',
|
|
13
|
+
standalone: true,
|
|
14
|
+
}]
|
|
15
|
+
}] });
|
|
16
|
+
class SkraButtonEnd {
|
|
17
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButtonEnd, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
18
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.7", type: SkraButtonEnd, isStandalone: true, selector: "[skra-end]", ngImport: i0 });
|
|
19
|
+
}
|
|
20
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButtonEnd, decorators: [{
|
|
21
|
+
type: Directive,
|
|
22
|
+
args: [{
|
|
23
|
+
selector: '[skra-end]',
|
|
24
|
+
standalone: true,
|
|
25
|
+
}]
|
|
26
|
+
}] });
|
|
27
|
+
|
|
28
|
+
class SkraButton {
|
|
29
|
+
// --- Variant Surface (ADR 035) ---
|
|
30
|
+
intent = input('neutral', ...(ngDevMode ? [{ debugName: "intent" }] : /* istanbul ignore next */ []));
|
|
31
|
+
emphasis = input('high', ...(ngDevMode ? [{ debugName: "emphasis" }] : /* istanbul ignore next */ []));
|
|
32
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
33
|
+
// --- State Surface — Transient (ADR 027 cat 4 + ADR 028) ---
|
|
34
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
35
|
+
// --- Smart Slots v2.0 (ADR 020) ---
|
|
36
|
+
startSlot = contentChild(SkraButtonStart, ...(ngDevMode ? [{ debugName: "startSlot" }] : /* istanbul ignore next */ []));
|
|
37
|
+
endSlot = contentChild(SkraButtonEnd, ...(ngDevMode ? [{ debugName: "endSlot" }] : /* istanbul ignore next */ []));
|
|
38
|
+
hasStart = computed(() => !!this.startSlot(), ...(ngDevMode ? [{ debugName: "hasStart" }] : /* istanbul ignore next */ []));
|
|
39
|
+
hasEnd = computed(() => !!this.endSlot(), ...(ngDevMode ? [{ debugName: "hasEnd" }] : /* istanbul ignore next */ []));
|
|
40
|
+
// --- Loading Indicator Slot (ADR 028) ---
|
|
41
|
+
loadingIndicatorSlot = contentChild(SkraLoadingIndicator, ...(ngDevMode ? [{ debugName: "loadingIndicatorSlot" }] : /* istanbul ignore next */ []));
|
|
42
|
+
hasLoadingIndicator = computed(() => !!this.loadingIndicatorSlot(), ...(ngDevMode ? [{ debugName: "hasLoadingIndicator" }] : /* istanbul ignore next */ []));
|
|
43
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
44
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SkraButton, isStandalone: true, selector: "button[skraButton]", inputs: { intent: { classPropertyName: "intent", publicName: "intent", isSignal: true, isRequired: false, transformFunction: null }, emphasis: { classPropertyName: "emphasis", publicName: "emphasis", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-skra-intent": "intent()", "attr.data-skra-emphasis": "emphasis()", "attr.data-skra-size": "size()", "attr.data-skra-loading": "loading() ? \"\" : null", "attr.aria-busy": "loading() ? \"true\" : null", "attr.data-skra-has-start": "hasStart() ? \"\" : null", "attr.data-skra-has-end": "hasEnd() ? \"\" : null" } }, queries: [{ propertyName: "startSlot", first: true, predicate: SkraButtonStart, descendants: true, isSignal: true }, { propertyName: "endSlot", first: true, predicate: SkraButtonEnd, descendants: true, isSignal: true }, { propertyName: "loadingIndicatorSlot", first: true, predicate: SkraLoadingIndicator, descendants: true, isSignal: true }], ngImport: i0, template: "@if (hasStart()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-start]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-button-label-wrapper\">\n <ng-content></ng-content>\n</span>\n\n@if (hasEnd()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-end]\"></ng-content>\n</span>\n} @if (loading()) {\n<span class=\"skra-button-loading-indicator\" data-skra-loading-indicator>\n @if (hasLoadingIndicator()) {\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\n } @else {\n <span data-skra-loading-indicator-fallback aria-hidden=\"true\"></span>\n }\n</span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;cursor:pointer;font-family:var(--skra-comp-button-font-family);font-size:var(--skra-comp-button-font-size);font-weight:var(--skra-comp-button-font-weight);line-height:var(--skra-comp-button-line-height);letter-spacing:var(--skra-comp-button-letter-spacing);min-block-size:var(--skra-comp-button-min-block-size);background:var(--skra-comp-button-background);color:var(--skra-comp-button-foreground);border-color:var(--skra-comp-button-border-color);border-width:var(--skra-comp-button-border-width);border-style:var(--skra-comp-button-border-style);border-radius:var(--skra-comp-button-radius);padding-block:var(--skra-comp-button-padding-block);padding-inline:var(--skra-comp-button-padding-inline);box-shadow:var(--skra-comp-button-shadow);transition:background var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),color var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),border-color var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),box-shadow var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing)}:host[data-skra-has-start],:host[data-skra-has-end]{gap:var(--skra-comp-button-icon-gap)}:host:focus-visible{outline-style:solid;outline-color:var(--skra-comp-button-focus-ring-color);outline-width:var(--skra-comp-button-focus-ring-width);outline-offset:2px}:host:hover:not(:disabled){box-shadow:var(--skra-comp-button-shadow-hover)}:host([data-skra-size=sm]){--skra-comp-button-padding-block: var( --skra-comp-button-size-sm-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-sm-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-sm-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-small-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-small-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-small-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-small-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-small-letter-spacing )}:host([data-skra-size=md]){--skra-comp-button-padding-block: var( --skra-comp-button-size-md-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-md-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-md-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-medium-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-medium-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-medium-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-medium-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-medium-letter-spacing )}:host([data-skra-size=lg]){--skra-comp-button-padding-block: var( --skra-comp-button-size-lg-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-lg-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-lg-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-large-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-large-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-large-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-large-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-large-letter-spacing )}:host([data-skra-emphasis=high]){--skra-comp-button-border-width: 0}:host([data-skra-emphasis=medium]){--skra-comp-button-border-width: .0625rem}:host([data-skra-emphasis=low]){--skra-comp-button-border-width: 0}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-base-hover)}:host([data-skra-intent=neutral][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-base-active)}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-medium, var(--skra-sys-color-neutral-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-medium, var(--skra-sys-color-neutral-ink) )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-soft)}:host([data-skra-intent=neutral][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-low, var(--skra-sys-color-neutral-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-low, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-soft)}:host([data-skra-intent=primary][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-primary-high, var(--skra-sys-color-primary-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-high, var(--skra-sys-color-primary-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-high, transparent )}:host([data-skra-intent=primary][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-base-hover)}:host([data-skra-intent=primary][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-base-active)}:host([data-skra-intent=primary][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-primary-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-medium, var(--skra-sys-color-primary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-medium, var(--skra-sys-color-primary-ink) )}:host([data-skra-intent=primary][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-soft)}:host([data-skra-intent=primary][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-primary-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-low, var(--skra-sys-color-primary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-low, transparent )}:host([data-skra-intent=primary][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-soft)}:host([data-skra-intent=secondary][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-high, var(--skra-sys-color-secondary-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-high, var(--skra-sys-color-secondary-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-high, transparent )}:host([data-skra-intent=secondary][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-base-hover)}:host([data-skra-intent=secondary][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-base-active)}:host([data-skra-intent=secondary][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-medium, var(--skra-sys-color-secondary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-medium, var(--skra-sys-color-secondary-ink) )}:host([data-skra-intent=secondary][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-soft)}:host([data-skra-intent=secondary][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-low, var(--skra-sys-color-secondary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-low, transparent )}:host([data-skra-intent=secondary][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-soft)}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-base-hover)}:host([data-skra-intent=info][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-base-active)}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-info-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-medium, var(--skra-sys-color-info-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-medium, var(--skra-sys-color-info-ink) )}:host([data-skra-intent=info][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-soft)}:host([data-skra-intent=info][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-info-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-low, var(--skra-sys-color-info-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-low, transparent )}:host([data-skra-intent=info][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-soft)}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-base-hover)}:host([data-skra-intent=success][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-base-active)}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-success-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-medium, var(--skra-sys-color-success-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-medium, var(--skra-sys-color-success-ink) )}:host([data-skra-intent=success][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-soft)}:host([data-skra-intent=success][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-success-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-low, var(--skra-sys-color-success-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-low, transparent )}:host([data-skra-intent=success][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-soft)}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-base-hover)}:host([data-skra-intent=warning][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-base-active)}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-warning-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-medium, var(--skra-sys-color-warning-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-medium, var(--skra-sys-color-warning-ink) )}:host([data-skra-intent=warning][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-soft)}:host([data-skra-intent=warning][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-warning-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-low, var(--skra-sys-color-warning-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-low, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-soft)}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-base-hover)}:host([data-skra-intent=danger][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-base-active)}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-danger-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-medium, var(--skra-sys-color-danger-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-medium, var(--skra-sys-color-danger-ink) )}:host([data-skra-intent=danger][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-soft)}:host([data-skra-intent=danger][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-danger-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-low, var(--skra-sys-color-danger-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-low, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-soft)}:host:disabled{background:var(--skra-comp-button-background-disabled);color:var(--skra-comp-button-foreground-disabled);border-color:var(--skra-comp-button-border-color-disabled);cursor:not-allowed;box-shadow:none}.skra-button-slot-wrapper{display:inline-flex;align-items:center;flex-shrink:0;align-self:center;--skra-comp-icon-size: var(--skra-comp-button-icon-size)}.skra-button-label-wrapper{display:inline-block}.skra-button-loading-indicator{display:inline-flex;align-items:center}:host([data-skra-loading]){pointer-events:none;position:relative}:host([data-skra-loading]) .skra-button-label-wrapper,:host([data-skra-loading]) .skra-button-slot-wrapper{opacity:0}:host([data-skra-loading]) .skra-button-loading-indicator{position:absolute;inset:0;justify-content:center}[data-skra-loading-indicator-fallback]{display:inline-block;inline-size:var(--skra-comp-button-loading-indicator-size);block-size:var(--skra-comp-button-loading-indicator-size);border:.125em solid var(--skra-comp-button-loading-indicator-color);border-block-start-color:transparent;border-radius:50%;animation:skra-button-spin .8s linear infinite}@keyframes skra-button-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){[data-skra-loading-indicator-fallback]{animation:none;opacity:.6}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
45
|
+
}
|
|
46
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraButton, decorators: [{
|
|
47
|
+
type: Component,
|
|
48
|
+
args: [{ selector: 'button[skraButton]', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
49
|
+
// Variant Surface reflection (ADR 035 §3.5)
|
|
50
|
+
'[attr.data-skra-intent]': 'intent()',
|
|
51
|
+
'[attr.data-skra-emphasis]': 'emphasis()',
|
|
52
|
+
'[attr.data-skra-size]': 'size()',
|
|
53
|
+
// State Surface reflection — Transient category (ADR 027 §3.1 cat 4)
|
|
54
|
+
'[attr.data-skra-loading]': 'loading() ? "" : null',
|
|
55
|
+
'[attr.aria-busy]': 'loading() ? "true" : null',
|
|
56
|
+
// Smart Slots layout reflection (ADR 020 v2.0 §3.2 — slot-presence, exempt from State Surface per ADR 027 §3.6)
|
|
57
|
+
'[attr.data-skra-has-start]': 'hasStart() ? "" : null',
|
|
58
|
+
'[attr.data-skra-has-end]': 'hasEnd() ? "" : null',
|
|
59
|
+
}, template: "@if (hasStart()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-start]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-button-label-wrapper\">\n <ng-content></ng-content>\n</span>\n\n@if (hasEnd()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-end]\"></ng-content>\n</span>\n} @if (loading()) {\n<span class=\"skra-button-loading-indicator\" data-skra-loading-indicator>\n @if (hasLoadingIndicator()) {\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\n } @else {\n <span data-skra-loading-indicator-fallback aria-hidden=\"true\"></span>\n }\n</span>\n}\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;cursor:pointer;font-family:var(--skra-comp-button-font-family);font-size:var(--skra-comp-button-font-size);font-weight:var(--skra-comp-button-font-weight);line-height:var(--skra-comp-button-line-height);letter-spacing:var(--skra-comp-button-letter-spacing);min-block-size:var(--skra-comp-button-min-block-size);background:var(--skra-comp-button-background);color:var(--skra-comp-button-foreground);border-color:var(--skra-comp-button-border-color);border-width:var(--skra-comp-button-border-width);border-style:var(--skra-comp-button-border-style);border-radius:var(--skra-comp-button-radius);padding-block:var(--skra-comp-button-padding-block);padding-inline:var(--skra-comp-button-padding-inline);box-shadow:var(--skra-comp-button-shadow);transition:background var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),color var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),border-color var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing),box-shadow var(--skra-comp-button-motion-duration) var(--skra-comp-button-motion-easing)}:host[data-skra-has-start],:host[data-skra-has-end]{gap:var(--skra-comp-button-icon-gap)}:host:focus-visible{outline-style:solid;outline-color:var(--skra-comp-button-focus-ring-color);outline-width:var(--skra-comp-button-focus-ring-width);outline-offset:2px}:host:hover:not(:disabled){box-shadow:var(--skra-comp-button-shadow-hover)}:host([data-skra-size=sm]){--skra-comp-button-padding-block: var( --skra-comp-button-size-sm-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-sm-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-sm-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-small-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-small-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-small-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-small-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-small-letter-spacing )}:host([data-skra-size=md]){--skra-comp-button-padding-block: var( --skra-comp-button-size-md-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-md-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-md-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-medium-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-medium-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-medium-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-medium-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-medium-letter-spacing )}:host([data-skra-size=lg]){--skra-comp-button-padding-block: var( --skra-comp-button-size-lg-padding-block );--skra-comp-button-padding-inline: var( --skra-comp-button-size-lg-padding-inline );--skra-comp-button-min-block-size: var( --skra-comp-button-size-lg-min-block-size );--skra-comp-button-font-family: var( --skra-sys-typography-role-label-large-font-family );--skra-comp-button-font-size: var( --skra-sys-typography-role-label-large-font-size );--skra-comp-button-font-weight: var( --skra-sys-typography-role-label-large-font-weight );--skra-comp-button-line-height: var( --skra-sys-typography-role-label-large-line-height );--skra-comp-button-letter-spacing: var( --skra-sys-typography-role-label-large-letter-spacing )}:host([data-skra-emphasis=high]){--skra-comp-button-border-width: 0}:host([data-skra-emphasis=medium]){--skra-comp-button-border-width: .0625rem}:host([data-skra-emphasis=low]){--skra-comp-button-border-width: 0}:host([data-skra-intent=neutral][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-high, var(--skra-sys-color-neutral-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-high, var(--skra-sys-color-neutral-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-high, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-base-hover)}:host([data-skra-intent=neutral][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-base-active)}:host([data-skra-intent=neutral][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-medium, var(--skra-sys-color-neutral-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-medium, var(--skra-sys-color-neutral-ink) )}:host([data-skra-intent=neutral][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-soft)}:host([data-skra-intent=neutral][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-neutral-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-neutral-low, var(--skra-sys-color-neutral-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-neutral-low, transparent )}:host([data-skra-intent=neutral][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-neutral-soft)}:host([data-skra-intent=primary][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-primary-high, var(--skra-sys-color-primary-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-high, var(--skra-sys-color-primary-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-high, transparent )}:host([data-skra-intent=primary][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-base-hover)}:host([data-skra-intent=primary][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-base-active)}:host([data-skra-intent=primary][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-primary-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-medium, var(--skra-sys-color-primary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-medium, var(--skra-sys-color-primary-ink) )}:host([data-skra-intent=primary][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-soft)}:host([data-skra-intent=primary][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-primary-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-primary-low, var(--skra-sys-color-primary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-primary-low, transparent )}:host([data-skra-intent=primary][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-primary-soft)}:host([data-skra-intent=secondary][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-high, var(--skra-sys-color-secondary-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-high, var(--skra-sys-color-secondary-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-high, transparent )}:host([data-skra-intent=secondary][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-base-hover)}:host([data-skra-intent=secondary][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-base-active)}:host([data-skra-intent=secondary][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-medium, var(--skra-sys-color-secondary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-medium, var(--skra-sys-color-secondary-ink) )}:host([data-skra-intent=secondary][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-soft)}:host([data-skra-intent=secondary][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-secondary-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-secondary-low, var(--skra-sys-color-secondary-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-secondary-low, transparent )}:host([data-skra-intent=secondary][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-secondary-soft)}:host([data-skra-intent=info][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-info-high, var(--skra-sys-color-info-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-high, var(--skra-sys-color-info-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-high, transparent )}:host([data-skra-intent=info][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-base-hover)}:host([data-skra-intent=info][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-base-active)}:host([data-skra-intent=info][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-info-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-medium, var(--skra-sys-color-info-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-medium, var(--skra-sys-color-info-ink) )}:host([data-skra-intent=info][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-soft)}:host([data-skra-intent=info][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-info-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-info-low, var(--skra-sys-color-info-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-info-low, transparent )}:host([data-skra-intent=info][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-info-soft)}:host([data-skra-intent=success][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-success-high, var(--skra-sys-color-success-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-high, var(--skra-sys-color-success-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-high, transparent )}:host([data-skra-intent=success][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-base-hover)}:host([data-skra-intent=success][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-base-active)}:host([data-skra-intent=success][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-success-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-medium, var(--skra-sys-color-success-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-medium, var(--skra-sys-color-success-ink) )}:host([data-skra-intent=success][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-soft)}:host([data-skra-intent=success][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-success-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-success-low, var(--skra-sys-color-success-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-success-low, transparent )}:host([data-skra-intent=success][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-success-soft)}:host([data-skra-intent=warning][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-warning-high, var(--skra-sys-color-warning-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-high, var(--skra-sys-color-warning-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-high, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-base-hover)}:host([data-skra-intent=warning][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-base-active)}:host([data-skra-intent=warning][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-warning-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-medium, var(--skra-sys-color-warning-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-medium, var(--skra-sys-color-warning-ink) )}:host([data-skra-intent=warning][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-soft)}:host([data-skra-intent=warning][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-warning-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-warning-low, var(--skra-sys-color-warning-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-warning-low, transparent )}:host([data-skra-intent=warning][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-warning-soft)}:host([data-skra-intent=danger][data-skra-emphasis=high]){--skra-comp-button-background: var( --skra-comp-button-background-danger-high, var(--skra-sys-color-danger-base) );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-high, var(--skra-sys-color-danger-contrast) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-high, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=high]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-base-hover)}:host([data-skra-intent=danger][data-skra-emphasis=high]):active:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-base-active)}:host([data-skra-intent=danger][data-skra-emphasis=medium]){--skra-comp-button-background: var( --skra-comp-button-background-danger-medium, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-medium, var(--skra-sys-color-danger-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-medium, var(--skra-sys-color-danger-ink) )}:host([data-skra-intent=danger][data-skra-emphasis=medium]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-soft)}:host([data-skra-intent=danger][data-skra-emphasis=low]){--skra-comp-button-background: var( --skra-comp-button-background-danger-low, transparent );--skra-comp-button-foreground: var( --skra-comp-button-foreground-danger-low, var(--skra-sys-color-danger-ink) );--skra-comp-button-border-color: var( --skra-comp-button-border-color-danger-low, transparent )}:host([data-skra-intent=danger][data-skra-emphasis=low]):hover:not(:disabled){--skra-comp-button-background: var(--skra-sys-color-danger-soft)}:host:disabled{background:var(--skra-comp-button-background-disabled);color:var(--skra-comp-button-foreground-disabled);border-color:var(--skra-comp-button-border-color-disabled);cursor:not-allowed;box-shadow:none}.skra-button-slot-wrapper{display:inline-flex;align-items:center;flex-shrink:0;align-self:center;--skra-comp-icon-size: var(--skra-comp-button-icon-size)}.skra-button-label-wrapper{display:inline-block}.skra-button-loading-indicator{display:inline-flex;align-items:center}:host([data-skra-loading]){pointer-events:none;position:relative}:host([data-skra-loading]) .skra-button-label-wrapper,:host([data-skra-loading]) .skra-button-slot-wrapper{opacity:0}:host([data-skra-loading]) .skra-button-loading-indicator{position:absolute;inset:0;justify-content:center}[data-skra-loading-indicator-fallback]{display:inline-block;inline-size:var(--skra-comp-button-loading-indicator-size);block-size:var(--skra-comp-button-loading-indicator-size);border:.125em solid var(--skra-comp-button-loading-indicator-color);border-block-start-color:transparent;border-radius:50%;animation:skra-button-spin .8s linear infinite}@keyframes skra-button-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){[data-skra-loading-indicator-fallback]{animation:none;opacity:.6}}\n"] }]
|
|
60
|
+
}], propDecorators: { intent: [{ type: i0.Input, args: [{ isSignal: true, alias: "intent", required: false }] }], emphasis: [{ type: i0.Input, args: [{ isSignal: true, alias: "emphasis", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], startSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraButtonStart), { isSignal: true }] }], endSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraButtonEnd), { isSignal: true }] }], loadingIndicatorSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SkraLoadingIndicator), { isSignal: true }] }] } });
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generated bundle index. Do not edit.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
export { SkraButton, SkraButtonEnd, SkraButtonStart };
|
|
67
|
+
//# sourceMappingURL=skra-ui-button.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-button.mjs","sources":["../../../../libs/ui/button/src/lib/button.directives.ts","../../../../libs/ui/button/src/lib/button/button.ts","../../../../libs/ui/button/src/lib/button/button.html","../../../../libs/ui/button/src/skra-ui-button.ts"],"sourcesContent":["import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[skra-start]',\n standalone: true,\n})\nexport class SkraButtonStart {}\n\n@Directive({\n selector: '[skra-end]',\n standalone: true,\n})\nexport class SkraButtonEnd {}\n","import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n booleanAttribute,\n input,\n computed,\n contentChild,\n} from '@angular/core';\nimport { SkraButtonStart, SkraButtonEnd } from '../button.directives';\nimport { SkraLoadingIndicator } from '@skra/ui/loading-indicator';\n\n/**\n * ButtonIntent — semantic role of the action (ADR 035 §3.1).\n * Closed enum; extension requires amendment to ADR 035.\n */\nexport type ButtonIntent =\n | 'neutral'\n | 'primary'\n | 'secondary'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger';\n\n/**\n * ButtonEmphasis — visual loudness within the chosen intent (ADR 035 §3.1).\n * Skin maps each level to its own treatment (filled / outlined / ghost / other).\n */\nexport type ButtonEmphasis = 'high' | 'medium' | 'low';\n\n/**\n * ButtonSize — per-instance scale relative to typography and spacing (ADR 035 §3.1).\n * Distinct from Density (ADR 016 §3.5), which is subtree-runtime.\n */\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'button[skraButton]',\n standalone: true,\n templateUrl: './button.html',\n styleUrl: './button.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n // Variant Surface reflection (ADR 035 §3.5)\n '[attr.data-skra-intent]': 'intent()',\n '[attr.data-skra-emphasis]': 'emphasis()',\n '[attr.data-skra-size]': 'size()',\n\n // State Surface reflection — Transient category (ADR 027 §3.1 cat 4)\n '[attr.data-skra-loading]': 'loading() ? \"\" : null',\n '[attr.aria-busy]': 'loading() ? \"true\" : null',\n\n // Smart Slots layout reflection (ADR 020 v2.0 §3.2 — slot-presence, exempt from State Surface per ADR 027 §3.6)\n '[attr.data-skra-has-start]': 'hasStart() ? \"\" : null',\n '[attr.data-skra-has-end]': 'hasEnd() ? \"\" : null',\n },\n})\nexport class SkraButton {\n // --- Variant Surface (ADR 035) ---\n readonly intent = input<ButtonIntent>('neutral');\n readonly emphasis = input<ButtonEmphasis>('high');\n readonly size = input<ButtonSize>('md');\n\n // --- State Surface — Transient (ADR 027 cat 4 + ADR 028) ---\n readonly loading = input(false, { transform: booleanAttribute });\n\n // --- Smart Slots v2.0 (ADR 020) ---\n readonly startSlot = contentChild(SkraButtonStart);\n readonly endSlot = contentChild(SkraButtonEnd);\n\n readonly hasStart = computed(() => !!this.startSlot());\n readonly hasEnd = computed(() => !!this.endSlot());\n\n // --- Loading Indicator Slot (ADR 028) ---\n readonly loadingIndicatorSlot = contentChild(SkraLoadingIndicator);\n readonly hasLoadingIndicator = computed(() => !!this.loadingIndicatorSlot());\n}\n","@if (hasStart()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-start]\"></ng-content>\n</span>\n}\n\n<span class=\"skra-button-label-wrapper\">\n <ng-content></ng-content>\n</span>\n\n@if (hasEnd()) {\n<span class=\"skra-button-slot-wrapper\">\n <ng-content select=\"[skra-end]\"></ng-content>\n</span>\n} @if (loading()) {\n<span class=\"skra-button-loading-indicator\" data-skra-loading-indicator>\n @if (hasLoadingIndicator()) {\n <ng-content select=\"[skraLoadingIndicator]\"></ng-content>\n } @else {\n <span data-skra-loading-indicator-fallback aria-hidden=\"true\"></span>\n }\n</span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, input, booleanAttribute, effect, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { SkraFieldContext } from '@skra/ui/field';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wires a native `<input type="checkbox">` to SkraFieldContext when inside
|
|
7
|
+
* `<skra-field>` (ADR 013), or stands alone with no cascade. Per ADR 039/040,
|
|
8
|
+
* Forge ships no `<skra-checkbox>` wrapper — this is a Component-on-the-native-
|
|
9
|
+
* host (`<input>` is void → `template: ''`, ADR 040 §2) carrying its own
|
|
10
|
+
* `checkbox.scss` via Emulated encapsulation. Value channel stays consumer-owned
|
|
11
|
+
* per ADR 037: bind
|
|
12
|
+
* via native `[checked]` + `(change)`, Reactive Forms `formControlName`,
|
|
13
|
+
* Signal Forms `[formField]`, or `[(ngModel)]` — all work orthogonally with
|
|
14
|
+
* this directive (which only handles a11y + skin context).
|
|
15
|
+
*
|
|
16
|
+
* <!-- inside Field -->
|
|
17
|
+
* <skra-field>
|
|
18
|
+
* <input type="checkbox" skraCheckbox [checked]="x()" (change)="x.set($any($event.target).checked)" />
|
|
19
|
+
* <label skraLabelFor>Accept ToS</label>
|
|
20
|
+
* </skra-field>
|
|
21
|
+
*
|
|
22
|
+
* <!-- standalone (label wraps input — no id/for needed) -->
|
|
23
|
+
* <label>
|
|
24
|
+
* <input type="checkbox" skraCheckbox [checked]="x()" (change)="x.set($any($event.target).checked)" />
|
|
25
|
+
* Accept ToS
|
|
26
|
+
* </label>
|
|
27
|
+
*
|
|
28
|
+
* Toggle state (`checked`, `indeterminate`) is NOT reflected as `data-skra-*`
|
|
29
|
+
* — that would require Forge to observe native state, violating ADR 037. Slice
|
|
30
|
+
* CSS targets native `:checked` and `:indeterminate` pseudo-classes directly;
|
|
31
|
+
* browsers handle `aria-checked` natively (including `"mixed"` for indeterminate).
|
|
32
|
+
*/
|
|
33
|
+
class SkraCheckbox {
|
|
34
|
+
el = inject(ElementRef);
|
|
35
|
+
context = inject(SkraFieldContext, { optional: true });
|
|
36
|
+
// `indeterminate` is the ONE state Forge writes back to the native input,
|
|
37
|
+
// because HTML does not expose it as an attribute — only the JS property.
|
|
38
|
+
// Consumer is expected to drive it (e.g. tristate parent-of-children checkbox).
|
|
39
|
+
indeterminate = input(false, { ...(ngDevMode ? { debugName: "indeterminate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
40
|
+
constructor() {
|
|
41
|
+
const el = this.el.nativeElement;
|
|
42
|
+
effect(() => {
|
|
43
|
+
el.indeterminate = this.indeterminate();
|
|
44
|
+
});
|
|
45
|
+
// Field-cascaded native properties — only bind when context is present so
|
|
46
|
+
// standalone consumers retain full control of their static attributes.
|
|
47
|
+
if (this.context) {
|
|
48
|
+
const ctx = this.context;
|
|
49
|
+
effect(() => {
|
|
50
|
+
el.id = ctx.fieldId();
|
|
51
|
+
});
|
|
52
|
+
effect(() => {
|
|
53
|
+
el.disabled = ctx.isDisabled();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Readonly enforcement — native checkbox has no `[readonly]`, so without this
|
|
59
|
+
* the `aria-readonly` we announce would be a lie (the user could still toggle
|
|
60
|
+
* it). `preventDefault` on `click` blocks the toggle from both pointer and
|
|
61
|
+
* Space/Enter (keyboard activation dispatches a click on form controls).
|
|
62
|
+
* Mirrors the `<select skraSelect>` / `skraRadio` readonly guards.
|
|
63
|
+
*/
|
|
64
|
+
blockWhenReadonly(event) {
|
|
65
|
+
if (this.context?.isReadonly())
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
}
|
|
68
|
+
/** Block keyboard activation (Space) when readonly; let `Tab` through. */
|
|
69
|
+
blockKeyWhenReadonly(event) {
|
|
70
|
+
if (this.context?.isReadonly() && event.key !== 'Tab') {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
75
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: SkraCheckbox, isStandalone: true, selector: "input[type=\"checkbox\"][skraCheckbox]", inputs: { indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "blockWhenReadonly($event)", "keydown": "blockKeyWhenReadonly($event)" }, properties: { "attr.data-skra-disabled": "context?.isDisabled() ? \"\" : null", "attr.aria-invalid": "context?.hasError() ? \"true\" : null", "attr.aria-readonly": "context?.isReadonly() ? \"true\" : null", "attr.aria-describedby": "context?.describedByIds() ?? null" } }, ngImport: i0, template: '', isInline: true, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-checkbox-size);block-size:var(--skra-comp-field-checkbox-size);border-width:var(--skra-comp-field-checkbox-border-width);border-style:solid;border-color:var(--skra-comp-field-checkbox-border-color);border-radius:var(--skra-comp-field-checkbox-border-radius);background-color:var(--skra-comp-field-checkbox-background);background-size:70% 70%;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-checkbox-checked-background);border-color:var(--skra-comp-field-checkbox-checked-background);background-image:var(--skra-comp-field-checkbox-check-glyph)}:host(:indeterminate){background-color:var(--skra-comp-field-checkbox-checked-background);border-color:var(--skra-comp-field-checkbox-checked-background);background-image:linear-gradient(to bottom,transparent 42%,var(--skra-comp-field-checkbox-check-color) 42%,var(--skra-comp-field-checkbox-check-color) 58%,transparent 58%)}:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-checkbox-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])),:host(:indeterminate:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-checkbox-checked-background-hover);border-color:var(--skra-comp-field-checkbox-checked-background-hover)}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]),:host(:indeterminate[aria-readonly=true]){background-color:var(--skra-comp-field-checkbox-checked-background-readonly);border-color:var(--skra-comp-field-checkbox-checked-background-readonly)}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
76
|
+
}
|
|
77
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SkraCheckbox, decorators: [{
|
|
78
|
+
type: Component,
|
|
79
|
+
args: [{ selector: 'input[type="checkbox"][skraCheckbox]', standalone: true, template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, host: {
|
|
80
|
+
'[attr.data-skra-disabled]': 'context?.isDisabled() ? "" : null',
|
|
81
|
+
'[attr.aria-invalid]': 'context?.hasError() ? "true" : null',
|
|
82
|
+
// Readonly is ARIA-only (native `readonly` doesn't apply to checkboxes per
|
|
83
|
+
// HTML spec). No `data-skra-readonly` — it was dead surface (no SCSS
|
|
84
|
+
// consumer; styling keys off `[aria-readonly]`), removed 2026-05-31 to match
|
|
85
|
+
// the select atom's Q-3 ARIA-only pattern (Batch 5 cleanroom review).
|
|
86
|
+
'[attr.aria-readonly]': 'context?.isReadonly() ? "true" : null',
|
|
87
|
+
'[attr.aria-describedby]': 'context?.describedByIds() ?? null',
|
|
88
|
+
'(click)': 'blockWhenReadonly($event)',
|
|
89
|
+
'(keydown)': 'blockKeyWhenReadonly($event)',
|
|
90
|
+
}, styles: [":host{appearance:none;flex-shrink:0;cursor:pointer;outline:none;background-repeat:no-repeat;background-position:center;inline-size:var(--skra-comp-field-checkbox-size);block-size:var(--skra-comp-field-checkbox-size);border-width:var(--skra-comp-field-checkbox-border-width);border-style:solid;border-color:var(--skra-comp-field-checkbox-border-color);border-radius:var(--skra-comp-field-checkbox-border-radius);background-color:var(--skra-comp-field-checkbox-background);background-size:70% 70%;transition:background-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),border-color var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing),box-shadow var(--skra-comp-field-motion-duration) var(--skra-comp-field-motion-easing)}:host(:checked){background-color:var(--skra-comp-field-checkbox-checked-background);border-color:var(--skra-comp-field-checkbox-checked-background);background-image:var(--skra-comp-field-checkbox-check-glyph)}:host(:indeterminate){background-color:var(--skra-comp-field-checkbox-checked-background);border-color:var(--skra-comp-field-checkbox-checked-background);background-image:linear-gradient(to bottom,transparent 42%,var(--skra-comp-field-checkbox-check-color) 42%,var(--skra-comp-field-checkbox-check-color) 58%,transparent 58%)}:host(:hover:not(:disabled):not([aria-readonly=true])){border-color:var(--skra-comp-field-checkbox-border-color-hover)}:host(:checked:hover:not(:disabled):not([aria-readonly=true])),:host(:indeterminate:hover:not(:disabled):not([aria-readonly=true])){background-color:var(--skra-comp-field-checkbox-checked-background-hover);border-color:var(--skra-comp-field-checkbox-checked-background-hover)}:host(:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-focus-ring-color)}:host(:disabled),:host([data-skra-disabled]){cursor:not-allowed;opacity:var(--skra-sys-opacity-disabled)}:host([aria-readonly=true]){cursor:default;background-color:var(--skra-comp-field-background-readonly);border-color:var(--skra-comp-field-border-color-readonly)}:host(:checked[aria-readonly=true]),:host(:indeterminate[aria-readonly=true]){background-color:var(--skra-comp-field-checkbox-checked-background-readonly);border-color:var(--skra-comp-field-checkbox-checked-background-readonly)}:host([aria-invalid=true]){border-color:var(--skra-comp-field-error-color)}:host([aria-invalid=true]:focus-visible){box-shadow:0 0 0 var(--skra-comp-field-focus-ring-width) var(--skra-comp-field-error-color)}@media(prefers-reduced-motion:reduce){:host{transition:none}}\n"] }]
|
|
91
|
+
}], ctorParameters: () => [], propDecorators: { indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }] } });
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generated bundle index. Do not edit.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
export { SkraCheckbox };
|
|
98
|
+
//# sourceMappingURL=skra-ui-checkbox.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skra-ui-checkbox.mjs","sources":["../../../../libs/ui/checkbox/src/lib/checkbox/checkbox.ts","../../../../libs/ui/checkbox/src/skra-ui-checkbox.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n booleanAttribute,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { SkraFieldContext } from '@skra/ui/field';\n\n/**\n * Wires a native `<input type=\"checkbox\">` to SkraFieldContext when inside\n * `<skra-field>` (ADR 013), or stands alone with no cascade. Per ADR 039/040,\n * Forge ships no `<skra-checkbox>` wrapper — this is a Component-on-the-native-\n * host (`<input>` is void → `template: ''`, ADR 040 §2) carrying its own\n * `checkbox.scss` via Emulated encapsulation. Value channel stays consumer-owned\n * per ADR 037: bind\n * via native `[checked]` + `(change)`, Reactive Forms `formControlName`,\n * Signal Forms `[formField]`, or `[(ngModel)]` — all work orthogonally with\n * this directive (which only handles a11y + skin context).\n *\n * <!-- inside Field -->\n * <skra-field>\n * <input type=\"checkbox\" skraCheckbox [checked]=\"x()\" (change)=\"x.set($any($event.target).checked)\" />\n * <label skraLabelFor>Accept ToS</label>\n * </skra-field>\n *\n * <!-- standalone (label wraps input — no id/for needed) -->\n * <label>\n * <input type=\"checkbox\" skraCheckbox [checked]=\"x()\" (change)=\"x.set($any($event.target).checked)\" />\n * Accept ToS\n * </label>\n *\n * Toggle state (`checked`, `indeterminate`) is NOT reflected as `data-skra-*`\n * — that would require Forge to observe native state, violating ADR 037. Slice\n * CSS targets native `:checked` and `:indeterminate` pseudo-classes directly;\n * browsers handle `aria-checked` natively (including `\"mixed\"` for indeterminate).\n */\n@Component({\n selector: 'input[type=\"checkbox\"][skraCheckbox]',\n standalone: true,\n // Void host — no <ng-content>. See ADR 040 §2.\n template: '',\n styleUrl: './checkbox.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.Emulated,\n host: {\n '[attr.data-skra-disabled]': 'context?.isDisabled() ? \"\" : null',\n '[attr.aria-invalid]': 'context?.hasError() ? \"true\" : null',\n // Readonly is ARIA-only (native `readonly` doesn't apply to checkboxes per\n // HTML spec). No `data-skra-readonly` — it was dead surface (no SCSS\n // consumer; styling keys off `[aria-readonly]`), removed 2026-05-31 to match\n // the select atom's Q-3 ARIA-only pattern (Batch 5 cleanroom review).\n '[attr.aria-readonly]': 'context?.isReadonly() ? \"true\" : null',\n '[attr.aria-describedby]': 'context?.describedByIds() ?? null',\n '(click)': 'blockWhenReadonly($event)',\n '(keydown)': 'blockKeyWhenReadonly($event)',\n },\n})\nexport class SkraCheckbox {\n private readonly el = inject<ElementRef<HTMLInputElement>>(ElementRef);\n readonly context = inject(SkraFieldContext, { optional: true });\n\n // `indeterminate` is the ONE state Forge writes back to the native input,\n // because HTML does not expose it as an attribute — only the JS property.\n // Consumer is expected to drive it (e.g. tristate parent-of-children checkbox).\n readonly indeterminate = input(false, { transform: booleanAttribute });\n\n constructor() {\n const el = this.el.nativeElement;\n\n effect(() => {\n el.indeterminate = this.indeterminate();\n });\n\n // Field-cascaded native properties — only bind when context is present so\n // standalone consumers retain full control of their static attributes.\n if (this.context) {\n const ctx = this.context;\n effect(() => {\n el.id = ctx.fieldId();\n });\n effect(() => {\n el.disabled = ctx.isDisabled();\n });\n }\n }\n\n /**\n * Readonly enforcement — native checkbox has no `[readonly]`, so without this\n * the `aria-readonly` we announce would be a lie (the user could still toggle\n * it). `preventDefault` on `click` blocks the toggle from both pointer and\n * Space/Enter (keyboard activation dispatches a click on form controls).\n * Mirrors the `<select skraSelect>` / `skraRadio` readonly guards.\n */\n protected blockWhenReadonly(event: Event): void {\n if (this.context?.isReadonly()) event.preventDefault();\n }\n\n /** Block keyboard activation (Space) when readonly; let `Tab` through. */\n protected blockKeyWhenReadonly(event: KeyboardEvent): void {\n if (this.context?.isReadonly() && event.key !== 'Tab') {\n event.preventDefault();\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;MAsBU,YAAY,CAAA;AACN,IAAA,EAAE,GAAG,MAAM,CAA+B,UAAU,CAAC;IAC7D,OAAO,GAAG,MAAM,CAAC,gBAAgB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;;;IAKtD,aAAa,GAAG,KAAK,CAAC,KAAK,qFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;AAEtE,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa;QAEhC,MAAM,CAAC,MAAK;AACV,YAAA,EAAE,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE;AACzC,QAAA,CAAC,CAAC;;;AAIF,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE;AAChB,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO;YACxB,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE;AACvB,YAAA,CAAC,CAAC;YACF,MAAM,CAAC,MAAK;AACV,gBAAA,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,EAAE;AAChC,YAAA,CAAC,CAAC;QACJ;IACF;AAEA;;;;;;AAMG;AACO,IAAA,iBAAiB,CAAC,KAAY,EAAA;AACtC,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE;YAAE,KAAK,CAAC,cAAc,EAAE;IACxD;;AAGU,IAAA,oBAAoB,CAAC,KAAoB,EAAA;AACjD,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE;YACrD,KAAK,CAAC,cAAc,EAAE;QACxB;IACF;uGA7CW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,unBAjBb,EAAE,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+hFAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAiBD,YAAY,EAAA,UAAA,EAAA,CAAA;kBArBxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,sCAAsC,EAAA,UAAA,EACpC,IAAI,EAAA,QAAA,EAEN,EAAE,EAAA,eAAA,EAEK,uBAAuB,CAAC,MAAM,EAAA,aAAA,EAChC,iBAAiB,CAAC,QAAQ,EAAA,IAAA,EACnC;AACJ,wBAAA,2BAA2B,EAAE,mCAAmC;AAChE,wBAAA,qBAAqB,EAAE,qCAAqC;;;;;AAK5D,wBAAA,sBAAsB,EAAE,uCAAuC;AAC/D,wBAAA,yBAAyB,EAAE,mCAAmC;AAC9D,wBAAA,SAAS,EAAE,2BAA2B;AACtC,wBAAA,WAAW,EAAE,8BAA8B;AAC5C,qBAAA,EAAA,MAAA,EAAA,CAAA,+hFAAA,CAAA,EAAA;;;AC3DH;;AAEG;;;;"}
|