@lifecorner/angular-ui-kit 3.11.5 → 3.12.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.
@@ -153,7 +153,7 @@ var AlertComponent = class AlertComponent {
153
153
  } },
154
154
  ngImport: i0,
155
155
  template: "@if (visible()) {\n <div [class]=\"containerClasses()\">\n @if (showIcon()) {\n <div class=\"lc-alert__icon\">\n <lc-icon\n [name]=\"defaultIcon()\"\n size=\"md\"\n [decorative]=\"!iconAriaLabel()\"\n [ariaLabel]=\"iconAriaLabel()\"\n />\n </div>\n }\n \n <div class=\"lc-alert__body\">\n @if (title()) {\n <div class=\"lc-alert__title\">{{ title() }}</div>\n }\n \n <div class=\"lc-alert__content\">\n @if (message()) {\n {{ message() }}\n } @else {\n <ng-content />\n }\n </div>\n </div>\n\n <div class=\"lc-alert__action\">\n <ng-content select=\"[slot='action']\" />\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"lc-alert__close\"\n [attr.aria-label]=\"closeAriaLabel()\"\n (click)=\"onCloseClick($event)\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" [decorative]=\"true\" />\n </button>\n }\n </div>\n}\n",
156
- styles: [".lc-alert{display:block;width:100%;border-radius:var(--border-radius-md);border-width:1px;border-style:solid;transition:opacity .15s ease-in-out}.lc-alert--hidden{display:none}.lc-alert__container{display:flex;align-items:flex-start;gap:var(--spacing-3);padding:var(--lc-density-padding-md, var(--spacing-4))}.lc-alert__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.lc-alert__body{flex:1;min-width:0}.lc-alert__title{font-weight:var(--font-weight-semibold);font-size:var(--font-size-sm);line-height:var(--line-height-tight);margin-bottom:var(--spacing-1)}.lc-alert__content{font-size:var(--font-size-sm);line-height:var(--line-height-normal)}.lc-alert--actions-stacked{container:lc-alert/inline-size}.lc-alert--actions-stacked .lc-alert__container{flex-wrap:wrap}.lc-alert--actions-stacked .lc-alert__body{flex:1 1 0}.lc-alert--actions-stacked .lc-alert__action{order:1;flex:1 1 100%;align-self:auto;display:flex;flex-direction:column;align-items:stretch;gap:var(--spacing-2, .5rem);margin-top:var(--spacing-1, .25rem)}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:100%}.lc-alert--actions-stacked.lc-alert--with-icon .lc-alert__action{padding-inline-start:calc(1.5rem + var(--spacing-3, .75rem))}@container lc-alert (width >= 36rem){.lc-alert--actions-stacked .lc-alert__action{flex-direction:row;flex-wrap:wrap;align-items:center}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:auto}}.lc-alert__action{flex-shrink:0;align-self:center}.lc-alert__action:empty{display:none}.lc-alert__close{flex-shrink:0;background:transparent;border:none;cursor:pointer;padding:var(--spacing-1);margin:calc(var(--spacing-1) * -1);border-radius:var(--border-radius-sm);transition:background-color .15s ease-in-out;display:flex;align-items:center;justify-content:center}.lc-alert__close:hover{background-color:var(--color-surface-hover)}.lc-alert__close:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert--success{background-color:#8ea4751f;border-color:#8ea47566;color:var(--color-text-primary)}.lc-alert--success .lc-alert__icon{color:var(--color-success)}.lc-alert--success .lc-alert__close:hover{background-color:#8ea4752e}.lc-alert--error{background-color:#9d0e0e24;border-color:#9d0e0e73;color:var(--color-text-primary)}.lc-alert--error .lc-alert__icon{color:var(--color-error)}.lc-alert--error .lc-alert__close:hover{background-color:#9d0e0e33}.lc-alert--warning{background-color:#e1a04024;border-color:#e1a0406b;color:var(--color-text-primary)}.lc-alert--warning .lc-alert__icon{color:var(--color-warning)}.lc-alert--warning .lc-alert__close:hover{background-color:#e1a04033}.lc-alert--info{background-color:#3b658829;border-color:#3b65886b;color:var(--color-text-primary)}.lc-alert--info .lc-alert__icon{color:var(--color-info)}.lc-alert--info .lc-alert__close:hover{background-color:#3b658838}@media(prefers-reduced-motion:no-preference){.lc-alert--hidden{animation:fadeOut .15s ease-in-out}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"],
156
+ styles: [".lc-alert{display:block;width:100%;border-radius:var(--border-radius-md);border-width:1px;border-style:solid;transition:opacity .15s ease-in-out}.lc-alert--hidden{display:none}.lc-alert__container{display:flex;align-items:flex-start;gap:var(--spacing-3);padding:var(--lc-density-padding-md, var(--spacing-4))}.lc-alert__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.lc-alert__body{flex:1;min-width:0}.lc-alert__title{font-weight:var(--font-weight-semibold);font-size:var(--font-size-sm);line-height:var(--line-height-tight);margin-bottom:var(--spacing-1)}.lc-alert__content{font-size:var(--font-size-sm);line-height:var(--line-height-normal)}.lc-alert--actions-stacked{container:lc-alert/inline-size}.lc-alert--actions-stacked .lc-alert__container{flex-wrap:wrap}.lc-alert--actions-stacked .lc-alert__body{flex:1 1 0}.lc-alert--actions-stacked .lc-alert__action{order:1;flex:1 1 100%;align-self:auto;display:flex;flex-direction:column;align-items:stretch;gap:var(--spacing-2, .5rem);margin-top:var(--spacing-1, .25rem)}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:100%}.lc-alert--actions-stacked.lc-alert--with-icon .lc-alert__action{padding-inline-start:calc(24px + var(--spacing-3, .75rem))}@container lc-alert (width >= 36rem){.lc-alert--actions-stacked .lc-alert__action{flex-direction:row;flex-wrap:wrap;align-items:center}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:auto}}.lc-alert__action{flex-shrink:0;align-self:center}.lc-alert__action:empty{display:none}.lc-alert__action>a:where(:not(.btn)){display:flex;align-items:center;width:fit-content;max-width:100%;min-width:var(--size-min-touch-width);min-height:var(--size-min-touch-height);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);line-height:var(--line-height-normal);color:var(--color-link);text-align:start;text-decoration-line:underline;text-decoration-thickness:max(1px,.0625em);text-underline-offset:.15em;overflow-wrap:anywhere;border-radius:var(--border-radius-sm)}.lc-alert__action>a:where(:not(.btn)):hover{color:var(--color-link-hover);text-decoration-thickness:max(2px,.125em)}.lc-alert__action>a:where(:not(.btn)):focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert:has(>.lc-alert__container>.lc-alert__action>a:where(:not(.btn))){container:lc-alert/inline-size}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__action:has(>a:where(:not(.btn))){flex-shrink:1;min-width:0;max-width:50%}@container lc-alert (width < 36rem){.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn))){flex-wrap:wrap}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn)))>.lc-alert__body{flex:1 1 0}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn)))>.lc-alert__action{order:1;flex:1 1 100%;max-width:none;margin-top:var(--spacing-1)}.lc-alert--with-icon:not(.lc-alert--actions-stacked) .lc-alert__action:has(>a:where(:not(.btn))){padding-inline-start:calc(24px + var(--spacing-3))}}.lc-alert__close{flex-shrink:0;background:transparent;border:none;cursor:pointer;padding:var(--spacing-1);margin:calc(var(--spacing-1) * -1);border-radius:var(--border-radius-sm);transition:background-color .15s ease-in-out;display:flex;align-items:center;justify-content:center}.lc-alert__close:hover{background-color:var(--color-surface-hover)}.lc-alert__close:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert--success{background-color:#8ea4751f;border-color:#8ea47566;color:var(--color-text-primary)}.lc-alert--success .lc-alert__icon{color:var(--color-success)}.lc-alert--success .lc-alert__close:hover{background-color:#8ea4752e}.lc-alert--error{background-color:#9d0e0e24;border-color:#9d0e0e73;color:var(--color-text-primary)}.lc-alert--error .lc-alert__icon{color:var(--color-error)}.lc-alert--error .lc-alert__close:hover{background-color:#9d0e0e33}.lc-alert--warning{background-color:#e1a04024;border-color:#e1a0406b;color:var(--color-text-primary)}.lc-alert--warning .lc-alert__icon{color:var(--color-warning)}.lc-alert--warning .lc-alert__close:hover{background-color:#e1a04033}.lc-alert--info{background-color:#3b658829;border-color:#3b65886b;color:var(--color-text-primary)}.lc-alert--info .lc-alert__icon{color:var(--color-info)}.lc-alert--info .lc-alert__close:hover{background-color:#3b658838}@media(prefers-reduced-motion:no-preference){.lc-alert--hidden{animation:fadeOut .15s ease-in-out}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"],
157
157
  dependencies: [{
158
158
  kind: "component",
159
159
  type: IconComponent,
@@ -206,7 +206,7 @@ i0.ɵɵngDeclareClassMetadata({
206
206
  "[attr.aria-label]": "ariaLabel()"
207
207
  },
208
208
  template: "@if (visible()) {\n <div [class]=\"containerClasses()\">\n @if (showIcon()) {\n <div class=\"lc-alert__icon\">\n <lc-icon\n [name]=\"defaultIcon()\"\n size=\"md\"\n [decorative]=\"!iconAriaLabel()\"\n [ariaLabel]=\"iconAriaLabel()\"\n />\n </div>\n }\n \n <div class=\"lc-alert__body\">\n @if (title()) {\n <div class=\"lc-alert__title\">{{ title() }}</div>\n }\n \n <div class=\"lc-alert__content\">\n @if (message()) {\n {{ message() }}\n } @else {\n <ng-content />\n }\n </div>\n </div>\n\n <div class=\"lc-alert__action\">\n <ng-content select=\"[slot='action']\" />\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"lc-alert__close\"\n [attr.aria-label]=\"closeAriaLabel()\"\n (click)=\"onCloseClick($event)\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" [decorative]=\"true\" />\n </button>\n }\n </div>\n}\n",
209
- styles: [".lc-alert{display:block;width:100%;border-radius:var(--border-radius-md);border-width:1px;border-style:solid;transition:opacity .15s ease-in-out}.lc-alert--hidden{display:none}.lc-alert__container{display:flex;align-items:flex-start;gap:var(--spacing-3);padding:var(--lc-density-padding-md, var(--spacing-4))}.lc-alert__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.lc-alert__body{flex:1;min-width:0}.lc-alert__title{font-weight:var(--font-weight-semibold);font-size:var(--font-size-sm);line-height:var(--line-height-tight);margin-bottom:var(--spacing-1)}.lc-alert__content{font-size:var(--font-size-sm);line-height:var(--line-height-normal)}.lc-alert--actions-stacked{container:lc-alert/inline-size}.lc-alert--actions-stacked .lc-alert__container{flex-wrap:wrap}.lc-alert--actions-stacked .lc-alert__body{flex:1 1 0}.lc-alert--actions-stacked .lc-alert__action{order:1;flex:1 1 100%;align-self:auto;display:flex;flex-direction:column;align-items:stretch;gap:var(--spacing-2, .5rem);margin-top:var(--spacing-1, .25rem)}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:100%}.lc-alert--actions-stacked.lc-alert--with-icon .lc-alert__action{padding-inline-start:calc(1.5rem + var(--spacing-3, .75rem))}@container lc-alert (width >= 36rem){.lc-alert--actions-stacked .lc-alert__action{flex-direction:row;flex-wrap:wrap;align-items:center}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:auto}}.lc-alert__action{flex-shrink:0;align-self:center}.lc-alert__action:empty{display:none}.lc-alert__close{flex-shrink:0;background:transparent;border:none;cursor:pointer;padding:var(--spacing-1);margin:calc(var(--spacing-1) * -1);border-radius:var(--border-radius-sm);transition:background-color .15s ease-in-out;display:flex;align-items:center;justify-content:center}.lc-alert__close:hover{background-color:var(--color-surface-hover)}.lc-alert__close:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert--success{background-color:#8ea4751f;border-color:#8ea47566;color:var(--color-text-primary)}.lc-alert--success .lc-alert__icon{color:var(--color-success)}.lc-alert--success .lc-alert__close:hover{background-color:#8ea4752e}.lc-alert--error{background-color:#9d0e0e24;border-color:#9d0e0e73;color:var(--color-text-primary)}.lc-alert--error .lc-alert__icon{color:var(--color-error)}.lc-alert--error .lc-alert__close:hover{background-color:#9d0e0e33}.lc-alert--warning{background-color:#e1a04024;border-color:#e1a0406b;color:var(--color-text-primary)}.lc-alert--warning .lc-alert__icon{color:var(--color-warning)}.lc-alert--warning .lc-alert__close:hover{background-color:#e1a04033}.lc-alert--info{background-color:#3b658829;border-color:#3b65886b;color:var(--color-text-primary)}.lc-alert--info .lc-alert__icon{color:var(--color-info)}.lc-alert--info .lc-alert__close:hover{background-color:#3b658838}@media(prefers-reduced-motion:no-preference){.lc-alert--hidden{animation:fadeOut .15s ease-in-out}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"]
209
+ styles: [".lc-alert{display:block;width:100%;border-radius:var(--border-radius-md);border-width:1px;border-style:solid;transition:opacity .15s ease-in-out}.lc-alert--hidden{display:none}.lc-alert__container{display:flex;align-items:flex-start;gap:var(--spacing-3);padding:var(--lc-density-padding-md, var(--spacing-4))}.lc-alert__icon{flex-shrink:0;display:flex;align-items:center;justify-content:center}.lc-alert__body{flex:1;min-width:0}.lc-alert__title{font-weight:var(--font-weight-semibold);font-size:var(--font-size-sm);line-height:var(--line-height-tight);margin-bottom:var(--spacing-1)}.lc-alert__content{font-size:var(--font-size-sm);line-height:var(--line-height-normal)}.lc-alert--actions-stacked{container:lc-alert/inline-size}.lc-alert--actions-stacked .lc-alert__container{flex-wrap:wrap}.lc-alert--actions-stacked .lc-alert__body{flex:1 1 0}.lc-alert--actions-stacked .lc-alert__action{order:1;flex:1 1 100%;align-self:auto;display:flex;flex-direction:column;align-items:stretch;gap:var(--spacing-2, .5rem);margin-top:var(--spacing-1, .25rem)}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:100%}.lc-alert--actions-stacked.lc-alert--with-icon .lc-alert__action{padding-inline-start:calc(24px + var(--spacing-3, .75rem))}@container lc-alert (width >= 36rem){.lc-alert--actions-stacked .lc-alert__action{flex-direction:row;flex-wrap:wrap;align-items:center}.lc-alert--actions-stacked .lc-alert__action>lc-button>.btn{width:auto}}.lc-alert__action{flex-shrink:0;align-self:center}.lc-alert__action:empty{display:none}.lc-alert__action>a:where(:not(.btn)){display:flex;align-items:center;width:fit-content;max-width:100%;min-width:var(--size-min-touch-width);min-height:var(--size-min-touch-height);font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);line-height:var(--line-height-normal);color:var(--color-link);text-align:start;text-decoration-line:underline;text-decoration-thickness:max(1px,.0625em);text-underline-offset:.15em;overflow-wrap:anywhere;border-radius:var(--border-radius-sm)}.lc-alert__action>a:where(:not(.btn)):hover{color:var(--color-link-hover);text-decoration-thickness:max(2px,.125em)}.lc-alert__action>a:where(:not(.btn)):focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert:has(>.lc-alert__container>.lc-alert__action>a:where(:not(.btn))){container:lc-alert/inline-size}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__action:has(>a:where(:not(.btn))){flex-shrink:1;min-width:0;max-width:50%}@container lc-alert (width < 36rem){.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn))){flex-wrap:wrap}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn)))>.lc-alert__body{flex:1 1 0}.lc-alert:not(.lc-alert--actions-stacked) .lc-alert__container:has(>.lc-alert__action>a:where(:not(.btn)))>.lc-alert__action{order:1;flex:1 1 100%;max-width:none;margin-top:var(--spacing-1)}.lc-alert--with-icon:not(.lc-alert--actions-stacked) .lc-alert__action:has(>a:where(:not(.btn))){padding-inline-start:calc(24px + var(--spacing-3))}}.lc-alert__close{flex-shrink:0;background:transparent;border:none;cursor:pointer;padding:var(--spacing-1);margin:calc(var(--spacing-1) * -1);border-radius:var(--border-radius-sm);transition:background-color .15s ease-in-out;display:flex;align-items:center;justify-content:center}.lc-alert__close:hover{background-color:var(--color-surface-hover)}.lc-alert__close:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-alert--success{background-color:#8ea4751f;border-color:#8ea47566;color:var(--color-text-primary)}.lc-alert--success .lc-alert__icon{color:var(--color-success)}.lc-alert--success .lc-alert__close:hover{background-color:#8ea4752e}.lc-alert--error{background-color:#9d0e0e24;border-color:#9d0e0e73;color:var(--color-text-primary)}.lc-alert--error .lc-alert__icon{color:var(--color-error)}.lc-alert--error .lc-alert__close:hover{background-color:#9d0e0e33}.lc-alert--warning{background-color:#e1a04024;border-color:#e1a0406b;color:var(--color-text-primary)}.lc-alert--warning .lc-alert__icon{color:var(--color-warning)}.lc-alert--warning .lc-alert__close:hover{background-color:#e1a04033}.lc-alert--info{background-color:#3b658829;border-color:#3b65886b;color:var(--color-text-primary)}.lc-alert--info .lc-alert__icon{color:var(--color-info)}.lc-alert--info .lc-alert__close:hover{background-color:#3b658838}@media(prefers-reduced-motion:no-preference){.lc-alert--hidden{animation:fadeOut .15s ease-in-out}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}\n"]
210
210
  }]
211
211
  }],
