@lifecorner/angular-ui-kit 3.28.0 → 3.30.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/fesm2022/lifecorner-angular-ui-kit-card.mjs +23 -3
- package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +73 -5
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs +281 -10
- package/fesm2022/lifecorner-angular-ui-kit-datepicker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-money.mjs +90 -7
- package/fesm2022/lifecorner-angular-ui-kit-money.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +93 -9
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
- package/package.json +1 -1
- package/src/styles/theme-contrast.spec.ts +58 -0
- package/types/lifecorner-angular-ui-kit-card.d.ts +34 -0
- package/types/lifecorner-angular-ui-kit-combobox.d.ts +41 -1
- package/types/lifecorner-angular-ui-kit-datepicker.d.ts +162 -3
- package/types/lifecorner-angular-ui-kit-money.d.ts +81 -2
- package/types/lifecorner-angular-ui-kit-number-input.d.ts +61 -0
|
@@ -24,6 +24,7 @@ var CardComponent = class CardComponent {
|
|
|
24
24
|
href = input(void 0, ...ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []);
|
|
25
25
|
route = input(void 0, ...ngDevMode ? [{ debugName: "route" }] : /* istanbul ignore next */ []);
|
|
26
26
|
queryParams = input(void 0, ...ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []);
|
|
27
|
+
footerLayout = input("default", ...ngDevMode ? [{ debugName: "footerLayout" }] : /* istanbul ignore next */ []);
|
|
27
28
|
cardClick = output();
|
|
28
29
|
linkMode = computed(() => {
|
|
29
30
|
if (this.variant() === "dashed") return null;
|
|
@@ -198,13 +199,23 @@ var CardComponent = class CardComponent {
|
|
|
198
199
|
isSignal: true,
|
|
199
200
|
isRequired: false,
|
|
200
201
|
transformFunction: null
|
|
202
|
+
},
|
|
203
|
+
footerLayout: {
|
|
204
|
+
classPropertyName: "footerLayout",
|
|
205
|
+
publicName: "footerLayout",
|
|
206
|
+
isSignal: true,
|
|
207
|
+
isRequired: false,
|
|
208
|
+
transformFunction: null
|
|
201
209
|
}
|
|
202
210
|
},
|
|
203
211
|
outputs: { cardClick: "cardClick" },
|
|
204
|
-
host: {
|
|
212
|
+
host: {
|
|
213
|
+
listeners: { "keydown": "handleKeydown($event)" },
|
|
214
|
+
properties: { "class.lc-card--footer-equal": "footerLayout() === 'equal'" }
|
|
215
|
+
},
|
|
205
216
|
ngImport: i0,
|
|
206
217
|
template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n",
|
|
207
|
-
styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable,.card-linked{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:hover,.card-linked:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active,.card-linked:active{transform:scale(.98)}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-linked{isolation:isolate}.card-linked:has(.card__link:focus-visible){outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.card-linked:has(.card__link:focus-visible){outline-color:Highlight}}.card__link{color:inherit;text-decoration:none;outline:none}.card__link:after{content:\"\";position:absolute;inset:0;z-index:1}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary);hyphens:auto;hyphenate-limit-chars:12 4 3}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:var(--color-primary-subtle);color:var(--color-on-primary-subtle);border:1px solid var(--color-primary-500)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep :where(.card-linked :is(a[href],button,input,select,textarea,summary,[tabindex]):not(.card__link)){position:relative;z-index:2}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"],
|
|
218
|
+
styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable,.card-linked{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:hover,.card-linked:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active,.card-linked:active{transform:scale(.98)}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-linked{isolation:isolate}.card-linked:has(.card__link:focus-visible){outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.card-linked:has(.card__link:focus-visible){outline-color:Highlight}}.card__link{color:inherit;text-decoration:none;outline:none}.card__link:after{content:\"\";position:absolute;inset:0;z-index:1}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary);hyphens:auto;hyphenate-limit-chars:12 4 3}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:var(--color-primary-subtle);color:var(--color-on-primary-subtle);border:1px solid var(--color-primary-500)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host(.lc-card--footer-equal){container:lc-card/inline-size;inline-size:100%}:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer){display:grid;grid-template-columns:minmax(0,1fr);align-items:stretch}@container lc-card (width >= 30rem){:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer){grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr)}}:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer)>lc-button>.btn{block-size:100%}:host ::ng-deep :where(.card-linked :is(a[href],button,input,select,textarea,summary,[tabindex]):not(.card__link)){position:relative;z-index:2}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"],
|
|
208
219
|
dependencies: [
|
|
209
220
|
{
|
|
210
221
|
kind: "component",
|
|
@@ -269,8 +280,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
269
280
|
RouterLink
|
|
270
281
|
],
|
|
271
282
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
283
|
+
host: { "[class.lc-card--footer-equal]": "footerLayout() === 'equal'" },
|
|
272
284
|
template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n",
|
|
273
|
-
styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable,.card-linked{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:hover,.card-linked:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active,.card-linked:active{transform:scale(.98)}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-linked{isolation:isolate}.card-linked:has(.card__link:focus-visible){outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.card-linked:has(.card__link:focus-visible){outline-color:Highlight}}.card__link{color:inherit;text-decoration:none;outline:none}.card__link:after{content:\"\";position:absolute;inset:0;z-index:1}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary);hyphens:auto;hyphenate-limit-chars:12 4 3}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:var(--color-primary-subtle);color:var(--color-on-primary-subtle);border:1px solid var(--color-primary-500)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep :where(.card-linked :is(a[href],button,input,select,textarea,summary,[tabindex]):not(.card__link)){position:relative;z-index:2}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"]
|
|
285
|
+
styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable,.card-linked{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:hover,.card-linked:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active,.card-linked:active{transform:scale(.98)}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-linked{isolation:isolate}.card-linked:has(.card__link:focus-visible){outline:2px solid var(--color-primary-500);outline-offset:2px}@media(forced-colors:active){.card-linked:has(.card__link:focus-visible){outline-color:Highlight}}.card__link{color:inherit;text-decoration:none;outline:none}.card__link:after{content:\"\";position:absolute;inset:0;z-index:1}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary);hyphens:auto;hyphenate-limit-chars:12 4 3}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:var(--color-primary-subtle);color:var(--color-on-primary-subtle);border:1px solid var(--color-primary-500)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host(.lc-card--footer-equal){container:lc-card/inline-size;inline-size:100%}:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer){display:grid;grid-template-columns:minmax(0,1fr);align-items:stretch}@container lc-card (width >= 30rem){:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer){grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr)}}:host(.lc-card--footer-equal)>.card>::ng-deep :is([card-footer],.card-footer)>lc-button>.btn{block-size:100%}:host ::ng-deep :where(.card-linked :is(a[href],button,input,select,textarea,summary,[tabindex]):not(.card__link)){position:relative;z-index:2}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"]
|
|
274
286
|
}]
|
|
275
287
|
}],
|
|
276
288
|
ctorParameters: () => [],
|
|
@@ -411,6 +423,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
411
423
|
required: false
|
|
412
424
|
}]
|
|
413
425
|
}],
|
|
426
|
+
footerLayout: [{
|
|
427
|
+
type: i0.Input,
|
|
428
|
+
args: [{
|
|
429
|
+
isSignal: true,
|
|
430
|
+
alias: "footerLayout",
|
|
431
|
+
required: false
|
|
432
|
+
}]
|
|
433
|
+
}],
|
|
414
434
|
cardClick: [{
|
|
415
435
|
type: i0.Output,
|
|
416
436
|
args: ["cardClick"]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n HostListener,\n computed,\n effect,\n isDevMode,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, type Params } from '@angular/router';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/** Whether a `route` value is a link: `''`, `null` and `undefined` are not. */\nfunction hasRoute(route: string | readonly unknown[] | null | undefined): boolean {\n return route !== '' && route !== null && route !== undefined;\n}\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n * - Link mode (3.24): `href` or `route` make the whole card a real link,\n * the title stays a heading\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout & profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n *\n * <!-- An overview card that opens a page: heading h2 with a link inside -->\n * <lc-card title=\"SAP-Ist\" variant=\"outlined\" [headingLevel]=\"2\"\n * [route]=\"['/quellen', 'sap-ist']\" [queryParams]=\"{ ansicht: 'laeufe' }\">\n * ...\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet, RouterLink],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored;\n * `href` and `route` are ignored too\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer). Has no\n * effect on a link card (`href`/`route`).\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * Accessible name. On a link card (`href`/`route`) it names the link\n * instead of the card; it must contain the visible title (WCAG 2.5.3) and\n * the heading reads it too, so keep it short. Without it the link is named\n * by the title, and the card's content (badge, list) is read as normal\n * content. On other cards it labels the card.\n * @default undefined\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * URL the card links to (3.24, quellen row 1). The card becomes a real\n * link: the built-in `title` is an `<a href>` inside its heading, and the\n * link covers the whole card (click, Enter, middle click, \"open in new\n * tab\", \"copy link\"). No router needed; works in prerendered HTML. Wins\n * over `route`. Ignored by `dashed`. On a link card `clickable` has no\n * effect and `cardClick` is not emitted.\n * @default undefined\n */\n href = input<string | undefined>(undefined);\n\n /**\n * In-app route the card links to through the Angular router (`RouterLink`\n * commands: `'/quellen/usm'` or `['/quellen', id]`), relative to the\n * consumer's route, rendered as a real `<a href>` like `href`. Needs the\n * app's router (`provideRouter`); without one, use `href`. `''` and `null`\n * mean no link.\n * @default undefined\n */\n route = input<string | readonly unknown[] | null | undefined>(undefined);\n\n /**\n * Query parameters of `route`, as `RouterLink`'s `queryParams`\n * (`{ ansicht: 'laeufe' }`). Ignored with `href` (write the query into the\n * URL).\n * @default undefined\n */\n queryParams = input<Params | null | undefined>(undefined);\n\n /**\n * Emitted when a `clickable` card is clicked or activated with Enter or\n * Space. Not emitted on a link card (`href`/`route`): the link navigates by\n * itself (one navigation); bind the native `(click)` for analytics.\n */\n readonly cardClick = output<MouseEvent>();\n\n /**\n * How the card links (3.24): `href` wins over `route`; `''`/`null` routes\n * are no link, and the dashed \"coming soon\" tile never links.\n */\n protected readonly linkMode = computed<'href' | 'route' | null>(() => {\n if (this.variant() === 'dashed') return null;\n if (this.href()) return 'href';\n return hasRoute(this.route()) ? 'route' : null;\n });\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive, and a link card, whose\n * anchor is the only interactive element.\n */\n protected isClickable = computed(\n () => this.clickable() && this.variant() !== 'dashed' && !this.linkMode(),\n );\n\n /** `ariaLabel` on the card; on a link card it names the anchor instead. */\n protected readonly cardAriaLabel = computed(() => (this.linkMode() ? undefined : this.ariaLabel()));\n\n constructor() {\n if (isDevMode()) {\n effect(() => {\n if (!this.linkMode()) return;\n if (this.href() && hasRoute(this.route())) {\n console.warn('[lc-card] href and route are both set: href wins');\n }\n if (!this.title() && !this.ariaLabel()) {\n console.warn('[lc-card] a link card without title needs an ariaLabel to name its link');\n }\n });\n }\n }\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // A link card looks and reacts like a clickable one (3.24).\n if (this.linkMode()) {\n classes.push('card-linked');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n HostListener,\n computed,\n effect,\n isDevMode,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { RouterLink, type Params } from '@angular/router';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/** Layout of `lc-card`'s `[card-footer]` slot: `default` (a flex row, end-aligned) or `equal` (children at equal width). */\nexport type CardFooterLayout = 'default' | 'equal';\n\n/** Whether a `route` value is a link: `''`, `null` and `undefined` are not. */\nfunction hasRoute(route: string | readonly unknown[] | null | undefined): boolean {\n return route !== '' && route !== null && route !== undefined;\n}\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n * - Link mode (3.24): `href` or `route` make the whole card a real link,\n * the title stays a heading\n * - Equal footer (3.29): `footerLayout=\"equal\"` gives two answers of equal\n * weight in the `[card-footer]` slot the same width\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout & profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n *\n * <!-- An overview card that opens a page: heading h2 with a link inside -->\n * <lc-card title=\"SAP-Ist\" variant=\"outlined\" [headingLevel]=\"2\"\n * [route]=\"['/quellen', 'sap-ist']\" [queryParams]=\"{ ansicht: 'laeufe' }\">\n * ...\n * </lc-card>\n *\n * <!-- Two answers of equal weight (3.29): primary answer first -->\n * <lc-card title=\"Netflix\" variant=\"outlined\" footerLayout=\"equal\">\n * ...\n * <div card-footer>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Stimmt</lc-button>\n * <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">Kein Vertrag</lc-button>\n * </div>\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent, NgTemplateOutlet, RouterLink],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class.lc-card--footer-equal]': \"footerLayout() === 'equal'\" },\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored;\n * `href` and `route` are ignored too\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer). Has no\n * effect on a link card (`href`/`route`).\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * Accessible name. On a link card (`href`/`route`) it names the link\n * instead of the card; it must contain the visible title (WCAG 2.5.3) and\n * the heading reads it too, so keep it short. Without it the link is named\n * by the title, and the card's content (badge, list) is read as normal\n * content. On other cards it labels the card.\n * @default undefined\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * URL the card links to (3.24, quellen row 1). The card becomes a real\n * link: the built-in `title` is an `<a href>` inside its heading, and the\n * link covers the whole card (click, Enter, middle click, \"open in new\n * tab\", \"copy link\"). No router needed; works in prerendered HTML. Wins\n * over `route`. Ignored by `dashed`. On a link card `clickable` has no\n * effect and `cardClick` is not emitted.\n * @default undefined\n */\n href = input<string | undefined>(undefined);\n\n /**\n * In-app route the card links to through the Angular router (`RouterLink`\n * commands: `'/quellen/usm'` or `['/quellen', id]`), relative to the\n * consumer's route, rendered as a real `<a href>` like `href`. Needs the\n * app's router (`provideRouter`); without one, use `href`. `''` and `null`\n * mean no link.\n * @default undefined\n */\n route = input<string | readonly unknown[] | null | undefined>(undefined);\n\n /**\n * Query parameters of `route`, as `RouterLink`'s `queryParams`\n * (`{ ansicht: 'laeufe' }`). Ignored with `href` (write the query into the\n * URL).\n * @default undefined\n */\n queryParams = input<Params | null | undefined>(undefined);\n\n /**\n * Layout of the `[card-footer]` slot (3.29, CC-2). `equal`: its children\n * share the row at equal width once the card is at least 480 px (30rem)\n * wide, and stack full width, 8 px apart, below that (the card's own width,\n * at any viewport; long labels wrap inside the buttons, which get the\n * row's height). For two answers of equal weight, put\n * `<lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"true\">` in it,\n * primary answer first; the card adds layout only (gap, divider and\n * spacing as `default`) and never restyles the buttons. The card becomes a\n * size container and takes its parent's full width. Keep the default\n * `padding` (with `none` the buttons' focus ring is clipped). Works in link\n * cards (the buttons stay clickable above the link) and in prerendered\n * HTML (CSS only).\n * @default 'default'\n */\n footerLayout = input<CardFooterLayout>('default');\n\n /**\n * Emitted when a `clickable` card is clicked or activated with Enter or\n * Space. Not emitted on a link card (`href`/`route`): the link navigates by\n * itself (one navigation); bind the native `(click)` for analytics.\n */\n readonly cardClick = output<MouseEvent>();\n\n /**\n * How the card links (3.24): `href` wins over `route`; `''`/`null` routes\n * are no link, and the dashed \"coming soon\" tile never links.\n */\n protected readonly linkMode = computed<'href' | 'route' | null>(() => {\n if (this.variant() === 'dashed') return null;\n if (this.href()) return 'href';\n return hasRoute(this.route()) ? 'route' : null;\n });\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive, and a link card, whose\n * anchor is the only interactive element.\n */\n protected isClickable = computed(\n () => this.clickable() && this.variant() !== 'dashed' && !this.linkMode(),\n );\n\n /** `ariaLabel` on the card; on a link card it names the anchor instead. */\n protected readonly cardAriaLabel = computed(() => (this.linkMode() ? undefined : this.ariaLabel()));\n\n constructor() {\n if (isDevMode()) {\n effect(() => {\n if (!this.linkMode()) return;\n if (this.href() && hasRoute(this.route())) {\n console.warn('[lc-card] href and route are both set: href wins');\n }\n if (!this.title() && !this.ariaLabel()) {\n console.warn('[lc-card] a link card without title needs an ariaLabel to name its link');\n }\n });\n }\n }\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // A link card looks and reacts like a clickable one (3.24).\n if (this.linkMode()) {\n classes.push('card-linked');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"cardAriaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (linkMode() && !title()) {\n <!-- Link card without a title (3.24): an empty anchor, named by ariaLabel, covers the card. -->\n <ng-container [ngTemplateOutlet]=\"cardLink\" [ngTemplateOutletContext]=\"{ bare: true }\" />\n }\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @if (linkMode()) {\n <!-- Link card (3.24): the heading stays a heading and holds the link. -->\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h2>\n }\n @case (4) {\n <h4 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h4>\n }\n @case (5) {\n <h5 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h5>\n }\n @case (6) {\n <h6 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h6>\n }\n @default {\n <h3 class=\"card__title\"><ng-container [ngTemplateOutlet]=\"cardLink\" /></h3>\n }\n }\n } @else {\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n\n<!-- The link of a link card (3.24): `href` (plain, no router) or `route`\n (RouterLink, created only here, so other cards never need a router).\n Its ::after covers the card. -->\n<ng-template #cardLink let-bare=\"bare\">\n @if (linkMode() === 'href') {\n <a class=\"card__link\" [class.card__link--bare]=\"bare\" [href]=\"href()\" [attr.aria-label]=\"ariaLabel()\">{{ bare ? '' : title() }}</a>\n } @else {\n <a\n class=\"card__link\"\n [class.card__link--bare]=\"bare\"\n [routerLink]=\"route()\"\n [queryParams]=\"queryParams()\"\n [attr.aria-label]=\"ariaLabel()\"\n >{{ bare ? '' : title() }}</a\n >\n }\n</ng-template>\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, Component, ElementRef, HostListener, afterRenderEffect, computed, forwardRef, inject, input, output, signal, untracked, viewChild } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, ElementRef, HostListener, afterRenderEffect, computed, effect, forwardRef, inject, input, isDevMode, model, output, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|
4
4
|
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
5
5
|
import * as i1 from "@angular/cdk/overlay";
|
|
@@ -34,6 +34,7 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
34
34
|
clearAriaLabel = input("Clear", ...ngDevMode ? [{ debugName: "clearAriaLabel" }] : /* istanbul ignore next */ []);
|
|
35
35
|
createOptionLabel = input("Create", ...ngDevMode ? [{ debugName: "createOptionLabel" }] : /* istanbul ignore next */ []);
|
|
36
36
|
size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
|
|
37
|
+
selectedValue = model(null, ...ngDevMode ? [{ debugName: "selectedValue" }] : /* istanbul ignore next */ []);
|
|
37
38
|
valueChange = output();
|
|
38
39
|
queryChange = output();
|
|
39
40
|
optionSelected = output();
|
|
@@ -47,6 +48,8 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
47
48
|
isDisabled = computed(() => this.disabled() || this.formDisabled(), ...ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []);
|
|
48
49
|
selectedSingle = signal(null, ...ngDevMode ? [{ debugName: "selectedSingle" }] : /* istanbul ignore next */ []);
|
|
49
50
|
selectedMultiple = signal([], ...ngDevMode ? [{ debugName: "selectedMultiple" }] : /* istanbul ignore next */ []);
|
|
51
|
+
seenSelectedValue = null;
|
|
52
|
+
pendingSelectedValue = null;
|
|
50
53
|
visibleOptions = computed(() => {
|
|
51
54
|
const q = this.query().toLowerCase();
|
|
52
55
|
const loader = this.loadOptions();
|
|
@@ -125,11 +128,42 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
125
128
|
this.dropdownEl()?.nativeElement.querySelector(`#${id}`)?.scrollIntoView?.({ block: "nearest" });
|
|
126
129
|
});
|
|
127
130
|
});
|
|
131
|
+
effect(() => {
|
|
132
|
+
const value = this.selectedValue();
|
|
133
|
+
const options = this.options();
|
|
134
|
+
untracked(() => {
|
|
135
|
+
if (value !== this.seenSelectedValue) {
|
|
136
|
+
this.seenSelectedValue = value;
|
|
137
|
+
if (!this.multiple()) this.applySelectedValue(value, options);
|
|
138
|
+
else if (value !== null && isDevMode()) console.warn("[lc-combobox] selectedValue is ignored with multiple");
|
|
139
|
+
} else if (this.pendingSelectedValue !== null && !this.multiple()) this.applySelectedValue(this.pendingSelectedValue, options);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
applySelectedValue(value, options) {
|
|
144
|
+
this.pendingSelectedValue = null;
|
|
145
|
+
if (value === null) {
|
|
146
|
+
this.selectedSingle.set(null);
|
|
147
|
+
this.query.set("");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (this.selectedSingle()?.value === value) return;
|
|
151
|
+
const match = options.find((o) => o.value === value) ?? null;
|
|
152
|
+
if (!match) this.pendingSelectedValue = value;
|
|
153
|
+
this.selectedSingle.set(match);
|
|
154
|
+
this.query.set(match ? match.label : "");
|
|
155
|
+
}
|
|
156
|
+
emitSelectedValue(value) {
|
|
157
|
+
if (this.multiple()) return;
|
|
158
|
+
this.pendingSelectedValue = null;
|
|
159
|
+
this.seenSelectedValue = value;
|
|
160
|
+
this.selectedValue.set(value);
|
|
128
161
|
}
|
|
129
162
|
writeValue(value) {
|
|
130
163
|
if (this.multiple()) this.selectedMultiple.set(Array.isArray(value) ? value : []);
|
|
131
164
|
else {
|
|
132
165
|
const single = value && !Array.isArray(value) ? value : null;
|
|
166
|
+
this.pendingSelectedValue = null;
|
|
133
167
|
this.selectedSingle.set(single);
|
|
134
168
|
this.query.set(single ? single.label : "");
|
|
135
169
|
}
|
|
@@ -153,12 +187,26 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
153
187
|
else this.selectedSingle.set(null);
|
|
154
188
|
this.onChange(value);
|
|
155
189
|
this.valueChange.emit(value);
|
|
190
|
+
this.emitSelectedValue(null);
|
|
156
191
|
}
|
|
157
192
|
onClearClick() {
|
|
158
193
|
if (this.isDisabled()) return;
|
|
159
194
|
this.clear();
|
|
160
195
|
this.inputEl()?.nativeElement.focus();
|
|
161
196
|
}
|
|
197
|
+
hitsFrameControl(event) {
|
|
198
|
+
const target = event.target;
|
|
199
|
+
if (!target || target === this.inputEl()?.nativeElement) return true;
|
|
200
|
+
const button = target.closest("button");
|
|
201
|
+
return !!button && event.currentTarget.contains(button);
|
|
202
|
+
}
|
|
203
|
+
onFrameMousedown(event) {
|
|
204
|
+
if (!this.hitsFrameControl(event)) event.preventDefault();
|
|
205
|
+
}
|
|
206
|
+
onFrameClick(event) {
|
|
207
|
+
if (this.isDisabled() || this.hitsFrameControl(event)) return;
|
|
208
|
+
this.inputEl()?.nativeElement.focus();
|
|
209
|
+
}
|
|
162
210
|
onInputChange(event) {
|
|
163
211
|
const value = event.target.value;
|
|
164
212
|
this.query.set(value);
|
|
@@ -226,6 +274,7 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
226
274
|
this.query.set(option.label);
|
|
227
275
|
this.onChange(option);
|
|
228
276
|
this.valueChange.emit(option);
|
|
277
|
+
this.emitSelectedValue(option.value);
|
|
229
278
|
}
|
|
230
279
|
this.optionSelected.emit(option);
|
|
231
280
|
this.isOpen.set(false);
|
|
@@ -379,9 +428,17 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
379
428
|
isSignal: true,
|
|
380
429
|
isRequired: false,
|
|
381
430
|
transformFunction: null
|
|
431
|
+
},
|
|
432
|
+
selectedValue: {
|
|
433
|
+
classPropertyName: "selectedValue",
|
|
434
|
+
publicName: "selectedValue",
|
|
435
|
+
isSignal: true,
|
|
436
|
+
isRequired: false,
|
|
437
|
+
transformFunction: null
|
|
382
438
|
}
|
|
383
439
|
},
|
|
384
440
|
outputs: {
|
|
441
|
+
selectedValue: "selectedValueChange",
|
|
385
442
|
valueChange: "valueChange",
|
|
386
443
|
queryChange: "queryChange",
|
|
387
444
|
optionSelected: "optionSelected",
|
|
@@ -407,8 +464,8 @@ var ComboboxComponent = class ComboboxComponent {
|
|
|
407
464
|
isSignal: true
|
|
408
465
|
}],
|
|
409
466
|
ngImport: i0,
|
|
410
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div
|
|
411
|
-
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:
|
|
467
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
|
|
468
|
+
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.75rem;margin-block:-.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;border:none;background:transparent;background-clip:content-box;cursor:pointer;color:var(--color-text-tertiary);border-radius:calc(var(--border-radius-sm) + .125rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-combobox--lg .lc-combobox__clear{margin-inline-start:.625rem}.lc-combobox--lg .lc-combobox__clear:before{content:\"\";position:absolute;inset:-.625rem -.375rem -.625rem -.875rem}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}.lc-combobox__clear:focus-visible{outline-color:Highlight}}\n"],
|
|
412
469
|
dependencies: [
|
|
413
470
|
{
|
|
414
471
|
kind: "ngmodule",
|
|
@@ -522,8 +579,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
522
579
|
useExisting: forwardRef(() => ComboboxComponent),
|
|
523
580
|
multi: true
|
|
524
581
|
}],
|
|
525
|
-
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <div
|
|
526
|
-
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:
|
|
582
|
+
template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\" [for]=\"inputId\">{{ label() }}</label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- pointer shortcut; the input is the keyboard target -->\n <div\n #inputWrapper\n class=\"lc-combobox__input-wrapper\"\n (mousedown)=\"onFrameMousedown($event)\"\n (click)=\"onFrameClick($event)\"\n >\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!isDisabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [id]=\"inputId\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-describedby]=\"describedBy()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n [disabled]=\"isDisabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClearClick()\"\n [attr.aria-label]=\"clearAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </div>\n\n <!-- Dropdown: rendered in the CDK overlay container so overflow-hidden\n ancestors cannot clip it. Focus stays in the input (mousedown is\n prevented on the panel), keyboard nav uses aria-activedescendant. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"inputWrapper\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayMatchWidth]=\"true\"\n (detach)=\"isOpen.set(false)\"\n >\n <div\n #dropdownEl\n class=\"lc-combobox__dropdown\"\n role=\"listbox\"\n [id]=\"listboxId\"\n [attr.aria-multiselectable]=\"multiple() ? true : null\"\n (mousedown)=\"$event.preventDefault()\"\n >\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\" role=\"presentation\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value) {\n @let idx = flatOptions().indexOf(option);\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"idx === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [id]=\"optionId(idx)\"\n [attr.aria-selected]=\"idx === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(idx)\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" [decorative]=\"true\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -- listbox option: focus stays in the input, keyboard selection via aria-activedescendant -->\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n role=\"option\"\n [id]=\"createOptionId\"\n [attr.aria-selected]=\"highlightedIndex() === flatOptions().length\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ createOptionLabel() }} \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n </ng-template>\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\" [id]=\"inputId + '-helper'\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\" [id]=\"inputId + '-error'\" role=\"alert\">{{ error() }}</span>\n }\n</div>\n",
|
|
583
|
+
styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text-primary);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-field-border);border-radius:var(--border-radius-md, .375rem);background-color:var(--color-surface-2);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:hover{border-color:var(--color-text-secondary)}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 20%,transparent)}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline:2px solid var(--color-error-default);outline-offset:0;box-shadow:none}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.6;cursor:not-allowed;background-color:var(--color-surface-muted)}.lc-combobox--disabled .lc-combobox__input-wrapper:hover{border-color:var(--color-border)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text-primary);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-text-tertiary)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.75rem;margin-block:-.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin:-.125rem;padding:.125rem;border:none;background:transparent;background-clip:content-box;cursor:pointer;color:var(--color-text-tertiary);border-radius:calc(var(--border-radius-sm) + .125rem);flex-shrink:0}.lc-combobox__clear:hover:not(:disabled){color:var(--color-text-primary);background-color:var(--color-surface-hover)}.lc-combobox__clear:disabled{cursor:not-allowed}.lc-combobox__clear:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:0}.lc-combobox--lg .lc-combobox__clear{margin-inline-start:.625rem}.lc-combobox--lg .lc-combobox__clear:before{content:\"\";position:absolute;inset:-.625rem -.375rem -.625rem -.875rem}.lc-combobox__dropdown{margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-surface-raised);border:1px solid var(--color-border-strong);border-radius:var(--border-radius-md, .375rem);box-shadow:var(--elevation-3)}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-surface-hover)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600);font-weight:500;border-top:1px solid var(--color-divider)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-text-tertiary)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-text-error)}@media(forced-colors:active){.lc-combobox--error .lc-combobox__input-wrapper:focus-within{outline-color:Highlight}.lc-combobox__clear:focus-visible{outline-color:Highlight}}\n"]
|
|
527
584
|
}]
|
|
528
585
|
}],
|
|
529
586
|
ctorParameters: () => [],
|
|
@@ -664,6 +721,17 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
664
721
|
required: false
|
|
665
722
|
}]
|
|
666
723
|
}],
|
|
724
|
+
selectedValue: [{
|
|
725
|
+
type: i0.Input,
|
|
726
|
+
args: [{
|
|
727
|
+
isSignal: true,
|
|
728
|
+
alias: "selectedValue",
|
|
729
|
+
required: false
|
|
730
|
+
}]
|
|
731
|
+
}, {
|
|
732
|
+
type: i0.Output,
|
|
733
|
+
args: ["selectedValueChange"]
|
|
734
|
+
}],
|
|
667
735
|
valueChange: [{
|
|
668
736
|
type: i0.Output,
|
|
669
737
|
args: ["valueChange"]
|