@lifecorner/angular-ui-kit 3.5.1 → 3.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-accordion.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-alert.mjs +16 -1
- package/fesm2022/lifecorner-angular-ui-kit-alert.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-bottom-nav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs +4 -4
- package/fesm2022/lifecorner-angular-ui-kit-button.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-callout.mjs +16 -1
- package/fesm2022/lifecorner-angular-ui-kit-callout.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs +14 -1
- package/fesm2022/lifecorner-angular-ui-kit-chat.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +46 -32
- package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-combobox.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-confirm-dialog.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-date-range-picker.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +14 -1
- package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-drawer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs +4 -0
- package/fesm2022/lifecorner-angular-ui-kit-email-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-footer.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-footer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs +14 -1
- package/fesm2022/lifecorner-angular-ui-kit-gallery.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs +18 -1
- package/fesm2022/lifecorner-angular-ui-kit-header.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +186 -275
- package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-icons.mjs +803 -0
- package/fesm2022/lifecorner-angular-ui-kit-icons.mjs.map +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs +297 -0
- package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs.map +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs +38 -0
- package/fesm2022/lifecorner-angular-ui-kit-internal-navigation.mjs.map +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-modal.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-modal.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs +12 -1
- package/fesm2022/lifecorner-angular-ui-kit-next-step-card.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +14 -1
- package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-number-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-password-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +12 -1
- package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs +262 -0
- package/fesm2022/lifecorner-angular-ui-kit-sidenav-placeholder.mjs.map +1 -0
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs +18 -40
- package/fesm2022/lifecorner-angular-ui-kit-sidenav.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs +4 -1
- package/fesm2022/lifecorner-angular-ui-kit-table.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs +16 -1
- package/fesm2022/lifecorner-angular-ui-kit-toast.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs +18 -1
- package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
- package/fesm2022/lifecorner-angular-ui-kit.mjs +2 -0
- package/package.json +17 -1
- package/types/lifecorner-angular-ui-kit-code-block.d.ts +10 -4
- package/types/lifecorner-angular-ui-kit-icon.d.ts +125 -5
- package/types/lifecorner-angular-ui-kit-icons.d.ts +140 -0
- package/types/lifecorner-angular-ui-kit-internal-icon-set.d.ts +31 -0
- package/types/lifecorner-angular-ui-kit-internal-navigation.d.ts +28 -0
- package/types/lifecorner-angular-ui-kit-sidenav-placeholder.d.ts +163 -0
- package/types/lifecorner-angular-ui-kit-sidenav.d.ts +2 -1
- package/types/lifecorner-angular-ui-kit.d.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-date-range-picker.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/date-range-picker/date-range-picker.component.ts","../../../../libs/angular-ui-kit/src/lib/date-range-picker/date-range-picker.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n HostListener,\n afterRenderEffect,\n computed,\n forwardRef,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\n/** Monday..Sunday of a known week — only used to derive localised weekday names */\nconst REFERENCE_WEEK = Array.from({ length: 7 }, (_, i) => new Date(2024, 0, 1 + i));\n\n@Component({\n selector: 'lc-date-range-picker',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './date-range-picker.component.html',\n styleUrls: ['./date-range-picker.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DateRangePickerComponent),\n multi: true,\n },\n ],\n})\nexport class DateRangePickerComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids for label ↔ trigger ↔ panel ARIA wiring */\n readonly triggerId = `lc-drp-${++DateRangePickerComponent.nextId}`;\n readonly labelId = `${this.triggerId}-label`;\n readonly valueId = `${this.triggerId}-value`;\n readonly panelId = `${this.triggerId}-panel`;\n\n private readonly elRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly triggerEl = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n private readonly panelEl = viewChild<ElementRef<HTMLElement>>('panelEl');\n\n readonly label = input('');\n readonly placeholder = input('Select date range');\n readonly disabled = input(false);\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n /** BCP 47 locale for the displayed dates, month title and weekday headers */\n readonly locale = input('de-DE');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Accessible name of the previous-month button. */\n readonly previousMonthAriaLabel = input('Previous month');\n\n /** Accessible name of the next-month button. */\n readonly nextMonthAriaLabel = input('Next month');\n\n readonly rangeChange = output<DateRange>();\n\n protected isOpen = signal(false);\n protected range = signal<DateRange>({ start: null, end: null });\n protected selecting = signal<'start' | 'end'>('start');\n protected hoveredDate = signal<Date | null>(null);\n protected viewingMonth = signal(new Date());\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private onChange: (val: DateRange) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n protected readonly displayValue = computed(() => {\n const r = this.range();\n if (!r.start) return '';\n const locale = this.locale();\n const fmt = (d: Date) => d.toLocaleDateString(locale, { day: '2-digit', month: '2-digit', year: 'numeric' });\n if (!r.end) return fmt(r.start) + ' – …';\n return `${fmt(r.start)} – ${fmt(r.end)}`;\n });\n\n protected readonly monthLabel = computed(() => {\n const d = this.viewingMonth();\n return d.toLocaleDateString(this.locale(), { month: 'long', year: 'numeric' });\n });\n\n /** Monday-first weekday headers in the configured locale (e.g. Mo…So / Mon…Sun) */\n protected readonly weekdays = computed(() => {\n const locale = this.locale();\n // some ICU builds abbreviate with a trailing period (\"Mo.\") — keep the compact form\n return REFERENCE_WEEK.map((d) => d.toLocaleDateString(locale, { weekday: 'short' }).replace(/\\.$/, ''));\n });\n\n protected readonly calendarDays = computed(() => {\n const viewing = this.viewingMonth();\n const locale = this.locale();\n const year = viewing.getFullYear();\n const month = viewing.getMonth();\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n\n let startDow = firstDay.getDay();\n if (startDow === 0) startDow = 7; // Monday = 1\n const prefixDays = startDow - 1;\n\n const days: { date: Date; inMonth: boolean; disabled: boolean; ariaLabel: string }[] = [];\n const push = (date: Date, inMonth: boolean) =>\n days.push({\n date,\n inMonth,\n disabled: this.isDateDisabled(date),\n ariaLabel: date.toLocaleDateString(locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }),\n });\n\n for (let i = prefixDays; i > 0; i--) {\n push(new Date(year, month, 1 - i), false);\n }\n\n for (let d = 1; d <= lastDay.getDate(); d++) {\n push(new Date(year, month, d), true);\n }\n\n const remaining = 42 - days.length;\n for (let i = 1; i <= remaining; i++) {\n push(new Date(year, month + 1, i), false);\n }\n\n return days;\n });\n\n constructor() {\n // Move focus into the panel once it is rendered so keyboard users land on\n // the calendar instead of having to tab through the trigger's siblings.\n afterRenderEffect(() => {\n if (!this.isOpen()) return;\n untracked(() => {\n const panel = this.panelEl()?.nativeElement;\n if (!panel || panel.contains(document.activeElement)) return;\n const target =\n panel.querySelector<HTMLButtonElement>('.lc-drp__day--start:not(:disabled)') ??\n panel.querySelector<HTMLButtonElement>('.lc-drp__day--today:not(:disabled)') ??\n panel.querySelector<HTMLButtonElement>('.lc-drp__day:not(.lc-drp__day--outside):not(:disabled)');\n target?.focus();\n });\n });\n }\n\n writeValue(value: DateRange | null): void {\n this.range.set(value ?? { start: null, end: null });\n if (value?.start) {\n this.viewingMonth.set(new Date(value.start.getFullYear(), value.start.getMonth(), 1));\n }\n }\n\n registerOnChange(fn: (val: DateRange) => void): void { this.onChange = fn; }\n registerOnTouched(fn: () => void): void { this.onTouched = fn; }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n if (isDisabled) this.isOpen.set(false);\n }\n\n protected toggle(): void {\n if (this.isDisabled()) return;\n if (this.isOpen()) {\n this.close();\n return;\n }\n this.selecting.set('start');\n this.hoveredDate.set(null);\n this.isOpen.set(true);\n }\n\n protected close(): void {\n if (!this.isOpen()) return;\n this.isOpen.set(false);\n this.onTouched();\n // Return focus to the trigger when it was inside the (now removed) panel\n if (this.elRef.nativeElement.contains(document.activeElement)) {\n this.triggerEl()?.nativeElement.focus();\n }\n }\n\n /** Escape (from the trigger or anywhere in the panel) closes the picker */\n @HostListener('keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.isOpen()) {\n this.close();\n // consumed here — an enclosing modal/drawer must not close as well\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n if (this.isOpen() && !this.elRef.nativeElement.contains(event.target as Node)) {\n this.close();\n }\n }\n\n protected prevMonth(): void {\n this.viewingMonth.update(d => new Date(d.getFullYear(), d.getMonth() - 1, 1));\n }\n\n protected nextMonth(): void {\n this.viewingMonth.update(d => new Date(d.getFullYear(), d.getMonth() + 1, 1));\n }\n\n protected selectDate(date: Date): void {\n if (this.isDateDisabled(date)) return;\n\n const current = this.range();\n if (this.selecting() === 'start') {\n this.range.set({ start: date, end: null });\n this.selecting.set('end');\n } else {\n if (date < current.start!) {\n this.range.set({ start: date, end: current.start });\n } else {\n this.range.set({ start: current.start, end: date });\n }\n this.selecting.set('start');\n this.emitRange();\n this.close();\n }\n }\n\n protected onDayHover(date: Date): void {\n if (this.selecting() === 'end') {\n this.hoveredDate.set(date);\n }\n }\n\n protected isStart(date: Date): boolean {\n return this.sameDay(date, this.range().start);\n }\n\n protected isEnd(date: Date): boolean {\n return this.sameDay(date, this.range().end);\n }\n\n protected isInRange(date: Date): boolean {\n const r = this.range();\n if (!r.start) return false;\n const end = r.end || this.hoveredDate();\n if (!end) return false;\n const s = Math.min(r.start.getTime(), end.getTime());\n const e = Math.max(r.start.getTime(), end.getTime());\n const t = date.getTime();\n return t > s && t < e;\n }\n\n protected isToday(date: Date): boolean {\n return this.sameDay(date, new Date());\n }\n\n protected clearRange(event: Event): void {\n event.stopPropagation();\n if (this.isDisabled()) return;\n this.range.set({ start: null, end: null });\n const r = { start: null, end: null };\n this.onChange(r);\n this.rangeChange.emit(r);\n }\n\n /** min/max are compared by calendar day — `minDate = new Date()` keeps today selectable */\n private isDateDisabled(date: Date): boolean {\n const min = this.minDate();\n const max = this.maxDate();\n const day = this.dayStamp(date);\n if (min && day < this.dayStamp(min)) return true;\n if (max && day > this.dayStamp(max)) return true;\n return false;\n }\n\n private dayStamp(d: Date): number {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n }\n\n private sameDay(a: Date | null, b: Date | null): boolean {\n if (!a || !b) return false;\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n }\n\n private emitRange(): void {\n const r = this.range();\n this.onChange(r);\n this.rangeChange.emit(r);\n }\n}\n","@if (label()) {\n <label class=\"lc-drp__label\" [id]=\"labelId\" [for]=\"triggerId\">{{ label() }}</label>\n}\n\n<!-- The clear button must not be nested inside the trigger button, so the\n bordered \"field\" wraps both. -->\n<div class=\"lc-drp__field\" [class.lc-drp__field--disabled]=\"isDisabled()\">\n <button\n #triggerEl\n type=\"button\"\n class=\"lc-drp__trigger\"\n [id]=\"triggerId\"\n [class.lc-drp__trigger--disabled]=\"isDisabled()\"\n [disabled]=\"isDisabled()\"\n aria-haspopup=\"dialog\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? panelId : null\"\n [attr.aria-labelledby]=\"label() ? labelId + ' ' + valueId : null\"\n (click)=\"toggle()\"\n >\n <lc-icon class=\"lc-drp__icon\" name=\"calendar\" size=\"sm\" [decorative]=\"true\" />\n <span class=\"lc-drp__value\" [id]=\"valueId\" [class.lc-drp__value--placeholder]=\"!displayValue()\">\n {{ displayValue() || placeholder() }}\n </span>\n </button>\n @if (displayValue() && !isDisabled()) {\n <button type=\"button\" class=\"lc-drp__clear\" (click)=\"clearRange($event)\" [attr.aria-label]=\"clearAriaLabel()\">×</button>\n }\n</div>\n\n@if (isOpen()) {\n <div\n #panelEl\n class=\"lc-drp__dropdown\"\n role=\"dialog\"\n [id]=\"panelId\"\n [attr.aria-label]=\"label() || placeholder()\"\n >\n <div class=\"lc-drp__header\">\n <button type=\"button\" class=\"lc-drp__nav\" [attr.aria-label]=\"previousMonthAriaLabel()\" (click)=\"prevMonth()\">‹</button>\n <span class=\"lc-drp__month\" aria-live=\"polite\">{{ monthLabel() }}</span>\n <button type=\"button\" class=\"lc-drp__nav\" [attr.aria-label]=\"nextMonthAriaLabel()\" (click)=\"nextMonth()\">›</button>\n </div>\n\n <div class=\"lc-drp__weekdays\" aria-hidden=\"true\">\n @for (wd of weekdays(); track $index) {\n <span class=\"lc-drp__weekday\">{{ wd }}</span>\n }\n </div>\n\n <div class=\"lc-drp__grid\">\n @for (day of calendarDays(); track day.date.getTime()) {\n <button\n type=\"button\"\n class=\"lc-drp__day\"\n [class.lc-drp__day--outside]=\"!day.inMonth\"\n [class.lc-drp__day--disabled]=\"day.disabled\"\n [class.lc-drp__day--today]=\"isToday(day.date)\"\n [class.lc-drp__day--start]=\"isStart(day.date)\"\n [class.lc-drp__day--end]=\"isEnd(day.date)\"\n [class.lc-drp__day--in-range]=\"isInRange(day.date)\"\n [disabled]=\"day.disabled\"\n [attr.aria-label]=\"day.ariaLabel\"\n [attr.aria-pressed]=\"isStart(day.date) || isEnd(day.date)\"\n (click)=\"selectDate(day.date)\"\n (mouseenter)=\"onDayHover(day.date)\"\n >{{ day.date.getDate() }}</button>\n }\n </div>\n\n <div class=\"lc-drp__footer\">\n <span class=\"lc-drp__hint\" aria-live=\"polite\">\n @if (selecting() === 'start') { Select start date }\n @else { Select end date }\n </span>\n </div>\n </div>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-date-range-picker.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/date-range-picker/date-range-picker.component.ts","../../../../libs/angular-ui-kit/src/lib/date-range-picker/date-range-picker.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n HostListener,\n afterRenderEffect,\n computed,\n forwardRef,\n inject,\n input,\n output,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CALENDAR } from '@lifecorner/angular-ui-kit/icons';\n\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\n/** Monday..Sunday of a known week — only used to derive localised weekday names */\nconst REFERENCE_WEEK = Array.from({ length: 7 }, (_, i) => new Date(2024, 0, 1 + i));\n\n@Component({\n selector: 'lc-date-range-picker',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CALENDAR])],\n templateUrl: './date-range-picker.component.html',\n styleUrls: ['./date-range-picker.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DateRangePickerComponent),\n multi: true,\n },\n ],\n})\nexport class DateRangePickerComponent implements ControlValueAccessor {\n private static nextId = 0;\n\n /** Per-instance ids for label ↔ trigger ↔ panel ARIA wiring */\n readonly triggerId = `lc-drp-${++DateRangePickerComponent.nextId}`;\n readonly labelId = `${this.triggerId}-label`;\n readonly valueId = `${this.triggerId}-value`;\n readonly panelId = `${this.triggerId}-panel`;\n\n private readonly elRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly triggerEl = viewChild<ElementRef<HTMLButtonElement>>('triggerEl');\n private readonly panelEl = viewChild<ElementRef<HTMLElement>>('panelEl');\n\n readonly label = input('');\n readonly placeholder = input('Select date range');\n readonly disabled = input(false);\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n /** BCP 47 locale for the displayed dates, month title and weekday headers */\n readonly locale = input('de-DE');\n\n /** Accessible name of the clear button. */\n readonly clearAriaLabel = input('Clear');\n\n /** Accessible name of the previous-month button. */\n readonly previousMonthAriaLabel = input('Previous month');\n\n /** Accessible name of the next-month button. */\n readonly nextMonthAriaLabel = input('Next month');\n\n readonly rangeChange = output<DateRange>();\n\n protected isOpen = signal(false);\n protected range = signal<DateRange>({ start: null, end: null });\n protected selecting = signal<'start' | 'end'>('start');\n protected hoveredDate = signal<Date | null>(null);\n protected viewingMonth = signal(new Date());\n\n /** Disabled via `setDisabledState` (reactive forms) */\n private readonly formDisabled = signal(false);\n\n /** Effective disabled state: `disabled` input OR form control disabled */\n protected readonly isDisabled = computed(() => this.disabled() || this.formDisabled());\n\n private onChange: (val: DateRange) => void = () => { /* set by registerOnChange */ };\n private onTouched: () => void = () => { /* set by registerOnTouched */ };\n\n protected readonly displayValue = computed(() => {\n const r = this.range();\n if (!r.start) return '';\n const locale = this.locale();\n const fmt = (d: Date) => d.toLocaleDateString(locale, { day: '2-digit', month: '2-digit', year: 'numeric' });\n if (!r.end) return fmt(r.start) + ' – …';\n return `${fmt(r.start)} – ${fmt(r.end)}`;\n });\n\n protected readonly monthLabel = computed(() => {\n const d = this.viewingMonth();\n return d.toLocaleDateString(this.locale(), { month: 'long', year: 'numeric' });\n });\n\n /** Monday-first weekday headers in the configured locale (e.g. Mo…So / Mon…Sun) */\n protected readonly weekdays = computed(() => {\n const locale = this.locale();\n // some ICU builds abbreviate with a trailing period (\"Mo.\") — keep the compact form\n return REFERENCE_WEEK.map((d) => d.toLocaleDateString(locale, { weekday: 'short' }).replace(/\\.$/, ''));\n });\n\n protected readonly calendarDays = computed(() => {\n const viewing = this.viewingMonth();\n const locale = this.locale();\n const year = viewing.getFullYear();\n const month = viewing.getMonth();\n const firstDay = new Date(year, month, 1);\n const lastDay = new Date(year, month + 1, 0);\n\n let startDow = firstDay.getDay();\n if (startDow === 0) startDow = 7; // Monday = 1\n const prefixDays = startDow - 1;\n\n const days: { date: Date; inMonth: boolean; disabled: boolean; ariaLabel: string }[] = [];\n const push = (date: Date, inMonth: boolean) =>\n days.push({\n date,\n inMonth,\n disabled: this.isDateDisabled(date),\n ariaLabel: date.toLocaleDateString(locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }),\n });\n\n for (let i = prefixDays; i > 0; i--) {\n push(new Date(year, month, 1 - i), false);\n }\n\n for (let d = 1; d <= lastDay.getDate(); d++) {\n push(new Date(year, month, d), true);\n }\n\n const remaining = 42 - days.length;\n for (let i = 1; i <= remaining; i++) {\n push(new Date(year, month + 1, i), false);\n }\n\n return days;\n });\n\n constructor() {\n // Move focus into the panel once it is rendered so keyboard users land on\n // the calendar instead of having to tab through the trigger's siblings.\n afterRenderEffect(() => {\n if (!this.isOpen()) return;\n untracked(() => {\n const panel = this.panelEl()?.nativeElement;\n if (!panel || panel.contains(document.activeElement)) return;\n const target =\n panel.querySelector<HTMLButtonElement>('.lc-drp__day--start:not(:disabled)') ??\n panel.querySelector<HTMLButtonElement>('.lc-drp__day--today:not(:disabled)') ??\n panel.querySelector<HTMLButtonElement>('.lc-drp__day:not(.lc-drp__day--outside):not(:disabled)');\n target?.focus();\n });\n });\n }\n\n writeValue(value: DateRange | null): void {\n this.range.set(value ?? { start: null, end: null });\n if (value?.start) {\n this.viewingMonth.set(new Date(value.start.getFullYear(), value.start.getMonth(), 1));\n }\n }\n\n registerOnChange(fn: (val: DateRange) => void): void { this.onChange = fn; }\n registerOnTouched(fn: () => void): void { this.onTouched = fn; }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n if (isDisabled) this.isOpen.set(false);\n }\n\n protected toggle(): void {\n if (this.isDisabled()) return;\n if (this.isOpen()) {\n this.close();\n return;\n }\n this.selecting.set('start');\n this.hoveredDate.set(null);\n this.isOpen.set(true);\n }\n\n protected close(): void {\n if (!this.isOpen()) return;\n this.isOpen.set(false);\n this.onTouched();\n // Return focus to the trigger when it was inside the (now removed) panel\n if (this.elRef.nativeElement.contains(document.activeElement)) {\n this.triggerEl()?.nativeElement.focus();\n }\n }\n\n /** Escape (from the trigger or anywhere in the panel) closes the picker */\n @HostListener('keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && this.isOpen()) {\n this.close();\n // consumed here — an enclosing modal/drawer must not close as well\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: MouseEvent): void {\n if (this.isOpen() && !this.elRef.nativeElement.contains(event.target as Node)) {\n this.close();\n }\n }\n\n protected prevMonth(): void {\n this.viewingMonth.update(d => new Date(d.getFullYear(), d.getMonth() - 1, 1));\n }\n\n protected nextMonth(): void {\n this.viewingMonth.update(d => new Date(d.getFullYear(), d.getMonth() + 1, 1));\n }\n\n protected selectDate(date: Date): void {\n if (this.isDateDisabled(date)) return;\n\n const current = this.range();\n if (this.selecting() === 'start') {\n this.range.set({ start: date, end: null });\n this.selecting.set('end');\n } else {\n if (date < current.start!) {\n this.range.set({ start: date, end: current.start });\n } else {\n this.range.set({ start: current.start, end: date });\n }\n this.selecting.set('start');\n this.emitRange();\n this.close();\n }\n }\n\n protected onDayHover(date: Date): void {\n if (this.selecting() === 'end') {\n this.hoveredDate.set(date);\n }\n }\n\n protected isStart(date: Date): boolean {\n return this.sameDay(date, this.range().start);\n }\n\n protected isEnd(date: Date): boolean {\n return this.sameDay(date, this.range().end);\n }\n\n protected isInRange(date: Date): boolean {\n const r = this.range();\n if (!r.start) return false;\n const end = r.end || this.hoveredDate();\n if (!end) return false;\n const s = Math.min(r.start.getTime(), end.getTime());\n const e = Math.max(r.start.getTime(), end.getTime());\n const t = date.getTime();\n return t > s && t < e;\n }\n\n protected isToday(date: Date): boolean {\n return this.sameDay(date, new Date());\n }\n\n protected clearRange(event: Event): void {\n event.stopPropagation();\n if (this.isDisabled()) return;\n this.range.set({ start: null, end: null });\n const r = { start: null, end: null };\n this.onChange(r);\n this.rangeChange.emit(r);\n }\n\n /** min/max are compared by calendar day — `minDate = new Date()` keeps today selectable */\n private isDateDisabled(date: Date): boolean {\n const min = this.minDate();\n const max = this.maxDate();\n const day = this.dayStamp(date);\n if (min && day < this.dayStamp(min)) return true;\n if (max && day > this.dayStamp(max)) return true;\n return false;\n }\n\n private dayStamp(d: Date): number {\n return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n }\n\n private sameDay(a: Date | null, b: Date | null): boolean {\n if (!a || !b) return false;\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n }\n\n private emitRange(): void {\n const r = this.range();\n this.onChange(r);\n this.rangeChange.emit(r);\n }\n}\n","@if (label()) {\n <label class=\"lc-drp__label\" [id]=\"labelId\" [for]=\"triggerId\">{{ label() }}</label>\n}\n\n<!-- The clear button must not be nested inside the trigger button, so the\n bordered \"field\" wraps both. -->\n<div class=\"lc-drp__field\" [class.lc-drp__field--disabled]=\"isDisabled()\">\n <button\n #triggerEl\n type=\"button\"\n class=\"lc-drp__trigger\"\n [id]=\"triggerId\"\n [class.lc-drp__trigger--disabled]=\"isDisabled()\"\n [disabled]=\"isDisabled()\"\n aria-haspopup=\"dialog\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? panelId : null\"\n [attr.aria-labelledby]=\"label() ? labelId + ' ' + valueId : null\"\n (click)=\"toggle()\"\n >\n <lc-icon class=\"lc-drp__icon\" name=\"calendar\" size=\"sm\" [decorative]=\"true\" />\n <span class=\"lc-drp__value\" [id]=\"valueId\" [class.lc-drp__value--placeholder]=\"!displayValue()\">\n {{ displayValue() || placeholder() }}\n </span>\n </button>\n @if (displayValue() && !isDisabled()) {\n <button type=\"button\" class=\"lc-drp__clear\" (click)=\"clearRange($event)\" [attr.aria-label]=\"clearAriaLabel()\">×</button>\n }\n</div>\n\n@if (isOpen()) {\n <div\n #panelEl\n class=\"lc-drp__dropdown\"\n role=\"dialog\"\n [id]=\"panelId\"\n [attr.aria-label]=\"label() || placeholder()\"\n >\n <div class=\"lc-drp__header\">\n <button type=\"button\" class=\"lc-drp__nav\" [attr.aria-label]=\"previousMonthAriaLabel()\" (click)=\"prevMonth()\">‹</button>\n <span class=\"lc-drp__month\" aria-live=\"polite\">{{ monthLabel() }}</span>\n <button type=\"button\" class=\"lc-drp__nav\" [attr.aria-label]=\"nextMonthAriaLabel()\" (click)=\"nextMonth()\">›</button>\n </div>\n\n <div class=\"lc-drp__weekdays\" aria-hidden=\"true\">\n @for (wd of weekdays(); track $index) {\n <span class=\"lc-drp__weekday\">{{ wd }}</span>\n }\n </div>\n\n <div class=\"lc-drp__grid\">\n @for (day of calendarDays(); track day.date.getTime()) {\n <button\n type=\"button\"\n class=\"lc-drp__day\"\n [class.lc-drp__day--outside]=\"!day.inMonth\"\n [class.lc-drp__day--disabled]=\"day.disabled\"\n [class.lc-drp__day--today]=\"isToday(day.date)\"\n [class.lc-drp__day--start]=\"isStart(day.date)\"\n [class.lc-drp__day--end]=\"isEnd(day.date)\"\n [class.lc-drp__day--in-range]=\"isInRange(day.date)\"\n [disabled]=\"day.disabled\"\n [attr.aria-label]=\"day.ariaLabel\"\n [attr.aria-pressed]=\"isStart(day.date) || isEnd(day.date)\"\n (click)=\"selectDate(day.date)\"\n (mouseenter)=\"onDayHover(day.date)\"\n >{{ day.date.getDate() }}</button>\n }\n </div>\n\n <div class=\"lc-drp__footer\">\n <span class=\"lc-drp__hint\" aria-live=\"polite\">\n @if (selecting() === 'start') { Select start date }\n @else { Select end date }\n </span>\n </div>\n </div>\n}\n"],"mappings":";;;;;AAyBA,MAAM,iBAAiB,MAAM,KAAK,EAAE,QAAQ,EAAC,IAAK,GAAG,MAAM,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC;AAkBnF,IAAa,2BAAb,MAAa,yBAAwB;CAC3B,OAAO,SAAS;CAGf,YAAY,UAAU,EAAE,yBAAyB;CACjD,UAAU,GAAG,KAAK,UAAS;CAC3B,UAAU,GAAG,KAAK,UAAS;CAC3B,UAAU,GAAG,KAAK,UAAS;CAEnB,QAAQ,OAAgC,UAAU;CAClD,YAAY,UAAyC,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CACjE,UAAU,UAAmC,WAAU,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAE/D,QAAQ,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACjB,cAAc,MAAM,qBAAoB,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CACxC,WAAW,MAAM,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CACvB,UAAU,MAAmB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAClC,UAAU,MAAmB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAElC,SAAS,MAAM,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAGvB,iBAAiB,MAAM,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAG/B,yBAAyB,MAAM,kBAAiB,GAAA,YAAA,CAAA,EAAA,WAAA,yBAAA,CAAA,+BAAA,CAAA,CAAA;CAGhD,qBAAqB,MAAM,cAAa,GAAA,YAAA,CAAA,EAAA,WAAA,qBAAA,CAAA,+BAAA,CAAA,CAAA;CAExC,cAAc,OAAM;CAEnB,SAAS,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CACtB,QAAQ,OAAkB;EAAE,OAAO;EAAM,KAAK;CAAI,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACrD,YAAY,OAAwB,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAC5C,cAAc,OAAoB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CACvC,eAAe,uBAAO,IAAI,KAAI,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG1B,eAAe,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG1B,aAAa,eAAe,KAAK,SAAQ,KAAM,KAAK,aAAY,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAE9E,iBAA0C,CAAiC;CAC3E,kBAA6B,CAAkC;CAEpD,eAAe,eAAc;EAC9C,MAAM,IAAI,KAAK,MAAK;EACpB,IAAI,CAAC,EAAE,OAAO,OAAO;EACrB,MAAM,SAAS,KAAK,OAAM;EAC1B,MAAM,OAAO,MAAY,EAAE,mBAAmB,QAAQ;GAAE,KAAK;GAAW,OAAO;GAAW,MAAM;EAAS,CAAE;EAC3G,IAAI,CAAC,EAAE,KAAK,OAAO,IAAI,EAAE,KAAK,IAAI;EAClC,OAAO,GAAG,IAAI,EAAE,KAAK,EAAC,KAAM,IAAI,EAAE,GAAG;CACvC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,aAAa,eAAc;EAE5C,OADU,KAAK,aACR,CAAA,CAAE,mBAAmB,KAAK,OAAM,GAAI;GAAE,OAAO;GAAQ,MAAM;EAAS,CAAE;CAC/E,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGiB,WAAW,eAAc;EAC1C,MAAM,SAAS,KAAK,OAAM;EAE1B,OAAO,eAAe,KAAK,MAAM,EAAE,mBAAmB,QAAQ,EAAE,SAAS,QAAO,CAAE,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC;CACxG,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,eAAe,eAAc;EAC9C,MAAM,UAAU,KAAK,aAAY;EACjC,MAAM,SAAS,KAAK,OAAM;EAC1B,MAAM,OAAO,QAAQ,YAAW;EAChC,MAAM,QAAQ,QAAQ,SAAQ;EAC9B,MAAM,WAAW,IAAI,KAAK,MAAM,OAAO,CAAC;EACxC,MAAM,UAAU,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;EAE3C,IAAI,WAAW,SAAS,OAAM;EAC9B,IAAI,aAAa,GAAG,WAAW;EAC/B,MAAM,aAAa,WAAW;EAE9B,MAAM,OAAiF,CAAA;EACvF,MAAM,QAAQ,MAAY,YACxB,KAAK,KAAK;GACR;GACA;GACA,UAAU,KAAK,eAAe,IAAI;GAClC,WAAW,KAAK,mBAAmB,QAAQ;IAAE,SAAS;IAAQ,KAAK;IAAW,OAAO;IAAQ,MAAM;GAAS,CAAE;EAC/G,CAAA;EAEH,KAAK,IAAI,IAAI,YAAY,IAAI,GAAG,KAC9B,KAAK,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK;EAG1C,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ,QAAO,GAAI,KACtC,KAAK,IAAI,KAAK,MAAM,OAAO,CAAC,GAAG,IAAI;EAGrC,MAAM,YAAY,KAAK,KAAK;EAC5B,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW,KAC9B,KAAK,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK;EAG1C,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEF,cAAA;EAGE,wBAAuB;GACrB,IAAI,CAAC,KAAK,OAAM,GAAI;GACpB,gBAAe;IACb,MAAM,QAAQ,KAAK,QAAO,CAAE,EAAE;IAC9B,IAAI,CAAC,SAAS,MAAM,SAAS,SAAS,aAAa,GAAG;IAKtD,CAHE,MAAM,cAAiC,oCAAoC,KAC3E,MAAM,cAAiC,oCAAoC,KAC3E,MAAM,cAAiC,wDAAwD,EACjG,EAAQ,MAAK;GACf,CAAC;EACH,CAAC;CACH;CAEA,WAAW,OAAuB;EAChC,KAAK,MAAM,IAAI,SAAS;GAAE,OAAO;GAAM,KAAK;EAAI,CAAE;EAClD,IAAI,OAAO,OACT,KAAK,aAAa,IAAI,IAAI,KAAK,MAAM,MAAM,YAAW,GAAI,MAAM,MAAM,SAAQ,GAAI,CAAC,CAAC;CAExF;CAEA,iBAAiB,IAA4B;EAAU,KAAK,WAAW;CAAI;CAC3E,kBAAkB,IAAc;EAAU,KAAK,YAAY;CAAI;CAE/D,iBAAiB,YAAmB;EAClC,KAAK,aAAa,IAAI,UAAU;EAChC,IAAI,YAAY,KAAK,OAAO,IAAI,KAAK;CACvC;CAEU,SAAM;EACd,IAAI,KAAK,WAAU,GAAI;EACvB,IAAI,KAAK,OAAM,GAAI;GACjB,KAAK,MAAK;GACV;EACF;EACA,KAAK,UAAU,IAAI,OAAO;EAC1B,KAAK,YAAY,IAAI,IAAI;EACzB,KAAK,OAAO,IAAI,IAAI;CACtB;CAEU,QAAK;EACb,IAAI,CAAC,KAAK,OAAM,GAAI;EACpB,KAAK,OAAO,IAAI,KAAK;EACrB,KAAK,UAAS;EAEd,IAAI,KAAK,MAAM,cAAc,SAAS,SAAS,aAAa,GAC1D,KAAK,UAAS,CAAE,EAAE,cAAc,MAAK;CAEzC;CAIU,UAAU,OAAoB;EACtC,IAAI,MAAM,QAAQ,YAAY,KAAK,OAAM,GAAI;GAC3C,KAAK,MAAK;GAEV,MAAM,gBAAe;GACrB,MAAM,eAAc;EACtB;CACF;CAGU,gBAAgB,OAAiB;EACzC,IAAI,KAAK,OAAM,KAAM,CAAC,KAAK,MAAM,cAAc,SAAS,MAAM,MAAc,GAC1E,KAAK,MAAK;CAEd;CAEU,YAAS;EACjB,KAAK,aAAa,QAAO,MAAK,IAAI,KAAK,EAAE,YAAW,GAAI,EAAE,SAAQ,IAAK,GAAG,CAAC,CAAC;CAC9E;CAEU,YAAS;EACjB,KAAK,aAAa,QAAO,MAAK,IAAI,KAAK,EAAE,YAAW,GAAI,EAAE,SAAQ,IAAK,GAAG,CAAC,CAAC;CAC9E;CAEU,WAAW,MAAU;EAC7B,IAAI,KAAK,eAAe,IAAI,GAAG;EAE/B,MAAM,UAAU,KAAK,MAAK;EAC1B,IAAI,KAAK,UAAS,MAAO,SAAS;GAChC,KAAK,MAAM,IAAI;IAAE,OAAO;IAAM,KAAK;GAAI,CAAE;GACzC,KAAK,UAAU,IAAI,KAAK;EAC1B,OAAO;GACL,IAAI,OAAO,QAAQ,OACjB,KAAK,MAAM,IAAI;IAAE,OAAO;IAAM,KAAK,QAAQ;GAAK,CAAE;QAElD,KAAK,MAAM,IAAI;IAAE,OAAO,QAAQ;IAAO,KAAK;GAAI,CAAE;GAEpD,KAAK,UAAU,IAAI,OAAO;GAC1B,KAAK,UAAS;GACd,KAAK,MAAK;EACZ;CACF;CAEU,WAAW,MAAU;EAC7B,IAAI,KAAK,UAAS,MAAO,OACvB,KAAK,YAAY,IAAI,IAAI;CAE7B;CAEU,QAAQ,MAAU;EAC1B,OAAO,KAAK,QAAQ,MAAM,KAAK,MAAK,CAAE,CAAC,KAAK;CAC9C;CAEU,MAAM,MAAU;EACxB,OAAO,KAAK,QAAQ,MAAM,KAAK,MAAK,CAAE,CAAC,GAAG;CAC5C;CAEU,UAAU,MAAU;EAC5B,MAAM,IAAI,KAAK,MAAK;EACpB,IAAI,CAAC,EAAE,OAAO,OAAO;EACrB,MAAM,MAAM,EAAE,OAAO,KAAK,YAAW;EACrC,IAAI,CAAC,KAAK,OAAO;EACjB,MAAM,IAAI,KAAK,IAAI,EAAE,MAAM,QAAO,GAAI,IAAI,QAAO,CAAE;EACnD,MAAM,IAAI,KAAK,IAAI,EAAE,MAAM,QAAO,GAAI,IAAI,QAAO,CAAE;EACnD,MAAM,IAAI,KAAK,QAAO;EACtB,OAAO,IAAI,KAAK,IAAI;CACtB;CAEU,QAAQ,MAAU;EAC1B,OAAO,KAAK,QAAQ,sBAAM,IAAI,KAAI,CAAE;CACtC;CAEU,WAAW,OAAY;EAC/B,MAAM,gBAAe;EACrB,IAAI,KAAK,WAAU,GAAI;EACvB,KAAK,MAAM,IAAI;GAAE,OAAO;GAAM,KAAK;EAAI,CAAE;EACzC,MAAM,IAAI;GAAE,OAAO;GAAM,KAAK;EAAI;EAClC,KAAK,SAAS,CAAC;EACf,KAAK,YAAY,KAAK,CAAC;CACzB;CAGQ,eAAe,MAAU;EAC/B,MAAM,MAAM,KAAK,QAAO;EACxB,MAAM,MAAM,KAAK,QAAO;EACxB,MAAM,MAAM,KAAK,SAAS,IAAI;EAC9B,IAAI,OAAO,MAAM,KAAK,SAAS,GAAG,GAAG,OAAO;EAC5C,IAAI,OAAO,MAAM,KAAK,SAAS,GAAG,GAAG,OAAO;EAC5C,OAAO;CACT;CAEQ,SAAS,GAAO;EACtB,OAAO,IAAI,KAAK,EAAE,YAAW,GAAI,EAAE,SAAQ,GAAI,EAAE,QAAO,CAAE,CAAC,CAAC,QAAO;CACrE;CAEQ,QAAQ,GAAgB,GAAc;EAC5C,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO;EACrB,OAAO,EAAE,YAAW,MAAO,EAAE,YAAW,KAAM,EAAE,SAAQ,MAAO,EAAE,SAAQ,KAAM,EAAE,QAAO,MAAO,EAAE,QAAO;CAC1G;CAEQ,YAAS;EACf,MAAM,IAAI,KAAK,MAAK;EACpB,KAAK,SAAS,CAAC;EACf,KAAK,YAAY,KAAK,CAAC;CACzB;CAxQW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAwB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAxB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAwB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,OAAA;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,UAAA;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,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,QAAA;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,wBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,oBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,aAAA,cAAA;EAAA,MAAA,EAAA,WAAA;GAAA,WAAA;GAAA,kBAAA;EAAA,EAAA;EAAA,WARxB,CACT;GACE,SAAS;GACT,aAAa,iBAAiB,wBAAwB;GACtD,OAAO;EACR,CACF;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,WAAA;GAAA,aAAA;GAAA,UAAA;EAAA,GAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,SAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UCzCH;EA8EA,QAAA,CAAA,24HAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDhDY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,eACR,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAYvC,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAwB,YAAA,CAAA;EAhBpC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAsB,YACpB;GAAI,SACP,CAAC,aAAa;GAAC,eACT,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;GAAC,iBAGjC,wBAAwB;GAAM,WACpC,CACT;IACE,SAAS;IACT,aAAa,iBAAgB,wBAAyB;IACtD,OAAO;GACR,CACF;GAAA,UAAA;GAAA,QAAA,CAAA,24HAAA;EAAA,CAAA;CAYqE,CAAA;CAAA,sBAAA,CAAA;CAAA,gBAAA;EAAA,WAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAW,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CACnB,WAAS,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;EAAA,OAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,SAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,gBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,wBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,oBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,aAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAA;EAAA,CAAA;EAAA,WAAA,CAAA;GAoJtE,MAAA;GAAa,MAAA,CAAA,WAAW,CAAC,QAAQ,CAAC;;;GAUlC,MAAA;GAAa,MAAA,CAAA,kBAAkB,CAAC,QAAQ,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { ChangeDetectionStrategy, Component, ElementRef, ViewEncapsulation, computed, effect, inject, input, signal, untracked } from "@angular/core";
|
|
3
3
|
import { DomSanitizer } from "@angular/platform-browser";
|
|
4
|
-
import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
|
|
4
|
+
import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
|
|
5
|
+
import { ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT, ICON_PLUS } from "@lifecorner/angular-ui-kit/icons";
|
|
5
6
|
import { SpinnerComponent } from "@lifecorner/angular-ui-kit/spinner";
|
|
6
7
|
import { CodeBlockComponent } from "@lifecorner/angular-ui-kit/code-block";
|
|
7
8
|
import { MarkdownComponent } from "@lifecorner/angular-ui-kit/markdown";
|
|
@@ -523,6 +524,12 @@ var DocumentViewerComponent = class DocumentViewerComponent {
|
|
|
523
524
|
]
|
|
524
525
|
}
|
|
525
526
|
],
|
|
527
|
+
viewProviders: [ɵprovideKitIcons([
|
|
528
|
+
ICON_PLUS,
|
|
529
|
+
ICON_EXCLAMATION_TRIANGLE,
|
|
530
|
+
ICON_FILE,
|
|
531
|
+
ICON_FILE_TEXT
|
|
532
|
+
])],
|
|
526
533
|
changeDetection: i0.ChangeDetectionStrategy.OnPush,
|
|
527
534
|
encapsulation: i0.ViewEncapsulation.None
|
|
528
535
|
});
|
|
@@ -543,6 +550,12 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
543
550
|
CodeBlockComponent,
|
|
544
551
|
MarkdownComponent
|
|
545
552
|
],
|
|
553
|
+
viewProviders: [ɵprovideKitIcons([
|
|
554
|
+
ICON_PLUS,
|
|
555
|
+
ICON_EXCLAMATION_TRIANGLE,
|
|
556
|
+
ICON_FILE,
|
|
557
|
+
ICON_FILE_TEXT
|
|
558
|
+
])],
|
|
546
559
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
547
560
|
encapsulation: ViewEncapsulation.None,
|
|
548
561
|
host: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-document-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { MarkdownComponent } from '@lifecorner/angular-ui-kit/markdown';\nimport { isSafeResourceUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport type DocumentType = 'pdf' | 'markdown' | 'image' | 'text' | 'code' | 'auto';\n\nconst EXTENSION_TYPE_MAP: Record<string, DocumentType> = {\n pdf: 'pdf',\n md: 'markdown',\n markdown: 'markdown',\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ts: 'code',\n js: 'code',\n tsx: 'code',\n jsx: 'code',\n py: 'code',\n java: 'code',\n html: 'code',\n css: 'code',\n scss: 'code',\n json: 'code',\n sh: 'code',\n bash: 'code',\n txt: 'text',\n log: 'text',\n csv: 'text',\n};\n\nconst EXTENSION_LANGUAGE_MAP: Record<string, CodeBlockLanguage> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n java: 'java',\n html: 'html',\n css: 'css',\n scss: 'scss',\n json: 'json',\n sh: 'bash',\n bash: 'bash',\n};\n\nconst TYPE_ICONS: Record<string, string> = {\n pdf: 'document-text',\n markdown: 'document-text',\n image: 'photo',\n text: 'document',\n code: 'code-bracket',\n unknown: 'document',\n};\n\n/**\n * Document viewer component for previewing various file types.\n *\n * Features:\n * - Auto-detects file type from URL extension or explicit type input\n * - PDF rendering via browser-native iframe viewer\n * - Markdown rendered through `lc-markdown` (escaped and sanitised — a fetched\n * `.md` file cannot inject markup)\n * - Image display with zoom controls (25% – 500%)\n * - Code display using the built-in code block component\n * - Plain text display\n * - Toolbar with filename, type badge, zoom, download, and fullscreen\n * - Loading and error states\n * - Dark/light theme support\n *\n * `src` is only ever embedded (iframe) or downloaded when it is a http(s),\n * blob: or relative URL — or a `data:application/pdf` — so a `javascript:` URL\n * handed in as a document can neither run in the frame nor be \"downloaded\".\n *\n * @example\n * ```html\n * <lc-document-viewer src=\"https://example.com/report.pdf\" />\n * <lc-document-viewer [content]=\"markdownString\" type=\"markdown\" filename=\"README.md\" />\n * <lc-document-viewer src=\"/assets/diagram.png\" />\n * ```\n */\n@Component({\n selector: 'lc-document-viewer',\n standalone: true,\n imports: [IconComponent, SpinnerComponent, CodeBlockComponent, MarkdownComponent],\n templateUrl: './document-viewer.component.html',\n styleUrls: ['./document-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '\"doc-viewer\"',\n '[style.height]': 'height()',\n },\n})\nexport class DocumentViewerComponent implements OnDestroy {\n /** URL of the document to display (for PDF, images, or remote files) */\n readonly src = input<string>('');\n\n /** Direct content string (for markdown, text, or code — takes precedence over src for content-based types) */\n readonly content = input<string>('');\n\n /** File type — set to 'auto' to detect from URL extension */\n readonly type = input<DocumentType>('auto');\n\n /** Display name shown in the toolbar */\n readonly filename = input<string>('');\n\n /** Code language for syntax highlighting (when type is 'code') */\n readonly language = input<CodeBlockLanguage>('text');\n\n /** Whether to display the toolbar */\n readonly showToolbar = input(true);\n\n /** Whether to show the download button in the toolbar */\n readonly showDownload = input(true);\n\n /** Height of the viewer container */\n readonly height = input('500px');\n\n /** Accessible name of the zoom-out button. */\n readonly zoomOutAriaLabel = input('Zoom out');\n\n /** Accessible name of the zoom-in button. */\n readonly zoomInAriaLabel = input('Zoom in');\n\n /** Accessible name of the reset-zoom button. */\n readonly resetZoomAriaLabel = input('Reset zoom');\n\n /** Accessible name of the toolbar download button. */\n readonly downloadAriaLabel = input('Download');\n\n /** Accessible name of the fullscreen button. */\n readonly fullscreenAriaLabel = input('Fullscreen');\n\n /** Accessible name of the fullscreen button while in fullscreen. */\n readonly exitFullscreenAriaLabel = input('Exit fullscreen');\n\n /** Shown while the document loads. */\n readonly loadingLabel = input('Loading document…');\n\n /** Title shown for file types without a preview. */\n readonly previewUnavailableTitle = input('Preview not available');\n\n /** Message shown for file types without a preview. */\n readonly previewUnavailableMessage = input('This file type cannot be previewed.');\n\n /** Label of the download button shown instead of a preview. */\n readonly downloadFileLabel = input('Download file');\n\n /** Toolbar name when neither `filename` nor a file name in `src` is available. */\n readonly untitledLabel = input('Document');\n\n /** Type badge for Markdown documents. */\n readonly markdownTypeLabel = input('Markdown');\n\n /** Type badge for images. */\n readonly imageTypeLabel = input('Image');\n\n /** Type badge for plain text. */\n readonly textTypeLabel = input('Text');\n\n /** Type badge for other files. */\n readonly fileTypeLabel = input('File');\n\n /** Prefix of the error shown when fetching the document fails. */\n readonly loadErrorLabel = input('Failed to load document:');\n\n /** Error shown when `src` is not an allowed URL (e.g. `javascript:`). */\n readonly blockedUrlMessage = input('This document URL cannot be displayed');\n\n // ── Internal state ───────────────────────────────────────────────────────\n\n protected loading = signal(true);\n protected error = signal<string | null>(null);\n protected fetchedContent = signal<string>('');\n protected zoom = signal(100);\n protected isFullscreen = signal(false);\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly elementRef = inject(ElementRef);\n private fullscreenHandler: (() => void) | null = null;\n\n // ── Computed values ──────────────────────────────────────────────────────\n\n /** Resolved document type from input or URL extension */\n protected resolvedType = computed<DocumentType | 'unknown'>(() => {\n const t = this.type();\n if (t !== 'auto') return t;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_TYPE_MAP[ext] ? EXTENSION_TYPE_MAP[ext] : 'unknown';\n });\n\n /** Display name for the toolbar */\n protected displayName = computed(() => {\n if (this.filename()) return this.filename();\n const src = this.src();\n if (!src) return this.untitledLabel();\n try {\n const url = new URL(src, 'http://localhost');\n const segments = url.pathname.split('/');\n return segments[segments.length - 1] || this.untitledLabel();\n } catch {\n return src.split('/').pop() || this.untitledLabel();\n }\n });\n\n /** Icon name for the document type */\n protected typeIcon = computed(() => TYPE_ICONS[this.resolvedType()] || TYPE_ICONS['unknown']);\n\n /** Type label for the toolbar badge */\n protected typeLabel = computed(() => {\n const t = this.resolvedType();\n switch (t) {\n case 'pdf': return 'PDF';\n case 'markdown': return this.markdownTypeLabel();\n case 'image': return this.imageTypeLabel();\n case 'text': return this.textTypeLabel();\n case 'code': return this.resolvedLanguage().toUpperCase();\n default: return this.fileTypeLabel();\n }\n });\n\n /** Resolved code language */\n protected resolvedLanguage = computed<CodeBlockLanguage>(() => {\n const lang = this.language();\n if (lang !== 'text') return lang;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_LANGUAGE_MAP[ext] ? EXTENSION_LANGUAGE_MAP[ext] : 'text';\n });\n\n /** Whether `src` may be embedded in a frame or downloaded at all. */\n protected srcEmbeddable = computed(() => isSafeResourceUrl(this.src()));\n\n /**\n * `src` as the iframe accepts it. Bypassing the resource-URL check is what\n * lets a PDF URL into an iframe at all; it is only done for URLs that passed\n * `isSafeResourceUrl` — anything else resolves to an empty frame.\n */\n protected safeSrc = computed<SafeResourceUrl>(() => {\n const src = this.src();\n return this.sanitizer.bypassSecurityTrustResourceUrl(this.srcEmbeddable() ? src : 'about:blank');\n });\n\n /** Effective content: direct content input or fetched content */\n protected effectiveContent = computed(() => {\n return this.content() || this.fetchedContent();\n });\n\n /** Image transform style for zoom */\n protected imageTransform = computed(() => {\n const z = this.zoom() / 100;\n return `scale(${z})`;\n });\n\n /** Whether zoom controls should be shown */\n protected showZoom = computed(() => {\n const t = this.resolvedType();\n return t === 'image';\n });\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n constructor() {\n // Load whenever the document changes — not just once. A new `src` starts a\n // fresh load (and cancels the one still in flight, so a slow old response\n // can't overwrite the new document), resets any earlier error, and re-enters\n // the loading state; a `content` string short-circuits all of that.\n effect((onCleanup) => {\n const type = this.resolvedType();\n const src = this.src();\n const content = this.content();\n const controller = typeof AbortController === 'function' ? new AbortController() : null;\n onCleanup(() => controller?.abort());\n untracked(() => this.load(type, src, content, controller));\n });\n }\n\n private load(\n type: DocumentType | 'unknown',\n src: string,\n content: string,\n controller: AbortController | null,\n ): void {\n this.error.set(null);\n this.fetchedContent.set('');\n\n if (content) {\n this.loading.set(false);\n return;\n }\n\n if (!src) {\n this.loading.set(false);\n this.error.set('No source or content provided');\n return;\n }\n\n // PDF and images are embedded directly — no fetch needed. They do have to be\n // embeddable, though: a `javascript:` (or other exotic) URL is refused up\n // front instead of being handed to an iframe.\n if (type === 'pdf' || type === 'image') {\n if (!this.srcEmbeddable()) {\n this.loading.set(false);\n this.error.set(this.blockedUrlMessage());\n return;\n }\n // Not \"loading\": the frame/img element only exists once loading is off,\n // and it is its own load event that would end the state — a deadlock.\n this.loading.set(false);\n return;\n }\n\n // Fetch text-based content\n if (type === 'markdown' || type === 'text' || type === 'code') {\n this.loading.set(true);\n this.fetchContent(src, controller);\n } else {\n this.loading.set(false);\n }\n }\n\n ngOnDestroy(): void {\n if (this.fullscreenHandler) {\n document.removeEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n protected zoomIn(): void {\n this.zoom.update(z => Math.min(z + 25, 500));\n }\n\n protected zoomOut(): void {\n this.zoom.update(z => Math.max(z - 25, 25));\n }\n\n protected resetZoom(): void {\n this.zoom.set(100);\n }\n\n protected download(): void {\n const src = this.src();\n // A download is a click the user never sees the target of — only URLs that\n // could be embedded are worth clicking.\n if (!src || !this.srcEmbeddable()) return;\n const a = document.createElement('a');\n a.href = src;\n a.download = this.displayName();\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n\n protected toggleFullscreen(): void {\n const el = this.elementRef.nativeElement as HTMLElement;\n if (!document.fullscreenElement) {\n el.requestFullscreen?.();\n this.isFullscreen.set(true);\n } else {\n document.exitFullscreen?.();\n this.isFullscreen.set(false);\n }\n\n if (!this.fullscreenHandler) {\n this.fullscreenHandler = () => {\n this.isFullscreen.set(!!document.fullscreenElement);\n };\n document.addEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n protected onIframeLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageError(): void {\n this.loading.set(false);\n this.error.set('Failed to load image');\n }\n\n // ── Private helpers ──────────────────────────────────────────────────────\n\n private extractExtension(url: string): string | null {\n if (!url) return null;\n try {\n const pathname = new URL(url, 'http://localhost').pathname;\n const parts = pathname.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : null;\n } catch {\n const parts = url.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase().split('?')[0] : null;\n }\n }\n\n private async fetchContent(url: string, controller: AbortController | null): Promise<void> {\n if (typeof fetch !== 'function') {\n this.loading.set(false);\n this.error.set('Failed to load document: fetch is not available');\n return;\n }\n try {\n const response = await fetch(url, controller ? { signal: controller.signal } : undefined);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const text = await response.text();\n // A response for a document that has since been replaced is dropped: the\n // effect that replaced it aborted this controller and started its own load.\n if (controller?.signal.aborted) return;\n this.fetchedContent.set(text);\n this.loading.set(false);\n } catch (err) {\n if (controller?.signal.aborted) return;\n this.loading.set(false);\n this.error.set(`${this.loadErrorLabel()} ${err instanceof Error ? err.message : 'Unknown error'}`);\n }\n }\n}\n","<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"doc-viewer__toolbar\">\n <div class=\"doc-viewer__toolbar-left\">\n <lc-icon [name]=\"typeIcon()\" size=\"sm\" />\n <span class=\"doc-viewer__filename\">{{ displayName() }}</span>\n <span class=\"doc-viewer__badge\">{{ typeLabel() }}</span>\n </div>\n <div class=\"doc-viewer__toolbar-right\">\n @if (showZoom()) {\n <div class=\"doc-viewer__zoom\">\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomOutAriaLabel()\"\n (click)=\"zoomOut()\"\n >\n <lc-icon name=\"minus\" size=\"xs\" />\n </button>\n <span class=\"doc-viewer__zoom-value\">{{ zoom() }}%</span>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomInAriaLabel()\"\n (click)=\"zoomIn()\"\n >\n <lc-icon name=\"plus\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"resetZoomAriaLabel()\"\n (click)=\"resetZoom()\"\n >\n <lc-icon name=\"arrows-pointing-out\" size=\"xs\" />\n </button>\n </div>\n }\n @if (showDownload() && src()) {\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"downloadAriaLabel()\"\n (click)=\"download()\"\n >\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"isFullscreen() ? exitFullscreenAriaLabel() : fullscreenAriaLabel()\"\n (click)=\"toggleFullscreen()\"\n >\n <lc-icon [name]=\"isFullscreen() ? 'arrows-pointing-in' : 'arrows-pointing-out'\" size=\"sm\" />\n </button>\n </div>\n </div>\n}\n\n<!-- Content area -->\n<div class=\"doc-viewer__content\">\n @if (loading()) {\n <div class=\"doc-viewer__loading\">\n <lc-spinner size=\"lg\" />\n <span>{{ loadingLabel() }}</span>\n </div>\n }\n\n @if (error(); as errorMsg) {\n <div class=\"doc-viewer__error\">\n <lc-icon name=\"exclamation-triangle\" size=\"lg\" color=\"var(--color-error-default)\" />\n <span>{{ errorMsg }}</span>\n </div>\n }\n\n @if (!loading() && !error()) {\n @switch (resolvedType()) {\n @case ('pdf') {\n <iframe\n class=\"doc-viewer__iframe\"\n [src]=\"safeSrc()\"\n [title]=\"displayName()\"\n type=\"application/pdf\"\n (load)=\"onIframeLoad()\"\n ></iframe>\n }\n\n @case ('image') {\n <div class=\"doc-viewer__image-container\">\n <img\n class=\"doc-viewer__image\"\n [src]=\"src()\"\n [alt]=\"displayName()\"\n [style.transform]=\"imageTransform()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n </div>\n }\n\n @case ('markdown') {\n <!-- lc-markdown escapes the source and runs Angular's sanitizer — the\n one markdown renderer in the kit, so its safety work isn't duplicated\n (or missed) here. -->\n <div class=\"doc-viewer__markdown\">\n <lc-markdown [content]=\"effectiveContent()\" linkTarget=\"_blank\" />\n </div>\n }\n\n @case ('code') {\n <lc-code-block\n [code]=\"effectiveContent()\"\n [language]=\"resolvedLanguage()\"\n [filename]=\"displayName()\"\n [showLineNumbers]=\"true\"\n [showHeader]=\"false\"\n />\n }\n\n @case ('text') {\n <pre class=\"doc-viewer__text\">{{ effectiveContent() }}</pre>\n }\n\n @default {\n <div class=\"doc-viewer__unsupported\">\n <lc-icon name=\"document\" size=\"lg\" />\n <span class=\"doc-viewer__unsupported-title\">{{ previewUnavailableTitle() }}</span>\n <span class=\"doc-viewer__unsupported-msg\">{{ previewUnavailableMessage() }}</span>\n @if (src()) {\n <button type=\"button\" class=\"doc-viewer__download-btn\" (click)=\"download()\">\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n {{ downloadFileLabel() }}\n </button>\n }\n </div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-document-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT, ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { MarkdownComponent } from '@lifecorner/angular-ui-kit/markdown';\nimport { isSafeResourceUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport type DocumentType = 'pdf' | 'markdown' | 'image' | 'text' | 'code' | 'auto';\n\nconst EXTENSION_TYPE_MAP: Record<string, DocumentType> = {\n pdf: 'pdf',\n md: 'markdown',\n markdown: 'markdown',\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ts: 'code',\n js: 'code',\n tsx: 'code',\n jsx: 'code',\n py: 'code',\n java: 'code',\n html: 'code',\n css: 'code',\n scss: 'code',\n json: 'code',\n sh: 'code',\n bash: 'code',\n txt: 'text',\n log: 'text',\n csv: 'text',\n};\n\nconst EXTENSION_LANGUAGE_MAP: Record<string, CodeBlockLanguage> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n java: 'java',\n html: 'html',\n css: 'css',\n scss: 'scss',\n json: 'json',\n sh: 'bash',\n bash: 'bash',\n};\n\nconst TYPE_ICONS: Record<string, string> = {\n pdf: 'document-text',\n markdown: 'document-text',\n image: 'photo',\n text: 'document',\n code: 'code-bracket',\n unknown: 'document',\n};\n\n/**\n * Document viewer component for previewing various file types.\n *\n * Features:\n * - Auto-detects file type from URL extension or explicit type input\n * - PDF rendering via browser-native iframe viewer\n * - Markdown rendered through `lc-markdown` (escaped and sanitised — a fetched\n * `.md` file cannot inject markup)\n * - Image display with zoom controls (25% – 500%)\n * - Code display using the built-in code block component\n * - Plain text display\n * - Toolbar with filename, type badge, zoom, download, and fullscreen\n * - Loading and error states\n * - Dark/light theme support\n *\n * `src` is only ever embedded (iframe) or downloaded when it is a http(s),\n * blob: or relative URL — or a `data:application/pdf` — so a `javascript:` URL\n * handed in as a document can neither run in the frame nor be \"downloaded\".\n *\n * @example\n * ```html\n * <lc-document-viewer src=\"https://example.com/report.pdf\" />\n * <lc-document-viewer [content]=\"markdownString\" type=\"markdown\" filename=\"README.md\" />\n * <lc-document-viewer src=\"/assets/diagram.png\" />\n * ```\n */\n@Component({\n selector: 'lc-document-viewer',\n standalone: true,\n imports: [IconComponent, SpinnerComponent, CodeBlockComponent, MarkdownComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS, ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT])],\n templateUrl: './document-viewer.component.html',\n styleUrls: ['./document-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '\"doc-viewer\"',\n '[style.height]': 'height()',\n },\n})\nexport class DocumentViewerComponent implements OnDestroy {\n /** URL of the document to display (for PDF, images, or remote files) */\n readonly src = input<string>('');\n\n /** Direct content string (for markdown, text, or code — takes precedence over src for content-based types) */\n readonly content = input<string>('');\n\n /** File type — set to 'auto' to detect from URL extension */\n readonly type = input<DocumentType>('auto');\n\n /** Display name shown in the toolbar */\n readonly filename = input<string>('');\n\n /** Code language for syntax highlighting (when type is 'code') */\n readonly language = input<CodeBlockLanguage>('text');\n\n /** Whether to display the toolbar */\n readonly showToolbar = input(true);\n\n /** Whether to show the download button in the toolbar */\n readonly showDownload = input(true);\n\n /** Height of the viewer container */\n readonly height = input('500px');\n\n /** Accessible name of the zoom-out button. */\n readonly zoomOutAriaLabel = input('Zoom out');\n\n /** Accessible name of the zoom-in button. */\n readonly zoomInAriaLabel = input('Zoom in');\n\n /** Accessible name of the reset-zoom button. */\n readonly resetZoomAriaLabel = input('Reset zoom');\n\n /** Accessible name of the toolbar download button. */\n readonly downloadAriaLabel = input('Download');\n\n /** Accessible name of the fullscreen button. */\n readonly fullscreenAriaLabel = input('Fullscreen');\n\n /** Accessible name of the fullscreen button while in fullscreen. */\n readonly exitFullscreenAriaLabel = input('Exit fullscreen');\n\n /** Shown while the document loads. */\n readonly loadingLabel = input('Loading document…');\n\n /** Title shown for file types without a preview. */\n readonly previewUnavailableTitle = input('Preview not available');\n\n /** Message shown for file types without a preview. */\n readonly previewUnavailableMessage = input('This file type cannot be previewed.');\n\n /** Label of the download button shown instead of a preview. */\n readonly downloadFileLabel = input('Download file');\n\n /** Toolbar name when neither `filename` nor a file name in `src` is available. */\n readonly untitledLabel = input('Document');\n\n /** Type badge for Markdown documents. */\n readonly markdownTypeLabel = input('Markdown');\n\n /** Type badge for images. */\n readonly imageTypeLabel = input('Image');\n\n /** Type badge for plain text. */\n readonly textTypeLabel = input('Text');\n\n /** Type badge for other files. */\n readonly fileTypeLabel = input('File');\n\n /** Prefix of the error shown when fetching the document fails. */\n readonly loadErrorLabel = input('Failed to load document:');\n\n /** Error shown when `src` is not an allowed URL (e.g. `javascript:`). */\n readonly blockedUrlMessage = input('This document URL cannot be displayed');\n\n // ── Internal state ───────────────────────────────────────────────────────\n\n protected loading = signal(true);\n protected error = signal<string | null>(null);\n protected fetchedContent = signal<string>('');\n protected zoom = signal(100);\n protected isFullscreen = signal(false);\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly elementRef = inject(ElementRef);\n private fullscreenHandler: (() => void) | null = null;\n\n // ── Computed values ──────────────────────────────────────────────────────\n\n /** Resolved document type from input or URL extension */\n protected resolvedType = computed<DocumentType | 'unknown'>(() => {\n const t = this.type();\n if (t !== 'auto') return t;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_TYPE_MAP[ext] ? EXTENSION_TYPE_MAP[ext] : 'unknown';\n });\n\n /** Display name for the toolbar */\n protected displayName = computed(() => {\n if (this.filename()) return this.filename();\n const src = this.src();\n if (!src) return this.untitledLabel();\n try {\n const url = new URL(src, 'http://localhost');\n const segments = url.pathname.split('/');\n return segments[segments.length - 1] || this.untitledLabel();\n } catch {\n return src.split('/').pop() || this.untitledLabel();\n }\n });\n\n /** Icon name for the document type */\n protected typeIcon = computed(() => TYPE_ICONS[this.resolvedType()] || TYPE_ICONS['unknown']);\n\n /** Type label for the toolbar badge */\n protected typeLabel = computed(() => {\n const t = this.resolvedType();\n switch (t) {\n case 'pdf': return 'PDF';\n case 'markdown': return this.markdownTypeLabel();\n case 'image': return this.imageTypeLabel();\n case 'text': return this.textTypeLabel();\n case 'code': return this.resolvedLanguage().toUpperCase();\n default: return this.fileTypeLabel();\n }\n });\n\n /** Resolved code language */\n protected resolvedLanguage = computed<CodeBlockLanguage>(() => {\n const lang = this.language();\n if (lang !== 'text') return lang;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_LANGUAGE_MAP[ext] ? EXTENSION_LANGUAGE_MAP[ext] : 'text';\n });\n\n /** Whether `src` may be embedded in a frame or downloaded at all. */\n protected srcEmbeddable = computed(() => isSafeResourceUrl(this.src()));\n\n /**\n * `src` as the iframe accepts it. Bypassing the resource-URL check is what\n * lets a PDF URL into an iframe at all; it is only done for URLs that passed\n * `isSafeResourceUrl` — anything else resolves to an empty frame.\n */\n protected safeSrc = computed<SafeResourceUrl>(() => {\n const src = this.src();\n return this.sanitizer.bypassSecurityTrustResourceUrl(this.srcEmbeddable() ? src : 'about:blank');\n });\n\n /** Effective content: direct content input or fetched content */\n protected effectiveContent = computed(() => {\n return this.content() || this.fetchedContent();\n });\n\n /** Image transform style for zoom */\n protected imageTransform = computed(() => {\n const z = this.zoom() / 100;\n return `scale(${z})`;\n });\n\n /** Whether zoom controls should be shown */\n protected showZoom = computed(() => {\n const t = this.resolvedType();\n return t === 'image';\n });\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n constructor() {\n // Load whenever the document changes — not just once. A new `src` starts a\n // fresh load (and cancels the one still in flight, so a slow old response\n // can't overwrite the new document), resets any earlier error, and re-enters\n // the loading state; a `content` string short-circuits all of that.\n effect((onCleanup) => {\n const type = this.resolvedType();\n const src = this.src();\n const content = this.content();\n const controller = typeof AbortController === 'function' ? new AbortController() : null;\n onCleanup(() => controller?.abort());\n untracked(() => this.load(type, src, content, controller));\n });\n }\n\n private load(\n type: DocumentType | 'unknown',\n src: string,\n content: string,\n controller: AbortController | null,\n ): void {\n this.error.set(null);\n this.fetchedContent.set('');\n\n if (content) {\n this.loading.set(false);\n return;\n }\n\n if (!src) {\n this.loading.set(false);\n this.error.set('No source or content provided');\n return;\n }\n\n // PDF and images are embedded directly — no fetch needed. They do have to be\n // embeddable, though: a `javascript:` (or other exotic) URL is refused up\n // front instead of being handed to an iframe.\n if (type === 'pdf' || type === 'image') {\n if (!this.srcEmbeddable()) {\n this.loading.set(false);\n this.error.set(this.blockedUrlMessage());\n return;\n }\n // Not \"loading\": the frame/img element only exists once loading is off,\n // and it is its own load event that would end the state — a deadlock.\n this.loading.set(false);\n return;\n }\n\n // Fetch text-based content\n if (type === 'markdown' || type === 'text' || type === 'code') {\n this.loading.set(true);\n this.fetchContent(src, controller);\n } else {\n this.loading.set(false);\n }\n }\n\n ngOnDestroy(): void {\n if (this.fullscreenHandler) {\n document.removeEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n protected zoomIn(): void {\n this.zoom.update(z => Math.min(z + 25, 500));\n }\n\n protected zoomOut(): void {\n this.zoom.update(z => Math.max(z - 25, 25));\n }\n\n protected resetZoom(): void {\n this.zoom.set(100);\n }\n\n protected download(): void {\n const src = this.src();\n // A download is a click the user never sees the target of — only URLs that\n // could be embedded are worth clicking.\n if (!src || !this.srcEmbeddable()) return;\n const a = document.createElement('a');\n a.href = src;\n a.download = this.displayName();\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n\n protected toggleFullscreen(): void {\n const el = this.elementRef.nativeElement as HTMLElement;\n if (!document.fullscreenElement) {\n el.requestFullscreen?.();\n this.isFullscreen.set(true);\n } else {\n document.exitFullscreen?.();\n this.isFullscreen.set(false);\n }\n\n if (!this.fullscreenHandler) {\n this.fullscreenHandler = () => {\n this.isFullscreen.set(!!document.fullscreenElement);\n };\n document.addEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n protected onIframeLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageError(): void {\n this.loading.set(false);\n this.error.set('Failed to load image');\n }\n\n // ── Private helpers ──────────────────────────────────────────────────────\n\n private extractExtension(url: string): string | null {\n if (!url) return null;\n try {\n const pathname = new URL(url, 'http://localhost').pathname;\n const parts = pathname.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : null;\n } catch {\n const parts = url.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase().split('?')[0] : null;\n }\n }\n\n private async fetchContent(url: string, controller: AbortController | null): Promise<void> {\n if (typeof fetch !== 'function') {\n this.loading.set(false);\n this.error.set('Failed to load document: fetch is not available');\n return;\n }\n try {\n const response = await fetch(url, controller ? { signal: controller.signal } : undefined);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const text = await response.text();\n // A response for a document that has since been replaced is dropped: the\n // effect that replaced it aborted this controller and started its own load.\n if (controller?.signal.aborted) return;\n this.fetchedContent.set(text);\n this.loading.set(false);\n } catch (err) {\n if (controller?.signal.aborted) return;\n this.loading.set(false);\n this.error.set(`${this.loadErrorLabel()} ${err instanceof Error ? err.message : 'Unknown error'}`);\n }\n }\n}\n","<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"doc-viewer__toolbar\">\n <div class=\"doc-viewer__toolbar-left\">\n <lc-icon [name]=\"typeIcon()\" size=\"sm\" />\n <span class=\"doc-viewer__filename\">{{ displayName() }}</span>\n <span class=\"doc-viewer__badge\">{{ typeLabel() }}</span>\n </div>\n <div class=\"doc-viewer__toolbar-right\">\n @if (showZoom()) {\n <div class=\"doc-viewer__zoom\">\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomOutAriaLabel()\"\n (click)=\"zoomOut()\"\n >\n <lc-icon name=\"minus\" size=\"xs\" />\n </button>\n <span class=\"doc-viewer__zoom-value\">{{ zoom() }}%</span>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomInAriaLabel()\"\n (click)=\"zoomIn()\"\n >\n <lc-icon name=\"plus\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"resetZoomAriaLabel()\"\n (click)=\"resetZoom()\"\n >\n <lc-icon name=\"arrows-pointing-out\" size=\"xs\" />\n </button>\n </div>\n }\n @if (showDownload() && src()) {\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"downloadAriaLabel()\"\n (click)=\"download()\"\n >\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"isFullscreen() ? exitFullscreenAriaLabel() : fullscreenAriaLabel()\"\n (click)=\"toggleFullscreen()\"\n >\n <lc-icon [name]=\"isFullscreen() ? 'arrows-pointing-in' : 'arrows-pointing-out'\" size=\"sm\" />\n </button>\n </div>\n </div>\n}\n\n<!-- Content area -->\n<div class=\"doc-viewer__content\">\n @if (loading()) {\n <div class=\"doc-viewer__loading\">\n <lc-spinner size=\"lg\" />\n <span>{{ loadingLabel() }}</span>\n </div>\n }\n\n @if (error(); as errorMsg) {\n <div class=\"doc-viewer__error\">\n <lc-icon name=\"exclamation-triangle\" size=\"lg\" color=\"var(--color-error-default)\" />\n <span>{{ errorMsg }}</span>\n </div>\n }\n\n @if (!loading() && !error()) {\n @switch (resolvedType()) {\n @case ('pdf') {\n <iframe\n class=\"doc-viewer__iframe\"\n [src]=\"safeSrc()\"\n [title]=\"displayName()\"\n type=\"application/pdf\"\n (load)=\"onIframeLoad()\"\n ></iframe>\n }\n\n @case ('image') {\n <div class=\"doc-viewer__image-container\">\n <img\n class=\"doc-viewer__image\"\n [src]=\"src()\"\n [alt]=\"displayName()\"\n [style.transform]=\"imageTransform()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n </div>\n }\n\n @case ('markdown') {\n <!-- lc-markdown escapes the source and runs Angular's sanitizer — the\n one markdown renderer in the kit, so its safety work isn't duplicated\n (or missed) here. -->\n <div class=\"doc-viewer__markdown\">\n <lc-markdown [content]=\"effectiveContent()\" linkTarget=\"_blank\" />\n </div>\n }\n\n @case ('code') {\n <lc-code-block\n [code]=\"effectiveContent()\"\n [language]=\"resolvedLanguage()\"\n [filename]=\"displayName()\"\n [showLineNumbers]=\"true\"\n [showHeader]=\"false\"\n />\n }\n\n @case ('text') {\n <pre class=\"doc-viewer__text\">{{ effectiveContent() }}</pre>\n }\n\n @default {\n <div class=\"doc-viewer__unsupported\">\n <lc-icon name=\"document\" size=\"lg\" />\n <span class=\"doc-viewer__unsupported-title\">{{ previewUnavailableTitle() }}</span>\n <span class=\"doc-viewer__unsupported-msg\">{{ previewUnavailableMessage() }}</span>\n @if (src()) {\n <button type=\"button\" class=\"doc-viewer__download-btn\" (click)=\"download()\">\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n {{ downloadFileLabel() }}\n </button>\n }\n </div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
|
|
@@ -2,7 +2,8 @@ import * as i0 from "@angular/core";
|
|
|
2
2
|
import { ChangeDetectionStrategy, Component, HostListener, computed, effect, inject, input, output, signal, untracked } from "@angular/core";
|
|
3
3
|
import * as i1 from "@angular/cdk/a11y";
|
|
4
4
|
import { A11yModule } from "@angular/cdk/a11y";
|
|
5
|
-
import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
|
|
5
|
+
import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
|
|
6
|
+
import { ICON_X_MARK } from "@lifecorner/angular-ui-kit/icons";
|
|
6
7
|
import { OverlayStackService } from "@lifecorner/angular-ui-kit/internal/overlay";
|
|
7
8
|
const CLOSE_ANIMATION_MS = 250;
|
|
8
9
|
var DrawerComponent = class DrawerComponent {
|
|
@@ -200,6 +201,7 @@ var DrawerComponent = class DrawerComponent {
|
|
|
200
201
|
]
|
|
201
202
|
}
|
|
202
203
|
],
|
|
204
|
+
viewProviders: [ɵprovideKitIcons([ICON_X_MARK])],
|
|
203
205
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
204
206
|
});
|
|
205
207
|
};
|
|
@@ -214,6 +216,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
214
216
|
selector: "lc-drawer",
|
|
215
217
|
standalone: true,
|
|
216
218
|
imports: [A11yModule, IconComponent],
|
|
219
|
+
viewProviders: [ɵprovideKitIcons([ICON_X_MARK])],
|
|
217
220
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
218
221
|
template: "@if (_visible()) {\n <div class=\"lc-drawer\">\n <!-- Overlay -->\n @if (hasOverlay()) {\n <div\n class=\"lc-drawer__overlay\"\n [class.lc-drawer__overlay--visible]=\"open()\"\n (click)=\"onOverlayClick($event)\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <!-- Panel. Focus moves in on open and back to the opener when the panel\n is torn down (cdkTrapFocusAutoCapture); focus is only trapped while\n the backdrop makes the page behind inert. -->\n <aside\n [class]=\"panelClasses()\"\n [style.width]=\"widthValue()\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel() || heading() || 'Drawer'\"\n [attr.aria-modal]=\"hasOverlay() ? 'true' : null\"\n [cdkTrapFocus]=\"hasOverlay()\"\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n <!-- Header -->\n @if (heading()) {\n <header class=\"lc-drawer__header\">\n <h2 class=\"lc-drawer__heading\">{{ heading() }}</h2>\n <button\n type=\"button\"\n class=\"lc-drawer__close\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n </header>\n } @else {\n <button\n type=\"button\"\n class=\"lc-drawer__close lc-drawer__close--floating\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n }\n\n <!-- Projected content -->\n <div class=\"lc-drawer__body\">\n <ng-content />\n </div>\n </aside>\n </div>\n}\n",
|
|
219
222
|
styles: ["@charset \"UTF-8\";.lc-drawer{position:fixed;inset:0;z-index:1100;pointer-events:none}.lc-drawer__overlay{position:absolute;inset:0;background:#0000;transition:background .25s ease;pointer-events:auto;cursor:pointer}.lc-drawer__overlay--visible{background:#00000073}.lc-drawer__panel{position:absolute;top:0;bottom:0;display:flex;flex-direction:column;background-color:var(--color-surface);background-image:var(--gradient-surface);box-shadow:var(--elevation-4);pointer-events:auto;transition:transform .25s cubic-bezier(.4,0,.2,1);max-width:90vw;overflow:hidden}.lc-drawer__panel--right{right:0;transform:translate(100%);border-left:1px solid var(--color-border-strong)}.lc-drawer__panel--left{left:0;transform:translate(-100%);border-right:1px solid var(--color-border-strong)}.lc-drawer__panel--open{transform:translate(0)}.lc-drawer__header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1.25rem;border-bottom:1px solid var(--color-divider);flex-shrink:0}.lc-drawer__heading{margin:0;font-size:1rem;font-weight:600;color:var(--color-text-primary);letter-spacing:-.01em}.lc-drawer__close{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;background:none;border:none;border-radius:var(--border-radius-md, .375rem);color:var(--color-text-tertiary);cursor:pointer;transition:background .15s,color .15s}.lc-drawer__close:hover{background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-drawer__close:focus-visible{outline:2px solid var(--color-primary-500, #208497);outline-offset:2px}.lc-drawer__close--floating{position:absolute;top:.75rem;right:.75rem;z-index:1}.lc-drawer__body{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch}@media(max-width:640px){.lc-drawer__panel{max-width:100vw;width:100vw!important}}\n"]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecorner-angular-ui-kit-drawer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/drawer/drawer.component.ts","../../../../libs/angular-ui-kit/src/lib/drawer/drawer.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n effect,\n untracked,\n inject,\n HostListener,\n OnDestroy,\n} from '@angular/core';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\nimport { OverlayStackService } from '@lifecorner/angular-ui-kit/internal/overlay';\n\nexport type DrawerPosition = 'left' | 'right';\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/** Duration of the slide-out transition (see drawer.component.scss). */\nconst CLOSE_ANIMATION_MS = 250;\n\n/**\n * Drawer component for slide-out overlay panels.\n *\n * Features:\n * - Slide-in from left or right\n * - Size presets (sm, md, lg, xl)\n * - Optional backdrop overlay with click-to-close\n * - Close on Escape key support\n * - Heading text display\n * - Content projection for arbitrary body content\n * - Accessible with ARIA dialog role, focus trap and focus restore\n *\n * @example\n * ```html\n * <lc-drawer [open]=\"showPanel\" position=\"right\" size=\"md\"\n * heading=\"Settings\" (closed)=\"showPanel = false\">\n * <p>Any content goes here</p>\n * </lc-drawer>\n * ```\n */\n@Component({\n selector: 'lc-drawer',\n standalone: true,\n imports: [A11yModule, IconComponent],\n templateUrl: './drawer.component.html',\n styleUrl: './drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DrawerComponent implements OnDestroy {\n private static nextId = 0;\n\n /** Whether the drawer is open. */\n readonly open = input<boolean>(false);\n\n /** Slide-in direction. */\n readonly position = input<DrawerPosition>('right');\n\n /** Predefined width (sm=320, md=400, lg=520, xl=640). */\n readonly size = input<DrawerSize>('md');\n\n /** Optional heading shown in the drawer header. */\n readonly heading = input<string>('');\n\n /** Whether the overlay backdrop is shown. */\n readonly hasOverlay = input<boolean>(true);\n\n /** Whether clicking the overlay closes the drawer. */\n readonly closeOnOverlayClick = input<boolean>(true);\n\n /** Whether pressing Escape closes the drawer. */\n readonly closeOnEscape = input<boolean>(true);\n\n /** ARIA label for assistive technology. */\n readonly ariaLabel = input<string>('');\n\n /** Accessible name of the close button. */\n readonly closeAriaLabel = input('Close drawer');\n\n /** Emitted when the drawer should close. */\n readonly closed = output<void>();\n\n /** Internal visibility — stays true during the slide-out animation. */\n protected _visible = signal(false);\n\n /** Width token → CSS. */\n protected readonly widthValue = computed(() => {\n const map: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '520px',\n xl: '640px',\n };\n return map[this.size()];\n });\n\n protected readonly panelClasses = computed(() => {\n const cls = ['lc-drawer__panel', `lc-drawer__panel--${this.position()}`];\n if (this.open()) cls.push('lc-drawer__panel--open');\n return cls.join(' ');\n });\n\n private readonly overlayStack = inject(OverlayStackService);\n /** Identifies this instance in the overlay stack. */\n private readonly drawerId = `lc-drawer-${++DrawerComponent.nextId}`;\n private closeTimer?: ReturnType<typeof setTimeout>;\n private originalOverflow?: string;\n\n constructor() {\n effect(() => {\n const isOpen = this.open();\n untracked(() => {\n if (isOpen) {\n // Re-opening within the close animation must cancel the pending hide,\n // otherwise the stale timer hides the drawer and unlocks scroll while\n // `open()` is still true.\n this.clearCloseTimer();\n this._visible.set(true);\n this.lockScroll();\n this.overlayStack.push(this.drawerId);\n } else if (this._visible()) {\n this.overlayStack.remove(this.drawerId);\n // Keep the panel mounted until the slide-out transition has finished\n this.closeTimer = setTimeout(() => {\n this.closeTimer = undefined;\n this._visible.set(false);\n this.unlockScroll();\n }, CLOSE_ANIMATION_MS);\n }\n });\n });\n }\n\n ngOnDestroy(): void {\n // Navigating away while open must not leave the page scroll-locked\n this.clearCloseTimer();\n this.overlayStack.remove(this.drawerId);\n this.unlockScroll();\n }\n\n /**\n * Handle backdrop click. Ignored while another overlay (menu, popover, …)\n * sits above this drawer — the click belongs to that layer.\n */\n protected onOverlayClick(event: MouseEvent): void {\n if (!this.overlayStack.claim(this.drawerId, event)) return;\n if (this.closeOnOverlayClick()) {\n this.close();\n }\n }\n\n /** Handle close action. */\n protected close(): void {\n this.closed.emit();\n }\n\n /**\n * Escape closes the drawer only while it is the top-most overlay; the event\n * is consumed either way so overlays underneath stay open.\n */\n @HostListener('document:keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape' || !this.open()) return;\n if (!this.overlayStack.claim(this.drawerId, event)) return;\n event.stopPropagation();\n if (this.closeOnEscape()) {\n this.close();\n }\n }\n\n private lockScroll(): void {\n if (typeof document === 'undefined' || this.originalOverflow !== undefined) return;\n this.originalOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n\n private unlockScroll(): void {\n if (typeof document === 'undefined' || this.originalOverflow === undefined) return;\n document.body.style.overflow = this.originalOverflow;\n this.originalOverflow = undefined;\n }\n\n private clearCloseTimer(): void {\n if (this.closeTimer !== undefined) {\n clearTimeout(this.closeTimer);\n this.closeTimer = undefined;\n }\n }\n}\n","@if (_visible()) {\n <div class=\"lc-drawer\">\n <!-- Overlay -->\n @if (hasOverlay()) {\n <div\n class=\"lc-drawer__overlay\"\n [class.lc-drawer__overlay--visible]=\"open()\"\n (click)=\"onOverlayClick($event)\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <!-- Panel. Focus moves in on open and back to the opener when the panel\n is torn down (cdkTrapFocusAutoCapture); focus is only trapped while\n the backdrop makes the page behind inert. -->\n <aside\n [class]=\"panelClasses()\"\n [style.width]=\"widthValue()\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel() || heading() || 'Drawer'\"\n [attr.aria-modal]=\"hasOverlay() ? 'true' : null\"\n [cdkTrapFocus]=\"hasOverlay()\"\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n <!-- Header -->\n @if (heading()) {\n <header class=\"lc-drawer__header\">\n <h2 class=\"lc-drawer__heading\">{{ heading() }}</h2>\n <button\n type=\"button\"\n class=\"lc-drawer__close\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n </header>\n } @else {\n <button\n type=\"button\"\n class=\"lc-drawer__close lc-drawer__close--floating\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n }\n\n <!-- Projected content -->\n <div class=\"lc-drawer__body\">\n <ng-content />\n </div>\n </aside>\n </div>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecorner-angular-ui-kit-drawer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/drawer/drawer.component.ts","../../../../libs/angular-ui-kit/src/lib/drawer/drawer.component.html"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n effect,\n untracked,\n inject,\n HostListener,\n OnDestroy,\n} from '@angular/core';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\nimport { OverlayStackService } from '@lifecorner/angular-ui-kit/internal/overlay';\n\nexport type DrawerPosition = 'left' | 'right';\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/** Duration of the slide-out transition (see drawer.component.scss). */\nconst CLOSE_ANIMATION_MS = 250;\n\n/**\n * Drawer component for slide-out overlay panels.\n *\n * Features:\n * - Slide-in from left or right\n * - Size presets (sm, md, lg, xl)\n * - Optional backdrop overlay with click-to-close\n * - Close on Escape key support\n * - Heading text display\n * - Content projection for arbitrary body content\n * - Accessible with ARIA dialog role, focus trap and focus restore\n *\n * @example\n * ```html\n * <lc-drawer [open]=\"showPanel\" position=\"right\" size=\"md\"\n * heading=\"Settings\" (closed)=\"showPanel = false\">\n * <p>Any content goes here</p>\n * </lc-drawer>\n * ```\n */\n@Component({\n selector: 'lc-drawer',\n standalone: true,\n imports: [A11yModule, IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_X_MARK])],\n templateUrl: './drawer.component.html',\n styleUrl: './drawer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DrawerComponent implements OnDestroy {\n private static nextId = 0;\n\n /** Whether the drawer is open. */\n readonly open = input<boolean>(false);\n\n /** Slide-in direction. */\n readonly position = input<DrawerPosition>('right');\n\n /** Predefined width (sm=320, md=400, lg=520, xl=640). */\n readonly size = input<DrawerSize>('md');\n\n /** Optional heading shown in the drawer header. */\n readonly heading = input<string>('');\n\n /** Whether the overlay backdrop is shown. */\n readonly hasOverlay = input<boolean>(true);\n\n /** Whether clicking the overlay closes the drawer. */\n readonly closeOnOverlayClick = input<boolean>(true);\n\n /** Whether pressing Escape closes the drawer. */\n readonly closeOnEscape = input<boolean>(true);\n\n /** ARIA label for assistive technology. */\n readonly ariaLabel = input<string>('');\n\n /** Accessible name of the close button. */\n readonly closeAriaLabel = input('Close drawer');\n\n /** Emitted when the drawer should close. */\n readonly closed = output<void>();\n\n /** Internal visibility — stays true during the slide-out animation. */\n protected _visible = signal(false);\n\n /** Width token → CSS. */\n protected readonly widthValue = computed(() => {\n const map: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '520px',\n xl: '640px',\n };\n return map[this.size()];\n });\n\n protected readonly panelClasses = computed(() => {\n const cls = ['lc-drawer__panel', `lc-drawer__panel--${this.position()}`];\n if (this.open()) cls.push('lc-drawer__panel--open');\n return cls.join(' ');\n });\n\n private readonly overlayStack = inject(OverlayStackService);\n /** Identifies this instance in the overlay stack. */\n private readonly drawerId = `lc-drawer-${++DrawerComponent.nextId}`;\n private closeTimer?: ReturnType<typeof setTimeout>;\n private originalOverflow?: string;\n\n constructor() {\n effect(() => {\n const isOpen = this.open();\n untracked(() => {\n if (isOpen) {\n // Re-opening within the close animation must cancel the pending hide,\n // otherwise the stale timer hides the drawer and unlocks scroll while\n // `open()` is still true.\n this.clearCloseTimer();\n this._visible.set(true);\n this.lockScroll();\n this.overlayStack.push(this.drawerId);\n } else if (this._visible()) {\n this.overlayStack.remove(this.drawerId);\n // Keep the panel mounted until the slide-out transition has finished\n this.closeTimer = setTimeout(() => {\n this.closeTimer = undefined;\n this._visible.set(false);\n this.unlockScroll();\n }, CLOSE_ANIMATION_MS);\n }\n });\n });\n }\n\n ngOnDestroy(): void {\n // Navigating away while open must not leave the page scroll-locked\n this.clearCloseTimer();\n this.overlayStack.remove(this.drawerId);\n this.unlockScroll();\n }\n\n /**\n * Handle backdrop click. Ignored while another overlay (menu, popover, …)\n * sits above this drawer — the click belongs to that layer.\n */\n protected onOverlayClick(event: MouseEvent): void {\n if (!this.overlayStack.claim(this.drawerId, event)) return;\n if (this.closeOnOverlayClick()) {\n this.close();\n }\n }\n\n /** Handle close action. */\n protected close(): void {\n this.closed.emit();\n }\n\n /**\n * Escape closes the drawer only while it is the top-most overlay; the event\n * is consumed either way so overlays underneath stay open.\n */\n @HostListener('document:keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape' || !this.open()) return;\n if (!this.overlayStack.claim(this.drawerId, event)) return;\n event.stopPropagation();\n if (this.closeOnEscape()) {\n this.close();\n }\n }\n\n private lockScroll(): void {\n if (typeof document === 'undefined' || this.originalOverflow !== undefined) return;\n this.originalOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n\n private unlockScroll(): void {\n if (typeof document === 'undefined' || this.originalOverflow === undefined) return;\n document.body.style.overflow = this.originalOverflow;\n this.originalOverflow = undefined;\n }\n\n private clearCloseTimer(): void {\n if (this.closeTimer !== undefined) {\n clearTimeout(this.closeTimer);\n this.closeTimer = undefined;\n }\n }\n}\n","@if (_visible()) {\n <div class=\"lc-drawer\">\n <!-- Overlay -->\n @if (hasOverlay()) {\n <div\n class=\"lc-drawer__overlay\"\n [class.lc-drawer__overlay--visible]=\"open()\"\n (click)=\"onOverlayClick($event)\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <!-- Panel. Focus moves in on open and back to the opener when the panel\n is torn down (cdkTrapFocusAutoCapture); focus is only trapped while\n the backdrop makes the page behind inert. -->\n <aside\n [class]=\"panelClasses()\"\n [style.width]=\"widthValue()\"\n role=\"dialog\"\n [attr.aria-label]=\"ariaLabel() || heading() || 'Drawer'\"\n [attr.aria-modal]=\"hasOverlay() ? 'true' : null\"\n [cdkTrapFocus]=\"hasOverlay()\"\n [cdkTrapFocusAutoCapture]=\"true\"\n >\n <!-- Header -->\n @if (heading()) {\n <header class=\"lc-drawer__header\">\n <h2 class=\"lc-drawer__heading\">{{ heading() }}</h2>\n <button\n type=\"button\"\n class=\"lc-drawer__close\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n </header>\n } @else {\n <button\n type=\"button\"\n class=\"lc-drawer__close lc-drawer__close--floating\"\n (click)=\"close()\"\n [attr.aria-label]=\"closeAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" size=\"sm\" />\n </button>\n }\n\n <!-- Projected content -->\n <div class=\"lc-drawer__body\">\n <ng-content />\n </div>\n </aside>\n </div>\n}\n"],"mappings":";;;;;;;AAsBA,MAAM,qBAAqB;AA+B3B,IAAa,kBAAb,MAAa,gBAAe;CAClB,OAAO,SAAS;CAGf,OAAO,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,WAAW,MAAsB,SAAQ,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAGzC,OAAO,MAAkB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAG9B,UAAU,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAG3B,aAAa,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAGjC,sBAAsB,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,sBAAA,CAAA,+BAAA,CAAA,CAAA;CAG1C,gBAAgB,MAAe,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAGpC,YAAY,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAG7B,iBAAiB,MAAM,gBAAe,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAGtC,SAAS,OAAM;CAGd,WAAW,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAGf,aAAa,eAAc;EAO5C,OAAO;GALL,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EAEC,EAAI,KAAK,KAAI;CACtB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,eAAe,eAAc;EAC9C,MAAM,MAAM,CAAC,oBAAoB,qBAAqB,KAAK,SAAQ,GAAI;EACvE,IAAI,KAAK,KAAI,GAAI,IAAI,KAAK,wBAAwB;EAClD,OAAO,IAAI,KAAK,GAAG;CACrB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEe,eAAe,OAAO,mBAAmB;CAEzC,WAAW,aAAa,EAAE,gBAAgB;CACnD;CACA;CAER,cAAA;EACE,aAAY;GACV,MAAM,SAAS,KAAK,KAAI;GACxB,gBAAe;IACb,IAAI,QAAQ;KAIV,KAAK,gBAAe;KACpB,KAAK,SAAS,IAAI,IAAI;KACtB,KAAK,WAAU;KACf,KAAK,aAAa,KAAK,KAAK,QAAQ;IACtC,OAAO,IAAI,KAAK,SAAQ,GAAI;KAC1B,KAAK,aAAa,OAAO,KAAK,QAAQ;KAEtC,KAAK,aAAa,iBAAgB;MAChC,KAAK,aAAa;MAClB,KAAK,SAAS,IAAI,KAAK;MACvB,KAAK,aAAY;KACnB,GAAG,kBAAkB;IACvB;GACF,CAAC;EACH,CAAC;CACH;CAEA,cAAW;EAET,KAAK,gBAAe;EACpB,KAAK,aAAa,OAAO,KAAK,QAAQ;EACtC,KAAK,aAAY;CACnB;CAMU,eAAe,OAAiB;EACxC,IAAI,CAAC,KAAK,aAAa,MAAM,KAAK,UAAU,KAAK,GAAG;EACpD,IAAI,KAAK,oBAAmB,GAC1B,KAAK,MAAK;CAEd;CAGU,QAAK;EACb,KAAK,OAAO,KAAI;CAClB;CAOU,UAAU,OAAoB;EACtC,IAAI,MAAM,QAAQ,YAAY,CAAC,KAAK,KAAI,GAAI;EAC5C,IAAI,CAAC,KAAK,aAAa,MAAM,KAAK,UAAU,KAAK,GAAG;EACpD,MAAM,gBAAe;EACrB,IAAI,KAAK,cAAa,GACpB,KAAK,MAAK;CAEd;CAEQ,aAAU;EAChB,IAAI,OAAO,aAAa,eAAe,KAAK,qBAAqB,QAAW;EAC5E,KAAK,mBAAmB,SAAS,KAAK,MAAM;EAC5C,SAAS,KAAK,MAAM,WAAW;CACjC;CAEQ,eAAY;EAClB,IAAI,OAAO,aAAa,eAAe,KAAK,qBAAqB,QAAW;EAC5E,SAAS,KAAK,MAAM,WAAW,KAAK;EACpC,KAAK,mBAAmB;CAC1B;CAEQ,kBAAe;EACrB,IAAI,KAAK,eAAe,QAAW;GACjC,aAAa,KAAK,UAAU;GAC5B,KAAK,aAAa;EACpB;CACF;CA1IW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAe,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAf,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAe,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;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,SAAA;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,qBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,eAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,WAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,gBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA,EAAA,QAAA,SAAA;EAAA,MAAA,EAAA,WAAA,EAAA,oBAAA,oBAAA,EAAA;EAAA,UAAA;EAAA,UCrD5B;EAuDA,QAAA,CAAA,ixDAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MDRY;GAAU;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA,CAAA,gBAAA,yBAAA;IAAA,UAAA,CAAA,cAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAa,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;EAAA;EAAA,eACpB,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKrC,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAe,YAAA,CAAA;EAT3B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAW,YACT;GAAI,SACP,CAAC,YAAY,aAAa;GAAC,eACrB,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;GAAC,iBAG/B,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,ixDAAA;EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiH9C,MAAA;GAAa,MAAA,CAAA,oBAAoB,CAAC,QAAQ,CAAC"}
|
|
@@ -3,6 +3,8 @@ import { ChangeDetectionStrategy, Component, computed, effect, forwardRef, input
|
|
|
3
3
|
import { CommonModule } from "@angular/common";
|
|
4
4
|
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
5
5
|
import { InputComponent } from "@lifecorner/angular-ui-kit/input";
|
|
6
|
+
import { ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
|
|
7
|
+
import { ICON_MAIL } from "@lifecorner/angular-ui-kit/icons";
|
|
6
8
|
var EmailInputComponent = class EmailInputComponent {
|
|
7
9
|
label = input("Email", ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
|
|
8
10
|
placeholder = input("you@example.com", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
|
|
@@ -307,6 +309,7 @@ var EmailInputComponent = class EmailInputComponent {
|
|
|
307
309
|
"enterPressed"
|
|
308
310
|
]
|
|
309
311
|
}],
|
|
312
|
+
viewProviders: [ɵprovideKitIcons([ICON_MAIL])],
|
|
310
313
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
311
314
|
});
|
|
312
315
|
};
|
|
@@ -320,6 +323,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
320
323
|
args: [{
|
|
321
324
|
selector: "lc-email-input",
|
|
322
325
|
imports: [CommonModule, InputComponent],
|
|
326
|
+
viewProviders: [ɵprovideKitIcons([ICON_MAIL])],
|
|
323
327
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
324
328
|
providers: [{
|
|
325
329
|
provide: NG_VALUE_ACCESSOR,
|
|
@@ -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';\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 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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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { ChangeDetectionStrategy, Component, computed, input, output, signal, viewChild } from "@angular/core";
|
|
3
|
-
import { IconComponent } from "@lifecorner/angular-ui-kit/icon";
|
|
3
|
+
import { IconComponent, ɵprovideKitIcons } from "@lifecorner/angular-ui-kit/icon";
|
|
4
|
+
import { ICON_FILE, ICON_X_MARK } from "@lifecorner/angular-ui-kit/icons";
|
|
4
5
|
var FileUploadComponent = class FileUploadComponent {
|
|
5
6
|
accept = input("", ...ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []);
|
|
6
7
|
multiple = input(false, ...ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []);
|
|
@@ -178,6 +179,7 @@ var FileUploadComponent = class FileUploadComponent {
|
|
|
178
179
|
"strict"
|
|
179
180
|
]
|
|
180
181
|
}],
|
|
182
|
+
viewProviders: [ɵprovideKitIcons([ICON_FILE, ICON_X_MARK])],
|
|
181
183
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
182
184
|
});
|
|
183
185
|
};
|
|
@@ -192,6 +194,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
192
194
|
selector: "lc-file-upload",
|
|
193
195
|
standalone: true,
|
|
194
196
|
imports: [IconComponent],
|
|
197
|
+
viewProviders: [ɵprovideKitIcons([ICON_FILE, ICON_X_MARK])],
|
|
195
198
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
196
199
|
template: "<div\n [class]=\"dropzoneClasses()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (click)=\"openFilePicker()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openFilePicker()\"\n (keydown.space)=\"openFilePicker()\"\n [attr.aria-label]=\"hint()\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <input\n #fileInput\n type=\"file\"\n class=\"file-upload__input\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <div class=\"file-upload__content\">\n <lc-icon name=\"cloud-arrow-up\" class=\"file-upload__icon\"></lc-icon>\n <p class=\"file-upload__hint\">{{ hint() }}</p>\n @if (accept()) {\n <p class=\"file-upload__accept\">{{ acceptedLabel() }} {{ accept() }}</p>\n }\n @if (maxSizeBytes() > 0) {\n <p class=\"file-upload__accept\">{{ maxSizeLabel() }} {{ formatSize(maxSizeBytes()) }}</p>\n }\n </div>\n</div>\n\n@if (selectedFiles().length > 0) {\n <ul class=\"file-upload__list\">\n @for (f of selectedFiles(); track f.name; let i = $index) {\n <li class=\"file-upload__file\">\n <lc-icon name=\"document\" class=\"file-upload__file-icon\"></lc-icon>\n <span class=\"file-upload__file-name\">{{ f.name }}</span>\n <span class=\"file-upload__file-size\">{{ formatSize(f.size) }}</span>\n <button\n type=\"button\"\n class=\"file-upload__file-remove\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"removeFileAriaLabel()\"\n >\n <lc-icon name=\"x-mark\"></lc-icon>\n </button>\n </li>\n }\n </ul>\n}\n",
|
|
197
200
|
styles: [":host{display:block;width:100%}.file-upload{display:flex;align-items:center;justify-content:center;padding:2rem 1.5rem;border:2px dashed var(--color-border-strong);border-radius:var(--border-radius-lg);background-color:var(--color-surface-2);cursor:pointer;transition:border-color .15s ease,background-color .15s ease;text-align:center}.file-upload:hover{border-color:var(--color-primary-500);background-color:var(--color-surface-hover)}.file-upload:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.file-upload--drag-over{border-color:var(--color-primary-500);background-color:var(--color-surface-selected)}.file-upload--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.file-upload__input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}.file-upload__content{display:flex;flex-direction:column;align-items:center;gap:.5rem}.file-upload__icon{--icon-size: 2rem;color:var(--color-primary-500)}.file-upload__hint{margin:0;font-size:.875rem;color:var(--color-text-secondary)}.file-upload__accept{margin:0;font-size:.75rem;color:var(--color-text-tertiary)}.file-upload__list{list-style:none;margin:.75rem 0 0;padding:0;display:flex;flex-direction:column;gap:.5rem}.file-upload__file{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-md);font-size:.8125rem}.file-upload__file-icon{--icon-size: 1rem;color:var(--color-text-tertiary);flex-shrink:0}.file-upload__file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.file-upload__file-size{flex-shrink:0;color:var(--color-text-tertiary);font-size:.75rem}.file-upload__file-remove{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:var(--color-surface-hover);cursor:pointer;padding:0;--icon-size: .75rem;color:var(--color-text-secondary)}.file-upload__file-remove:hover{background:#9d0e0e2e;color:var(--color-error)}\n"]
|