212
212
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-alert.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/alert/alert.component.ts","../../../../libs/angular-ui-kit/src/lib/alert/alert.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n model,\n output,\n computed,\n ViewEncapsulation,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport {\n ICON_CHECK_CIRCLE,\n ICON_CIRCLE_X,\n ICON_EXCLAMATION_TRIANGLE,\n ICON_INFORMATION_CIRCLE,\n ICON_X_MARK,\n} from '@lifecorner/angular-ui-kit/icons';\n\nexport type AlertVariant = 'success' | 'error' | 'warning' | 'info';\n\n/**\n * Layout of the projected actions (`slot=\"action\"`).\n * - `inline`: one action right of the body, vertically centred (3.2)\n * - `stacked`: the actions in their own row below the title and message;\n * below 576 px of alert width one per line at full width, 8 px apart,\n * from 576 px side by side at their natural width\n */\nexport type AlertActionsLayout = 'inline' | 'stacked';\n\n/**\n * Alert component for inline notifications.\n *\n * Features:\n * - Semantic variants (success, warning, error, info)\n * - Optional title and icon display\n * - Auto-mapped variant icons\n * - Dismissible with close button; `visible` is two-way bindable so a\n * dismissed alert can be shown again\n * - Content projection for custom body\n * - Action slot for a button rendered right of the body (e.g. retry), or\n * several actions stacked below it (`actionsLayout=\"stacked\"`)\n * - The variant icon is decorative unless `iconAriaLabel` names it (the\n * title and the colour carry the tone; no built-in English word)\n * - Accessible: `role=\"alert\"` for error / warning, `role=\"status\"` otherwise\n *\n * @example\n * ```html\n * <lc-alert variant=\"success\" title=\"Success!\" [dismissible]=\"true\">\n * Your changes have been saved.\n * </lc-alert>\n *\n * <lc-alert variant=\"error\" title=\"Laden fehlgeschlagen\">\n * Die Liste konnte nicht geladen werden.\n * <lc-button slot=\"action\" variant=\"outline\" size=\"sm\">Erneut versuchen</lc-button>\n * </lc-alert>\n * ```\n */\n@Component({\n selector: 'lc-alert',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [\n ɵprovideKitIcons([ICON_X_MARK, ICON_CHECK_CIRCLE, ICON_CIRCLE_X, ICON_EXCLAMATION_TRIANGLE, ICON_INFORMATION_CIRCLE]),\n ],\n templateUrl: './alert.component.html',\n styleUrls: ['./alert.component.scss'],\n encapsulation: ViewEncapsulation.None, // Required for dynamic variant class styling\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '[attr.role]': 'role()',\n '[attr.aria-live]': 'ariaLive()',\n '[attr.aria-label]': 'ariaLabel()',\n },\n})\nexport class AlertComponent {\n /**\n * Visual variant of the alert\n * @default 'info'\n */\n variant = input<AlertVariant>('info');\n\n /**\n * Alert title (optional)\n */\n title = input<string>();\n\n /**\n * Alert message (alternative to content projection)\n */\n message = input<string>();\n\n /**\n * Icon to display (defaults based on variant)\n */\n icon = input<string>();\n\n /**\n * Whether to show icon\n * @default true\n */\n showIcon = input<boolean>(true);\n\n /**\n * Accessible name of the variant icon, e.g. „Warnung“. Leave it unset when\n * the title already says what kind of message this is: the icon is then\n * decorative (`aria-hidden`). The kit sets no default text, so no English\n * word is read on a page in another language.\n */\n iconAriaLabel = input<string | undefined>(undefined);\n\n /**\n * Layout of the projected actions (`slot=\"action\"`): `inline` — one action\n * right of the body (3.2); `stacked` — the actions in their own row below\n * the title and message, one per line at full width and 8 px apart below\n * 576 px of alert width, side by side at their natural width from there.\n * @default 'inline'\n */\n actionsLayout = input<AlertActionsLayout>('inline');\n\n /**\n * Whether the alert can be dismissed\n * @default false\n */\n dismissible = input<boolean>(false);\n\n /**\n * ARIA label for accessibility\n */\n ariaLabel = input<string>();\n\n /** Accessible name of the close button (`dismissible`). */\n closeAriaLabel = input('Close alert');\n\n /**\n * Emitted when alert is dismissed\n */\n readonly dismissed = output<void>();\n\n /**\n * Visibility (two-way bindable). Dismissing sets it to `false`; the parent\n * can set it back to `true` to show the alert again.\n */\n visible = model<boolean>(true);\n\n /**\n * Computed host classes\n */\n protected hostClasses = computed(() => {\n const classes = ['lc-alert', `lc-alert--${this.variant()}`];\n if (this.actionsLayout() === 'stacked') {\n classes.push('lc-alert--actions-stacked');\n }\n if (this.showIcon()) {\n classes.push('lc-alert--with-icon');\n }\n if (!this.visible()) {\n classes.push('lc-alert--hidden');\n }\n return classes.join(' ');\n });\n\n /**\n * Computed container classes\n */\n protected containerClasses = computed(() => {\n return 'lc-alert__container';\n });\n\n /** Whether the variant should interrupt the user (error / warning). */\n private readonly isUrgent = computed(() => {\n const variant = this.variant();\n return variant === 'error' || variant === 'warning';\n });\n\n /**\n * Live-region role: `alert` (assertive) only for error / warning,\n * `status` (polite) for info / success.\n */\n protected role = computed(() => (this.isUrgent() ? 'alert' : 'status'));\n\n /**\n * Computed ARIA live region priority\n */\n protected ariaLive = computed(() => (this.isUrgent() ? 'assertive' : 'polite'));\n\n /**\n * Computed default icon based on variant\n */\n protected defaultIcon = computed(() => {\n const icons: Record<AlertVariant, string> = {\n success: 'check-circle',\n error: 'x-circle',\n warning: 'exclamation-triangle',\n info: 'information-circle',\n };\n return this.icon() || icons[this.variant()];\n });\n\n /**\n * Dismiss the alert\n */\n dismiss(): void {\n this.visible.set(false);\n this.dismissed.emit();\n }\n\n /**\n * Handle close button click\n */\n protected onCloseClick(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n this.dismiss();\n }\n}\n","@if (visible()) {\n <div [class]=\"containerClasses()\">\n @if (showIcon()) {\n <div class=\"lc-alert__icon\">\n <lc-icon\n [name]=\"defaultIcon()\"\n size=\"md\"\n [decorative]=\"!iconAriaLabel()\"\n [ariaLabel]=\"iconAriaLabel()\"\n />\n </div>\n }\n \n <div class=\"lc-alert__body\">\n @if (title()) {\n <div class=\"lc-alert__title\">{{ title() }}</div>\n }\n \n <div class=\"lc-alert__content\">\n @if (message()) {\n {{ message() }}\n } @else {\n <ng-content />\n }\n </div>\n </div>\n\n <div class=\"lc-alert__action\">\n <ng-content select=\"[slot='action']\" />\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"lc-alert__close\"\n [attr.aria-label]=\"closeAriaLabel()\"\n (click)=\"onCloseClick($event)\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" [decorative]=\"true\" />\n </button>\n }\n </div>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-alert.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/alert/alert.component.ts","../../../../libs/angular-ui-kit/src/lib/alert/alert.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n model,\n output,\n computed,\n ViewEncapsulation,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport {\n ICON_CHECK_CIRCLE,\n ICON_CIRCLE_X,\n ICON_EXCLAMATION_TRIANGLE,\n ICON_INFORMATION_CIRCLE,\n ICON_X_MARK,\n} from '@lifecorner/angular-ui-kit/icons';\n\nexport type AlertVariant = 'success' | 'error' | 'warning' | 'info';\n\n/**\n * Layout of the projected actions (`slot=\"action\"`).\n * - `inline`: one action right of the body, vertically centred (3.2)\n * - `stacked`: the actions in their own row below the title and message;\n * below 576 px of alert width one per line at full width, 8 px apart,\n * from 576 px side by side at their natural width\n */\nexport type AlertActionsLayout = 'inline' | 'stacked';\n\n/**\n * Alert component for inline notifications.\n *\n * Features:\n * - Semantic variants (success, warning, error, info)\n * - Optional title and icon display\n * - Auto-mapped variant icons\n * - Dismissible with close button; `visible` is two-way bindable so a\n * dismissed alert can be shown again\n * - Content projection for custom body\n * - Action slot for a button rendered right of the body (e.g. retry), or\n * several actions stacked below it (`actionsLayout=\"stacked\"`); 3.12: a text\n * link (`<a slot=\"action\" href=\"…\">`) as the one way forward gets a\n * 44 × 44 px target, link colour, an underline and a focus ring\n * - The variant icon is decorative unless `iconAriaLabel` names it (the\n * title and the colour carry the tone; no built-in English word)\n * - Accessible: `role=\"alert\"` for error / warning, `role=\"status\"` otherwise\n *\n * @example\n * ```html\n * <lc-alert variant=\"success\" title=\"Success!\" [dismissible]=\"true\">\n * Your changes have been saved.\n * </lc-alert>\n *\n * <lc-alert variant=\"error\" title=\"Laden fehlgeschlagen\">\n * Die Liste konnte nicht geladen werden.\n * <lc-button slot=\"action\" variant=\"outline\" size=\"sm\">Erneut versuchen</lc-button>\n * </lc-alert>\n *\n * <lc-alert variant=\"error\" title=\"Senden gerade nicht möglich\">\n * Wir können Ihre Nachricht gerade nicht senden.\n * <a slot=\"action\" href=\"mailto:kontakt@beispiel.example\">Schreiben Sie uns an kontakt@beispiel.example</a>\n * </lc-alert>\n * ```\n */\n@Component({\n selector: 'lc-alert',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [\n ɵprovideKitIcons([ICON_X_MARK, ICON_CHECK_CIRCLE, ICON_CIRCLE_X, ICON_EXCLAMATION_TRIANGLE, ICON_INFORMATION_CIRCLE]),\n ],\n templateUrl: './alert.component.html',\n styleUrls: ['./alert.component.scss'],\n encapsulation: ViewEncapsulation.None, // Required for dynamic variant class styling\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses()',\n '[attr.role]': 'role()',\n '[attr.aria-live]': 'ariaLive()',\n '[attr.aria-label]': 'ariaLabel()',\n },\n})\nexport class AlertComponent {\n /**\n * Visual variant of the alert\n * @default 'info'\n */\n variant = input<AlertVariant>('info');\n\n /**\n * Alert title (optional)\n */\n title = input<string>();\n\n /**\n * Alert message (alternative to content projection)\n */\n message = input<string>();\n\n /**\n * Icon to display (defaults based on variant)\n */\n icon = input<string>();\n\n /**\n * Whether to show icon\n * @default true\n */\n showIcon = input<boolean>(true);\n\n /**\n * Accessible name of the variant icon, e.g. „Warnung“. Leave it unset when\n * the title already says what kind of message this is: the icon is then\n * decorative (`aria-hidden`). The kit sets no default text, so no English\n * word is read on a page in another language.\n */\n iconAriaLabel = input<string | undefined>(undefined);\n\n /**\n * Layout of the projected actions (`slot=\"action\"`): `inline` — one action\n * right of the body (3.2); `stacked` — the actions in their own row below\n * the title and message, one per line at full width and 8 px apart below\n * 576 px of alert width, side by side at their natural width from there.\n * @default 'inline'\n */\n actionsLayout = input<AlertActionsLayout>('inline');\n\n /**\n * Whether the alert can be dismissed\n * @default false\n */\n dismissible = input<boolean>(false);\n\n /**\n * ARIA label for accessibility\n */\n ariaLabel = input<string>();\n\n /** Accessible name of the close button (`dismissible`). */\n closeAriaLabel = input('Close alert');\n\n /**\n * Emitted when alert is dismissed\n */\n readonly dismissed = output<void>();\n\n /**\n * Visibility (two-way bindable). Dismissing sets it to `false`; the parent\n * can set it back to `true` to show the alert again.\n */\n visible = model<boolean>(true);\n\n /**\n * Computed host classes\n */\n protected hostClasses = computed(() => {\n const classes = ['lc-alert', `lc-alert--${this.variant()}`];\n if (this.actionsLayout() === 'stacked') {\n classes.push('lc-alert--actions-stacked');\n }\n if (this.showIcon()) {\n classes.push('lc-alert--with-icon');\n }\n if (!this.visible()) {\n classes.push('lc-alert--hidden');\n }\n return classes.join(' ');\n });\n\n /**\n * Computed container classes\n */\n protected containerClasses = computed(() => {\n return 'lc-alert__container';\n });\n\n /** Whether the variant should interrupt the user (error / warning). */\n private readonly isUrgent = computed(() => {\n const variant = this.variant();\n return variant === 'error' || variant === 'warning';\n });\n\n /**\n * Live-region role: `alert` (assertive) only for error / warning,\n * `status` (polite) for info / success.\n */\n protected role = computed(() => (this.isUrgent() ? 'alert' : 'status'));\n\n /**\n * Computed ARIA live region priority\n */\n protected ariaLive = computed(() => (this.isUrgent() ? 'assertive' : 'polite'));\n\n /**\n * Computed default icon based on variant\n */\n protected defaultIcon = computed(() => {\n const icons: Record<AlertVariant, string> = {\n success: 'check-circle',\n error: 'x-circle',\n warning: 'exclamation-triangle',\n info: 'information-circle',\n };\n return this.icon() || icons[this.variant()];\n });\n\n /**\n * Dismiss the alert\n */\n dismiss(): void {\n this.visible.set(false);\n this.dismissed.emit();\n }\n\n /**\n * Handle close button click\n */\n protected onCloseClick(event: Event): void {\n event.preventDefault();\n event.stopPropagation();\n this.dismiss();\n }\n}\n","@if (visible()) {\n <div [class]=\"containerClasses()\">\n @if (showIcon()) {\n <div class=\"lc-alert__icon\">\n <lc-icon\n [name]=\"defaultIcon()\"\n size=\"md\"\n [decorative]=\"!iconAriaLabel()\"\n [ariaLabel]=\"iconAriaLabel()\"\n />\n </div>\n }\n \n <div class=\"lc-alert__body\">\n @if (title()) {\n <div class=\"lc-alert__title\">{{ title() }}</div>\n }\n \n <div class=\"lc-alert__content\">\n @if (message()) {\n {{ message() }}\n } @else {\n <ng-content />\n }\n </div>\n </div>\n\n <div class=\"lc-alert__action\">\n <ng-content select=\"[slot='action']\" />\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"lc-alert__close\"\n [attr.aria-label]=\"closeAriaLabel()\"\n (click)=\"onCloseClick($event)\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" [decorative]=\"true\" />\n </button>\n }\n </div>\n}\n"],"mappings":"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"}
@@ -272,6 +272,8 @@ var ConfirmDialogComponent = class ConfirmDialogComponent {
272
272
  "spellcheck",
273
273
  "enterkeyhint",
274
274
  "requiredAriaLabel",
275
+ "controlId",
276
+ "announceError",
275
277
  "value"
276
278
  ],
277
279
  outputs: [
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-confirm-dialog.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm-dialog.component.ts","../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm-dialog.component.html","../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm.service.ts"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n effect,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EXCLAMATION_CIRCLE, ICON_EXCLAMATION_TRIANGLE } from '@lifecorner/angular-ui-kit/icons';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { InputComponent } from '@lifecorner/angular-ui-kit/input';\nimport { ModalComponent, ModalPlacement } from '@lifecorner/angular-ui-kit/modal';\n\nexport type ConfirmDialogVariant = 'default' | 'destructive' | 'warning';\n\n/** Size of the confirm and cancel buttons (`lg` = 48.5 px, a 44 px target). */\nexport type ConfirmDialogButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface RequireTextConfig {\n prompt: string;\n expected: string;\n}\n\n/**\n * Confirm dialog component for confirming user actions.\n *\n * Builds on `<lc-modal>` to provide a standardized confirmation pattern\n * with optional destructive variant and text-matching confirmation.\n *\n * The dialog is named by its title (`aria-labelledby`); `[slot='preview']`\n * shows what the action affects below the message; `confirmLoading` keeps the\n * dialog open with a busy confirm button while the action runs.\n *\n * @example\n * ```html\n * <lc-confirm-dialog\n * [open]=\"showConfirm()\"\n * variant=\"destructive\"\n * title=\"Delete project?\"\n * message=\"This cannot be undone.\"\n * (confirmed)=\"onDelete()\"\n * (cancelled)=\"showConfirm.set(false)\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-confirm-dialog',\n standalone: true,\n imports: [FormsModule, IconComponent, ButtonComponent, ModalComponent, InputComponent],\n viewProviders: [ɵprovideKitIcons([ICON_EXCLAMATION_TRIANGLE, ICON_EXCLAMATION_CIRCLE])],\n templateUrl: './confirm-dialog.component.html',\n styleUrls: ['./confirm-dialog.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ConfirmDialogComponent {\n private static nextId = 0;\n\n /** Whether the dialog is open */\n readonly open = input<boolean>(false);\n\n /** Dialog variant */\n readonly variant = input<ConfirmDialogVariant>('default');\n\n /** Dialog title */\n readonly title = input.required<string>();\n\n /** Dialog message */\n readonly message = input.required<string>();\n\n /** Confirm button label */\n readonly confirmLabel = input<string>('Confirm');\n\n /** Cancel button label */\n readonly cancelLabel = input<string>('Cancel');\n\n /** Icon name (auto-set by variant if not provided) */\n readonly icon = input<string>();\n\n /** Require text match to enable confirm */\n readonly requireText = input<RequireTextConfig>();\n\n /** Accessible name of the close button in the header. */\n readonly closeAriaLabel = input('Close modal');\n\n /**\n * Shows a spinner on the confirm button and blocks a second confirm while\n * the action runs; the app closes the dialog when it is done.\n * @default false\n */\n readonly confirmLoading = input<boolean>(false);\n\n /**\n * Size of the confirm and cancel buttons: `md` (3.2) or `lg` (48.5 px, the\n * 44 px touch target), or `sm`.\n * @default 'md'\n */\n readonly buttonSize = input<ConfirmDialogButtonSize>('md');\n\n /**\n * Where the dialog sits (passed to `lc-modal`): `center` (3.2), `auto` (a\n * bottom sheet below 640 px) or `sheet`.\n * @default 'center'\n */\n readonly placement = input<ModalPlacement>('center');\n\n /** Id of the title, which names the dialog. */\n protected readonly titleId = `lc-confirm-title-${++ConfirmDialogComponent.nextId}`;\n\n /** Emitted when user confirms */\n readonly confirmed = output<void>();\n\n /** Emitted when user cancels */\n readonly cancelled = output<void>();\n\n /** Internal text input value */\n protected inputValue = signal('');\n\n /** Resolved icon name */\n protected resolvedIcon = computed(() => {\n const icon = this.icon();\n if (icon) return icon;\n switch (this.variant()) {\n case 'destructive':\n return 'exclamation-triangle';\n case 'warning':\n return 'exclamation-circle';\n default:\n // Tabler name — 'question-mark-circle' has no alias and rendered a placeholder\n return 'help-circle';\n }\n });\n\n /** Resolved icon color */\n protected iconColor = computed(() => {\n switch (this.variant()) {\n case 'destructive':\n return 'var(--color-error-default)';\n case 'warning':\n return 'var(--color-warning-default)';\n default:\n return 'var(--color-primary-600)';\n }\n });\n\n /** Confirm button variant */\n protected confirmButtonVariant = computed(() => {\n switch (this.variant()) {\n case 'destructive':\n return 'danger';\n case 'warning':\n return 'warning';\n default:\n return 'primary';\n }\n });\n\n /** Whether confirm is allowed (text match check) */\n protected confirmAllowed = computed(() => {\n const req = this.requireText();\n if (!req) return true;\n return this.inputValue() === req.expected;\n });\n\n constructor() {\n // Reset input when dialog opens\n effect(() => {\n if (this.open()) {\n this.inputValue.set('');\n }\n });\n }\n\n /**\n * Confirm via button click or Enter inside the require-text input.\n * Enter is deliberately NOT handled on the whole dialog: the focus trap\n * lands on the close/cancel buttons first, and Enter there must activate\n * *that* button, never the (possibly destructive) confirm action.\n */\n protected onConfirm(): void {\n if (!this.confirmAllowed() || this.confirmLoading()) return;\n this.confirmed.emit();\n }\n\n protected onCancel(): void {\n this.cancelled.emit();\n }\n\n protected onModalClose(): void {\n this.cancelled.emit();\n }\n\n protected onInputChange(value: string | number): void {\n this.inputValue.set(String(value));\n }\n}\n","<lc-modal\n [open]=\"open()\"\n size=\"sm\"\n [placement]=\"placement()\"\n [showCloseButton]=\"true\"\n [closeAriaLabel]=\"closeAriaLabel()\"\n [ariaLabelledBy]=\"titleId\"\n (openChange)=\"$event ? null : onModalClose()\"\n>\n <div slot=\"header\">\n <div class=\"lc-confirm__header\">\n <lc-icon\n [name]=\"resolvedIcon()\"\n size=\"md\"\n [color]=\"iconColor()\"\n [decorative]=\"true\"\n />\n <h2 [id]=\"titleId\">{{ title() }}</h2>\n </div>\n </div>\n\n <div slot=\"body\">\n <p class=\"lc-confirm__message\">{{ message() }}</p>\n\n <div class=\"lc-confirm__preview\">\n <ng-content select=\"[slot='preview']\" />\n </div>\n\n @if (requireText(); as req) {\n <div class=\"lc-confirm__require-text\">\n <!-- lc-input renders the prompt as an associated <label for> -->\n <lc-input\n [label]=\"req.prompt\"\n [placeholder]=\"req.expected\"\n (valueChange)=\"onInputChange($event)\"\n (enterPressed)=\"onConfirm()\"\n />\n </div>\n }\n </div>\n\n <div slot=\"footer\" class=\"lc-confirm__footer\">\n <lc-button\n variant=\"ghost\"\n [size]=\"buttonSize()\"\n (clicked)=\"onCancel()\"\n >\n {{ cancelLabel() }}\n </lc-button>\n <lc-button\n [variant]=\"confirmButtonVariant()\"\n [size]=\"buttonSize()\"\n [disabled]=\"!confirmAllowed()\"\n [loading]=\"confirmLoading()\"\n (clicked)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </lc-button>\n </div>\n</lc-modal>\n","import {\n Injectable,\n ApplicationRef,\n createComponent,\n EnvironmentInjector,\n inject,\n} from '@angular/core';\nimport {\n ConfirmDialogButtonSize,\n ConfirmDialogComponent,\n ConfirmDialogVariant,\n RequireTextConfig,\n} from './confirm-dialog.component';\nimport type { ModalPlacement } from '@lifecorner/angular-ui-kit/modal';\n\nexport interface ConfirmOptions {\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: ConfirmDialogVariant;\n icon?: string;\n requireText?: RequireTextConfig;\n /** Accessible name of the close button (3.3). */\n closeAriaLabel?: string;\n /** Size of both buttons (3.3): `lg` for a 44 px touch target. */\n buttonSize?: ConfirmDialogButtonSize;\n /** `center`, `auto` (a bottom sheet below 640 px) or `sheet` (3.3). */\n placement?: ModalPlacement;\n}\n\n/**\n * Imperative confirm dialog service.\n *\n * @example\n * ```ts\n * const ok = await this.confirmService.confirm({\n * title: 'Delete item?',\n * message: 'This cannot be undone.',\n * });\n * if (ok) { ... }\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ConfirmService {\n private readonly appRef = inject(ApplicationRef);\n private readonly injector = inject(EnvironmentInjector);\n\n /**\n * Show a confirmation dialog and return a promise that resolves\n * to true (confirmed) or false (cancelled).\n */\n confirm(opts: ConfirmOptions): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n // Create a host element\n const hostEl = document.createElement('div');\n document.body.appendChild(hostEl);\n\n const componentRef = createComponent(ConfirmDialogComponent, {\n hostElement: hostEl,\n environmentInjector: this.injector,\n });\n\n // Set inputs\n componentRef.setInput('open', true);\n componentRef.setInput('title', opts.title);\n componentRef.setInput('message', opts.message);\n if (opts.confirmLabel) componentRef.setInput('confirmLabel', opts.confirmLabel);\n if (opts.cancelLabel) componentRef.setInput('cancelLabel', opts.cancelLabel);\n if (opts.variant) componentRef.setInput('variant', opts.variant);\n if (opts.icon) componentRef.setInput('icon', opts.icon);\n if (opts.requireText) componentRef.setInput('requireText', opts.requireText);\n if (opts.closeAriaLabel) componentRef.setInput('closeAriaLabel', opts.closeAriaLabel);\n if (opts.buttonSize) componentRef.setInput('buttonSize', opts.buttonSize);\n if (opts.placement) componentRef.setInput('placement', opts.placement);\n\n const cleanup = () => {\n this.appRef.detachView(componentRef.hostView);\n componentRef.destroy();\n hostEl.remove();\n };\n\n componentRef.instance.confirmed.subscribe(() => {\n cleanup();\n resolve(true);\n });\n\n componentRef.instance.cancelled.subscribe(() => {\n cleanup();\n resolve(false);\n });\n\n this.appRef.attachView(componentRef.hostView);\n });\n }\n\n /**\n * Show a destructive confirmation dialog.\n * Shorthand for `confirm({ ...opts, variant: 'destructive' })`.\n */\n destructive(opts: Omit<ConfirmOptions, 'variant'>): Promise<boolean> {\n return this.confirm({ ...opts, variant: 'destructive' });\n }\n\n /**\n * Show a warning confirmation dialog.\n * Shorthand for `confirm({ ...opts, variant: 'warning' })`.\n */\n warning(opts: Omit<ConfirmOptions, 'variant'>): Promise<boolean> {\n return this.confirm({ ...opts, variant: 'warning' });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-confirm-dialog.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm-dialog.component.ts","../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm-dialog.component.html","../../../../libs/angular-ui-kit/src/lib/confirm-dialog/confirm.service.ts"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n effect,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EXCLAMATION_CIRCLE, ICON_EXCLAMATION_TRIANGLE } from '@lifecorner/angular-ui-kit/icons';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { InputComponent } from '@lifecorner/angular-ui-kit/input';\nimport { ModalComponent, ModalPlacement } from '@lifecorner/angular-ui-kit/modal';\n\nexport type ConfirmDialogVariant = 'default' | 'destructive' | 'warning';\n\n/** Size of the confirm and cancel buttons (`lg` = 48.5 px, a 44 px target). */\nexport type ConfirmDialogButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface RequireTextConfig {\n prompt: string;\n expected: string;\n}\n\n/**\n * Confirm dialog component for confirming user actions.\n *\n * Builds on `<lc-modal>` to provide a standardized confirmation pattern\n * with optional destructive variant and text-matching confirmation.\n *\n * The dialog is named by its title (`aria-labelledby`); `[slot='preview']`\n * shows what the action affects below the message; `confirmLoading` keeps the\n * dialog open with a busy confirm button while the action runs.\n *\n * @example\n * ```html\n * <lc-confirm-dialog\n * [open]=\"showConfirm()\"\n * variant=\"destructive\"\n * title=\"Delete project?\"\n * message=\"This cannot be undone.\"\n * (confirmed)=\"onDelete()\"\n * (cancelled)=\"showConfirm.set(false)\"\n * />\n * ```\n */\n@Component({\n selector: 'lc-confirm-dialog',\n standalone: true,\n imports: [FormsModule, IconComponent, ButtonComponent, ModalComponent, InputComponent],\n viewProviders: [ɵprovideKitIcons([ICON_EXCLAMATION_TRIANGLE, ICON_EXCLAMATION_CIRCLE])],\n templateUrl: './confirm-dialog.component.html',\n styleUrls: ['./confirm-dialog.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ConfirmDialogComponent {\n private static nextId = 0;\n\n /** Whether the dialog is open */\n readonly open = input<boolean>(false);\n\n /** Dialog variant */\n readonly variant = input<ConfirmDialogVariant>('default');\n\n /** Dialog title */\n readonly title = input.required<string>();\n\n /** Dialog message */\n readonly message = input.required<string>();\n\n /** Confirm button label */\n readonly confirmLabel = input<string>('Confirm');\n\n /** Cancel button label */\n readonly cancelLabel = input<string>('Cancel');\n\n /** Icon name (auto-set by variant if not provided) */\n readonly icon = input<string>();\n\n /** Require text match to enable confirm */\n readonly requireText = input<RequireTextConfig>();\n\n /** Accessible name of the close button in the header. */\n readonly closeAriaLabel = input('Close modal');\n\n /**\n * Shows a spinner on the confirm button and blocks a second confirm while\n * the action runs; the app closes the dialog when it is done.\n * @default false\n */\n readonly confirmLoading = input<boolean>(false);\n\n /**\n * Size of the confirm and cancel buttons: `md` (3.2) or `lg` (48.5 px, the\n * 44 px touch target), or `sm`.\n * @default 'md'\n */\n readonly buttonSize = input<ConfirmDialogButtonSize>('md');\n\n /**\n * Where the dialog sits (passed to `lc-modal`): `center` (3.2), `auto` (a\n * bottom sheet below 640 px) or `sheet`.\n * @default 'center'\n */\n readonly placement = input<ModalPlacement>('center');\n\n /** Id of the title, which names the dialog. */\n protected readonly titleId = `lc-confirm-title-${++ConfirmDialogComponent.nextId}`;\n\n /** Emitted when user confirms */\n readonly confirmed = output<void>();\n\n /** Emitted when user cancels */\n readonly cancelled = output<void>();\n\n /** Internal text input value */\n protected inputValue = signal('');\n\n /** Resolved icon name */\n protected resolvedIcon = computed(() => {\n const icon = this.icon();\n if (icon) return icon;\n switch (this.variant()) {\n case 'destructive':\n return 'exclamation-triangle';\n case 'warning':\n return 'exclamation-circle';\n default:\n // Tabler name — 'question-mark-circle' has no alias and rendered a placeholder\n return 'help-circle';\n }\n });\n\n /** Resolved icon color */\n protected iconColor = computed(() => {\n switch (this.variant()) {\n case 'destructive':\n return 'var(--color-error-default)';\n case 'warning':\n return 'var(--color-warning-default)';\n default:\n return 'var(--color-primary-600)';\n }\n });\n\n /** Confirm button variant */\n protected confirmButtonVariant = computed(() => {\n switch (this.variant()) {\n case 'destructive':\n return 'danger';\n case 'warning':\n return 'warning';\n default:\n return 'primary';\n }\n });\n\n /** Whether confirm is allowed (text match check) */\n protected confirmAllowed = computed(() => {\n const req = this.requireText();\n if (!req) return true;\n return this.inputValue() === req.expected;\n });\n\n constructor() {\n // Reset input when dialog opens\n effect(() => {\n if (this.open()) {\n this.inputValue.set('');\n }\n });\n }\n\n /**\n * Confirm via button click or Enter inside the require-text input.\n * Enter is deliberately NOT handled on the whole dialog: the focus trap\n * lands on the close/cancel buttons first, and Enter there must activate\n * *that* button, never the (possibly destructive) confirm action.\n */\n protected onConfirm(): void {\n if (!this.confirmAllowed() || this.confirmLoading()) return;\n this.confirmed.emit();\n }\n\n protected onCancel(): void {\n this.cancelled.emit();\n }\n\n protected onModalClose(): void {\n this.cancelled.emit();\n }\n\n protected onInputChange(value: string | number): void {\n this.inputValue.set(String(value));\n }\n}\n","<lc-modal\n [open]=\"open()\"\n size=\"sm\"\n [placement]=\"placement()\"\n [showCloseButton]=\"true\"\n [closeAriaLabel]=\"closeAriaLabel()\"\n [ariaLabelledBy]=\"titleId\"\n (openChange)=\"$event ? null : onModalClose()\"\n>\n <div slot=\"header\">\n <div class=\"lc-confirm__header\">\n <lc-icon\n [name]=\"resolvedIcon()\"\n size=\"md\"\n [color]=\"iconColor()\"\n [decorative]=\"true\"\n />\n <h2 [id]=\"titleId\">{{ title() }}</h2>\n </div>\n </div>\n\n <div slot=\"body\">\n <p class=\"lc-confirm__message\">{{ message() }}</p>\n\n <div class=\"lc-confirm__preview\">\n <ng-content select=\"[slot='preview']\" />\n </div>\n\n @if (requireText(); as req) {\n <div class=\"lc-confirm__require-text\">\n <!-- lc-input renders the prompt as an associated <label for> -->\n <lc-input\n [label]=\"req.prompt\"\n [placeholder]=\"req.expected\"\n (valueChange)=\"onInputChange($event)\"\n (enterPressed)=\"onConfirm()\"\n />\n </div>\n }\n </div>\n\n <div slot=\"footer\" class=\"lc-confirm__footer\">\n <lc-button\n variant=\"ghost\"\n [size]=\"buttonSize()\"\n (clicked)=\"onCancel()\"\n >\n {{ cancelLabel() }}\n </lc-button>\n <lc-button\n [variant]=\"confirmButtonVariant()\"\n [size]=\"buttonSize()\"\n [disabled]=\"!confirmAllowed()\"\n [loading]=\"confirmLoading()\"\n (clicked)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </lc-button>\n </div>\n</lc-modal>\n","import {\n Injectable,\n ApplicationRef,\n createComponent,\n EnvironmentInjector,\n inject,\n} from '@angular/core';\nimport {\n ConfirmDialogButtonSize,\n ConfirmDialogComponent,\n ConfirmDialogVariant,\n RequireTextConfig,\n} from './confirm-dialog.component';\nimport type { ModalPlacement } from '@lifecorner/angular-ui-kit/modal';\n\nexport interface ConfirmOptions {\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: ConfirmDialogVariant;\n icon?: string;\n requireText?: RequireTextConfig;\n /** Accessible name of the close button (3.3). */\n closeAriaLabel?: string;\n /** Size of both buttons (3.3): `lg` for a 44 px touch target. */\n buttonSize?: ConfirmDialogButtonSize;\n /** `center`, `auto` (a bottom sheet below 640 px) or `sheet` (3.3). */\n placement?: ModalPlacement;\n}\n\n/**\n * Imperative confirm dialog service.\n *\n * @example\n * ```ts\n * const ok = await this.confirmService.confirm({\n * title: 'Delete item?',\n * message: 'This cannot be undone.',\n * });\n * if (ok) { ... }\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ConfirmService {\n private readonly appRef = inject(ApplicationRef);\n private readonly injector = inject(EnvironmentInjector);\n\n /**\n * Show a confirmation dialog and return a promise that resolves\n * to true (confirmed) or false (cancelled).\n */\n confirm(opts: ConfirmOptions): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n // Create a host element\n const hostEl = document.createElement('div');\n document.body.appendChild(hostEl);\n\n const componentRef = createComponent(ConfirmDialogComponent, {\n hostElement: hostEl,\n environmentInjector: this.injector,\n });\n\n // Set inputs\n componentRef.setInput('open', true);\n componentRef.setInput('title', opts.title);\n componentRef.setInput('message', opts.message);\n if (opts.confirmLabel) componentRef.setInput('confirmLabel', opts.confirmLabel);\n if (opts.cancelLabel) componentRef.setInput('cancelLabel', opts.cancelLabel);\n if (opts.variant) componentRef.setInput('variant', opts.variant);\n if (opts.icon) componentRef.setInput('icon', opts.icon);\n if (opts.requireText) componentRef.setInput('requireText', opts.requireText);\n if (opts.closeAriaLabel) componentRef.setInput('closeAriaLabel', opts.closeAriaLabel);\n if (opts.buttonSize) componentRef.setInput('buttonSize', opts.buttonSize);\n if (opts.placement) componentRef.setInput('placement', opts.placement);\n\n const cleanup = () => {\n this.appRef.detachView(componentRef.hostView);\n componentRef.destroy();\n hostEl.remove();\n };\n\n componentRef.instance.confirmed.subscribe(() => {\n cleanup();\n resolve(true);\n });\n\n componentRef.instance.cancelled.subscribe(() => {\n cleanup();\n resolve(false);\n });\n\n this.appRef.attachView(componentRef.hostView);\n });\n }\n\n /**\n * Show a destructive confirmation dialog.\n * Shorthand for `confirm({ ...opts, variant: 'destructive' })`.\n */\n destructive(opts: Omit<ConfirmOptions, 'variant'>): Promise<boolean> {\n return this.confirm({ ...opts, variant: 'destructive' });\n }\n\n /**\n * Show a warning confirmation dialog.\n * Shorthand for `confirm({ ...opts, variant: 'warning' })`.\n */\n warning(opts: Omit<ConfirmOptions, 'variant'>): Promise<boolean> {\n return this.confirm({ ...opts, variant: 'warning' });\n }\n}\n"],"mappings":";;;;;;;;AAyDA,IAAa,yBAAb,MAAa,uBAAsB;CACzB,OAAO,SAAS;CAGf,OAAO,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,UAAU,MAA4B,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAGhD,QAAQ,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAGhC,UAAU,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAGlC,eAAe,MAAc,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGvC,cAAc,MAAc,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGrC,OAAO,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGtB,cAAc,MAA0B,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CAGxC,iBAAiB,MAAM,eAAc,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAOrC,iBAAiB,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAOtC,aAAa,MAA+B,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAOjD,YAAY,MAAsB,UAAS,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,UAAU,oBAAoB,EAAE,uBAAuB;CAGjE,YAAY,OAAM;CAGlB,YAAY,OAAM;CAGjB,aAAa,OAAO,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGvB,eAAe,eAAc;EACrC,MAAM,OAAO,KAAK,KAAI;EACtB,IAAI,MAAM,OAAO;EACjB,QAAQ,KAAK,QAAO,GAApB;GACE,KAAK,eACH,OAAO;GACT,KAAK,WACH,OAAO;GACT,SAEE,OAAO;EACX;CACF,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,YAAY,eAAc;EAClC,QAAQ,KAAK,QAAO,GAApB;GACE,KAAK,eACH,OAAO;GACT,KAAK,WACH,OAAO;GACT,SACE,OAAO;EACX;CACF,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,uBAAuB,eAAc;EAC7C,QAAQ,KAAK,QAAO,GAApB;GACE,KAAK,eACH,OAAO;GACT,KAAK,WACH,OAAO;GACT,SACE,OAAO;EACX;CACF,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,uBAAA,CAAA,+BAAA,CAAA,CAAA;CAGQ,iBAAiB,eAAc;EACvC,MAAM,MAAM,KAAK,YAAW;EAC5B,IAAI,CAAC,KAAK,OAAO;EACjB,OAAO,KAAK,WAAU,MAAO,IAAI;CACnC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EAEE,aAAY;GACV,IAAI,KAAK,KAAI,GACX,KAAK,WAAW,IAAI,EAAE;EAE1B,CAAC;CACH;CAQU,YAAS;EACjB,IAAI,CAAC,KAAK,eAAc,KAAM,KAAK,eAAc,GAAI;EACrD,KAAK,UAAU,KAAI;CACrB;CAEU,WAAQ;EAChB,KAAK,UAAU,KAAI;CACrB;CAEU,eAAY;EACpB,KAAK,UAAU,KAAI;CACrB;CAEU,cAAc,OAAsB;EAC5C,KAAK,WAAW,IAAI,OAAO,KAAK,CAAC;CACnC;CA3IW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAsB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAtB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAsB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,aAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,YAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,WAAA;GAAA,WAAA;EAAA;EAAA,UAAA;EAAA,UCzDnC;EA4DA,QAAA,CAAA,khBAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MDTY;GAAW;GAAA;IAAA,MAAA;IAAA,MAAE;IAAa,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAe,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAc,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAc,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;EAAA,eACtE,CAAC,iBAAiB,CAAC,2BAA2B,uBAAuB,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAK5E,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAsB,YAAA,CAAA;EATlC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAmB,YACjB;GAAI,SACP;IAAC;IAAa;IAAe;IAAiB;IAAgB;GAAc;GAAC,eACvE,CAAC,iBAAiB,CAAC,2BAA2B,uBAAuB,CAAC,CAAC;GAAC,iBAGtE,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,khBAAA;EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEXjD,IAAa,iBAAb,MAAa,eAAc;CACR,SAAS,OAAO,cAAc;CAC9B,WAAW,OAAO,mBAAmB;CAMtD,QAAQ,MAAoB;EAC1B,OAAO,IAAI,SAAkB,YAAW;GAEtC,MAAM,SAAS,SAAS,cAAc,KAAK;GAC3C,SAAS,KAAK,YAAY,MAAM;GAEhC,MAAM,eAAe,gBAAgB,wBAAwB;IAC3D,aAAa;IACb,qBAAqB,KAAK;GAC3B,CAAA;GAGD,aAAa,SAAS,QAAQ,IAAI;GAClC,aAAa,SAAS,SAAS,KAAK,KAAK;GACzC,aAAa,SAAS,WAAW,KAAK,OAAO;GAC7C,IAAI,KAAK,cAAc,aAAa,SAAS,gBAAgB,KAAK,YAAY;GAC9E,IAAI,KAAK,aAAa,aAAa,SAAS,eAAe,KAAK,WAAW;GAC3E,IAAI,KAAK,SAAS,aAAa,SAAS,WAAW,KAAK,OAAO;GAC/D,IAAI,KAAK,MAAM,aAAa,SAAS,QAAQ,KAAK,IAAI;GACtD,IAAI,KAAK,aAAa,aAAa,SAAS,eAAe,KAAK,WAAW;GAC3E,IAAI,KAAK,gBAAgB,aAAa,SAAS,kBAAkB,KAAK,cAAc;GACpF,IAAI,KAAK,YAAY,aAAa,SAAS,cAAc,KAAK,UAAU;GACxE,IAAI,KAAK,WAAW,aAAa,SAAS,aAAa,KAAK,SAAS;GAErE,MAAM,gBAAe;IACnB,KAAK,OAAO,WAAW,aAAa,QAAQ;IAC5C,aAAa,QAAO;IACpB,OAAO,OAAM;GACf;GAEA,aAAa,SAAS,UAAU,gBAAe;IAC7C,QAAO;IACP,QAAQ,IAAI;GACd,CAAC;GAED,aAAa,SAAS,UAAU,gBAAe;IAC7C,QAAO;IACP,QAAQ,KAAK;GACf,CAAC;GAED,KAAK,OAAO,WAAW,aAAa,QAAQ;EAC9C,CAAC;CACH;CAMA,YAAY,MAAqC;EAC/C,OAAO,KAAK,QAAQ;GAAE,GAAG;GAAM,SAAS;EAAa,CAAE;CACzD;CAMA,QAAQ,MAAqC;EAC3C,OAAO,KAAK,QAAQ;GAAE,GAAG;GAAM,SAAS;EAAS,CAAE;CACrD;CAlEW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAd,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,YADD;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAc,YAAA,CAAA;EAD1B,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE"}
@@ -155,6 +155,8 @@ var CopyFieldComponent = class CopyFieldComponent {
155
155
  "spellcheck",
156
156
  "enterkeyhint",
157
157
  "requiredAriaLabel",
158
+ "controlId",
159
+ "announceError",
158
160
  "value"
159
161
  ],
160
162
  outputs: [
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-copy-field.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/copy-field/copy-field.component.ts","../../../../libs/angular-ui-kit/src/lib/copy-field/copy-field.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK } from '@lifecorner/angular-ui-kit/icons';\nimport { InputComponent } from '@lifecorner/angular-ui-kit/input';\n\n/** `stacked`: the button below the value at full width (phones); `inline`: beside it. */\nexport type CopyFieldLayout = 'stacked' | 'inline';\n\n/** How long the button says \"Copied\" (ms). */\nconst CONFIRMATION_MS = 2000;\n\n/**\n * A value to copy — an invitation link, a token, a command: the value\n * read-only and wrapping (never clipped), a 48.5 px copy button, and one\n * polite announcement after copying. If the clipboard refuses, the value is\n * selected so the person can copy it by hand.\n *\n * @example\n * ```html\n * <lc-copy-field label=\"Einladungslink\" [value]=\"link\" copyLabel=\"Kopieren\"\n * copiedLabel=\"Kopiert\" copiedAnnouncement=\"Link kopiert\" />\n * ```\n */\n@Component({\n selector: 'lc-copy-field',\n imports: [InputComponent, ButtonComponent, IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK])],\n templateUrl: './copy-field.component.html',\n styleUrl: './copy-field.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CopyFieldComponent {\n /** Label of the value (its accessible name). */\n readonly label = input<string>('');\n\n /** The value to copy. */\n readonly value = input<string>('');\n\n /** Help below the value. */\n readonly helperText = input<string>();\n\n /** `stacked` (default): button below at full width; `inline`: button beside the value. */\n readonly layout = input<CopyFieldLayout>('stacked');\n\n /** Show the clipboard (and, after copying, check) icon in the button. */\n readonly showIcon = input<boolean>(true);\n\n /** Button label. */\n readonly copyLabel = input('Copy');\n\n /** Button label for two seconds after copying. */\n readonly copiedLabel = input('Copied');\n\n /** Spoken once after copying (polite live region). */\n readonly copiedAnnouncement = input('Copied to the clipboard');\n\n /** Emitted with the value after it was copied. */\n readonly copied = output<string>();\n\n /** Emitted when the clipboard refused; the value is then selected for copying by hand. */\n readonly copyFailed = output<void>();\n\n protected readonly isCopied = signal(false);\n\n private readonly field = viewChild(InputComponent);\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));\n }\n\n /** Copies the value; on refusal selects it instead. */\n async copy(): Promise<void> {\n const value = this.value();\n try {\n const clipboard = typeof navigator === 'undefined' ? undefined : navigator.clipboard;\n if (!clipboard) throw new Error('Clipboard API unavailable');\n await clipboard.writeText(value);\n } catch {\n this.copyFailed.emit();\n this.field()?.focus();\n this.field()?.select();\n return;\n }\n this.isCopied.set(true);\n this.copied.emit(value);\n clearTimeout(this.timer);\n this.timer = setTimeout(() => this.isCopied.set(false), CONFIRMATION_MS);\n }\n}\n","<div\n class=\"lc-copy-field\"\n [class.lc-copy-field--stacked]=\"layout() === 'stacked'\"\n [class.lc-copy-field--inline]=\"layout() === 'inline'\"\n>\n <div class=\"lc-copy-field__value\">\n <lc-input\n [label]=\"label()\"\n [value]=\"value()\"\n [helperText]=\"helperText()\"\n size=\"lg\"\n [readonly]=\"true\"\n [readonlyWrap]=\"true\"\n />\n </div>\n\n <div class=\"lc-copy-field__action\">\n <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"layout() === 'stacked'\" (clicked)=\"copy()\">\n @if (showIcon()) {\n <lc-icon slot=\"icon-before\" [name]=\"isCopied() ? 'check' : 'clipboard-document'\" size=\"sm\" [decorative]=\"true\" />\n }\n {{ isCopied() ? copiedLabel() : copyLabel() }}\n </lc-button>\n </div>\n\n <span class=\"lc-copy-field__live\" role=\"status\" aria-live=\"polite\">{{ isCopied() ? copiedAnnouncement() : '' }}</span>\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-copy-field.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/copy-field/copy-field.component.ts","../../../../libs/angular-ui-kit/src/lib/copy-field/copy-field.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ButtonComponent } from '@lifecorner/angular-ui-kit/button';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK } from '@lifecorner/angular-ui-kit/icons';\nimport { InputComponent } from '@lifecorner/angular-ui-kit/input';\n\n/** `stacked`: the button below the value at full width (phones); `inline`: beside it. */\nexport type CopyFieldLayout = 'stacked' | 'inline';\n\n/** How long the button says \"Copied\" (ms). */\nconst CONFIRMATION_MS = 2000;\n\n/**\n * A value to copy — an invitation link, a token, a command: the value\n * read-only and wrapping (never clipped), a 48.5 px copy button, and one\n * polite announcement after copying. If the clipboard refuses, the value is\n * selected so the person can copy it by hand.\n *\n * @example\n * ```html\n * <lc-copy-field label=\"Einladungslink\" [value]=\"link\" copyLabel=\"Kopieren\"\n * copiedLabel=\"Kopiert\" copiedAnnouncement=\"Link kopiert\" />\n * ```\n */\n@Component({\n selector: 'lc-copy-field',\n imports: [InputComponent, ButtonComponent, IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK])],\n templateUrl: './copy-field.component.html',\n styleUrl: './copy-field.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CopyFieldComponent {\n /** Label of the value (its accessible name). */\n readonly label = input<string>('');\n\n /** The value to copy. */\n readonly value = input<string>('');\n\n /** Help below the value. */\n readonly helperText = input<string>();\n\n /** `stacked` (default): button below at full width; `inline`: button beside the value. */\n readonly layout = input<CopyFieldLayout>('stacked');\n\n /** Show the clipboard (and, after copying, check) icon in the button. */\n readonly showIcon = input<boolean>(true);\n\n /** Button label. */\n readonly copyLabel = input('Copy');\n\n /** Button label for two seconds after copying. */\n readonly copiedLabel = input('Copied');\n\n /** Spoken once after copying (polite live region). */\n readonly copiedAnnouncement = input('Copied to the clipboard');\n\n /** Emitted with the value after it was copied. */\n readonly copied = output<string>();\n\n /** Emitted when the clipboard refused; the value is then selected for copying by hand. */\n readonly copyFailed = output<void>();\n\n protected readonly isCopied = signal(false);\n\n private readonly field = viewChild(InputComponent);\n private timer: ReturnType<typeof setTimeout> | undefined;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.timer));\n }\n\n /** Copies the value; on refusal selects it instead. */\n async copy(): Promise<void> {\n const value = this.value();\n try {\n const clipboard = typeof navigator === 'undefined' ? undefined : navigator.clipboard;\n if (!clipboard) throw new Error('Clipboard API unavailable');\n await clipboard.writeText(value);\n } catch {\n this.copyFailed.emit();\n this.field()?.focus();\n this.field()?.select();\n return;\n }\n this.isCopied.set(true);\n this.copied.emit(value);\n clearTimeout(this.timer);\n this.timer = setTimeout(() => this.isCopied.set(false), CONFIRMATION_MS);\n }\n}\n","<div\n class=\"lc-copy-field\"\n [class.lc-copy-field--stacked]=\"layout() === 'stacked'\"\n [class.lc-copy-field--inline]=\"layout() === 'inline'\"\n>\n <div class=\"lc-copy-field__value\">\n <lc-input\n [label]=\"label()\"\n [value]=\"value()\"\n [helperText]=\"helperText()\"\n size=\"lg\"\n [readonly]=\"true\"\n [readonlyWrap]=\"true\"\n />\n </div>\n\n <div class=\"lc-copy-field__action\">\n <lc-button variant=\"outline\" size=\"lg\" [fullWidth]=\"layout() === 'stacked'\" (clicked)=\"copy()\">\n @if (showIcon()) {\n <lc-icon slot=\"icon-before\" [name]=\"isCopied() ? 'check' : 'clipboard-document'\" size=\"sm\" [decorative]=\"true\" />\n }\n {{ isCopied() ? copiedLabel() : copyLabel() }}\n </lc-button>\n </div>\n\n <span class=\"lc-copy-field__live\" role=\"status\" aria-live=\"polite\">{{ isCopied() ? copiedAnnouncement() : '' }}</span>\n</div>\n"],"mappings":"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"}
@@ -300,6 +300,8 @@ var EmailInputComponent = class EmailInputComponent {
300
300
  "spellcheck",
301
301
  "enterkeyhint",
302
302
  "requiredAriaLabel",
303
+ "controlId",
304
+ "announceError",
303
305
  "value"
304
306
  ],
