ngx-tw
Version:
A comprehensive Angular component library built with Tailwind CSS, providing a modern and customizable set of UI components for Angular applications.
1 lines • 289 kB
Source Map (JSON)
{"version":3,"file":"ngx-tw.mjs","sources":["../../../projects/ngx-tw/src/lib/utlis.ts","../../../projects/ngx-tw/src/lib/notification/notification-config.ts","../../../projects/ngx-tw/src/lib/notification/notification-ref.ts","../../../projects/ngx-tw/src/lib/notification/notification.animation.ts","../../../projects/ngx-tw/src/lib/notification/notification.component.ts","../../../projects/ngx-tw/src/lib/notification/notification.component.html","../../../projects/ngx-tw/src/lib/notification/notification.service.ts","../../../projects/ngx-tw/src/lib/alerts/alert.service.ts","../../../projects/ngx-tw/src/lib/icon/icon.component.ts","../../../projects/ngx-tw/src/lib/input-field/input-field.component.ts","../../../projects/ngx-tw/src/lib/input-field/input-field.component.html","../../../projects/ngx-tw/src/lib/spinner/spinner.component.ts","../../../projects/ngx-tw/src/lib/html-events.ts","../../../projects/ngx-tw/src/lib/TwElement.ts","../../../projects/ngx-tw/src/lib/autocomplete/autocomplete-manager.ts","../../../projects/ngx-tw/src/lib/autocomplete/autocomplete.component.ts","../../../projects/ngx-tw/src/lib/autocomplete/autocomplete.component.html","../../../projects/ngx-tw/src/lib/button-group/button-group-Item.component.ts","../../../projects/ngx-tw/src/lib/button-group/button-group.component.ts","../../../projects/ngx-tw/src/lib/button/button-icon.component.ts","../../../projects/ngx-tw/src/lib/button/button.component.ts","../../../projects/ngx-tw/src/lib/calendar/calendar.component.ts","../../../projects/ngx-tw/src/lib/calendar/calendar.component.html","../../../projects/ngx-tw/src/lib/chip/chip-item-maker.component.ts","../../../projects/ngx-tw/src/lib/chip/chip.component.ts","../../../projects/ngx-tw/src/lib/chip/chip-list.component.ts","../../../projects/ngx-tw/src/lib/chip/chip-list.component.html","../../../projects/ngx-tw/src/lib/input-field/masked-input.component.ts","../../../projects/ngx-tw/src/lib/input-field/masked-input.component.html","../../../projects/ngx-tw/src/lib/date-picker/date-range-picker.component.ts","../../../projects/ngx-tw/src/lib/date-picker/date-range-picker.component.html","../../../projects/ngx-tw/src/lib/dialog/dialog.ts","../../../projects/ngx-tw/src/lib/expander/expander-content.component.ts","../../../projects/ngx-tw/src/lib/expander/expander-header.component.ts","../../../projects/ngx-tw/src/lib/expander/expander-item.component.ts","../../../projects/ngx-tw/src/lib/expander/expander-group.component.ts","../../../projects/ngx-tw/src/lib/expander/index.ts","../../../projects/ngx-tw/src/lib/menu/menu-item.directive.ts","../../../projects/ngx-tw/src/lib/menu/menu-trigger.directive.ts","../../../projects/ngx-tw/src/lib/menu/menu.component.ts","../../../projects/ngx-tw/src/lib/menu/index.ts","../../../projects/ngx-tw/src/lib/notification/notification-provider.ts","../../../projects/ngx-tw/src/lib/select/option/option.component.ts","../../../projects/ngx-tw/src/lib/select/option/option.component.html","../../../projects/ngx-tw/src/lib/select/select.component.ts","../../../projects/ngx-tw/src/lib/select/select.component.html","../../../projects/ngx-tw/src/lib/skeleton/skeleton.component.ts","../../../projects/ngx-tw/src/lib/skeleton/skeleton.directive.ts","../../../projects/ngx-tw/src/lib/sticky-content-header/sticky-content-header.component.ts","../../../projects/ngx-tw/src/lib/switch/switch.component.ts","../../../projects/ngx-tw/src/lib/tab/tab-item-maker.component.ts","../../../projects/ngx-tw/src/lib/tab/tab.component.ts","../../../projects/ngx-tw/src/lib/tab/tab-group/tab-group.component.ts","../../../projects/ngx-tw/src/lib/tab/tab-group/tab-group.component.html","../../../projects/ngx-tw/src/lib/table/column-definitions.directive.ts","../../../projects/ngx-tw/src/lib/table/table.component.ts","../../../projects/ngx-tw/src/lib/table/table.component.html","../../../projects/ngx-tw/src/lib/table/index.ts","../../../projects/ngx-tw/src/lib/toolbar/toolbar.component.ts","../../../projects/ngx-tw/src/lib/toolbar/toolbar.component.html","../../../projects/ngx-tw/src/lib/ngx-tw.module.ts","../../../projects/ngx-tw/src/public-api.ts","../../../projects/ngx-tw/src/ngx-tw.ts"],"sourcesContent":["export const tw = (\n template: TemplateStringsArray,\n ...values: (string | undefined)[]\n) => {\n return template.raw[0]\n .split(/\\s+/)\n .filter((c) => c.trim())\n .join(' ');\n};\n","import { InjectionToken, TemplateRef } from '@angular/core';\nimport { tw } from '../utlis';\n\nexport class TwNotificationData {\n type?: TwNotificationType;\n title?: string;\n text?: string;\n template?: TemplateRef<any>;\n templateContext?: any;\n autoClose?: boolean;\n autoCloseTimeout?: number;\n position?: {\n top: number;\n right: number;\n };\n animation?: {\n fadeOut: number;\n fadeIn: number;\n };\n classes?: {\n [key in TwNotificationClasses]: string;\n } = defaultTwNotificationConfig.classes;\n}\n\nexport type TwNotificationType = 'warning' | 'info' | 'success' | 'danger';\nexport type TwNotificationClasses =\n | 'container'\n | 'card'\n | 'success'\n | 'info'\n | 'warning'\n | 'danger'\n | 'svg'\n | 'button';\n\nexport interface TwNotificationConfig {\n type?: TwNotificationType;\n position?: {\n top: number;\n right: number;\n };\n animation?: {\n fadeOut: number;\n fadeIn: number;\n };\n autoClose?: boolean;\n autoCloseTimeout?: number;\n classes: {\n [key in TwNotificationClasses]: string;\n };\n}\n\nexport const defaultTwNotificationConfig: TwNotificationConfig = {\n type: 'success',\n position: {\n top: 20,\n right: 20,\n },\n animation: {\n fadeOut: 800,\n fadeIn: 300,\n },\n autoClose: true,\n autoCloseTimeout: 2500,\n classes: {\n container: tw`relative flex justify-around mb-5 w-[382px] max-w-full`,\n card: tw`max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden`,\n success: tw`text-green-400`,\n info: tw`text-primary-400`,\n warning: tw`text-yellow-400`,\n danger: tw`text-danger-400`,\n svg: tw`w-6 h-6`,\n button: tw`bg-white rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500`,\n },\n};\n\nexport const TW_NOTIFICATION_CONFIG_TOKEN = new InjectionToken(\n 'tw-notification-config'\n);\n","import { OverlayRef } from '@angular/cdk/overlay';\n\nexport class TwNotificationRef {\n constructor(private readonly overlay: OverlayRef) {}\n\n close() {\n this.overlay.dispose();\n }\n\n isVisible() {\n return this.overlay && this.overlay.overlayElement;\n }\n\n getPosition() {\n return this.overlay.overlayElement.getBoundingClientRect();\n }\n}\n","import { AnimationTriggerMetadata, trigger, state, transition, style, animate } from '@angular/animations';\n\nexport const twNotificationAnimations: {\n readonly fadeNotification: AnimationTriggerMetadata;\n} = {\n fadeNotification: trigger('fadeAnimation', [\n state('default', style({ opacity: 1 })),\n transition('void => *', [style({ opacity: 0 }), animate('{{ fadeIn }}ms')]),\n transition('default => closing', animate('{{ fadeOut }}ms', style({ opacity: 0 }))),\n ]),\n};\n\nexport type TwNotificationAnimationState = 'default' | 'closing';\n","import { AnimationEvent } from '@angular/animations';\nimport { CommonModule, NgTemplateOutlet } from '@angular/common';\nimport { Component, Inject, OnDestroy, OnInit } from '@angular/core';\nimport {\n TW_NOTIFICATION_CONFIG_TOKEN,\n TwNotificationConfig,\n TwNotificationData,\n} from './notification-config';\nimport { TwNotificationRef } from './notification-ref';\nimport {\n twNotificationAnimations,\n TwNotificationAnimationState,\n} from './notification.animation';\n\n@Component({\n selector: 'tw-notification',\n templateUrl: './notification.component.html',\n imports: [NgTemplateOutlet, CommonModule],\n animations: [twNotificationAnimations.fadeNotification],\n})\nexport class NotificationComponent implements OnInit, OnDestroy {\n animationState: TwNotificationAnimationState = 'default';\n\n private intervalId!: number | any;\n\n constructor(\n readonly data: TwNotificationData,\n readonly ref: TwNotificationRef,\n @Inject(TW_NOTIFICATION_CONFIG_TOKEN)\n public notificationConfig: TwNotificationConfig\n ) {\n //\n // Extend data with defalt notification config\n this.data = { ...this.notificationConfig, ...this.data };\n }\n\n ngOnInit() {\n //\n // Set autoclose\n if (this.data.autoClose === true)\n this.intervalId = setTimeout(\n () => (this.animationState = 'closing'),\n this.data.autoCloseTimeout\n );\n }\n\n ngOnDestroy() {\n //\n // Clear autoclose\n if (this.data.autoClose === true) clearTimeout(this.intervalId);\n }\n\n close() {\n this.ref.close();\n }\n\n onFadeFinished(event: AnimationEvent) {\n const { toState } = event;\n const isFadeOut = (toState as TwNotificationAnimationState) === 'closing';\n const itFinished = this.animationState === 'closing';\n\n if (isFadeOut && itFinished) {\n this.close();\n }\n }\n}\n","<div\n [class]=\"data.classes?.container\"\n [@fadeAnimation]=\"{value: animationState, params:\n { fadeIn: data.animation?.fadeIn, fadeOut: data.animation?.fadeOut }}\"\n (@fadeAnimation.done)=\"onFadeFinished($event)\"\n>\n <div [class]=\"data.classes?.card\">\n <div class=\"p-4\">\n @if(data.template){\n <ng-container *ngTemplateOutlet=\"data.template; context: { $implicit: data.templateContext }\"></ng-container>\n }@else {\n\n <div class=\"flex items-start\">\n <div class=\"flex-shrink-0\">\n <div [hidden]=\"data.type !== 'success'\">\n <svg\n [class]=\"data.classes?.svg + ' ' + data.classes?.success\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n\n <div [hidden]=\"data.type !== 'info'\">\n <svg\n [class]=\"data.classes?.svg + ' ' + data.classes?.info\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n\n <div [hidden]=\"data.type !== 'warning'\">\n <svg\n [class]=\"data.classes?.svg + ' ' + data.classes?.warning\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n\n <div [hidden]=\"data.type !== 'danger'\">\n <svg\n [class]=\"data.classes?.svg + ' ' + data.classes?.danger\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n </div>\n <div class=\"ml-3 w-0 flex-1 pt-0.5\">\n <p class=\"text-sm font-medium text-gray-900\">\n {{data.title}}\n </p>\n <p\n class=\"mt-1 text-sm text-gray-500\"\n [hidden]=\"!data.text\"\n >\n {{data.text}}\n </p>\n </div>\n <div class=\"ml-4 flex-shrink-0 flex\">\n <button\n [class]=\"data.classes?.button\"\n (click)=\"close()\"\n >\n <span class=\"sr-only\">Close</span>\n <!-- Heroicon name: solid/x -->\n <svg\n class=\"h-5 w-5\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n }\n </div>\n </div>\n</div>","import { Overlay } from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { Inject, Injectable, Injector, Optional } from '@angular/core';\nimport { provideAnimations } from '@angular/platform-browser/animations';\nimport {\n defaultTwNotificationConfig,\n TW_NOTIFICATION_CONFIG_TOKEN,\n TwNotificationConfig,\n TwNotificationData,\n} from './notification-config';\nimport { TwNotificationRef } from './notification-ref';\nimport { NotificationComponent } from './notification.component';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class TwNotification {\n private lastNotification!: TwNotificationRef;\n\n constructor(\n private overlay: Overlay,\n private parentInjector: Injector,\n @Optional()\n @Inject(TW_NOTIFICATION_CONFIG_TOKEN)\n private notificationConfig: TwNotificationConfig = defaultTwNotificationConfig\n ) {\n this.notificationConfig = notificationConfig || defaultTwNotificationConfig;\n }\n\n show(data: TwNotificationData) {\n const positionStrategy = this.getPositionStrategy();\n const overlayRef = this.overlay.create({ positionStrategy });\n\n const notificationRef = new TwNotificationRef(overlayRef);\n this.lastNotification = notificationRef;\n\n const injector = this.getInjector(\n data,\n notificationRef,\n this.parentInjector\n );\n const notificationPortal = new ComponentPortal(\n NotificationComponent,\n null,\n injector\n );\n\n overlayRef.attach(notificationPortal);\n\n return notificationRef;\n }\n\n getPositionStrategy() {\n return this.overlay\n .position()\n .global()\n .top(this.getPosition())\n .right(this.notificationConfig.position?.right + 'px');\n }\n\n getPosition() {\n const lastNotificationIsVisible =\n this.lastNotification && this.lastNotification.isVisible();\n const position = lastNotificationIsVisible\n ? this.lastNotification.getPosition().bottom\n : this.notificationConfig.position?.top;\n\n return position + 'px';\n }\n\n getInjector(\n data: TwNotificationData,\n notificationRef: TwNotificationRef,\n parentInjector: Injector\n ) {\n const tokens = new WeakMap();\n\n tokens.set(TwNotificationData, data);\n tokens.set(TwNotificationRef, notificationRef);\n\n return Injector.create({\n providers: [\n provideAnimations(),\n {\n provide: TW_NOTIFICATION_CONFIG_TOKEN,\n useValue: this.notificationConfig,\n },\n { provide: TwNotificationData, useValue: data },\n { provide: TwNotificationRef, useValue: notificationRef },\n ],\n parent: parentInjector,\n });\n }\n}\n","import { OverlayRef } from '@angular/cdk/overlay';\nimport { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport { TwNotification } from '../notification/notification.service';\nimport { AlertType, IAlert } from './alert';\n\n@Injectable({\n providedIn: 'root',\n})\n/** @deprecated This service is deprecated use TwNotification */\nexport class TwAlertService {\n private _alert$ = new BehaviorSubject<IAlert | null>(null);\n private _alertsContainerOverlay?: OverlayRef;\n\n constructor(private readonly _twNotification: TwNotification) {}\n\n /**\n *\n * @deprecated This method is deprecated use TwNotification.show() instead\n */\n info({\n title,\n description = void 0,\n icon = null,\n iconColor = null,\n duration = 3000,\n showActions = false,\n primaryActionText = null,\n secondaryActionText = null,\n }: {\n title: string;\n description?: string;\n icon?: string | null;\n iconColor?: string | null;\n duration?: number;\n showActions?: boolean;\n secondaryActionText?: string | null;\n primaryActionText?: string | null;\n }) {\n this.notify({\n title,\n type: 'info',\n description,\n icon,\n iconColor,\n duration: duration,\n primaryActionText,\n secondaryActionText,\n showActions,\n });\n }\n\n /**\n *\n * @deprecated This method is deprecated use TwNotification.show() instead\n */\n warning({\n title,\n description = void 0,\n icon = null,\n iconColor = null,\n duration = 3000,\n showActions = false,\n primaryActionText = null,\n secondaryActionText = null,\n }: {\n title: string;\n description?: string;\n icon?: string | null;\n iconColor?: string | null;\n duration?: number;\n showActions?: boolean;\n secondaryActionText?: string | null;\n primaryActionText?: string | null;\n }) {\n this.notify({\n title,\n type: 'warning',\n description,\n icon,\n iconColor,\n duration: duration,\n primaryActionText,\n secondaryActionText,\n showActions,\n });\n }\n\n /**\n *\n * @deprecated This method is deprecated use TwNotification.show() instead\n */\n error({\n title,\n description = void 0,\n icon = null,\n iconColor = null,\n duration = 3000,\n showActions = false,\n primaryActionText = null,\n secondaryActionText = null,\n }: {\n title: string;\n description?: string;\n icon?: string | null;\n iconColor?: string | null;\n duration?: number;\n showActions?: boolean;\n secondaryActionText?: string | null;\n primaryActionText?: string | null;\n }) {\n this.notify({\n title,\n type: 'danger',\n description,\n icon,\n iconColor,\n duration: duration,\n primaryActionText,\n secondaryActionText,\n showActions,\n });\n }\n private notify({\n title,\n type,\n description = void 0,\n icon = null,\n iconColor = null,\n duration = 3000,\n showActions = false,\n primaryActionText = null,\n secondaryActionText = null,\n }: {\n title: string;\n type: AlertType;\n description?: string;\n icon?: string | null;\n iconColor?: string | null;\n duration?: number;\n showActions?: boolean;\n secondaryActionText?: string | null;\n primaryActionText?: string | null;\n }) {\n this._twNotification.show({\n title,\n type,\n text: description,\n autoCloseTimeout: duration,\n });\n }\n}\n","import { HttpClient } from '@angular/common/http';\nimport {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Input,\n OnDestroy,\n} from '@angular/core';\nimport { Subscription } from 'rxjs';\n\n@Component({\n selector: 'tw-icon',\n template: `<ng-content></ng-content>`,\n styles: [\n `\n :host {\n display: block;\n width: var(--c-icon-width) !important;\n height: var(--c-icon-height) !important;\n min-width: var(--c-icon-width) !important;\n min-height: var(--c-icon-height) !important;\n }\n :host svg {\n width: var(--c-icon-width) !important;\n height: var(--c-icon-height) !important;\n min-width: var(--c-icon-width) !important;\n min-height: var(--c-icon-height) !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TwIcon implements AfterViewInit, OnDestroy {\n private static ICON_REGISTRY = new Map<string, string>();\n private _retrievalSubscription$?: Subscription;\n private _svgIcon?: string;\n private _size = 20;\n\n @Input() set size(value: number) {\n this._size = value;\n this._setStyles();\n }\n get size() {\n return this._size;\n }\n\n @Input() set svgIcon(value: string | undefined) {\n this._svgIcon = value;\n this._setIcon();\n }\n get svgIcon() {\n return this._svgIcon!;\n }\n\n constructor(\n private readonly _host: ElementRef<HTMLElement>,\n private readonly _httpClient: HttpClient\n ) {}\n\n ngAfterViewInit(): void {\n const nativeElement = this._host.nativeElement;\n\n if (!nativeElement) return;\n\n this._setStyles();\n\n if (!this.svgIcon) this.svgIcon = nativeElement.innerHTML;\n\n this._setIcon();\n }\n\n private _setStyles() {\n const nativeElement = this._host.nativeElement;\n\n if (!nativeElement) return;\n\n nativeElement.style.setProperty('--c-icon-width', this.size + 'px');\n nativeElement.style.setProperty('--c-icon-height', this.size + 'px');\n }\n\n private _setIcon() {\n const iconName = this.svgIcon;\n\n const nativeElement = this._host.nativeElement;\n\n if (!nativeElement) return;\n\n if (!iconName) throw new Error('tw-icon: invalid icon-name ' + iconName);\n\n const [namespace, name] = iconName.split(':');\n\n if (!namespace || !name)\n throw new Error(\n 'tw-icon: Could not determine icon namespace and name from ' + iconName\n );\n\n if (!TwIcon.ICON_REGISTRY.has(iconName)) {\n this._retriveIcon(nativeElement, iconName, namespace, name);\n } else {\n const icon = TwIcon.ICON_REGISTRY.get(iconName)!;\n nativeElement.innerHTML = icon;\n }\n }\n\n ngOnDestroy(): void {\n this._retrievalSubscription$?.unsubscribe();\n }\n\n private _retriveIcon(\n elt: Element,\n iconName: string,\n namespace: string,\n name: string\n ) {\n this._retrievalSubscription$ = this._httpClient\n .get<string>(`/assets/icons/${namespace}/${name}.svg`, {\n responseType: 'text' as any,\n })\n .subscribe((v) => {\n TwIcon.ICON_REGISTRY.set(iconName, v);\n elt.innerHTML = v;\n const svg = elt.firstElementChild as SVGElement;\n svg.classList.value = '';\n svg.style.cssText = `width: ${this.size}px!important; height: ${this.size}px!important;`;\n });\n }\n}\n","import { TextFieldModule } from '@angular/cdk/text-field';\nimport { NgClass } from '@angular/common';\nimport { Component, Input, Optional, Self } from '@angular/core';\nimport { ControlValueAccessor, FormsModule, NgControl } from '@angular/forms';\nimport { ColorTypes } from '../color-types';\nimport { TwIcon } from '../icon/icon.component';\nimport { InputField, InputTypes } from './input-field-interface';\n\n@Component({\n imports: [TwIcon, NgClass, FormsModule, TextFieldModule],\n selector: 'tw-input-field',\n host: {\n class: 'tw-input-wrapper',\n },\n templateUrl: './input-field.component.html',\n providers: [],\n})\nexport class TwInputField implements ControlValueAccessor, InputField {\n @Input() iconSuffix?: string;\n @Input() iconSuffixClass?: string;\n @Input() iconPrefix?: string;\n @Input() iconPrefixClass?: string;\n @Input() twClass?: string;\n @Input() name: string = '';\n @Input() label: string = '';\n\n @Input() value?: string = '';\n @Input() maxLength?: number;\n @Input() minLength?: number;\n @Input() required: string | boolean = false;\n @Input() pattern: string | RegExp = '';\n @Input() placeholder: string = '';\n @Input() disabled = false;\n @Input() inputType: InputTypes = 'text';\n @Input() color?: ColorTypes;\n @Input() showLabel: boolean = true;\n @Input() multiline: boolean = false;\n\n private _onChangeFns: any[] = [];\n private _onTouchedFns: any[] = [];\n\n get errors() {\n return this._ngControl?.dirty && this._ngControl?.errors\n ? Object.keys(this._ngControl.errors)\n .filter((e) => e !== 'required')\n .join('.\\n')\n : null;\n }\n\n constructor(@Self() @Optional() private readonly _ngControl?: NgControl) {\n if (this._ngControl) {\n this._ngControl.valueAccessor = this;\n }\n }\n writeValue(obj: any): void {\n this.value = obj || '';\n }\n registerOnChange(fn: any): void {\n if (fn) this._onChangeFns.push(fn);\n }\n registerOnTouched(fn: any): void {\n if (fn) this._onTouchedFns.push(fn);\n }\n setDisabledState?(isDisabled: boolean): void {\n this.disabled = isDisabled;\n }\n\n ngOnInit(): void {}\n\n onChange(event: Event) {\n const value = (event as any).target.value;\n this._onChangeFns?.forEach((fn) => fn(value));\n this._onTouchedFns?.forEach((fn) => fn(value));\n }\n}\n","<div class=\"flex flex-col gap-2\">\n @if (showLabel) {\n <label\n [for]=\"name\"\n class=\"font-semibold\"\n >{{label || placeholder}}</label>\n }\n <div class=\"relative flex items-center w-full\">\n @if (iconPrefix) {\n <span\n class=\"absolute ml-2\"\n >\n <tw-icon\n class=\"text-gray-400 {{ iconPrefixClass }}\"\n [svgIcon]=\"iconPrefix\"\n />\n </span>\n }\n\n @if (multiline) {\n <textarea\n cdkTextareaAutosize\n cdkAutosizeMinRows=\"3\"\n cdkAutosizeMaxRows=\"7\"\n [title]=\"placeholder\"\n [placeholder]=\"placeholder\"\n [(ngModel)]=\"value\"\n [disabled]=\"disabled\"\n [attr.maxLength]=\"maxLength\"\n [attr.minLength]=\"minLength\"\n [required]=\"required\"\n [name]=\"name\"\n [pattern]=\"pattern\"\n (input)=\"onChange($event)\"\n class=\"tw-input-field w-full {{ color }} {{twClass}}\"\n [ngClass]=\"{\n 'icon-left': iconPrefix && !iconSuffix,\n 'icon-right': iconSuffix && !iconPrefix,\n 'icon-left-right': iconSuffix && iconPrefix\n }\"\n ></textarea>\n }@else {\n <input\n [title]=\"placeholder\"\n [type]=\"inputType\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n [disabled]=\"disabled\"\n [attr.maxLength]=\"maxLength\"\n [attr.minLength]=\"minLength\"\n [required]=\"required\"\n [name]=\"name\"\n [pattern]=\"pattern\"\n (input)=\"onChange($event)\"\n class=\"tw-input-field w-full {{ color }} {{twClass}}\"\n [ngClass]=\"{\n 'icon-left': iconPrefix && !iconSuffix,\n 'icon-right': iconSuffix && !iconPrefix,\n 'icon-left-right': iconSuffix && iconPrefix\n }\"\n />\n\n }\n\n\n @if (iconSuffix) {\n <span\n class=\"absolute mr-2 right-0\"\n >\n <tw-icon\n class=\"text-gray-400 {{ iconSuffixClass }}\"\n [svgIcon]=\"iconSuffix\"\n />\n </span>\n }\n </div>\n @if (errors) {\n <span class=\"bg-red-100 text-red-900 rounded-lg shadow-sm px-2 py-1 text-sm\">{{errors}}</span>\n }\n</div>","import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'tw-spinner',\n standalone: true,\n template: `\n <svg\n class=\"animate-spin -ml-1 mr-3 h-5 w-5 {{ color }}\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n `,\n})\nexport class TwSpinner {\n @Input() color?: string = '';\n}\n","export const HTML_EVENTS = [\n 'abort',\n 'afterprint',\n 'animationend',\n 'animationiteration',\n 'animationstart',\n 'beforeprint',\n 'beforeunload',\n 'blur',\n 'canplay',\n 'canplaythrough',\n 'change',\n 'click',\n 'contextmenu',\n 'copy',\n 'cut',\n 'dblclick',\n 'DOMContentLoaded',\n 'drag',\n 'dragend',\n 'dragenter',\n 'dragleave',\n 'dragover',\n 'dragstart',\n 'drop',\n 'durationchange',\n 'ended',\n 'error',\n 'focus',\n 'focusin',\n 'focusout',\n 'fullscreenchange',\n 'fullscreenerror',\n 'hashchange',\n 'input',\n 'invalid',\n 'keydown',\n 'keypress',\n 'keyup',\n 'load',\n 'loadeddata',\n 'loadedmetadata',\n 'loadstart',\n 'message',\n 'mousedown',\n 'mouseenter',\n 'mouseleave',\n 'mousemove',\n 'mouseover',\n 'mouseout',\n 'mouseup',\n 'offline',\n 'online',\n 'open',\n 'pagehide',\n 'pageshow',\n 'paste',\n 'pause',\n 'play',\n 'playing',\n 'popstate',\n 'progress',\n 'ratechange',\n 'resize',\n 'reset',\n 'scroll',\n 'search',\n 'seeked',\n 'seeking',\n 'select',\n 'show',\n 'stalled',\n 'storage',\n 'submit',\n 'suspend',\n 'timeupdate',\n 'toggle',\n 'touchcancel',\n 'touchend',\n 'touchmove',\n 'touchstart',\n 'transitionend',\n 'unload',\n 'volumechange',\n 'waiting',\n 'wheel',\n];\n","import { ConnectedPosition } from '@angular/cdk/overlay';\nimport { EventEmitter } from '@angular/core';\nimport { HTML_EVENTS } from './html-events';\n\nexport class TwElement {\n bindEvents(element: HTMLElement, component: any) {\n HTML_EVENTS.forEach((ev) => {\n if (component[ev] && component[ev] instanceof EventEmitter) {\n element.addEventListener(ev, (event) => component[ev].emit(event));\n }\n });\n }\n}\n\nexport const OverlayPositions: ConnectedPosition[] = [\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'top',\n offsetY: 8,\n },\n {\n originX: 'end',\n originY: 'bottom',\n overlayX: 'end',\n overlayY: 'top',\n offsetY: 8,\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'bottom',\n offsetY: -8,\n },\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'bottom',\n offsetY: -8,\n },\n];\n","import {\n debounceTime,\n filter,\n isObservable,\n map,\n Observable,\n of,\n switchMap,\n tap,\n} from 'rxjs';\n\nexport type SuggestionsType<T> =\n | T[]\n | Observable<T[]>\n | ((searchTerm: string, item?: T) => Observable<T[]>)\n | ((searchTerm: string, item?: T) => T[]);\n\nfunction isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n\nexport class AutoCompleteManager<T> {\n filteredSuggestions: T[] = [];\n filterFn: (value: string, item: T) => boolean = (value: string, item: T) =>\n this.getDisplayText(item).toLowerCase().includes(value.toLowerCase());\n\n displayFactory: ((item: T) => { key: string; text: string }) | undefined =\n void 0;\n\n isOpen = false;\n\n disabled = false;\n\n suggestionsLoading = false;\n\n private _suggestions: SuggestionsType<T> = [];\n\n set suggestions(value: SuggestionsType<T>) {\n this._suggestions = value;\n }\n\n get suggestions() {\n return this._suggestions;\n }\n\n constructor(private valueChangeObservable: Observable<string | null>) {}\n\n init(): void {\n this.valueChangeObservable\n .pipe(\n debounceTime(300),\n tap((v) => (v?.length === 0 ? this.closeDropdown() : void 0)),\n filter((value) => !!value && value.length >= 1),\n tap(() => ((this.suggestionsLoading = true), this.openDropdown())),\n switchMap((value) => this.filterSuggestions(value!))\n )\n .subscribe((filtered) => {\n this.suggestionsLoading = false;\n this.filteredSuggestions = filtered;\n });\n }\n\n filterSuggestions(value: string): Observable<T[]> {\n const filterObs = (obs: Observable<T[]>) =>\n obs.pipe(\n map((suggestions) => suggestions.filter((s) => this.filterFn(value, s)))\n );\n\n const resolveObs = (fn: (searchTerm: string) => Observable<T[]> | T[]) => {\n const result = fn(value);\n return isObservable(result) ? result : of(result);\n };\n\n const suggestionsArray$ = isObservable(this._suggestions)\n ? filterObs(this._suggestions)\n : isFunction(this._suggestions)\n ? resolveObs(this._suggestions)\n : of(this._suggestions.filter((s) => this.filterFn(value, s)));\n\n return suggestionsArray$;\n }\n\n getDisplayText(item: any): string {\n if (this.displayFactory) {\n return this.displayFactory(item).text;\n }\n return typeof item === 'string' ? item : JSON.stringify(item);\n }\n\n openDropdown() {\n if (this.disabled === true) return;\n\n this.isOpen = true;\n }\n\n closeDropdown() {\n this.isOpen = false;\n }\n\n selectSuggestion(suggestion: any) {\n if (!this.filteredSuggestions.find((item) => item === suggestion)) {\n return false;\n }\n this.closeDropdown();\n return true;\n }\n}\n","import {\n BlockScrollStrategy,\n CdkConnectedOverlay,\n ConnectedPosition,\n Overlay,\n} from '@angular/cdk/overlay';\nimport { CommonModule } from '@angular/common';\nimport {\n AfterViewInit,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n forwardRef,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n} from '@angular/core';\nimport {\n AbstractControl,\n ControlValueAccessor,\n FormControl,\n NG_VALUE_ACCESSOR,\n ReactiveFormsModule,\n ValidationErrors,\n Validator,\n Validators,\n} from '@angular/forms';\nimport { ColorTypes } from '../color-types';\nimport { InputField, InputTypes } from '../input-field/input-field-interface';\nimport { TwInputField } from '../input-field/input-field.component';\nimport { TwSpinner } from '../spinner/spinner.component';\nimport { OverlayPositions } from '../TwElement';\nimport { AutoCompleteManager, SuggestionsType } from './autocomplete-manager';\n\n@Component({\n selector: 'tw-autocomplete',\n imports: [\n ReactiveFormsModule,\n CommonModule,\n CdkConnectedOverlay,\n TwInputField,\n TwSpinner,\n ],\n templateUrl: './autocomplete.component.html',\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TwAutocomplete),\n multi: true,\n },\n ],\n})\nexport class TwAutocomplete<T = any>\n implements\n OnInit,\n AfterViewInit,\n OnDestroy,\n ControlValueAccessor,\n Validator,\n InputField\n{\n @Input() iconSuffix?: string;\n @Input() iconSuffixClass?: string;\n @Input() iconPrefix?: string;\n @Input() iconPrefixClass?: string;\n @Input() twClass?: string;\n @Input() name: string = '';\n @Input() label: string = '';\n\n @Input() set maxLength(value: number | undefined) {\n this._maxLength = value;\n if (this.searchControl) {\n this.updateValidators();\n }\n }\n get maxLength() {\n return this._maxLength;\n }\n private _maxLength?: number;\n\n @Input() set minLength(value: number | undefined) {\n this._minLength = value;\n if (this.searchControl) {\n this.updateValidators();\n }\n }\n get minLength() {\n return this._minLength;\n }\n private _minLength?: number;\n @Input() set required(value: string | boolean) {\n this._required = value;\n if (this.searchControl) {\n this.updateValidators();\n }\n }\n get required() {\n return this._required;\n }\n private _required: string | boolean = false;\n\n @Input() set pattern(value: string | RegExp) {\n this._pattern = value;\n if (this.searchControl) {\n this.updateValidators();\n }\n }\n get pattern() {\n return this._pattern;\n }\n private _pattern: string | RegExp = '';\n @Input() placeholder: string = '';\n @Input() set disabled(value: boolean) {\n if (value) this.searchControl.disable();\n else this.searchControl.enable();\n }\n get disabled() {\n return this.searchControl.disabled;\n }\n @Input() inputType: InputTypes = 'text';\n @Input() color?: ColorTypes;\n @Input() showLabel: boolean = true;\n multiline = false;\n\n _value?: T;\n @Input() get value() {\n return this._value;\n }\n\n set value(val: T | undefined) {\n this._value = val;\n this.searchControl.setValue(this.autoCompleteManager.getDisplayText(val), {\n emitEvent: false,\n });\n }\n\n private _minFieldSize: number = 0;\n private resizeObserver?: ResizeObserver;\n\n @Input() set suggestions(value: SuggestionsType<T>) {\n this.autoCompleteManager.suggestions = value;\n }\n get suggestions() {\n return this.autoCompleteManager.suggestions;\n }\n\n get suggestionsLoading() {\n return this.autoCompleteManager.suggestionsLoading;\n }\n\n @Input() set displayFactory(\n value: ((item: any) => { key: string; text: string }) | undefined\n ) {\n this.autoCompleteManager.displayFactory = value;\n }\n\n get displayFactory() {\n return this.autoCompleteManager.displayFactory;\n }\n\n @Input() optionTemplate: TemplateRef<any> | null = null;\n\n @Input() set filterFn(fn: (value: string, item: any) => boolean) {\n this.autoCompleteManager.filterFn = fn;\n }\n\n get filterFn() {\n return this.autoCompleteManager.filterFn;\n }\n\n get minFieldSize() {\n return this._minFieldSize;\n }\n\n @Output() selectionChanged: EventEmitter<any> = new EventEmitter<any>();\n\n searchControl = new FormControl<string>('');\n get filteredSuggestions() {\n return this.autoCompleteManager.filteredSuggestions;\n }\n\n blockScrollStrategy: BlockScrollStrategy;\n\n positions: ConnectedPosition[] = OverlayPositions;\n\n onChange: ((value: any) => void)[] = [];\n onTouched: (() => void)[] = [];\n\n autoCompleteManager = new AutoCompleteManager<T>(\n this.searchControl.valueChanges\n );\n\n constructor(\n public elementRef: ElementRef,\n private overlay: Overlay,\n private cdr: ChangeDetectorRef\n ) {\n this.blockScrollStrategy = this.overlay.scrollStrategies.block();\n }\n\n ngOnInit(): void {\n this.updateValidators();\n }\n\n ngAfterViewInit(): void {\n // Initialize the offset width\n this._minFieldSize = this.elementRef.nativeElement.offsetWidth;\n\n // Set up ResizeObserver to watch for size changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n this._minFieldSize = entry.contentRect.width;\n this.cdr.detectChanges();\n }\n });\n this.resizeObserver.observe(this.elementRef.nativeElement);\n }\n\n this.autoCompleteManager.init();\n }\n\n ngOnDestroy(): void {\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n }\n\n updateValidators(): void {\n const validators = [];\n\n if (this.required) {\n validators.push(Validators.required);\n }\n\n if (this.minLength !== undefined) {\n validators.push(Validators.minLength(this.minLength));\n }\n\n if (this.maxLength !== undefined) {\n validators.push(Validators.maxLength(this.maxLength));\n }\n\n if (this.pattern) {\n validators.push(Validators.pattern(this.pattern));\n }\n\n this.searchControl.setValidators(validators);\n this.searchControl.updateValueAndValidity({\n emitEvent: false,\n });\n }\n\n validate(control: AbstractControl): ValidationErrors | null {\n if (!this.searchControl) {\n return null;\n }\n\n // If the component is disabled, it's valid\n if (this.disabled) {\n return null;\n }\n\n // Return the validation errors from the internal search control\n return this.searchControl.errors;\n }\n\n selectSuggestion(suggestion: any) {\n const value = this.autoCompleteManager.getDisplayText(suggestion);\n this.searchControl.setValue(value, {\n emitEvent: false,\n });\n this.propagateOnchange(suggestion);\n this.autoCompleteManager.closeDropdown();\n }\n\n writeValue(value: any): void {\n this._value = value;\n this.searchControl.setValue(\n this.autoCompleteManager.getDisplayText(value),\n {\n emitEvent: false,\n }\n );\n }\n\n propagateOnchange(value: any) {\n this.onChange.forEach((fn) => fn(value));\n this.selectionChanged.emit(value);\n }\n\n registerOnChange(fn: any): void {\n this.onChange.push(fn);\n }\n\n registerOnTouched(fn: any): void {\n this.onTouched.push(fn);\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.disabled = isDisabled;\n }\n}\n","<div class=\"relative w-full max-w-sm\">\n <tw-input-field\n [formControl]=\"searchControl\"\n (focus)=\"autoCompleteManager.isOpen = filteredSuggestions.length > 0\"\n [iconSuffix]=\"iconSuffix\"\n [iconSuffixClass]=\"iconSuffixClass\"\n [iconPrefix]=\"iconPrefix\"\n [iconPrefixClass]=\"iconPrefixClass\"\n [twClass]=\"twClass\"\n [name]=\"name\"\n [label]=\"label\"\n [maxLength]=\"maxLength\"\n [minLength]=\"minLength\"\n [required]=\"required\"\n [pattern]=\"pattern\"\n [placeholder]=\"placeholder\"\n [inputType]=\"inputType\"\n [color]=\"color\"\n [showLabel]=\"showLabel\"\n [multiline]=\"multiline\"\n />\n <!-- Dropdown Suggestions -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n [cdkConnectedOverlayBackdropClass]=\"'cdk-overlay-transparent-backdrop'\"\n [cdkConnectedOverlayOrigin]=\"elementRef.nativeElement.firstElementChild\"\n [cdkConnectedOverlayOpen]=\"autoCompleteManager.isOpen\"\n [cdkConnectedOverlayPositions]=\"positions\"\n cdkConnectedOverlayMinWidth=\"100px\"\n [cdkConnectedOverlayScrollStrategy]=\"blockScrollStrategy\"\n (backdropClick)=\"autoCompleteManager.closeDropdown()\"\n (detach)=\"autoCompleteManager.closeDropdown()\"\n >\n <div class=\"w-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg max-h-60 overflow-y-auto\">\n @if(!suggestionsLoading){\n @if (filteredSuggestions.length > 0) {\n <ul>\n @for (suggestion of filteredSuggestions; track $index) {\n <li\n (click)=\"selectSuggestion(suggestion)\"\n class=\"p-2 tw-option\"\n >\n @if (optionTemplate) {\n <ng-container *ngTemplateOutlet=\"optionTemplate; context: { $implicit: suggestion }\"></ng-container>\n\n }@else {\n <ng-container>{{ autoCompleteManager.getDisplayText(suggestion) }}</ng-container>\n }\n </li>\n }\n </ul>\n }\n @else{\n <div class=\"p-2 text-gray-500\">\n No suggestions found.\n </div>\n }\n }@else {\n <div class=\"flex justify-center p-4\">\n <tw-spinner></tw-spinner>\n </div>\n }\n </div>\n </ng-template>\n</div>","import { Component, Input, TemplateRef, ViewChild } from '@angular/core';\n\n@Component({\n selector: 'tw-btn-group-item',\n standalone: true,\n template: `<ng-template>\n <ng-content></ng-content>\n </ng-template>`,\n})\nexport class TwButtonGroupItem {\n @Input() value: any;\n @Input() disabled: boolean = false;\n @ViewChild(TemplateRef, { static: true }) content: TemplateRef<any> | null =\n null;\n constructor() {}\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n AfterViewInit,\n Component,\n ContentChildren,\n EventEmitter,\n Input,\n Output,\n QueryList,\n} from '@angular/core';\nimport { TwButtonGroupItem } from './button-group-Item.component';\n\n@Component({\n imports: [NgTemplateOutlet],\n selector: 'tw-btn-group',\n template: ` <div class=\"tw-button-group {{ orientation }}\">\n @for (item of children; track item; let i = $index) {\n <button\n class=\"tw-button-group-item\"\n [disabled]=\"item.disabled\"\n [class.selected-item]=\"selectedIndex === i\"\n (click)=\"changeSelection(i, item.value)\"\n >\n <ng-container [ngTemplateOutlet]=\"item.content\"></ng-container>\n </button>\n }\n </div>`\n})\nexport class TwButtonGroup implements AfterViewInit {\n @ContentChildren(TwButtonGroupItem)\n children?: QueryList<TwButtonGroupItem>;\n\n @Output() selectedIndexChange = new EventEmitter<number>();\n\n @Output() itemSelected = new EventEmitter<{\n selectedIndex: number;\n selectedValue: any;\n }>();\n @Input() selectedIndex: number = -1;\n\n @Input() orientation: 'vertical' | 'horizontal' = 'horizontal';\n\n constructor() {}\n\n ngAfterViewInit(): void {\n }\n\n changeSelection(index: number, value: any): void {\n this.itemSelected.emit({\n selectedIndex: index,\n selectedValue: value,\n });\n this.selectedIndexChange.emit(index);\n this.selectedIndex = index;\n }\n}\n","import { NgClass } from '@angular/common';\nimport { Component, Input, OnInit } from '@angular/core';\nimport { ColorTypes } from '../color-types';\nimport { TwIcon } from '../icon/icon.component';\nimport { ButtonType, TwButtonInterface } from './button-interface';\n\n@Component({\n imports: [NgClass, TwIcon],\n selector: 'tw-button-icon',\n template: `\n @if(href){\n <a\n class=\"tw-button-icon {{ twClass }} {{ type }} {{ color }}\"\n [href]=\"href\"\n [target]=\"target\"\n [attr.title]=\"title\"\n [ariaDisabled]=\"disabled\"\n >\n <tw-icon [svgIcon]=\"svgIcon\" [size]=\"svgIconSize\" />\n </a>\n\n }@else {\n <button\n class=\"tw-button-icon {{ twClass }} {{ type }} {{ color }}\"\n [type]=\"isSubmit ? 'submit' : 'button'\"\n [attr.title]=\"title\"\n [disabled]=\"disabled\"\n [ariaDisabled]=\"disabled\"\n >\n <tw-icon [svgIcon]=\"svgIcon\" [size]=\"svgIconSize\" />\n </button>\n\n }\n `\n})\nexport class TwButtonIcon implements OnInit, TwButtonInterface {\n @Input() type?: ButtonType = 'basic';\n @Input() color?: ColorTypes;\n @Input() isSubmit?: boolean;\n @Input({ alias: 'class' }) twClass?: string;\n @Input() disabled?: boolean;\n @Input() title?: string = '';\n @Input() svgIcon?: string;\n @Input() svgIconSize = 20;\n @Input() href?: string;\n @Input() target?: string;\n constructor() {}\n\n ngOnInit(): void {}\n}\n","import { Component, Input } from '@angular/core';\nimport { ColorTypes } from '../color-types';\nimport {\n ButtonType,\n RoundedTypes,\n TwButtonInterface,\n} from './button-interface';\n\n@Component({\n selector: 'tw-button',\n host: {\n role: 'button',\n '[attr.type]': 'isSubmit ? \"submit\" : \"button\"',\n class: 'inline-block w-auto',\n },\n template: `\n <button\n class=\"tw-button w-full {{ twClass }} {{ type }} rounded-{{ rounded }} {{\n color\n }}\"\n [type]=\"isSubmit ? 'submit' : 'button'\"\n [disabled]=\"disabled\"\n >\n <ng-content></ng-content>\n </button>\n `,\n})\nexport class TwButton implements TwButtonInterface {\n @Input() type?: ButtonType = 'basic';\n @Input() isSubmit?: boolean;\n @Input() rounded?: RoundedTypes = 'md';\n @Input() disabled?: boolean;\n @Input() color?: ColorTypes;\n @Input() twClass?: string;\n @Input() title?: string;\n}\n","import { CommonModule } from '@angular/common';\nimport {\n Component,\n EventEmitter,\n Input,\n OnChanges,\n OnInit,\n Output,\n SimpleChanges,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { TwButtonIcon } from '../button/button-icon.component';\n\nexport interface CalendarDate {\n date: Date;\n isCurrentMonth: boolean;\n isToday: boolean;\n isSelected: boolean;\n isInRange: boolean;\n isRangeStart: boolean;\n isRangeEnd: boolean;\n isDisabled: boolean;\n}\n\n@Component({\n selector: 'tw-calendar',\n templateUrl: './calendar.component.html',\n imports: [CommonModule, FormsModule, TwButtonIcon]\n})\nexport class TwCalendar implements OnInit, OnChanges {\n @Input() selectedDate: Date | null = null;\n @Input() rangeStart: Date | null = null;\n @Input() rangeEnd: Date | null = null;\n @Input() minDate: Date | null = null;\n @Input() maxDate: Date | null = null;\n @Input() isRange = false;\n @Input() displayDate: Date = new Date();\n\n @Output() dateSelected = new EventEmitter<Date>();\n @Output() rangeSelected = new EventEmitter<{\n start: Date;\n end: Date | null;\n }>();\n\n currentDate = new Date();\n calendarDates: CalendarDate[] = [];\n\n viewMode: 'days' | 'months' | 'years' = 'days';\n years: number[] = [];\n yearRangeStart = 0;\n\n weekdays = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];\n months = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n ];\n\n get currentMonthName(): string {\n return this.months[this.displayDate.getMonth()];\n }\n\n get currentYear(): number {\n return this.displayDate.getFullYear();\n }\n\n ngOnInit(): void {\n if (this.selectedDate && !this.displayDate) {\n this.displayDate = new Date(this.selectedDate);\n }\n this.generateCalendarDates();\n this.generateYearRange();\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['displayDate'] && !changes['displayDa