shirkasoft-ui-components 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs +199 -0
- package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-button.mjs +148 -0
- package/fesm2022/shirkasoft-ui-components-button.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-chart.mjs +153 -0
- package/fesm2022/shirkasoft-ui-components-chart.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs +143 -0
- package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-date-picker.mjs +638 -0
- package/fesm2022/shirkasoft-ui-components-date-picker.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-fieldset.mjs +94 -0
- package/fesm2022/shirkasoft-ui-components-fieldset.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-file-upload.mjs +242 -0
- package/fesm2022/shirkasoft-ui-components-file-upload.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-knob.mjs +269 -0
- package/fesm2022/shirkasoft-ui-components-knob.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-loading.mjs +255 -0
- package/fesm2022/shirkasoft-ui-components-loading.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-modal.mjs +290 -0
- package/fesm2022/shirkasoft-ui-components-modal.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-notification.mjs +143 -0
- package/fesm2022/shirkasoft-ui-components-notification.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-price-input.mjs +64 -0
- package/fesm2022/shirkasoft-ui-components-price-input.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs +205 -0
- package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-select.mjs +737 -0
- package/fesm2022/shirkasoft-ui-components-select.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-sidebar.mjs +185 -0
- package/fesm2022/shirkasoft-ui-components-sidebar.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-skeleton.mjs +74 -0
- package/fesm2022/shirkasoft-ui-components-skeleton.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-table.mjs +689 -0
- package/fesm2022/shirkasoft-ui-components-table.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-tabs.mjs +249 -0
- package/fesm2022/shirkasoft-ui-components-tabs.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-tag.mjs +70 -0
- package/fesm2022/shirkasoft-ui-components-tag.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-text-field.mjs +370 -0
- package/fesm2022/shirkasoft-ui-components-text-field.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-timeline.mjs +222 -0
- package/fesm2022/shirkasoft-ui-components-timeline.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-toggle.mjs +186 -0
- package/fesm2022/shirkasoft-ui-components-toggle.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-tooltip.mjs +169 -0
- package/fesm2022/shirkasoft-ui-components-tooltip.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-utils.mjs +64 -0
- package/fesm2022/shirkasoft-ui-components-utils.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components-wizard.mjs +167 -0
- package/fesm2022/shirkasoft-ui-components-wizard.mjs.map +1 -0
- package/fesm2022/shirkasoft-ui-components.mjs +0 -5729
- package/fesm2022/shirkasoft-ui-components.mjs.map +1 -1
- package/package.json +101 -1
- package/types/shirkasoft-ui-components-breadcrumb.d.ts +68 -0
- package/types/shirkasoft-ui-components-button.d.ts +59 -0
- package/types/shirkasoft-ui-components-chart.d.ts +65 -0
- package/types/shirkasoft-ui-components-confirm-dialog.d.ts +88 -0
- package/types/shirkasoft-ui-components-date-picker.d.ts +200 -0
- package/types/shirkasoft-ui-components-fieldset.d.ts +72 -0
- package/types/shirkasoft-ui-components-file-upload.d.ts +87 -0
- package/types/shirkasoft-ui-components-knob.d.ts +69 -0
- package/types/shirkasoft-ui-components-loading.d.ts +94 -0
- package/types/shirkasoft-ui-components-modal.d.ts +139 -0
- package/types/shirkasoft-ui-components-notification.d.ts +82 -0
- package/types/shirkasoft-ui-components-price-input.d.ts +41 -0
- package/types/shirkasoft-ui-components-rail-sidebar.d.ts +121 -0
- package/types/shirkasoft-ui-components-select.d.ts +182 -0
- package/types/shirkasoft-ui-components-sidebar.d.ts +106 -0
- package/types/shirkasoft-ui-components-skeleton.d.ts +36 -0
- package/types/shirkasoft-ui-components-table.d.ts +293 -0
- package/types/shirkasoft-ui-components-tabs.d.ts +102 -0
- package/types/shirkasoft-ui-components-tag.d.ts +31 -0
- package/types/shirkasoft-ui-components-text-field.d.ts +120 -0
- package/types/shirkasoft-ui-components-timeline.d.ts +178 -0
- package/types/shirkasoft-ui-components-toggle.d.ts +48 -0
- package/types/shirkasoft-ui-components-tooltip.d.ts +47 -0
- package/types/shirkasoft-ui-components-utils.d.ts +9 -0
- package/types/shirkasoft-ui-components-wizard.d.ts +101 -0
- package/types/shirkasoft-ui-components.d.ts +1 -2333
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shirkasoft-ui-components-date-picker.mjs","sources":["../../../projects/ui-components/date-picker/date-picker.component.ts","../../../projects/ui-components/date-picker/date-picker.component.html","../../../projects/ui-components/date-picker/shirkasoft-ui-components-date-picker.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n Component,\n ElementRef,\n DestroyRef,\n HostListener,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n computed,\n forwardRef,\n inject,\n input,\n signal,\n effect,\n} from '@angular/core';\nimport {\n ControlValueAccessor,\n FormControl,\n FormGroup,\n NG_VALUE_ACCESSOR,\n ReactiveFormsModule,\n} from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { TranslocoService } from '@jsverse/transloco';\n\n/** Which granularity the picker edits: a day, a month or a year. */\nexport type DatePickerView = 'date' | 'month' | 'year';\n\n/**\n * Shirkasoft UI Components.\n * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).\n * Belongs to Shirkasoft.\n */\n\n/**\n * Date/time picker implemented as a ControlValueAccessor. Supports choosing a\n * day, month or year, optional time selection (with seconds/12-hour format),\n * min/max constraints and locale-aware labels.\n */\n@Component({\n selector: 'shk-date-picker',\n standalone: true,\n imports: [CommonModule, ReactiveFormsModule],\n templateUrl: './date-picker.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DatePickerComponent),\n multi: true,\n },\n ],\n})\nexport class DatePickerComponent implements ControlValueAccessor {\n private elementRef = inject(ElementRef);\n private cdr = inject(ChangeDetectorRef);\n private destroyRef = inject(DestroyRef);\n private transloco = inject(TranslocoService);\n\n /** HTML id for the input (for label/for pairing). */\n id = input<string>('');\n /** Granularity of the picker ('date' | 'month' | 'year'). */\n view = input<DatePickerView>('date');\n /** Label shown above the input. */\n label = input<string>('');\n /** Placeholder of the input. */\n placeholder = input<string>('');\n /** FormGroup used to resolve the control from `label`. */\n formGroup = input<FormGroup | undefined>(undefined);\n /** Directly bound FormControl. */\n control = input<FormControl | undefined>(undefined);\n /** Disables the picker. */\n disabled = input<boolean>(false);\n /** Custom error message displayed (overrides translated ones). */\n errorMessage = input<string>('');\n /** Per-validation-key custom error messages. */\n errorMessages = input<{ [key: string]: string }>({});\n /** Minimum allowed date (YYYY-MM-DD) or year/month string. */\n minValue = input<string>('');\n /** Maximum allowed date (YYYY-MM-DD) or year/month string. */\n maxValue = input<string>('');\n /** Locale used for month/day names and formatted labels. */\n locale = input<string>('es');\n /** Edits only the time portion of the value. */\n timeOnly = input<boolean>(false);\n /** Shows the time selectors alongside the date. */\n showTime = input<boolean>(false);\n /** Hour format: '12' or '24'. */\n hourFormat = input<'12' | '24'>('24');\n /** Whether the seconds selector is shown. */\n showSeconds = input<boolean>(false);\n /** Step (in minutes) for the minute selector. */\n minuteStep = input<number>(1);\n\n /** Whether the dropdown calendar is open. */\n isOpen = signal(false);\n /** Year currently displayed in the calendar. */\n viewYear = signal(new Date().getFullYear());\n /** Month currently displayed in the calendar (0-based). */\n viewMonth = signal(new Date().getMonth());\n /** Starting year of the decade shown in year view. */\n viewDecade = signal(Math.floor(new Date().getFullYear() / 10) * 10);\n /** Hour selected in the time selectors. */\n viewHour = signal(0);\n /** Minute selected in the time selectors. */\n viewMinute = signal(0);\n /** Second selected in the time selectors. */\n viewSecond = signal(0);\n /** Meridiem selected in 12-hour format. */\n viewMeridiem = signal<'AM' | 'PM'>('AM');\n /** Date part selected but not yet committed until the time is confirmed. */\n private pendingDate = signal<string | null>(null);\n\n /** Underlying value (mirrors the reactive control). */\n private _value = signal<string | null>(null);\n /** Disabled state coming from the reactive form infrastructure. */\n private _disabledState = signal<boolean>(false);\n /** Whether status subscriptions have been attached to the control. */\n private subscribed = signal(false);\n private valueSubscribed = signal(false);\n /** Counter used to recompute the error helpers reactively. */\n private errorTracker = signal(0);\n /** Whether an error was displayed at some point. */\n hadError = signal(false);\n\n /** ControlValueAccessor callbacks. */\n private onChange: (value: any) => void = () => {};\n private onTouched: () => void = () => {};\n\n /** Current value of the picker. */\n value = computed(() => this._value());\n\n /** Whether the picker is disabled (input or reactive form). */\n isDisabled = computed(() => this.disabled() || this._disabledState());\n\n /** Resolves the control from `control` input or `formGroup`+`label`. */\n actualControl = computed(() => {\n const ctrl = this.control();\n if (ctrl) return ctrl;\n const fg = this.formGroup();\n const lbl = this.label();\n if (fg && lbl) return fg.get(lbl) as FormControl;\n return undefined;\n });\n\n /** Whether the bound control should show its error message. */\n shouldShowError = computed(() => {\n this.errorTracker();\n const ctrl = this.actualControl();\n return ctrl?.invalid && (ctrl?.dirty || ctrl?.touched);\n });\n\n /** Whether an error was fixed (invalid before, now valid and touched). */\n wasFixedError = computed(() => {\n this.errorTracker();\n const ctrl = this.actualControl();\n return this.hadError() && ctrl?.valid && ctrl?.touched;\n });\n\n /** Validation error messages to display, custom or translated. */\n getErrorMessages = computed(() => {\n this.errorTracker();\n const customError = this.errorMessage();\n if (customError) return [customError];\n\n const ctrl = this.actualControl();\n if (!ctrl?.errors) return [];\n\n const customErrors = this.errorMessages();\n const t = this.transloco;\n\n return Object.keys(ctrl.errors).map((key) => {\n if (customErrors[key]) return customErrors[key];\n switch (key) {\n case 'required':\n return t.translate('validation.required');\n default:\n return t.translate('validation.invalid');\n }\n });\n });\n\n /** Date part displayed in the calendar (pending date or committed value). */\n displayedDate = computed(() => {\n if (this.timeOnly()) return null;\n const value = this.pendingDate();\n if (value) return value;\n const current = this._value();\n if (!current) return null;\n return this.datePartOf(current);\n });\n\n /** Human-readable label for the selected value in the input. */\n displayLabel = computed(() => {\n const value = this._value();\n if (!value) return '';\n\n if (this.timeOnly()) {\n return this.formatTimeFromValue(value);\n }\n\n const datePart = this.datePartOf(value);\n const timePart = value.includes('T') ? value.split('T')[1] : null;\n const v = this.view();\n\n let label: string;\n if (v === 'year') {\n label = datePart;\n } else if (v === 'month') {\n const [y, m] = datePart.split('-').map(Number);\n const fmt = new Intl.DateTimeFormat(this.locale(), {\n month: 'long',\n year: 'numeric',\n });\n label = fmt.format(new Date(y, m - 1, 1));\n label = label.charAt(0).toUpperCase() + label.slice(1);\n } else {\n const fmt = new Intl.DateTimeFormat(this.locale(), {\n dateStyle: 'long',\n });\n label = fmt.format(new Date(datePart + 'T12:00:00'));\n }\n\n if (this.showTime() && timePart) {\n label += ' ' + this.formatTimeFromValue(timePart);\n }\n return label;\n });\n\n /** Localized short month names for the year view. */\n monthNames = computed(() => {\n const fmt = new Intl.DateTimeFormat(this.locale(), { month: 'short' });\n return Array.from({ length: 12 }, (_, i) => {\n const name = fmt.format(new Date(2000, i, 1)).replace('.', '');\n return name.charAt(0).toUpperCase() + name.slice(1);\n });\n });\n\n /** Localized short weekday names for the calendar header. */\n dayNames = computed(() => {\n const fmt = new Intl.DateTimeFormat(this.locale(), { weekday: 'short' });\n return Array.from({ length: 7 }, (_, i) => {\n const name = fmt.format(new Date(2024, 0, i + 1));\n return name.charAt(0).toUpperCase() + name.slice(1, 3);\n });\n });\n\n /** Number of days in the currently viewed month. */\n daysInMonth = computed(() => {\n return new Date(this.viewYear(), this.viewMonth() + 1, 0).getDate();\n });\n\n /** Weekday index (0=Sunday) of the first day of the viewed month. */\n firstDayOfMonth = computed(() => {\n return new Date(this.viewYear(), this.viewMonth(), 1).getDay();\n });\n\n /** Calendar grid: nulls for leading blanks then the days of the month. */\n calendarDays = computed(() => {\n const totalDays = this.daysInMonth();\n const firstDay = this.firstDayOfMonth();\n const days: (number | null)[] = [];\n for (let i = 0; i < firstDay; i++) days.push(null);\n for (let i = 1; i <= totalDays; i++) days.push(i);\n return days;\n });\n\n /** Whether the prev navigation button is allowed by `minValue`. */\n canGoPrev = computed(() => {\n const v = this.view();\n const min = this.minValue();\n if (v === 'date') {\n if (!min) return true;\n const prev = new Date(this.viewYear(), this.viewMonth() - 1, 1);\n return prev >= new Date(min + 'T12:00:00');\n }\n if (v === 'month') {\n const minY = min ? Number(min.split('-')[0]) : undefined;\n return minY === undefined || this.viewYear() > minY;\n }\n const minY = min ? Number(min) : undefined;\n return minY === undefined || this.viewYear() > minY;\n });\n\n /** Whether the next navigation button is allowed by `maxValue`. */\n canGoNext = computed(() => {\n const v = this.view();\n const max = this.maxValue();\n if (v === 'date') {\n if (!max) return true;\n const next = new Date(this.viewYear(), this.viewMonth() + 1, 1);\n return next <= new Date(max + 'T12:00:00');\n }\n if (v === 'month') {\n const maxY = max ? Number(max.split('-')[0]) : undefined;\n return maxY === undefined || this.viewYear() < maxY;\n }\n const maxY = max ? Number(max) : undefined;\n return maxY === undefined || this.viewYear() < maxY;\n });\n\n /** Year of the displayed date, if any. */\n selectedYear = computed(() => {\n const value = this.displayedDate();\n return value ? Number(value.split('-')[0]) : null;\n });\n\n /** Month index (0-based) of the displayed date, if any. */\n selectedMonth = computed(() => {\n const value = this.displayedDate();\n return value ? Number(value.split('-')[1]) - 1 : null;\n });\n\n /** Day of the displayed date, if any (date view only). */\n selectedDay = computed(() => {\n const value = this.displayedDate();\n return value && this.view() === 'date' ? Number(value.split('-')[2]) : null;\n });\n\n /** 12 years of the viewed decade for the year view. */\n decadeYears = computed(() => {\n const start = this.viewDecade();\n return Array.from({ length: 12 }, (_, i) => start + i);\n });\n\n /** (Unused legacy placeholder) plain year list. */\n years: number[] = [];\n\n constructor() {\n // Attach error-state subscriptions to the validated control once found.\n effect(() => {\n const ctrl = this.actualControl();\n if (!ctrl) {\n this.subscribed.set(false);\n return;\n }\n if (this.subscribed()) return;\n\n if (ctrl.invalid && ctrl.touched) this.hadError.set(true);\n\n ctrl.statusChanges\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => {\n this.errorTracker.update((v) => v + 1);\n if (ctrl.invalid && ctrl.touched) this.hadError.set(true);\n });\n\n this.subscribed.set(true);\n });\n\n // One-way sync from the reactive control to the internal value signal.\n effect(() => {\n const ctrl = this.actualControl();\n if (!ctrl) return;\n\n if (!this.valueSubscribed()) {\n this.writeValue(ctrl.value);\n\n ctrl.valueChanges\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((value) => {\n this.writeValue(value);\n });\n\n this.valueSubscribed.set(true);\n }\n });\n }\n\n /** Closes the dropdown when clicking outside the component. */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (!this.isOpen()) return;\n if (!this.elementRef.nativeElement.contains(event.target)) {\n this.close();\n }\n }\n\n /** Closes the dropdown on Escape. */\n @HostListener('document:keydown.escape')\n onEscape(): void {\n this.close();\n }\n\n /** Opens/closes the dropdown, syncing the calendar to the current value. */\n toggle(): void {\n if (this.isDisabled()) return;\n if (this.isOpen()) {\n this.close();\n } else {\n this.syncViewToValue();\n this.isOpen.set(true);\n }\n }\n\n /** Aligns the calendar view signals with the current value (or today). */\n private syncViewToValue(): void {\n const val = this._value();\n const v = this.view();\n if (!val) {\n const now = new Date();\n this.viewYear.set(now.getFullYear());\n this.viewMonth.set(now.getMonth());\n this.viewDecade.set(Math.floor(now.getFullYear() / 10) * 10);\n this.viewHour.set(now.getHours());\n this.viewMinute.set(now.getMinutes());\n this.viewSecond.set(now.getSeconds());\n this.pendingDate.set(this.toISODate(now));\n return;\n }\n const datePart = this.datePartOf(val);\n const timePart = val.includes('T') ? val.split('T')[1] : undefined;\n if (v === 'year') {\n this.viewYear.set(Number(datePart));\n } else if (v === 'month') {\n this.viewYear.set(Number(datePart.split('-')[0]));\n } else {\n const d = new Date(datePart + 'T12:00:00');\n this.viewYear.set(d.getFullYear());\n this.viewMonth.set(d.getMonth());\n }\n const time = timePart ?? (this.timeOnly() ? val : undefined);\n if (time) {\n const [h, m, ss] = time.split(':').map(Number);\n this.viewHour.set(h || 0);\n this.viewMinute.set(m || 0);\n this.viewSecond.set(ss || 0);\n if (this.hourFormat() === '12') {\n this.viewMeridiem.set(h >= 12 ? 'PM' : 'AM');\n }\n }\n this.pendingDate.set(datePart);\n }\n\n /** Closes the dropdown, marks the control as touched and refreshes errors. */\n close(): void {\n if (!this.isOpen()) return;\n this.isOpen.set(false);\n this.onTouched();\n const ctrl = this.actualControl();\n if (ctrl) ctrl.markAsTouched();\n this.errorTracker.update((v) => v + 1);\n if (ctrl?.invalid && ctrl?.touched) this.hadError.set(true);\n }\n\n /** Navigates the calendar to the previous year/month/decade. */\n prev(): void {\n const v = this.view();\n if (v === 'year') {\n this.viewDecade.update((d) => d - 12);\n this.viewYear.update((y) => y - 12);\n } else if (v === 'month') {\n this.viewYear.update((y) => y - 1);\n } else {\n this.viewMonth.update((m) => {\n if (m === 0) {\n this.viewYear.update((y) => y - 1);\n return 11;\n }\n return m - 1;\n });\n }\n }\n\n /** Navigates the calendar to the next year/month/decade. */\n next(): void {\n const v = this.view();\n if (v === 'year') {\n this.viewDecade.update((d) => d + 12);\n this.viewYear.update((y) => y + 12);\n } else if (v === 'month') {\n this.viewYear.update((y) => y + 1);\n } else {\n this.viewMonth.update((m) => {\n if (m === 11) {\n this.viewYear.update((y) => y + 1);\n return 0;\n }\n return m + 1;\n });\n }\n }\n\n /** Selects a day of the month (pending if time is shown). */\n selectDay(day: number): void {\n const y = this.viewYear();\n const m = String(this.viewMonth() + 1).padStart(2, '0');\n const d = String(day).padStart(2, '0');\n const value = `${y}-${m}-${d}`;\n if (this.showTime()) {\n this.pendingDate.set(value);\n return;\n }\n this.setValue(value);\n }\n\n /** Selects a month of the year (pending if time is shown). */\n selectMonth(monthIndex: number): void {\n const value = `${this.viewYear()}-${String(monthIndex + 1).padStart(2, '0')}`;\n if (this.showTime()) {\n this.pendingDate.set(value);\n return;\n }\n this.setValue(value);\n }\n\n /** Selects a year (pending if time is shown). */\n selectYear(year: number): void {\n const value = String(year);\n if (this.showTime()) {\n this.pendingDate.set(value);\n return;\n }\n this.setValue(value);\n }\n\n /** Commits a value, updates the component, the control and the outputs. */\n private setValue(value: string, close = true): void {\n this._value.set(value);\n this.onChange(value);\n const ctrl = this.actualControl();\n if (ctrl) {\n ctrl.setValue(value);\n ctrl.markAsDirty();\n }\n if (close) this.close();\n }\n\n /** Confirms the selected time and commits the combined date+time value. */\n confirmTime(): void {\n const h = this.viewHour();\n const m = this.viewMinute();\n const s = this.viewSecond();\n let time = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n if (this.showSeconds()) time += `:${String(s).padStart(2, '0')}`;\n\n if (this.timeOnly()) {\n this.setValue(time);\n return;\n }\n\n const datePart =\n this.pendingDate() ??\n `${this.viewYear()}-${String(this.viewMonth() + 1).padStart(2, '0')}-01`;\n this.setValue(`${datePart}T${time}`);\n }\n\n /** Reads the hour selected in the time selector. */\n onHourChange(event: Event): void {\n this.viewHour.set(Number((event.target as HTMLSelectElement).value));\n }\n\n /** Reads the minute selected in the time selector. */\n onMinuteChange(event: Event): void {\n this.viewMinute.set(Number((event.target as HTMLSelectElement).value));\n }\n\n /** Reads the second selected in the time selector. */\n onSecondChange(event: Event): void {\n this.viewSecond.set(Number((event.target as HTMLSelectElement).value));\n }\n\n /** Current meridiem (\"AM\"/\"PM\") for the 12-hour format. */\n meridiem(): 'AM' | 'PM' {\n return this.viewMeridiem();\n }\n\n /** Reads the meridiem selected in the time selector. */\n onMeridiemChange(event: Event): void {\n this.viewMeridiem.set((event.target as HTMLSelectElement).value as 'AM' | 'PM');\n }\n\n /** Splits the date part from a full ISO \"date\" or \"dateTtime\" value. */\n private datePartOf(value: string): string {\n const i = value.indexOf('T');\n return i >= 0 ? value.slice(0, i) : value;\n }\n\n /** Formats a raw time string (\"HH:mm[:ss]\") using the configured format. */\n private formatTimeFromValue(time: string): string {\n const [h, m, ss] = time.split(':').map(Number);\n return this.formatTime(h || 0, m || 0, ss || 0);\n }\n\n /** Formats hour/minute/second according to hour format and seconds option. */\n private formatTime(h: number, m: number, s: number): string {\n if (this.hourFormat() === '12') {\n const h12 = h % 12 || 12;\n const suffix = h < 12 ? ' AM' : ' PM';\n return `${String(h12).padStart(2, '0')}:${String(m).padStart(2, '0')}${suffix}`;\n }\n let label = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;\n if (this.showSeconds()) label += `:${String(s).padStart(2, '0')}`;\n return label;\n }\n\n /** Formats a Date as a YYYY-MM-DD ISO string. */\n private toISODate(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(\n 2,\n '0',\n )}-${String(date.getDate()).padStart(2, '0')}`;\n }\n\n /** Whether the given day matches the currently selected date. */\n isSelectedDay(day: number): boolean {\n return (\n this.selectedYear() === this.viewYear() &&\n this.selectedMonth() === this.viewMonth() &&\n this.selectedDay() === day\n );\n }\n\n /** Hours available in the time selector ('12' maps to 0..11 or 12..23). */\n hoursRange(): number[] {\n if (this.hourFormat() === '12') {\n const oneToTwelve = Array.from({ length: 12 }, (_, i) => i + 1);\n return this.viewMeridiem() === 'PM'\n ? oneToTwelve.map((h) => (h === 12 ? 12 : h + 12))\n : oneToTwelve.map((h) => (h === 12 ? 0 : h));\n }\n return Array.from({ length: 24 }, (_, i) => i);\n }\n\n /** Minutes available in the time selector, stepped by `minuteStep`. */\n minutesRange(): number[] {\n const step = this.minuteStep();\n return Array.from({ length: Math.ceil(60 / step) }, (_, i) => i * step);\n }\n\n /** Seconds available in the time selector (0..59). */\n secondsRange(): number[] {\n return Array.from({ length: 60 }, (_, i) => i);\n }\n\n /** Formats an hour for display in the selector (12-hour aware). */\n hourDisplay(hour: number): string {\n if (this.hourFormat() === '12') {\n return String(hour % 12 || 12).padStart(2, '0');\n }\n return String(hour).padStart(2, '0');\n }\n\n /** Pads a minute value with a leading zero. */\n minutesLabel(value: number): string {\n return String(value).padStart(2, '0');\n }\n\n /** Whether the given day is today's date. */\n isToday(day: number): boolean {\n const now = new Date();\n return (\n this.viewYear() === now.getFullYear() &&\n this.viewMonth() === now.getMonth() &&\n day === now.getDate()\n );\n }\n\n /** Whether the given month matches the selected month and year. */\n isSelectedMonth(monthIndex: number): boolean {\n return (\n this.selectedYear() === this.viewYear() &&\n this.selectedMonth() === monthIndex\n );\n }\n\n isCurrentMonth(monthIndex: number): boolean {\n const now = new Date();\n return (\n this.viewYear() === now.getFullYear() && monthIndex === now.getMonth()\n );\n }\n\n isSelectedYear(year: number): boolean {\n return this.selectedYear() === year;\n }\n\n isCurrentYear(year: number): boolean {\n return new Date().getFullYear() === year;\n }\n\n writeValue(value: any): void {\n this._value.set(value || null);\n }\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._disabledState.set(isDisabled);\n this.cdr.markForCheck();\n }\n}\n","<div class=\"relative mb-4\">\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n </div>\n }\n\n <div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n class=\"flex items-center justify-between w-full px-3 py-2 text-left _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-lg transition-colors sm:text-sm focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:[color:var(--shk-surface-400)] dark:disabled:[color:var(--shk-surface-500)]\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': shouldShowError(),\n 'border-green-500 focus:border-green-500 focus:ring-green-500': wasFixedError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !shouldShowError() && !wasFixedError(),\n }\"\n >\n <span\n [ngClass]=\"value() ? '_shk-text-surface-700 dark:[color:var(--shk-surface-700)]' : '_shk-text-surface-400 dark:[color:var(--shk-surface-400)]'\"\n >\n {{ displayLabel() || placeholder() || label() }}\n </span>\n <svg\n class=\"w-4 h-4 _shk-text-surface-400 dark:[color:var(--shk-surface-400)] shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"></rect>\n <path d=\"M16 2v4M8 2v4M3 10h18\"></path>\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"fixed inset-0 z-40 bg-black/30\" (click)=\"close()\"></div>\n <div\n class=\"fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-72 p-4 bg-white dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-lg shadow-xl\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (!timeOnly() && view() === 'date') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ monthNames()[viewMonth()] }} {{ viewYear() }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-7 gap-0 mb-1\">\n @for (name of dayNames(); track $index) {\n <div class=\"text-center text-xs font-medium _shk-text-surface-400 dark:[color:var(--shk-surface-400)] py-1\">\n {{ name }}\n </div>\n }\n </div>\n\n <div class=\"grid grid-cols-7 gap-0\">\n @for (day of calendarDays(); track $index) {\n @if (day !== null) {\n <button\n type=\"button\"\n (click)=\"selectDay(day)\"\n class=\"py-1.5 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedDay(day),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isToday(day) && !isSelectedDay(day),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedDay(day) && !isToday(day),\n }\"\n >\n {{ day }}\n </button>\n } @else {\n <div></div>\n }\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'month') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">{{ viewYear() }}</span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (name of monthNames(); track $index) {\n <button\n type=\"button\"\n (click)=\"selectMonth($index)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedMonth($index),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentMonth($index) && !isSelectedMonth($index),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedMonth($index) && !isCurrentMonth($index),\n }\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @if (!timeOnly() && view() === 'year') {\n <div class=\"flex items-center justify-between mb-3\">\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"!canGoPrev()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M15 18l-6-6 6-6\"></path>\n </svg>\n </button>\n <span class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)]\">\n {{ viewDecade() }} - {{ viewDecade() + 11 }}\n </span>\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"!canGoNext()\"\n class=\"p-1 rounded hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed\"\n >\n <svg class=\"w-4 h-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M9 18l6-6-6-6\"></path>\n </svg>\n </button>\n </div>\n\n <div class=\"grid grid-cols-3 gap-1\">\n @for (year of decadeYears(); track year) {\n <button\n type=\"button\"\n (click)=\"selectYear(year)\"\n class=\"py-2 text-sm rounded transition-colors\"\n [ngClass]=\"{\n '_shk-bg-primary _shk-text-white hover:[background-color:var(--shk-primary-700)]': isSelectedYear(year),\n 'ring-1 [--tw-ring-color:var(--shk-primary-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)]': isCurrentYear(year) && !isSelectedYear(year),\n '_shk-text-surface-700 dark:[color:var(--shk-surface-700)] hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)]': !isSelectedYear(year) && !isCurrentYear(year),\n }\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n\n @if (timeOnly() || showTime()) {\n <div class=\"mt-3 pt-3 _shk-border dark:[border-color:var(--shk-surface-200)]\">\n <div class=\"flex items-center justify-center gap-1\">\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewHour()\"\n (change)=\"onHourChange($event)\"\n aria-label=\"Hora\"\n >\n @for (h of hoursRange(); track h) {\n <option [value]=\"h\">{{ hourDisplay(h) }}</option>\n }\n </select>\n <span class=\"_shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewMinute()\"\n (change)=\"onMinuteChange($event)\"\n aria-label=\"Minutos\"\n >\n @for (minutes of minutesRange(); track minutes) {\n <option [value]=\"minutes\">{{ minutesLabel(minutes) }}</option>\n }\n </select>\n @if (showSeconds()) {\n <span class=\"text-sm _shk-text-surface-400\">:</span>\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"viewSecond()\"\n (change)=\"onSecondChange($event)\"\n aria-label=\"Segundos\"\n >\n @for (seconds of secondsRange(); track seconds) {\n <option [value]=\"seconds\">{{ minutesLabel(seconds) }}</option>\n }\n </select>\n }\n @if (hourFormat() === '12') {\n <select\n class=\"px-2 py-1.5 text-sm _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md\"\n [value]=\"meridiem()\"\n (change)=\"onMeridiemChange($event)\"\n aria-label=\"AM/PM\"\n >\n <option value=\"AM\">AM</option>\n <option value=\"PM\">PM</option>\n </select>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"confirmTime()\"\n class=\"mt-3 w-full py-2 text-sm _shk-bg-primary _shk-text-white rounded-lg hover:[background-color:var(--shk-primary-700)] transition-colors\"\n >\n {{ timeOnly() ? 'Confirmar' : 'Introducir hora' }}\n </button>\n </div>\n }\n </div>\n }\n </div>\n\n @if (shouldShowError()) {\n <div class=\"mt-1 text-xs text-red-500 max-h-20 overflow-y-auto _shk-scrollbar\">\n <ul class=\"list-disc pl-4 space-y-1\">\n @for (error of getErrorMessages(); track error) {\n <li>{{ error }}</li>\n }\n </ul>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, model, output, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { LucideChevronRight, LucideDynamicIcon } from '@lucide/angular';
|
|
6
|
+
import { getIcon } from 'shirkasoft-ui-components/utils';
|
|
7
|
+
|
|
8
|
+
let fieldsetCounter = 0;
|
|
9
|
+
/**
|
|
10
|
+
* Shirkasoft UI Components.
|
|
11
|
+
* Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).
|
|
12
|
+
* Belongs to Shirkasoft.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Collapsible content group with a legend header, optional leading icon,
|
|
16
|
+
* badge and action buttons on the right side.
|
|
17
|
+
*/
|
|
18
|
+
class FieldsetComponent {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.getIcon = (name) => getIcon(name, LucideChevronRight);
|
|
21
|
+
/** Title shown in the header. */
|
|
22
|
+
this.legend = input('', ...(ngDevMode ? [{ debugName: "legend" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Collapsed state (default: collapsed). Two-way bindable with [(collapsed)]. */
|
|
24
|
+
this.collapsed = model(true, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Whether the header toggles the content. */
|
|
26
|
+
this.toggleable = input(true, ...(ngDevMode ? [{ debugName: "toggleable" }] : /* istanbul ignore next */ []));
|
|
27
|
+
/** Shows the expand/collapse icon button. */
|
|
28
|
+
this.showToggleIcon = input(true, ...(ngDevMode ? [{ debugName: "showToggleIcon" }] : /* istanbul ignore next */ []));
|
|
29
|
+
/** Optional leading icon shown next to the legend. */
|
|
30
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Optional badge/count shown after the legend. */
|
|
32
|
+
this.badge = input('', ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** CSS classes for the badge (override). */
|
|
34
|
+
this.badgeClass = input('', ...(ngDevMode ? [{ debugName: "badgeClass" }] : /* istanbul ignore next */ []));
|
|
35
|
+
/** Icon shown when collapsed. */
|
|
36
|
+
this.collapsedIcon = input('chevron-right', ...(ngDevMode ? [{ debugName: "collapsedIcon" }] : /* istanbul ignore next */ []));
|
|
37
|
+
/** Icon shown when expanded. */
|
|
38
|
+
this.expandedIcon = input('chevron-down', ...(ngDevMode ? [{ debugName: "expandedIcon" }] : /* istanbul ignore next */ []));
|
|
39
|
+
/** Action buttons rendered on the right side of the header. */
|
|
40
|
+
this.headerActions = input([], ...(ngDevMode ? [{ debugName: "headerActions" }] : /* istanbul ignore next */ []));
|
|
41
|
+
/** Custom content templates: key 'legend' (context: $implicit collapsed, expanded). */
|
|
42
|
+
this.customTemplates = input({}, ...(ngDevMode ? [{ debugName: "customTemplates" }] : /* istanbul ignore next */ []));
|
|
43
|
+
/** CSS classes for the extra shadow styling of the panel. */
|
|
44
|
+
this.hasShadow = input(true, ...(ngDevMode ? [{ debugName: "hasShadow" }] : /* istanbul ignore next */ []));
|
|
45
|
+
/** Accessible label for the expand action. */
|
|
46
|
+
this.expandLabel = input('Expandir', ...(ngDevMode ? [{ debugName: "expandLabel" }] : /* istanbul ignore next */ []));
|
|
47
|
+
/** Accessible label for the collapse action. */
|
|
48
|
+
this.collapseLabel = input('Colapsar', ...(ngDevMode ? [{ debugName: "collapseLabel" }] : /* istanbul ignore next */ []));
|
|
49
|
+
/** Emits the new expanded state when toggled. */
|
|
50
|
+
this.toggle = output();
|
|
51
|
+
/** Emitted when the fieldset is expanded. */
|
|
52
|
+
this.expand = output();
|
|
53
|
+
/** Emitted when the fieldset is collapsed. */
|
|
54
|
+
this.collapse = output();
|
|
55
|
+
/** Stable id linking the header button and the content region. */
|
|
56
|
+
this.contentId = `shk-fieldset-content-${++fieldsetCounter}`;
|
|
57
|
+
}
|
|
58
|
+
/** Toggles the collapsed state and emits the new state (unless not toggleable). */
|
|
59
|
+
onToggle() {
|
|
60
|
+
if (!this.toggleable())
|
|
61
|
+
return;
|
|
62
|
+
const wasCollapsed = this.collapsed();
|
|
63
|
+
const nowExpanded = wasCollapsed;
|
|
64
|
+
this.collapsed.set(!wasCollapsed);
|
|
65
|
+
this.toggle.emit(nowExpanded);
|
|
66
|
+
if (nowExpanded) {
|
|
67
|
+
this.expand.emit();
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.collapse.emit();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Whether a header action can be rendered. */
|
|
74
|
+
isHeaderActionVisible(action) {
|
|
75
|
+
return action.isVisible ? action.isVisible() : true;
|
|
76
|
+
}
|
|
77
|
+
/** Whether a header action should be disabled. */
|
|
78
|
+
isHeaderActionDisabled(action) {
|
|
79
|
+
return action.isDisabled ? action.isDisabled() : false;
|
|
80
|
+
}
|
|
81
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
82
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FieldsetComponent, isStandalone: true, selector: "shk-fieldset", inputs: { legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, toggleable: { classPropertyName: "toggleable", publicName: "toggleable", isSignal: true, isRequired: false, transformFunction: null }, showToggleIcon: { classPropertyName: "showToggleIcon", publicName: "showToggleIcon", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, badgeClass: { classPropertyName: "badgeClass", publicName: "badgeClass", isSignal: true, isRequired: false, transformFunction: null }, collapsedIcon: { classPropertyName: "collapsedIcon", publicName: "collapsedIcon", isSignal: true, isRequired: false, transformFunction: null }, expandedIcon: { classPropertyName: "expandedIcon", publicName: "expandedIcon", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null }, hasShadow: { classPropertyName: "hasShadow", publicName: "hasShadow", isSignal: true, isRequired: false, transformFunction: null }, expandLabel: { classPropertyName: "expandLabel", publicName: "expandLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseLabel: { classPropertyName: "collapseLabel", publicName: "collapseLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", toggle: "toggle", expand: "expand", collapse: "collapse" }, ngImport: i0, template: "<div\n class=\"rounded-xl bg-white dark:[background-color:var(--shk-surface-50)] border _shk-border dark:[border-color:var(--shk-surface-200)] overflow-hidden\"\n [class.shadow-sm]=\"hasShadow()\"\n>\n <div\n class=\"flex items-center gap-2 px-4 py-3\"\n [class.border-b]=\"!collapsed()\"\n [class._shk-border]=\"!collapsed()\"\n [class.dark:[border-color:var(--shk-surface-200)]]=\"!collapsed()\"\n >\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [disabled]=\"!toggleable()\"\n [attr.aria-expanded]=\"!collapsed()\"\n [attr.aria-controls]=\"contentId\"\n class=\"flex items-center gap-2 flex-1 min-w-0 text-left group disabled:cursor-default\"\n >\n @if (customTemplates()['legend']) {\n <ng-container\n *ngTemplateOutlet=\"customTemplates()['legend']; context: { $implicit: collapsed(), expanded: !collapsed() }\"\n ></ng-container>\n } @else {\n @if (icon()) {\n <svg [lucideIcon]=\"getIcon(icon())\" class=\"w-4 h-4 _shk-text-primary shrink-0\"></svg>\n }\n <span class=\"font-semibold text-sm _shk-text-surface-800 dark:[color:var(--shk-surface-800)] truncate\">\n {{ legend() }}\n </span>\n @if (badge() !== undefined && badge() !== null && badge() !== '') {\n <span\n [class]=\"badgeClass() || 'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-semibold _shk-bg-primary-soft _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[color:var(--shk-primary-300)] shrink-0'\"\n >\n {{ badge() }}\n </span>\n }\n }\n </button>\n\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-0.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick()\"\n [class]=\"'fieldset-action-btn ' + (action.class || '')\"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n >\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n <span class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\">\n {{ action.label }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n @if (toggleable() && showToggleIcon()) {\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [attr.aria-label]=\"collapsed() ? expandLabel() : collapseLabel()\"\n class=\"shk-fieldset-toggle shrink-0\"\n [disabled]=\"!toggleable()\"\n >\n <svg [lucideIcon]=\"getIcon(collapsed() ? collapsedIcon() : expandedIcon())\" class=\"w-4 h-4 transition-transform\"></svg>\n </button>\n }\n </div>\n\n @if (!collapsed()) {\n <div [id]=\"contentId\" class=\"shk-fieldset-content px-4 py-3.5\">\n <ng-content></ng-content>\n </div>\n }\n</div>\n", styles: [":host{display:block}.shk-fieldset-toggle{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem;color:var(--shk-surface-400);transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-fieldset-toggle:hover:not(:disabled){background-color:var(--shk-surface-100);color:var(--shk-surface-700)}.shk-fieldset-toggle:disabled{cursor:default}.shk-fieldset-toggle svg{width:1rem!important;height:1rem!important}.fieldset-action-btn{display:inline-flex;align-items:center;justify-content:center;padding:.375rem;border-radius:.5rem;color:var(--shk-primary-600);transition:background-color .15s ease-in-out,color .15s ease-in-out,opacity .15s ease-in-out}.fieldset-action-btn:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-primary-50) 60%,transparent);color:var(--shk-primary-700)}.fieldset-action-btn:disabled{opacity:.4;cursor:not-allowed}.fieldset-action-btn svg{width:.875rem!important;height:.875rem!important}.shk-fieldset-content{animation:shk-fieldset-fade .25s ease-out}@keyframes shk-fieldset-fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
83
|
+
}
|
|
84
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldsetComponent, decorators: [{
|
|
85
|
+
type: Component,
|
|
86
|
+
args: [{ selector: 'shk-fieldset', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, LucideDynamicIcon], template: "<div\n class=\"rounded-xl bg-white dark:[background-color:var(--shk-surface-50)] border _shk-border dark:[border-color:var(--shk-surface-200)] overflow-hidden\"\n [class.shadow-sm]=\"hasShadow()\"\n>\n <div\n class=\"flex items-center gap-2 px-4 py-3\"\n [class.border-b]=\"!collapsed()\"\n [class._shk-border]=\"!collapsed()\"\n [class.dark:[border-color:var(--shk-surface-200)]]=\"!collapsed()\"\n >\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [disabled]=\"!toggleable()\"\n [attr.aria-expanded]=\"!collapsed()\"\n [attr.aria-controls]=\"contentId\"\n class=\"flex items-center gap-2 flex-1 min-w-0 text-left group disabled:cursor-default\"\n >\n @if (customTemplates()['legend']) {\n <ng-container\n *ngTemplateOutlet=\"customTemplates()['legend']; context: { $implicit: collapsed(), expanded: !collapsed() }\"\n ></ng-container>\n } @else {\n @if (icon()) {\n <svg [lucideIcon]=\"getIcon(icon())\" class=\"w-4 h-4 _shk-text-primary shrink-0\"></svg>\n }\n <span class=\"font-semibold text-sm _shk-text-surface-800 dark:[color:var(--shk-surface-800)] truncate\">\n {{ legend() }}\n </span>\n @if (badge() !== undefined && badge() !== null && badge() !== '') {\n <span\n [class]=\"badgeClass() || 'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-semibold _shk-bg-primary-soft _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[color:var(--shk-primary-300)] shrink-0'\"\n >\n {{ badge() }}\n </span>\n }\n }\n </button>\n\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-0.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick()\"\n [class]=\"'fieldset-action-btn ' + (action.class || '')\"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n >\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n <span class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\">\n {{ action.label }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n @if (toggleable() && showToggleIcon()) {\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [attr.aria-label]=\"collapsed() ? expandLabel() : collapseLabel()\"\n class=\"shk-fieldset-toggle shrink-0\"\n [disabled]=\"!toggleable()\"\n >\n <svg [lucideIcon]=\"getIcon(collapsed() ? collapsedIcon() : expandedIcon())\" class=\"w-4 h-4 transition-transform\"></svg>\n </button>\n }\n </div>\n\n @if (!collapsed()) {\n <div [id]=\"contentId\" class=\"shk-fieldset-content px-4 py-3.5\">\n <ng-content></ng-content>\n </div>\n }\n</div>\n", styles: [":host{display:block}.shk-fieldset-toggle{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem;color:var(--shk-surface-400);transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-fieldset-toggle:hover:not(:disabled){background-color:var(--shk-surface-100);color:var(--shk-surface-700)}.shk-fieldset-toggle:disabled{cursor:default}.shk-fieldset-toggle svg{width:1rem!important;height:1rem!important}.fieldset-action-btn{display:inline-flex;align-items:center;justify-content:center;padding:.375rem;border-radius:.5rem;color:var(--shk-primary-600);transition:background-color .15s ease-in-out,color .15s ease-in-out,opacity .15s ease-in-out}.fieldset-action-btn:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-primary-50) 60%,transparent);color:var(--shk-primary-700)}.fieldset-action-btn:disabled{opacity:.4;cursor:not-allowed}.fieldset-action-btn svg{width:.875rem!important;height:.875rem!important}.shk-fieldset-content{animation:shk-fieldset-fade .25s ease-out}@keyframes shk-fieldset-fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
87
|
+
}], propDecorators: { legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }], showToggleIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggleIcon", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], badgeClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeClass", required: false }] }], collapsedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedIcon", required: false }] }], expandedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIcon", required: false }] }], headerActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerActions", required: false }] }], customTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "customTemplates", required: false }] }], hasShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasShadow", required: false }] }], expandLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandLabel", required: false }] }], collapseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseLabel", required: false }] }], toggle: [{ type: i0.Output, args: ["toggle"] }], expand: [{ type: i0.Output, args: ["expand"] }], collapse: [{ type: i0.Output, args: ["collapse"] }] } });
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generated bundle index. Do not edit.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
export { FieldsetComponent };
|
|
94
|
+
//# sourceMappingURL=shirkasoft-ui-components-fieldset.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shirkasoft-ui-components-fieldset.mjs","sources":["../../../projects/ui-components/fieldset/fieldset.component.ts","../../../projects/ui-components/fieldset/fieldset.component.html","../../../projects/ui-components/fieldset/shirkasoft-ui-components-fieldset.ts"],"sourcesContent":["import { Component, input, model, output, ChangeDetectionStrategy } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { LucideDynamicIcon, LucideChevronRight } from '@lucide/angular';\nimport { getIcon as getIconFromCentral } from 'shirkasoft-ui-components/utils';\n\n/** An action button rendered on the right side of the fieldset header. */\nexport interface FieldsetHeaderAction {\n label: string;\n icon: string | any;\n onClick: () => void;\n class?: string;\n isVisible?: () => boolean;\n isDisabled?: () => boolean;\n}\n\nlet fieldsetCounter = 0;\n\n/**\n * Shirkasoft UI Components.\n * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).\n * Belongs to Shirkasoft.\n */\n\n/**\n * Collapsible content group with a legend header, optional leading icon,\n * badge and action buttons on the right side.\n */\n@Component({\n selector: 'shk-fieldset',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [CommonModule, LucideDynamicIcon],\n templateUrl: './fieldset.component.html',\n styles: [\n `\n :host {\n display: block;\n }\n .shk-fieldset-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: 0.5rem;\n color: var(--shk-surface-400);\n transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out;\n }\n .shk-fieldset-toggle:hover:not(:disabled) {\n background-color: var(--shk-surface-100);\n color: var(--shk-surface-700);\n }\n .shk-fieldset-toggle:disabled {\n cursor: default;\n }\n .shk-fieldset-toggle svg {\n width: 1rem !important;\n height: 1rem !important;\n }\n .fieldset-action-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0.375rem;\n border-radius: 0.5rem;\n color: var(--shk-primary-600);\n transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out,\n opacity 0.15s ease-in-out;\n }\n .fieldset-action-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--shk-primary-50) 60%, transparent);\n color: var(--shk-primary-700);\n }\n .fieldset-action-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n .fieldset-action-btn svg {\n width: 0.875rem !important;\n height: 0.875rem !important;\n }\n .shk-fieldset-content {\n animation: shk-fieldset-fade 0.25s ease-out;\n }\n @keyframes shk-fieldset-fade {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n `,\n ],\n})\nexport class FieldsetComponent {\n protected getIcon = (name: any) => getIconFromCentral(name, LucideChevronRight);\n\n /** Title shown in the header. */\n legend = input<string>('');\n /** Collapsed state (default: collapsed). Two-way bindable with [(collapsed)]. */\n collapsed = model<boolean>(true);\n /** Whether the header toggles the content. */\n toggleable = input<boolean>(true);\n /** Shows the expand/collapse icon button. */\n showToggleIcon = input<boolean>(true);\n /** Optional leading icon shown next to the legend. */\n icon = input<string | any>('');\n /** Optional badge/count shown after the legend. */\n badge = input<string | number>('');\n /** CSS classes for the badge (override). */\n badgeClass = input<string>('');\n /** Icon shown when collapsed. */\n collapsedIcon = input<string>('chevron-right');\n /** Icon shown when expanded. */\n expandedIcon = input<string>('chevron-down');\n /** Action buttons rendered on the right side of the header. */\n headerActions = input<FieldsetHeaderAction[]>([]);\n /** Custom content templates: key 'legend' (context: $implicit collapsed, expanded). */\n customTemplates = input<{ [key: string]: any }>({});\n /** CSS classes for the extra shadow styling of the panel. */\n hasShadow = input<boolean>(true);\n /** Accessible label for the expand action. */\n expandLabel = input<string>('Expandir');\n /** Accessible label for the collapse action. */\n collapseLabel = input<string>('Colapsar');\n\n /** Emits the new expanded state when toggled. */\n toggle = output<boolean>();\n /** Emitted when the fieldset is expanded. */\n expand = output<void>();\n /** Emitted when the fieldset is collapsed. */\n collapse = output<void>();\n\n /** Stable id linking the header button and the content region. */\n contentId = `shk-fieldset-content-${++fieldsetCounter}`;\n\n /** Toggles the collapsed state and emits the new state (unless not toggleable). */\n onToggle(): void {\n if (!this.toggleable()) return;\n const wasCollapsed = this.collapsed();\n const nowExpanded = wasCollapsed;\n this.collapsed.set(!wasCollapsed);\n this.toggle.emit(nowExpanded);\n if (nowExpanded) {\n this.expand.emit();\n } else {\n this.collapse.emit();\n }\n }\n\n /** Whether a header action can be rendered. */\n isHeaderActionVisible(action: FieldsetHeaderAction): boolean {\n return action.isVisible ? action.isVisible() : true;\n }\n\n /** Whether a header action should be disabled. */\n isHeaderActionDisabled(action: FieldsetHeaderAction): boolean {\n return action.isDisabled ? action.isDisabled() : false;\n }\n}\n","<div\n class=\"rounded-xl bg-white dark:[background-color:var(--shk-surface-50)] border _shk-border dark:[border-color:var(--shk-surface-200)] overflow-hidden\"\n [class.shadow-sm]=\"hasShadow()\"\n>\n <div\n class=\"flex items-center gap-2 px-4 py-3\"\n [class.border-b]=\"!collapsed()\"\n [class._shk-border]=\"!collapsed()\"\n [class.dark:[border-color:var(--shk-surface-200)]]=\"!collapsed()\"\n >\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [disabled]=\"!toggleable()\"\n [attr.aria-expanded]=\"!collapsed()\"\n [attr.aria-controls]=\"contentId\"\n class=\"flex items-center gap-2 flex-1 min-w-0 text-left group disabled:cursor-default\"\n >\n @if (customTemplates()['legend']) {\n <ng-container\n *ngTemplateOutlet=\"customTemplates()['legend']; context: { $implicit: collapsed(), expanded: !collapsed() }\"\n ></ng-container>\n } @else {\n @if (icon()) {\n <svg [lucideIcon]=\"getIcon(icon())\" class=\"w-4 h-4 _shk-text-primary shrink-0\"></svg>\n }\n <span class=\"font-semibold text-sm _shk-text-surface-800 dark:[color:var(--shk-surface-800)] truncate\">\n {{ legend() }}\n </span>\n @if (badge() !== undefined && badge() !== null && badge() !== '') {\n <span\n [class]=\"badgeClass() || 'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-semibold _shk-bg-primary-soft _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[color:var(--shk-primary-300)] shrink-0'\"\n >\n {{ badge() }}\n </span>\n }\n }\n </button>\n\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-0.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick()\"\n [class]=\"'fieldset-action-btn ' + (action.class || '')\"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n >\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n <span class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\">\n {{ action.label }}\n </span>\n </div>\n }\n }\n </div>\n }\n\n @if (toggleable() && showToggleIcon()) {\n <button\n type=\"button\"\n (click)=\"onToggle()\"\n [attr.aria-label]=\"collapsed() ? expandLabel() : collapseLabel()\"\n class=\"shk-fieldset-toggle shrink-0\"\n [disabled]=\"!toggleable()\"\n >\n <svg [lucideIcon]=\"getIcon(collapsed() ? collapsedIcon() : expandedIcon())\" class=\"w-4 h-4 transition-transform\"></svg>\n </button>\n }\n </div>\n\n @if (!collapsed()) {\n <div [id]=\"contentId\" class=\"shk-fieldset-content px-4 py-3.5\">\n <ng-content></ng-content>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["getIconFromCentral"],"mappings":"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"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, input, output, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { TranslocoService } from '@jsverse/transloco';
|
|
5
|
+
import { LucideUpload, LucideDynamicIcon } from '@lucide/angular';
|
|
6
|
+
import { getIcon } from 'shirkasoft-ui-components/utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Shirkasoft UI Components.
|
|
10
|
+
* Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).
|
|
11
|
+
* Belongs to Shirkasoft.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Drag & drop file uploader with file type/size validation, image previews,
|
|
15
|
+
* multiple files support and localized error messages.
|
|
16
|
+
*/
|
|
17
|
+
class FileUploadComponent {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.getIcon = (name) => getIcon(name, LucideUpload);
|
|
20
|
+
this.transloco = inject(TranslocoService);
|
|
21
|
+
/** Main label of the uploader. */
|
|
22
|
+
this.label = input('Cargar archivo', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Accepted MIME types/extensions (e.g. 'image/*', '.pdf'). */
|
|
24
|
+
this.accept = input('image/*', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Maximum allowed size per file (bytes). */
|
|
26
|
+
this.maxFileSize = input(2 * 1024 * 1024, ...(ngDevMode ? [{ debugName: "maxFileSize" }] : /* istanbul ignore next */ []));
|
|
27
|
+
/** Maximum number of files (0 = unlimited). */
|
|
28
|
+
this.maxFiles = input(0, ...(ngDevMode ? [{ debugName: "maxFiles" }] : /* istanbul ignore next */ []));
|
|
29
|
+
/** Text of the file picker button. */
|
|
30
|
+
this.fileUploadText = input('Seleccionar archivo', ...(ngDevMode ? [{ debugName: "fileUploadText" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Helper text shown next to the label. */
|
|
32
|
+
this.fileRecommendation = input('', ...(ngDevMode ? [{ debugName: "fileRecommendation" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** Text shown when files are already loaded. */
|
|
34
|
+
this.changeFilesText = input('Cambiar archivos', ...(ngDevMode ? [{ debugName: "changeFilesText" }] : /* istanbul ignore next */ []));
|
|
35
|
+
/** Enables selecting more than one file at once. */
|
|
36
|
+
this.multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
37
|
+
/** Emitted with the currently selected files. */
|
|
38
|
+
this.fileSelected = output();
|
|
39
|
+
/** Emitted when the last selected file is removed. */
|
|
40
|
+
this.fileRemoved = output();
|
|
41
|
+
/** Emitted when a file fails validation (type/size). */
|
|
42
|
+
this.fileError = output();
|
|
43
|
+
/** Emitted when a file is near the size limit. */
|
|
44
|
+
this.fileWarning = output();
|
|
45
|
+
/** Currently selected files. */
|
|
46
|
+
this.selectedFiles = signal([], ...(ngDevMode ? [{ debugName: "selectedFiles" }] : /* istanbul ignore next */ []));
|
|
47
|
+
/** Localized error message displayed to the user. */
|
|
48
|
+
this.errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
49
|
+
/** Whether the user is dragging files over the zone. */
|
|
50
|
+
this.isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/** Object URLs used for image previews. */
|
|
52
|
+
this.previewUrls = new Map();
|
|
53
|
+
/** Whether another selection is allowed. */
|
|
54
|
+
this.canAddMore = computed(() => {
|
|
55
|
+
if (!this.multiple())
|
|
56
|
+
return this.selectedFiles().length === 0;
|
|
57
|
+
const max = this.maxFiles();
|
|
58
|
+
if (max <= 0)
|
|
59
|
+
return true;
|
|
60
|
+
return this.selectedFiles().length < max;
|
|
61
|
+
}, ...(ngDevMode ? [{ debugName: "canAddMore" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/** True when the maximum number of files has been reached. */
|
|
63
|
+
this.maxReached = computed(() => {
|
|
64
|
+
const max = this.maxFiles();
|
|
65
|
+
if (max <= 0)
|
|
66
|
+
return false;
|
|
67
|
+
return this.selectedFiles().length >= max;
|
|
68
|
+
}, ...(ngDevMode ? [{ debugName: "maxReached" }] : /* istanbul ignore next */ []));
|
|
69
|
+
}
|
|
70
|
+
/** Whether the file is an image (used for previews). */
|
|
71
|
+
isImageFile(file) {
|
|
72
|
+
return file.type.startsWith('image/');
|
|
73
|
+
}
|
|
74
|
+
/** Returns (creating on demand) the object URL for a file preview. */
|
|
75
|
+
previewUrl(file) {
|
|
76
|
+
if (!this.previewUrls.has(file)) {
|
|
77
|
+
this.previewUrls.set(file, URL.createObjectURL(file));
|
|
78
|
+
}
|
|
79
|
+
return this.previewUrls.get(file) ?? '';
|
|
80
|
+
}
|
|
81
|
+
/** Marks the drop zone as active while dragging over it. */
|
|
82
|
+
onDragOver(event) {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
event.stopPropagation();
|
|
85
|
+
this.isDragOver.set(true);
|
|
86
|
+
}
|
|
87
|
+
/** Clears the drag-over state when the drag leaves the zone. */
|
|
88
|
+
onDragLeave(event) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
this.isDragOver.set(false);
|
|
92
|
+
}
|
|
93
|
+
/** Processes the files dropped on the zone. */
|
|
94
|
+
onDrop(event) {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
event.stopPropagation();
|
|
97
|
+
this.isDragOver.set(false);
|
|
98
|
+
const files = event.dataTransfer?.files;
|
|
99
|
+
if (files && files.length > 0) {
|
|
100
|
+
this.processFiles(Array.from(files));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Processes the files picked from the native file input. */
|
|
104
|
+
onFileChange(event) {
|
|
105
|
+
const input = event.target;
|
|
106
|
+
const files = input.files;
|
|
107
|
+
if (files && files.length > 0) {
|
|
108
|
+
this.processFiles(Array.from(files));
|
|
109
|
+
}
|
|
110
|
+
input.value = '';
|
|
111
|
+
}
|
|
112
|
+
/** Validates and appends the received files, emitting errors/warnings. */
|
|
113
|
+
processFiles(files) {
|
|
114
|
+
this.errorMessage.set('');
|
|
115
|
+
const validFiles = [];
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
const error = this.validateFile(file);
|
|
118
|
+
if (error) {
|
|
119
|
+
this.fileError.emit(error);
|
|
120
|
+
this.errorMessage.set(error.message);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const warning = this.checkSizeWarning(file);
|
|
124
|
+
if (warning) {
|
|
125
|
+
this.fileWarning.emit(warning);
|
|
126
|
+
}
|
|
127
|
+
validFiles.push(file);
|
|
128
|
+
}
|
|
129
|
+
const max = this.maxFiles();
|
|
130
|
+
if (max > 0) {
|
|
131
|
+
const currentCount = this.selectedFiles().length;
|
|
132
|
+
const allowed = max - currentCount;
|
|
133
|
+
if (validFiles.length > allowed) {
|
|
134
|
+
const errorMsg = this.transloco.translate('fileUpload.maxFiles', undefined, `Máximo ${max} archivos permitidos`);
|
|
135
|
+
this.errorMessage.set(errorMsg);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (this.multiple()) {
|
|
140
|
+
this.selectedFiles.set([...this.selectedFiles(), ...validFiles]);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.clearPreviews();
|
|
144
|
+
this.selectedFiles.set(validFiles);
|
|
145
|
+
}
|
|
146
|
+
this.fileSelected.emit(this.selectedFiles());
|
|
147
|
+
}
|
|
148
|
+
/** Validates a file against `accept` and `maxFileSize`. */
|
|
149
|
+
validateFile(file) {
|
|
150
|
+
const acceptStr = this.accept();
|
|
151
|
+
if (acceptStr && acceptStr !== '*') {
|
|
152
|
+
const acceptTypes = acceptStr.split(',').map((t) => t.trim());
|
|
153
|
+
const valid = acceptTypes.some((type) => {
|
|
154
|
+
if (type.endsWith('/*')) {
|
|
155
|
+
const category = type.replace('/*', '');
|
|
156
|
+
return file.type.startsWith(category);
|
|
157
|
+
}
|
|
158
|
+
if (type.startsWith('.')) {
|
|
159
|
+
const ext = type.slice(1).toLowerCase();
|
|
160
|
+
return file.name.toLowerCase().endsWith('.' + ext);
|
|
161
|
+
}
|
|
162
|
+
if (type.includes('/')) {
|
|
163
|
+
return file.type === type;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
});
|
|
167
|
+
if (!valid) {
|
|
168
|
+
return {
|
|
169
|
+
type: 'type',
|
|
170
|
+
message: this.transloco.translate('fileUpload.invalidType', { accept: acceptStr }, `Tipo de archivo no válido. Formatos aceptados: ${acceptStr}`),
|
|
171
|
+
file,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (this.maxFileSize() > 0 && file.size > this.maxFileSize()) {
|
|
176
|
+
return {
|
|
177
|
+
type: 'size',
|
|
178
|
+
message: this.transloco.translate('fileUpload.fileTooLarge', { size: this.formatFileSize(this.maxFileSize()) }, `El archivo excede el tamaño máximo de ${this.formatFileSize(this.maxFileSize())}`),
|
|
179
|
+
file,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
/** Emits a warning when the file is close to the size limit (>90%). */
|
|
185
|
+
checkSizeWarning(file) {
|
|
186
|
+
if (this.maxFileSize() > 0 && file.size > this.maxFileSize() * 0.9) {
|
|
187
|
+
return {
|
|
188
|
+
type: 'size',
|
|
189
|
+
message: this.transloco.translate('fileUpload.sizeWarning', { size: this.formatFileSize(this.maxFileSize()) }, `El archivo está cerca del límite de tamaño (${this.formatFileSize(this.maxFileSize())})`),
|
|
190
|
+
file,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
/** Removes a file, revoking its preview URL. */
|
|
196
|
+
removeFile(file) {
|
|
197
|
+
const url = this.previewUrls.get(file);
|
|
198
|
+
if (url) {
|
|
199
|
+
URL.revokeObjectURL(url);
|
|
200
|
+
this.previewUrls.delete(file);
|
|
201
|
+
}
|
|
202
|
+
this.selectedFiles.update((files) => files.filter((f) => f !== file));
|
|
203
|
+
this.errorMessage.set('');
|
|
204
|
+
if (this.selectedFiles().length === 0) {
|
|
205
|
+
this.fileRemoved.emit();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Clears all selected files and previews. */
|
|
209
|
+
changeFiles() {
|
|
210
|
+
this.clearPreviews();
|
|
211
|
+
this.selectedFiles.set([]);
|
|
212
|
+
this.errorMessage.set('');
|
|
213
|
+
}
|
|
214
|
+
/** Revokes every stored preview object URL. */
|
|
215
|
+
clearPreviews() {
|
|
216
|
+
for (const [file, url] of this.previewUrls) {
|
|
217
|
+
URL.revokeObjectURL(url);
|
|
218
|
+
}
|
|
219
|
+
this.previewUrls.clear();
|
|
220
|
+
}
|
|
221
|
+
/** Human readable size (B/KB/MB). */
|
|
222
|
+
formatFileSize(bytes) {
|
|
223
|
+
if (bytes < 1024)
|
|
224
|
+
return bytes + ' B';
|
|
225
|
+
if (bytes < 1024 * 1024)
|
|
226
|
+
return (bytes / 1024).toFixed(1) + ' KB';
|
|
227
|
+
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
228
|
+
}
|
|
229
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
230
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FileUploadComponent, isStandalone: true, selector: "shk-file-upload", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, fileUploadText: { classPropertyName: "fileUploadText", publicName: "fileUploadText", isSignal: true, isRequired: false, transformFunction: null }, fileRecommendation: { classPropertyName: "fileRecommendation", publicName: "fileRecommendation", isSignal: true, isRequired: false, transformFunction: null }, changeFilesText: { classPropertyName: "changeFilesText", publicName: "changeFilesText", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileSelected: "fileSelected", fileRemoved: "fileRemoved", fileError: "fileError", fileWarning: "fileWarning" }, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n @if (label()) {\n <label class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ label() }}\n </label>\n }\n\n @if (canAddMore()) {\n <div\n class=\"relative border-2 border-dashed _shk-border dark:[border-color:var(--shk-surface-700)] rounded-lg p-4 transition-colors hover:[border-color:var(--shk-primary-400)] dark:hover:[border-color:var(--shk-primary-500)]\"\n [class.border-red-400]=\"errorMessage()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n >\n <input\n #fileInput\n type=\"file\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n (change)=\"onFileChange($event)\"\n class=\"hidden\"\n />\n\n <div\n class=\"flex flex-col items-center gap-2 cursor-pointer\"\n (click)=\"fileInput.click()\"\n [class.drag-over]=\"isDragOver()\"\n >\n <svg [lucideIcon]=\"getIcon('upload')\" class=\"w-8 h-8 _shk-icon-subtle dark:[color:var(--shk-surface-600)]\"></svg>\n <span class=\"text-sm _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ fileUploadText() }}\n </span>\n @if (fileRecommendation()) {\n <span class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\">\n {{ fileRecommendation() }}\n </span>\n }\n </div>\n </div>\n }\n\n @if (maxReached()) {\n <div class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)] text-center py-2\">\n M\u00E1ximo {{ maxFiles() }} archivos alcanzado\n </div>\n }\n\n @if (selectedFiles().length > 0) {\n <div class=\"flex flex-wrap gap-3\">\n @for (file of selectedFiles(); track file.name + file.size + file.lastModified) {\n <div class=\"relative group\">\n @if (isImageFile(file)) {\n <img\n [src]=\"previewUrl(file)\"\n [alt]=\"file.name\"\n class=\"w-20 h-20 object-cover rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)]\"\n />\n } @else {\n <div class=\"w-20 h-20 rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)] flex flex-col items-center justify-center _shk-bg-surface-50 dark:[background-color:var(--shk-surface-800)] gap-1\">\n <svg [lucideIcon]=\"getIcon('file')\" class=\"w-6 h-6 _shk-text-surface-400\"></svg>\n <span class=\"text-[10px] _shk-text-surface-400 truncate max-w-[70px] px-1 text-center\">{{ file.name }}</span>\n </div>\n }\n <button\n type=\"button\"\n (click)=\"removeFile(file)\"\n class=\"absolute -top-1.5 -right-1.5 w-5 h-5 bg-red-500 text-white rounded-full text-xs flex items-center justify-center opacity-0 group-hover:opacity-100 transition shadow\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3 h-3\"></svg>\n </button>\n </div>\n }\n </div>\n @if (!multiple()) {\n <button\n type=\"button\"\n (click)=\"changeFiles()\"\n class=\"text-xs _shk-text-primary hover:[color:var(--shk-primary-700)] dark:[color:var(--shk-primary-400)] dark:hover:[color:var(--shk-primary-300)] underline cursor-pointer bg-transparent border-0\"\n >\n {{ changeFilesText() }}\n </button>\n }\n }\n\n @if (errorMessage()) {\n <p class=\"text-xs _shk-text-red-500\">{{ errorMessage() }}</p>\n }\n</div>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
231
|
+
}
|
|
232
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FileUploadComponent, decorators: [{
|
|
233
|
+
type: Component,
|
|
234
|
+
args: [{ selector: 'shk-file-upload', standalone: true, imports: [CommonModule, LucideDynamicIcon], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-2\">\n @if (label()) {\n <label class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ label() }}\n </label>\n }\n\n @if (canAddMore()) {\n <div\n class=\"relative border-2 border-dashed _shk-border dark:[border-color:var(--shk-surface-700)] rounded-lg p-4 transition-colors hover:[border-color:var(--shk-primary-400)] dark:hover:[border-color:var(--shk-primary-500)]\"\n [class.border-red-400]=\"errorMessage()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n >\n <input\n #fileInput\n type=\"file\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n (change)=\"onFileChange($event)\"\n class=\"hidden\"\n />\n\n <div\n class=\"flex flex-col items-center gap-2 cursor-pointer\"\n (click)=\"fileInput.click()\"\n [class.drag-over]=\"isDragOver()\"\n >\n <svg [lucideIcon]=\"getIcon('upload')\" class=\"w-8 h-8 _shk-icon-subtle dark:[color:var(--shk-surface-600)]\"></svg>\n <span class=\"text-sm _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ fileUploadText() }}\n </span>\n @if (fileRecommendation()) {\n <span class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\">\n {{ fileRecommendation() }}\n </span>\n }\n </div>\n </div>\n }\n\n @if (maxReached()) {\n <div class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)] text-center py-2\">\n M\u00E1ximo {{ maxFiles() }} archivos alcanzado\n </div>\n }\n\n @if (selectedFiles().length > 0) {\n <div class=\"flex flex-wrap gap-3\">\n @for (file of selectedFiles(); track file.name + file.size + file.lastModified) {\n <div class=\"relative group\">\n @if (isImageFile(file)) {\n <img\n [src]=\"previewUrl(file)\"\n [alt]=\"file.name\"\n class=\"w-20 h-20 object-cover rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)]\"\n />\n } @else {\n <div class=\"w-20 h-20 rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)] flex flex-col items-center justify-center _shk-bg-surface-50 dark:[background-color:var(--shk-surface-800)] gap-1\">\n <svg [lucideIcon]=\"getIcon('file')\" class=\"w-6 h-6 _shk-text-surface-400\"></svg>\n <span class=\"text-[10px] _shk-text-surface-400 truncate max-w-[70px] px-1 text-center\">{{ file.name }}</span>\n </div>\n }\n <button\n type=\"button\"\n (click)=\"removeFile(file)\"\n class=\"absolute -top-1.5 -right-1.5 w-5 h-5 bg-red-500 text-white rounded-full text-xs flex items-center justify-center opacity-0 group-hover:opacity-100 transition shadow\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3 h-3\"></svg>\n </button>\n </div>\n }\n </div>\n @if (!multiple()) {\n <button\n type=\"button\"\n (click)=\"changeFiles()\"\n class=\"text-xs _shk-text-primary hover:[color:var(--shk-primary-700)] dark:[color:var(--shk-primary-400)] dark:hover:[color:var(--shk-primary-300)] underline cursor-pointer bg-transparent border-0\"\n >\n {{ changeFilesText() }}\n </button>\n }\n }\n\n @if (errorMessage()) {\n <p class=\"text-xs _shk-text-red-500\">{{ errorMessage() }}</p>\n }\n</div>" }]
|
|
235
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], fileUploadText: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileUploadText", required: false }] }], fileRecommendation: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileRecommendation", required: false }] }], changeFilesText: [{ type: i0.Input, args: [{ isSignal: true, alias: "changeFilesText", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }], fileRemoved: [{ type: i0.Output, args: ["fileRemoved"] }], fileError: [{ type: i0.Output, args: ["fileError"] }], fileWarning: [{ type: i0.Output, args: ["fileWarning"] }] } });
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Generated bundle index. Do not edit.
|
|
239
|
+
*/
|
|
240
|
+
|
|
241
|
+
export { FileUploadComponent };
|
|
242
|
+
//# sourceMappingURL=shirkasoft-ui-components-file-upload.mjs.map
|