305
307
  outputs: [
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-email-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/email-input/email-input.component.ts","../../../../libs/angular-ui-kit/src/lib/email-input/email-input.component.html"],"sourcesContent":["/* eslint-disable @typescript-eslint/member-ordering */\n/* ControlValueAccessor implementation requires specific method placement */\n\nimport {\n Component,\n input,\n output,\n signal,\n computed,\n ChangeDetectionStrategy,\n forwardRef,\n effect,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { InputAutocapitalize, InputComponent, InputEnterKeyHint } from '@lifecorner/angular-ui-kit/input';\nimport { ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_MAIL } from '@lifecorner/angular-ui-kit/icons';\n\n/**\n * Email input component with built-in RFC 5322 validation.\n *\n * Features:\n * - RFC 5322 regex email format validation\n * - Real-time validation feedback on input\n * - Valid/invalid visual indicators\n * - Size presets (xs, sm, md, lg)\n * - Helper text and error message display\n * - Disabled and readonly states (read-only addresses can wrap: `readonlyWrap`)\n * - Optional leading mail icon (`showIcon`)\n * - Autofill and keyboard hints: `autocomplete`, `name`, `autocapitalize`, `spellcheck`, `enterkeyhint`\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-email-input label=\"Email\" placeholder=\"you@example.com\" [(ngModel)]=\"email\" />\n * ```\n */\n@Component({\n selector: 'lc-email-input',\n imports: [CommonModule, InputComponent],\n // The `envelope` icon it hands to lc-input (an alias of the Tabler `mail`).\n viewProviders: [ɵprovideKitIcons([ICON_MAIL])],\n templateUrl: './email-input.component.html',\n styleUrl: './email-input.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => EmailInputComponent),\n multi: true,\n },\n ],\n})\nexport class EmailInputComponent implements ControlValueAccessor {\n // Input Properties\n /**\n * Input label (displayed above input)\n * @default 'Email'\n */\n readonly label = input<string>('Email');\n\n /**\n * Placeholder text\n * @default 'you@example.com'\n */\n readonly placeholder = input<string>('you@example.com');\n\n /**\n * Size of the input\n * @default 'md'\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the input is disabled\n * @default false\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the input is read-only\n * @default false\n */\n readonly readonly = input<boolean>(false);\n\n /**\n * Whether the input is required\n * @default false\n */\n readonly required = input<boolean>(false);\n\n /**\n * Custom error message (overrides validation error)\n */\n readonly error = input<string>();\n\n /**\n * Helper text to display below input\n */\n readonly helperText = input<string>();\n\n /**\n * Whether to show validation feedback immediately\n * @default false (validates on blur)\n */\n readonly validateOnInput = input<boolean>(false);\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string>();\n\n /**\n * Native `autocomplete` (input purpose, WCAG 1.3.5): `email` (registration),\n * `username` (the address as the sign-in name), `username webauthn` (sign-in:\n * the browser offers passkeys in this field). Unset = no attribute.\n */\n readonly autocomplete = input<string>();\n\n /** Native `name` of the inner input. Unset = no attribute. */\n readonly name = input<string>();\n\n /** Native `autocapitalize` (`off` keeps keyboards from capitalising the address). Unset = no attribute. */\n readonly autocapitalize = input<InputAutocapitalize>();\n\n /** Native `spellcheck`. Unset = no attribute. */\n readonly spellcheck = input<boolean>();\n\n /** Native `enterkeyhint`: the label of the on-screen keyboard's Enter key. Unset = no attribute. */\n readonly enterkeyhint = input<InputEnterKeyHint>();\n\n /**\n * Show the leading mail icon. `false` gives the address the icon's width\n * (about 36 px) — long addresses fit on a phone.\n * @default true\n */\n readonly showIcon = input<boolean>(true);\n\n /**\n * While `readonly`: the address wraps onto more lines instead of being\n * clipped. A hidden native input keeps value, `name` and `autocomplete`\n * (e.g. `username` for password managers on a password page).\n * @default false\n */\n readonly readonlyWrap = input<boolean>(false);\n\n /**\n * Show the `*` of a `required` field; `false` keeps the required semantics without it.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /** Error shown for an empty `required` field. */\n readonly requiredMessage = input('Email address is required');\n\n /** Error shown for a malformed address. */\n readonly invalidMessage = input('Please enter a valid email address');\n\n // Output Events\n /**\n * Emitted when email value changes\n */\n readonly valueChange = output<string>();\n\n /**\n * Emitted when validation state changes\n */\n readonly validationChange = output<{ valid: boolean; error?: string }>();\n\n /**\n * Emitted when input receives focus\n */\n readonly focused = output<void>();\n\n /**\n * Emitted when input loses focus\n */\n readonly blurred = output<void>();\n\n /**\n * Emitted when Enter key is pressed\n */\n readonly enterPressed = output<void>();\n\n // Internal state\n readonly value = signal<string>('');\n private readonly touched = signal<boolean>(false);\n private readonly _formDisabled = signal<boolean>(false);\n\n /**\n * RFC 5322 compliant email regex (simplified but robust)\n * Validates: local-part@domain\n * - Local part: alphanumeric, dots, hyphens, underscores, plus signs\n * - Domain: alphanumeric, dots, hyphens with valid TLD\n */\n private readonly EMAIL_REGEX =\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n\n // ControlValueAccessor callbacks\n private onChange: (value: string) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n /**\n * Computed validation state\n */\n protected readonly isValid = computed(() => {\n const val = this.value();\n if (!val) return this.required() ? false : true;\n return this.EMAIL_REGEX.test(val);\n });\n\n /**\n * Computed error message\n */\n protected readonly validationError = computed(() => {\n // Custom error takes precedence\n if (this.error()) return this.error();\n\n // Show validation errors only after touched or validateOnInput\n if (!this.touched() && !this.validateOnInput()) return undefined;\n\n const val = this.value();\n if (this.required() && !val) {\n return this.requiredMessage();\n }\n if (val && !this.isValid()) {\n return this.invalidMessage();\n }\n return undefined;\n });\n\n /**\n * Computed disabled state\n */\n protected get isDisabled(): boolean {\n return this.disabled() || this._formDisabled();\n }\n\n constructor() {\n // Emit validation state changes\n effect(() => {\n const valid = this.isValid();\n const error = this.validationError();\n this.validationChange.emit({ valid, error });\n });\n }\n\n // ===== ControlValueAccessor implementation =====\n\n writeValue(value: string): void {\n this.value.set(value || '');\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._formDisabled.set(isDisabled);\n }\n\n // ===== Public API methods =====\n\n /**\n * Public method to trigger validation\n */\n public validate(): boolean {\n this.touched.set(true);\n return this.isValid();\n }\n\n /**\n * Public method to reset validation state\n */\n public resetValidation(): void {\n this.touched.set(false);\n }\n\n // ===== Event handlers (protected) =====\n\n /**\n * Handles value changes from base input\n */\n protected onValueChange(newValue: string): void {\n this.value.set(newValue);\n this.onChange(newValue);\n this.valueChange.emit(newValue);\n }\n\n /**\n * Handles focus events\n */\n protected onFocus(): void {\n this.focused.emit();\n }\n\n /**\n * Handles blur events\n */\n protected onBlur(): void {\n this.touched.set(true);\n this.onTouched();\n this.blurred.emit();\n }\n\n /**\n * Handles Enter key press\n */\n protected onEnterPressed(): void {\n this.enterPressed.emit();\n }\n}\n","<lc-input\n [label]=\"label()\"\n [placeholder]=\"placeholder()\"\n type=\"email\"\n [size]=\"size()\"\n [value]=\"value()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [readonlyWrap]=\"readonlyWrap()\"\n [required]=\"required()\"\n [requiredIndicator]=\"requiredIndicator()\"\n [error]=\"validationError()\"\n [helperText]=\"helperText()\"\n [iconBefore]=\"showIcon() ? 'envelope' : undefined\"\n [ariaLabel]=\"ariaLabel() || label()\"\n [autocomplete]=\"autocomplete()\"\n [name]=\"name()\"\n [autocapitalize]=\"autocapitalize()\"\n [spellcheck]=\"spellcheck()\"\n [enterkeyhint]=\"enterkeyhint()\"\n (valueChange)=\"onValueChange($event)\"\n (focused)=\"onFocus()\"\n (blurred)=\"onBlur()\"\n (enterPressed)=\"onEnterPressed()\"\n/>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-email-input.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/email-input/email-input.component.ts","../../../../libs/angular-ui-kit/src/lib/email-input/email-input.component.html"],"sourcesContent":["/* eslint-disable @typescript-eslint/member-ordering */\n/* ControlValueAccessor implementation requires specific method placement */\n\nimport {\n Component,\n input,\n output,\n signal,\n computed,\n ChangeDetectionStrategy,\n forwardRef,\n effect,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { InputAutocapitalize, InputComponent, InputEnterKeyHint } from '@lifecorner/angular-ui-kit/input';\nimport { ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_MAIL } from '@lifecorner/angular-ui-kit/icons';\n\n/**\n * Email input component with built-in RFC 5322 validation.\n *\n * Features:\n * - RFC 5322 regex email format validation\n * - Real-time validation feedback on input\n * - Valid/invalid visual indicators\n * - Size presets (xs, sm, md, lg)\n * - Helper text and error message display\n * - Disabled and readonly states (read-only addresses can wrap: `readonlyWrap`)\n * - Optional leading mail icon (`showIcon`)\n * - Autofill and keyboard hints: `autocomplete`, `name`, `autocapitalize`, `spellcheck`, `enterkeyhint`\n * - ControlValueAccessor integration for reactive forms\n *\n * @example\n * ```html\n * <lc-email-input label=\"Email\" placeholder=\"you@example.com\" [(ngModel)]=\"email\" />\n * ```\n */\n@Component({\n selector: 'lc-email-input',\n imports: [CommonModule, InputComponent],\n // The `envelope` icon it hands to lc-input (an alias of the Tabler `mail`).\n viewProviders: [ɵprovideKitIcons([ICON_MAIL])],\n templateUrl: './email-input.component.html',\n styleUrl: './email-input.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => EmailInputComponent),\n multi: true,\n },\n ],\n})\nexport class EmailInputComponent implements ControlValueAccessor {\n // Input Properties\n /**\n * Input label (displayed above input)\n * @default 'Email'\n */\n readonly label = input<string>('Email');\n\n /**\n * Placeholder text\n * @default 'you@example.com'\n */\n readonly placeholder = input<string>('you@example.com');\n\n /**\n * Size of the input\n * @default 'md'\n */\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the input is disabled\n * @default false\n */\n readonly disabled = input<boolean>(false);\n\n /**\n * Whether the input is read-only\n * @default false\n */\n readonly readonly = input<boolean>(false);\n\n /**\n * Whether the input is required\n * @default false\n */\n readonly required = input<boolean>(false);\n\n /**\n * Custom error message (overrides validation error)\n */\n readonly error = input<string>();\n\n /**\n * Helper text to display below input\n */\n readonly helperText = input<string>();\n\n /**\n * Whether to show validation feedback immediately\n * @default false (validates on blur)\n */\n readonly validateOnInput = input<boolean>(false);\n\n /**\n * ARIA label for accessibility\n */\n readonly ariaLabel = input<string>();\n\n /**\n * Native `autocomplete` (input purpose, WCAG 1.3.5): `email` (registration),\n * `username` (the address as the sign-in name), `username webauthn` (sign-in:\n * the browser offers passkeys in this field). Unset = no attribute.\n */\n readonly autocomplete = input<string>();\n\n /** Native `name` of the inner input. Unset = no attribute. */\n readonly name = input<string>();\n\n /** Native `autocapitalize` (`off` keeps keyboards from capitalising the address). Unset = no attribute. */\n readonly autocapitalize = input<InputAutocapitalize>();\n\n /** Native `spellcheck`. Unset = no attribute. */\n readonly spellcheck = input<boolean>();\n\n /** Native `enterkeyhint`: the label of the on-screen keyboard's Enter key. Unset = no attribute. */\n readonly enterkeyhint = input<InputEnterKeyHint>();\n\n /**\n * Show the leading mail icon. `false` gives the address the icon's width\n * (about 36 px) — long addresses fit on a phone.\n * @default true\n */\n readonly showIcon = input<boolean>(true);\n\n /**\n * While `readonly`: the address wraps onto more lines instead of being\n * clipped. A hidden native input keeps value, `name` and `autocomplete`\n * (e.g. `username` for password managers on a password page).\n * @default false\n */\n readonly readonlyWrap = input<boolean>(false);\n\n /**\n * Show the `*` of a `required` field; `false` keeps the required semantics without it.\n * @default true\n */\n readonly requiredIndicator = input<boolean>(true);\n\n /** Error shown for an empty `required` field. */\n readonly requiredMessage = input('Email address is required');\n\n /** Error shown for a malformed address. */\n readonly invalidMessage = input('Please enter a valid email address');\n\n // Output Events\n /**\n * Emitted when email value changes\n */\n readonly valueChange = output<string>();\n\n /**\n * Emitted when validation state changes\n */\n readonly validationChange = output<{ valid: boolean; error?: string }>();\n\n /**\n * Emitted when input receives focus\n */\n readonly focused = output<void>();\n\n /**\n * Emitted when input loses focus\n */\n readonly blurred = output<void>();\n\n /**\n * Emitted when Enter key is pressed\n */\n readonly enterPressed = output<void>();\n\n // Internal state\n readonly value = signal<string>('');\n private readonly touched = signal<boolean>(false);\n private readonly _formDisabled = signal<boolean>(false);\n\n /**\n * RFC 5322 compliant email regex (simplified but robust)\n * Validates: local-part@domain\n * - Local part: alphanumeric, dots, hyphens, underscores, plus signs\n * - Domain: alphanumeric, dots, hyphens with valid TLD\n */\n private readonly EMAIL_REGEX =\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;\n\n // ControlValueAccessor callbacks\n private onChange: (value: string) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n /**\n * Computed validation state\n */\n protected readonly isValid = computed(() => {\n const val = this.value();\n if (!val) return this.required() ? false : true;\n return this.EMAIL_REGEX.test(val);\n });\n\n /**\n * Computed error message\n */\n protected readonly validationError = computed(() => {\n // Custom error takes precedence\n if (this.error()) return this.error();\n\n // Show validation errors only after touched or validateOnInput\n if (!this.touched() && !this.validateOnInput()) return undefined;\n\n const val = this.value();\n if (this.required() && !val) {\n return this.requiredMessage();\n }\n if (val && !this.isValid()) {\n return this.invalidMessage();\n }\n return undefined;\n });\n\n /**\n * Computed disabled state\n */\n protected get isDisabled(): boolean {\n return this.disabled() || this._formDisabled();\n }\n\n constructor() {\n // Emit validation state changes\n effect(() => {\n const valid = this.isValid();\n const error = this.validationError();\n this.validationChange.emit({ valid, error });\n });\n }\n\n // ===== ControlValueAccessor implementation =====\n\n writeValue(value: string): void {\n this.value.set(value || '');\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._formDisabled.set(isDisabled);\n }\n\n // ===== Public API methods =====\n\n /**\n * Public method to trigger validation\n */\n public validate(): boolean {\n this.touched.set(true);\n return this.isValid();\n }\n\n /**\n * Public method to reset validation state\n */\n public resetValidation(): void {\n this.touched.set(false);\n }\n\n // ===== Event handlers (protected) =====\n\n /**\n * Handles value changes from base input\n */\n protected onValueChange(newValue: string): void {\n this.value.set(newValue);\n this.onChange(newValue);\n this.valueChange.emit(newValue);\n }\n\n /**\n * Handles focus events\n */\n protected onFocus(): void {\n this.focused.emit();\n }\n\n /**\n * Handles blur events\n */\n protected onBlur(): void {\n this.touched.set(true);\n this.onTouched();\n this.blurred.emit();\n }\n\n /**\n * Handles Enter key press\n */\n protected onEnterPressed(): void {\n this.enterPressed.emit();\n }\n}\n","<lc-input\n [label]=\"label()\"\n [placeholder]=\"placeholder()\"\n type=\"email\"\n [size]=\"size()\"\n [value]=\"value()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [readonlyWrap]=\"readonlyWrap()\"\n [required]=\"required()\"\n [requiredIndicator]=\"requiredIndicator()\"\n [error]=\"validationError()\"\n [helperText]=\"helperText()\"\n [iconBefore]=\"showIcon() ? 'envelope' : undefined\"\n [ariaLabel]=\"ariaLabel() || label()\"\n [autocomplete]=\"autocomplete()\"\n [name]=\"name()\"\n [autocapitalize]=\"autocapitalize()\"\n [spellcheck]=\"spellcheck()\"\n [enterkeyhint]=\"enterkeyhint()\"\n (valueChange)=\"onValueChange($event)\"\n (focused)=\"onFocus()\"\n (blurred)=\"onBlur()\"\n (enterPressed)=\"onEnterPressed()\"\n/>\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import * as i0 from "@angular/core";
2
2
  import { ChangeDetectionStrategy, Component, computed, forwardRef, input, model, output, signal, viewChild } from "@angular/core";
3
3
  import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
4
  import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
5
+ let nextUniqueId = 0;
5
6
  var InputComponent = class InputComponent {
6
7
  label = input(...ngDevMode ? [void 0, { debugName: "label" }] : /* istanbul ignore next */ []);
7
8
  placeholder = input("", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
@@ -27,11 +28,17 @@ var InputComponent = class InputComponent {
27
28
  spellcheck = input(...ngDevMode ? [void 0, { debugName: "spellcheck" }] : /* istanbul ignore next */ []);
28
29
  enterkeyhint = input(...ngDevMode ? [void 0, { debugName: "enterkeyhint" }] : /* istanbul ignore next */ []);
29
30
  requiredAriaLabel = input("required", ...ngDevMode ? [{ debugName: "requiredAriaLabel" }] : /* istanbul ignore next */ []);
31
+ controlId = input(void 0, ...ngDevMode ? [{ debugName: "controlId" }] : /* istanbul ignore next */ []);
32
+ announceError = input(true, ...ngDevMode ? [{ debugName: "announceError" }] : /* istanbul ignore next */ []);
30
33
  value = model("", ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
31
34
  focused = output();
32
35
  blurred = output();
33
36
  enterPressed = output();
34
- inputId = `lc-input-${Math.random().toString(36).substr(2, 9)}`;
37
+ generatedId = `lc-input-${nextUniqueId++}`;
38
+ resolvedId = computed(() => this.controlId() || this.generatedId, ...ngDevMode ? [{ debugName: "resolvedId" }] : /* istanbul ignore next */ []);
39
+ get inputId() {
40
+ return this.resolvedId();
41
+ }
35
42
  inputElement = viewChild("inputRef", ...ngDevMode ? [{ debugName: "inputElement" }] : /* istanbul ignore next */ []);
36
43
  readonlyValueElement = viewChild("readonlyValueRef", ...ngDevMode ? [{ debugName: "readonlyValueElement" }] : /* istanbul ignore next */ []);
37
44
  _formDisabled = signal(false, ...ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []);
@@ -292,6 +299,20 @@ var InputComponent = class InputComponent {
292
299
  isRequired: false,
293
300
  transformFunction: null
294
301
  },
302
+ controlId: {
303
+ classPropertyName: "controlId",
304
+ publicName: "controlId",
305
+ isSignal: true,
306
+ isRequired: false,
307
+ transformFunction: null
308
+ },
309
+ announceError: {
310
+ classPropertyName: "announceError",
311
+ publicName: "announceError",
312
+ isSignal: true,
313
+ isRequired: false,
314
+ transformFunction: null
315
+ },
295
316
  value: {
296
317
  classPropertyName: "value",
297
318
  publicName: "value",
@@ -325,8 +346,8 @@ var InputComponent = class InputComponent {
325
346
  isSignal: true
326
347
  }],
327
348
  ngImport: i0,
328
- template: "<div class=\"input-wrapper\">\n <!-- Label -->\n @if (label()) {\n <label [id]=\"inputId + '-label'\" [attr.for]=\"wrapsReadonlyValue() ? null : inputId\" class=\"input-label\">\n {{ label() }}\n @if (required() && requiredIndicator()) {\n <span class=\"required-indicator\" [attr.aria-label]=\"requiredAriaLabel()\">*</span>\n }\n </label>\n }\n\n <!-- Input Container -->\n <div\n class=\"input-container\"\n [class.input-xs]=\"size() === 'xs'\"\n [class.input-sm]=\"size() === 'sm'\"\n [class.input-md]=\"size() === 'md'\"\n [class.input-lg]=\"size() === 'lg'\"\n [class.input-error]=\"hasError\"\n [class.input-disabled]=\"isDisabled\"\n [class.input-readonly]=\"readonly()\"\n [class.input-readonly-wrap]=\"wrapsReadonlyValue()\"\n >\n <!-- Icon Before -->\n @if (iconBefore()) {\n <span class=\"input-icon input-icon-before\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconBefore()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n\n <!-- Read-only value that wraps (SI-10): a read-only textbox named by the label -->\n @if (wrapsReadonlyValue()) {\n <div\n #readonlyValueRef\n class=\"input-field input-readonly-value\"\n role=\"textbox\"\n aria-readonly=\"true\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"label() ? inputId + '-label' : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >{{ value() }}</div>\n }\n\n <!-- Input Element (hidden while the read-only value wraps; it keeps value, name and autocomplete) -->\n <input\n #inputRef\n [id]=\"inputId\"\n [type]=\"type()\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [hidden]=\"wrapsReadonlyValue()\"\n [attr.maxlength]=\"maxLength() || null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize() ?? null\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-required]=\"required() || undefined\"\n [attr.aria-invalid]=\"hasError || undefined\"\n [attr.aria-describedby]=\"describedBy()\"\n class=\"input-field\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n <!-- Icon After -->\n @if (iconAfter()) {\n <span class=\"input-icon input-icon-after\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconAfter()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n </div>\n\n <!-- Helper Text / Error Message / Character Count -->\n @if (error() || helperText() || (showCharCount() && maxLength())) {\n <div class=\"input-footer\">\n <!-- Error Message -->\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"input-error\" role=\"alert\">\n {{ error() }}\n </p>\n }\n\n <!-- Helper Text -->\n @if (helperText() && !error()) {\n <p [id]=\"inputId + '-helper'\" class=\"input-helper\">\n {{ helperText() }}\n </p>\n }\n\n <!-- Character Count: always visible; spoken only near the limit (live region below) -->\n @if (showCharCount() && maxLength()) {\n <p class=\"char-count\">\n {{ charCount }} / {{ maxLength() }}\n </p>\n }\n </div>\n }\n\n @if (showCharCount() && maxLength()) {\n <span class=\"input-char-count-live\" aria-live=\"polite\">{{ charCountAnnouncement() }}</span>\n }\n</div>\n",
329
- styles: [".input-wrapper{position:relative;display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%}.input-label{display:block;font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);margin-bottom:var(--spacing-1)}.input-label .required-indicator{color:var(--color-error-600);margin-left:var(--spacing-0-5)}.input-container{position:relative;display:flex;align-items:center;width:100%;background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease)}.input-container:hover:not(.input-disabled):not(.input-readonly){border-color:var(--color-text-secondary)}.input-container:focus-within{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.input-container.input-error{border-color:var(--color-error-600)}.input-container.input-error:focus-within{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.input-container.input-disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.input-container.input-readonly{background-color:var(--color-surface-muted)}.input-container.input-readonly-wrap{height:auto;min-height:var(--lc-input-height);align-items:stretch}.input-readonly-value{display:flex;align-items:center;min-width:0;white-space:normal;overflow-wrap:anywhere;-webkit-user-select:text;user-select:text}.input-char-count-live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.input-field{flex:1;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background:transparent;border:none;outline:none}.input-field::placeholder{color:var(--color-text-secondary)}.input-field:disabled{cursor:not-allowed}.input-field:read-only{cursor:default}.input-field[type=number]::-webkit-inner-spin-button,.input-field[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.input-field[type=number]{-moz-appearance:textfield}.input-icon{display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary);pointer-events:none}.input-icon .icon{width:1.25rem;height:1.25rem}.input-icon.input-icon-before{margin-left:var(--spacing-3);margin-right:var(--spacing-2)}.input-icon.input-icon-after{margin-left:var(--spacing-2);margin-right:var(--spacing-3)}.input-xs{--lc-input-height: var(--size-interactive-xs-height);height:var(--lc-input-height)}.input-xs .input-field{padding:var(--size-interactive-xs-padding);font-size:var(--size-interactive-xs-font-size)}.input-xs .icon{width:1rem;height:1rem}.input-sm{--lc-input-height: var(--size-interactive-sm-height);height:var(--lc-input-height)}.input-sm .input-field{padding:var(--size-interactive-sm-padding);font-size:var(--size-interactive-sm-font-size)}.input-md{--lc-input-height: var(--size-interactive-md-height);height:var(--lc-input-height)}.input-md .input-field{padding:var(--size-interactive-md-padding);font-size:var(--size-interactive-md-font-size)}.input-lg{--lc-input-height: var(--size-interactive-lg-height);height:var(--lc-input-height)}.input-lg .input-field{padding:var(--size-interactive-lg-padding);font-size:var(--size-interactive-lg-font-size)}.input-lg .icon{width:1.5rem;height:1.5rem}.input-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2));min-height:1.25rem}.input-error{flex:1;font-size:var(--font-size-xs);color:var(--color-error-600);margin:0}.input-helper{flex:1;font-size:var(--font-size-xs);color:var(--color-text-secondary);margin:0}.char-count{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;margin:0}\n"],
349
+ template: "<div class=\"input-wrapper\">\n <!-- Label -->\n @if (label()) {\n <!-- No whitespace before the *: the gap is its margin, and it never wraps onto a line alone -->\n <label [id]=\"inputId + '-label'\" [attr.for]=\"wrapsReadonlyValue() ? null : inputId\" class=\"input-label\">\n {{ label() }}@if (required() && requiredIndicator()) {<span class=\"required-indicator\" aria-hidden=\"true\">*</span>}\n </label>\n }\n\n <!-- Input Container -->\n <div\n class=\"input-container\"\n [class.input-xs]=\"size() === 'xs'\"\n [class.input-sm]=\"size() === 'sm'\"\n [class.input-md]=\"size() === 'md'\"\n [class.input-lg]=\"size() === 'lg'\"\n [class.input-error]=\"hasError\"\n [class.input-disabled]=\"isDisabled\"\n [class.input-readonly]=\"readonly()\"\n [class.input-readonly-wrap]=\"wrapsReadonlyValue()\"\n >\n <!-- Icon Before -->\n @if (iconBefore()) {\n <span class=\"input-icon input-icon-before\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconBefore()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n\n <!-- Read-only value that wraps (SI-10): a read-only textbox named by the label -->\n @if (wrapsReadonlyValue()) {\n <div\n #readonlyValueRef\n class=\"input-field input-readonly-value\"\n role=\"textbox\"\n aria-readonly=\"true\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"label() ? inputId + '-label' : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >{{ value() }}</div>\n }\n\n <!-- Input Element (hidden while the read-only value wraps; it keeps value, name and autocomplete) -->\n <input\n #inputRef\n [id]=\"inputId\"\n [type]=\"type()\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [hidden]=\"wrapsReadonlyValue()\"\n [attr.maxlength]=\"maxLength() || null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize() ?? null\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-required]=\"required() || undefined\"\n [attr.aria-invalid]=\"hasError || undefined\"\n [attr.aria-describedby]=\"describedBy()\"\n class=\"input-field\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n <!-- Icon After -->\n @if (iconAfter()) {\n <span class=\"input-icon input-icon-after\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconAfter()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n </div>\n\n <!-- Helper Text / Error Message / Character Count -->\n @if (error() || helperText() || (showCharCount() && maxLength())) {\n <div class=\"input-footer\">\n <!-- Error Message -->\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"input-error\" [attr.role]=\"announceError() ? 'alert' : null\">\n {{ error() }}\n </p>\n }\n\n <!-- Helper Text -->\n @if (helperText() && !error()) {\n <p [id]=\"inputId + '-helper'\" class=\"input-helper\">\n {{ helperText() }}\n </p>\n }\n\n <!-- Character Count: always visible; spoken only near the limit (live region below) -->\n @if (showCharCount() && maxLength()) {\n <p class=\"char-count\">\n {{ charCount }} / {{ maxLength() }}\n </p>\n }\n </div>\n }\n\n @if (showCharCount() && maxLength()) {\n <span class=\"input-char-count-live\" aria-live=\"polite\">{{ charCountAnnouncement() }}</span>\n }\n</div>\n",
350
+ styles: [".input-wrapper{position:relative;display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%}.input-label{display:block;font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);margin-bottom:var(--spacing-1)}.input-label .required-indicator{color:var(--color-error-600);margin-inline-start:var(--spacing-1)}.input-container{position:relative;display:flex;align-items:center;width:100%;background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease)}.input-container:hover:not(.input-disabled):not(.input-readonly){border-color:var(--color-text-secondary)}.input-container:focus-within{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.input-container.input-error{border-color:var(--color-error-600)}.input-container.input-error:focus-within{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.input-container.input-disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.input-container.input-readonly{background-color:var(--color-surface-muted)}.input-container.input-readonly-wrap{height:auto;min-height:var(--lc-input-height);align-items:stretch}.input-readonly-value{display:flex;align-items:center;min-width:0;white-space:normal;overflow-wrap:anywhere;-webkit-user-select:text;user-select:text}.input-char-count-live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.input-field{flex:1;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background:transparent;border:none;outline:none}.input-field::placeholder{color:var(--color-text-secondary)}.input-field:disabled{cursor:not-allowed}.input-field:read-only{cursor:default}.input-field[type=number]::-webkit-inner-spin-button,.input-field[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.input-field[type=number]{-moz-appearance:textfield}.input-icon{display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary);pointer-events:none}.input-icon .icon{width:1.25rem;height:1.25rem}.input-icon.input-icon-before{margin-left:var(--spacing-3);margin-right:var(--spacing-2)}.input-icon.input-icon-after{margin-left:var(--spacing-2);margin-right:var(--spacing-3)}.input-xs{--lc-input-height: var(--size-interactive-xs-height);height:var(--lc-input-height)}.input-xs .input-field{padding:var(--size-interactive-xs-padding);font-size:var(--size-interactive-xs-font-size)}.input-xs .icon{width:1rem;height:1rem}.input-sm{--lc-input-height: var(--size-interactive-sm-height);height:var(--lc-input-height)}.input-sm .input-field{padding:var(--size-interactive-sm-padding);font-size:var(--size-interactive-sm-font-size)}.input-md{--lc-input-height: var(--size-interactive-md-height);height:var(--lc-input-height)}.input-md .input-field{padding:var(--size-interactive-md-padding);font-size:var(--size-interactive-md-font-size)}.input-lg{--lc-input-height: var(--size-interactive-lg-height);height:var(--lc-input-height)}.input-lg .input-field{padding:var(--size-interactive-lg-padding);font-size:var(--size-interactive-lg-font-size)}.input-lg .icon{width:1.5rem;height:1.5rem}.input-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2));min-height:1.25rem}.input-error{flex:1;font-size:var(--font-size-xs);color:var(--color-error-600);margin:0}.input-helper{flex:1;font-size:var(--font-size-xs);color:var(--color-text-secondary);margin:0}.char-count{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;margin:0}\n"],
330
351
  dependencies: [{
331
352
  kind: "component",
332
353
  type: IconComponent,
@@ -360,8 +381,8 @@ i0.ɵɵngDeclareClassMetadata({
360
381
  useExisting: forwardRef(() => InputComponent),
361
382
  multi: true
362
383
  }],
363
- template: "<div class=\"input-wrapper\">\n <!-- Label -->\n @if (label()) {\n <label [id]=\"inputId + '-label'\" [attr.for]=\"wrapsReadonlyValue() ? null : inputId\" class=\"input-label\">\n {{ label() }}\n @if (required() && requiredIndicator()) {\n <span class=\"required-indicator\" [attr.aria-label]=\"requiredAriaLabel()\">*</span>\n }\n </label>\n }\n\n <!-- Input Container -->\n <div\n class=\"input-container\"\n [class.input-xs]=\"size() === 'xs'\"\n [class.input-sm]=\"size() === 'sm'\"\n [class.input-md]=\"size() === 'md'\"\n [class.input-lg]=\"size() === 'lg'\"\n [class.input-error]=\"hasError\"\n [class.input-disabled]=\"isDisabled\"\n [class.input-readonly]=\"readonly()\"\n [class.input-readonly-wrap]=\"wrapsReadonlyValue()\"\n >\n <!-- Icon Before -->\n @if (iconBefore()) {\n <span class=\"input-icon input-icon-before\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconBefore()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n\n <!-- Read-only value that wraps (SI-10): a read-only textbox named by the label -->\n @if (wrapsReadonlyValue()) {\n <div\n #readonlyValueRef\n class=\"input-field input-readonly-value\"\n role=\"textbox\"\n aria-readonly=\"true\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"label() ? inputId + '-label' : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >{{ value() }}</div>\n }\n\n <!-- Input Element (hidden while the read-only value wraps; it keeps value, name and autocomplete) -->\n <input\n #inputRef\n [id]=\"inputId\"\n [type]=\"type()\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [hidden]=\"wrapsReadonlyValue()\"\n [attr.maxlength]=\"maxLength() || null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize() ?? null\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-required]=\"required() || undefined\"\n [attr.aria-invalid]=\"hasError || undefined\"\n [attr.aria-describedby]=\"describedBy()\"\n class=\"input-field\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n <!-- Icon After -->\n @if (iconAfter()) {\n <span class=\"input-icon input-icon-after\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconAfter()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n </div>\n\n <!-- Helper Text / Error Message / Character Count -->\n @if (error() || helperText() || (showCharCount() && maxLength())) {\n <div class=\"input-footer\">\n <!-- Error Message -->\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"input-error\" role=\"alert\">\n {{ error() }}\n </p>\n }\n\n <!-- Helper Text -->\n @if (helperText() && !error()) {\n <p [id]=\"inputId + '-helper'\" class=\"input-helper\">\n {{ helperText() }}\n </p>\n }\n\n <!-- Character Count: always visible; spoken only near the limit (live region below) -->\n @if (showCharCount() && maxLength()) {\n <p class=\"char-count\">\n {{ charCount }} / {{ maxLength() }}\n </p>\n }\n </div>\n }\n\n @if (showCharCount() && maxLength()) {\n <span class=\"input-char-count-live\" aria-live=\"polite\">{{ charCountAnnouncement() }}</span>\n }\n</div>\n",
364
- styles: [".input-wrapper{position:relative;display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%}.input-label{display:block;font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);margin-bottom:var(--spacing-1)}.input-label .required-indicator{color:var(--color-error-600);margin-left:var(--spacing-0-5)}.input-container{position:relative;display:flex;align-items:center;width:100%;background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease)}.input-container:hover:not(.input-disabled):not(.input-readonly){border-color:var(--color-text-secondary)}.input-container:focus-within{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.input-container.input-error{border-color:var(--color-error-600)}.input-container.input-error:focus-within{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.input-container.input-disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.input-container.input-readonly{background-color:var(--color-surface-muted)}.input-container.input-readonly-wrap{height:auto;min-height:var(--lc-input-height);align-items:stretch}.input-readonly-value{display:flex;align-items:center;min-width:0;white-space:normal;overflow-wrap:anywhere;-webkit-user-select:text;user-select:text}.input-char-count-live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.input-field{flex:1;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background:transparent;border:none;outline:none}.input-field::placeholder{color:var(--color-text-secondary)}.input-field:disabled{cursor:not-allowed}.input-field:read-only{cursor:default}.input-field[type=number]::-webkit-inner-spin-button,.input-field[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.input-field[type=number]{-moz-appearance:textfield}.input-icon{display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary);pointer-events:none}.input-icon .icon{width:1.25rem;height:1.25rem}.input-icon.input-icon-before{margin-left:var(--spacing-3);margin-right:var(--spacing-2)}.input-icon.input-icon-after{margin-left:var(--spacing-2);margin-right:var(--spacing-3)}.input-xs{--lc-input-height: var(--size-interactive-xs-height);height:var(--lc-input-height)}.input-xs .input-field{padding:var(--size-interactive-xs-padding);font-size:var(--size-interactive-xs-font-size)}.input-xs .icon{width:1rem;height:1rem}.input-sm{--lc-input-height: var(--size-interactive-sm-height);height:var(--lc-input-height)}.input-sm .input-field{padding:var(--size-interactive-sm-padding);font-size:var(--size-interactive-sm-font-size)}.input-md{--lc-input-height: var(--size-interactive-md-height);height:var(--lc-input-height)}.input-md .input-field{padding:var(--size-interactive-md-padding);font-size:var(--size-interactive-md-font-size)}.input-lg{--lc-input-height: var(--size-interactive-lg-height);height:var(--lc-input-height)}.input-lg .input-field{padding:var(--size-interactive-lg-padding);font-size:var(--size-interactive-lg-font-size)}.input-lg .icon{width:1.5rem;height:1.5rem}.input-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2));min-height:1.25rem}.input-error{flex:1;font-size:var(--font-size-xs);color:var(--color-error-600);margin:0}.input-helper{flex:1;font-size:var(--font-size-xs);color:var(--color-text-secondary);margin:0}.char-count{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;margin:0}\n"]
384
+ template: "<div class=\"input-wrapper\">\n <!-- Label -->\n @if (label()) {\n <!-- No whitespace before the *: the gap is its margin, and it never wraps onto a line alone -->\n <label [id]=\"inputId + '-label'\" [attr.for]=\"wrapsReadonlyValue() ? null : inputId\" class=\"input-label\">\n {{ label() }}@if (required() && requiredIndicator()) {<span class=\"required-indicator\" aria-hidden=\"true\">*</span>}\n </label>\n }\n\n <!-- Input Container -->\n <div\n class=\"input-container\"\n [class.input-xs]=\"size() === 'xs'\"\n [class.input-sm]=\"size() === 'sm'\"\n [class.input-md]=\"size() === 'md'\"\n [class.input-lg]=\"size() === 'lg'\"\n [class.input-error]=\"hasError\"\n [class.input-disabled]=\"isDisabled\"\n [class.input-readonly]=\"readonly()\"\n [class.input-readonly-wrap]=\"wrapsReadonlyValue()\"\n >\n <!-- Icon Before -->\n @if (iconBefore()) {\n <span class=\"input-icon input-icon-before\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconBefore()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n\n <!-- Read-only value that wraps (SI-10): a read-only textbox named by the label -->\n @if (wrapsReadonlyValue()) {\n <div\n #readonlyValueRef\n class=\"input-field input-readonly-value\"\n role=\"textbox\"\n aria-readonly=\"true\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"label() ? inputId + '-label' : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"describedBy()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >{{ value() }}</div>\n }\n\n <!-- Input Element (hidden while the read-only value wraps; it keeps value, name and autocomplete) -->\n <input\n #inputRef\n [id]=\"inputId\"\n [type]=\"type()\"\n [value]=\"value()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [hidden]=\"wrapsReadonlyValue()\"\n [attr.maxlength]=\"maxLength() || null\"\n [attr.autocomplete]=\"autocomplete() ?? null\"\n [attr.name]=\"name() ?? null\"\n [attr.autocapitalize]=\"autocapitalize() ?? null\"\n [attr.spellcheck]=\"spellcheck() === undefined ? null : spellcheck() ? 'true' : 'false'\"\n [attr.enterkeyhint]=\"enterkeyhint() ?? null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-required]=\"required() || undefined\"\n [attr.aria-invalid]=\"hasError || undefined\"\n [attr.aria-describedby]=\"describedBy()\"\n class=\"input-field\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n <!-- Icon After -->\n @if (iconAfter()) {\n <span class=\"input-icon input-icon-after\" aria-hidden=\"true\">\n <lc-icon [name]=\"iconAfter()!\" size=\"sm\" [decorative]=\"true\" />\n </span>\n }\n </div>\n\n <!-- Helper Text / Error Message / Character Count -->\n @if (error() || helperText() || (showCharCount() && maxLength())) {\n <div class=\"input-footer\">\n <!-- Error Message -->\n @if (error()) {\n <p [id]=\"inputId + '-error'\" class=\"input-error\" [attr.role]=\"announceError() ? 'alert' : null\">\n {{ error() }}\n </p>\n }\n\n <!-- Helper Text -->\n @if (helperText() && !error()) {\n <p [id]=\"inputId + '-helper'\" class=\"input-helper\">\n {{ helperText() }}\n </p>\n }\n\n <!-- Character Count: always visible; spoken only near the limit (live region below) -->\n @if (showCharCount() && maxLength()) {\n <p class=\"char-count\">\n {{ charCount }} / {{ maxLength() }}\n </p>\n }\n </div>\n }\n\n @if (showCharCount() && maxLength()) {\n <span class=\"input-char-count-live\" aria-live=\"polite\">{{ charCountAnnouncement() }}</span>\n }\n</div>\n",
385
+ styles: [".input-wrapper{position:relative;display:flex;flex-direction:column;gap:var(--lc-density-gap-xs, var(--spacing-1));width:100%}.input-label{display:block;font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-text-primary);margin-bottom:var(--spacing-1)}.input-label .required-indicator{color:var(--color-error-600);margin-inline-start:var(--spacing-1)}.input-container{position:relative;display:flex;align-items:center;width:100%;background-color:var(--color-surface-2);border:1px solid var(--color-field-border);border-radius:var(--border-radius-md);transition:all var(--animation-duration-base) var(--animation-timing-ease)}.input-container:hover:not(.input-disabled):not(.input-readonly){border-color:var(--color-text-secondary)}.input-container:focus-within{outline:2px solid var(--color-primary-500);outline-offset:0px;border-color:var(--color-primary-500)}.input-container.input-error{border-color:var(--color-error-600)}.input-container.input-error:focus-within{outline-color:var(--color-error-600);border-color:var(--color-error-600)}.input-container.input-disabled{background-color:var(--color-surface-muted);border-color:var(--color-border);cursor:not-allowed;opacity:.6}.input-container.input-readonly{background-color:var(--color-surface-muted)}.input-container.input-readonly-wrap{height:auto;min-height:var(--lc-input-height);align-items:stretch}.input-readonly-value{display:flex;align-items:center;min-width:0;white-space:normal;overflow-wrap:anywhere;-webkit-user-select:text;user-select:text}.input-char-count-live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.input-field{flex:1;width:100%;font-family:var(--font-family-sans);font-weight:var(--font-weight-regular);line-height:var(--line-height-normal);color:var(--color-text-primary);background:transparent;border:none;outline:none}.input-field::placeholder{color:var(--color-text-secondary)}.input-field:disabled{cursor:not-allowed}.input-field:read-only{cursor:default}.input-field[type=number]::-webkit-inner-spin-button,.input-field[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.input-field[type=number]{-moz-appearance:textfield}.input-icon{display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary);pointer-events:none}.input-icon .icon{width:1.25rem;height:1.25rem}.input-icon.input-icon-before{margin-left:var(--spacing-3);margin-right:var(--spacing-2)}.input-icon.input-icon-after{margin-left:var(--spacing-2);margin-right:var(--spacing-3)}.input-xs{--lc-input-height: var(--size-interactive-xs-height);height:var(--lc-input-height)}.input-xs .input-field{padding:var(--size-interactive-xs-padding);font-size:var(--size-interactive-xs-font-size)}.input-xs .icon{width:1rem;height:1rem}.input-sm{--lc-input-height: var(--size-interactive-sm-height);height:var(--lc-input-height)}.input-sm .input-field{padding:var(--size-interactive-sm-padding);font-size:var(--size-interactive-sm-font-size)}.input-md{--lc-input-height: var(--size-interactive-md-height);height:var(--lc-input-height)}.input-md .input-field{padding:var(--size-interactive-md-padding);font-size:var(--size-interactive-md-font-size)}.input-lg{--lc-input-height: var(--size-interactive-lg-height);height:var(--lc-input-height)}.input-lg .input-field{padding:var(--size-interactive-lg-padding);font-size:var(--size-interactive-lg-font-size)}.input-lg .icon{width:1.5rem;height:1.5rem}.input-footer{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--lc-density-gap-sm, var(--spacing-2));min-height:1.25rem}.input-error{flex:1;font-size:var(--font-size-xs);color:var(--color-error-600);margin:0}.input-helper{flex:1;font-size:var(--font-size-xs);color:var(--color-text-secondary);margin:0}.char-count{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;margin:0}\n"]
365
386
  }]
366
387
  }],
367
388
  propDecorators: {
@@ -557,6 +578,22 @@ i0.ɵɵngDeclareClassMetadata({
557
578
  required: false
558
579
  }]
559
580
  }],
581
+ controlId: [{
582
+ type: i0.Input,
583
+ args: [{
584
+ isSignal: true,
585
+ alias: "controlId",
586
+ required: false
587
+ }]
588
+ }],
589
+ announceError: [{
590
+ type: i0.Input,
591
+ args: [{
592
+ isSignal: true,
593
+ alias: "announceError",
594
+ required: false
595
+ }]
596
+ }],
560
597
  value: [{
561
598
  type: i0.Input,
562
599
  args: [{