@balinasoftware/balinaui-components 3.3.9 → 3.3.10

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.
@@ -110,29 +110,52 @@ class Button {
110
110
  type = 'button';
111
111
  disabled = false;
112
112
  loading = false;
113
+ /**
114
+ * Controls whether the button is clickable.
115
+ */
116
+ clickable = true;
117
+ /**
118
+ * Developer custom CSS class.
119
+ */
113
120
  customClass = '';
114
121
  // =========================================
122
+ // HOST CLASSES
123
+ // =========================================
124
+ get buttonClickable() {
125
+ return (this.clickable &&
126
+ !this.disabled &&
127
+ !this.loading);
128
+ }
129
+ get buttonDisabled() {
130
+ return (this.disabled ||
131
+ this.loading);
132
+ }
133
+ // =========================================
115
134
  // OUTPUTS
116
135
  // =========================================
136
+ /**
137
+ * Developer click event.
138
+ */
117
139
  clicked = new EventEmitter();
118
140
  // =========================================
119
- // CLICK
141
+ // CLICK HANDLER
120
142
  // =========================================
121
143
  onClick(event) {
122
144
  if (this.disabled ||
123
- this.loading) {
145
+ this.loading ||
146
+ !this.clickable) {
124
147
  event.preventDefault();
148
+ event.stopPropagation();
125
149
  return;
126
150
  }
127
- // Developer event
128
151
  this.clicked.emit(event);
129
152
  }
130
153
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
131
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: Button, isStandalone: true, selector: "lib-button", inputs: { label: "label", type: "type", disabled: "disabled", loading: "loading", customClass: "customClass" }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button\n class=\"balina-button\"\n [class]=\"customClass\"\n [type]=\"type\"\n [disabled]=\"disabled || loading\"\n [attr.aria-busy]=\"loading\"\n (click)=\"onClick($event)\"\n>\n\n @if (loading) {\n\n <span\n class=\"button-spinner\"\n aria-hidden=\"true\"\n ></span>\n\n <span>\n Loading...\n </span>\n\n } @else {\n\n <span class=\"button-label\">\n {{ label }}\n </span>\n\n }\n\n</button>", styles: [".balina-button{--button-width: auto;--button-height: 44px;--button-padding: 0 22px;--button-background: #68479b;--button-background-hover: #593b84;--button-background-active: #4b326f;--button-text-color: #ffffff;--button-border: 1px solid #68479b;--button-radius: 6px;--button-font-family: Arial, Helvetica, sans-serif;--button-font-size: 14px;--button-font-weight: 600;--button-gap: 8px;--button-shadow: 0 2px 5px rgba(0, 0, 0, .08);--button-disabled-background: #dddddd;--button-disabled-text: #999999;--button-disabled-border: #dddddd;width:var(--button-width);min-height:var(--button-height);padding:var(--button-padding);display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);box-sizing:border-box;border:var(--button-border);border-radius:var(--button-radius);background:var(--button-background);color:var(--button-text-color);font-family:var(--button-font-family);font-size:var(--button-font-size);font-weight:var(--button-font-weight);box-shadow:var(--button-shadow);outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s ease,border-color .2s ease,box-shadow .2s ease,transform .1s ease}.balina-button:hover:not(:disabled){background:var(--button-background-hover)}.balina-button:active:not(:disabled){background:var(--button-background-active);transform:translateY(1px)}.balina-button:focus-visible{box-shadow:0 0 0 3px #68479b33}.balina-button:disabled{background:var(--button-disabled-background);color:var(--button-disabled-text);border-color:var(--button-disabled-border);cursor:not-allowed;box-shadow:none;transform:none}.balina-button[aria-busy=true]{cursor:wait}.button-spinner{width:16px;height:16px;box-sizing:border-box;border:2px solid rgba(255,255,255,.4);border-top-color:var(--button-text-color);border-radius:50%;animation:button-spin .7s linear infinite}@keyframes button-spin{to{transform:rotate(360deg)}}.button-label{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap}@media(max-width:768px){.balina-button{min-height:var(--button-height);font-size:var(--button-font-size)}}\n"] });
154
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: Button, isStandalone: true, selector: "lib-button", inputs: { label: "label", type: "type", disabled: "disabled", loading: "loading", clickable: "clickable", customClass: "customClass" }, outputs: { clicked: "clicked" }, host: { properties: { "class": "this.customClass", "class.button-clickable": "this.buttonClickable", "class.button-disabled": "this.buttonDisabled" } }, ngImport: i0, template: "<button\n class=\"balina-button\"\n\n [type]=\"type\"\n\n [disabled]=\"\n disabled ||\n loading ||\n !clickable\n \"\n\n [attr.aria-disabled]=\"\n disabled ||\n loading ||\n !clickable\n \"\n\n [attr.aria-busy]=\"loading\"\n\n (click)=\"onClick($event)\"\n>\n\n <!-- =====================================\n LOADING\n ===================================== -->\n\n @if (loading) {\n\n <span\n class=\"button-spinner\"\n aria-hidden=\"true\"\n ></span>\n\n <span class=\"button-loading-text\">\n Loading...\n </span>\n\n }\n\n <!-- =====================================\n NORMAL\n ===================================== -->\n\n @else {\n\n <span class=\"button-label\">\n {{ label }}\n </span>\n\n }\n\n</button>", styles: [":host{--button-width: auto;--button-height: 44px;--button-padding: 0 22px;--button-background: #68479b;--button-background-hover: #593b84;--button-background-active: #4b326f;--button-text-color: #ffffff;--button-border: 1px solid #68479b;--button-radius: 6px;--button-font-family: Arial, Helvetica, sans-serif;--button-font-size: 14px;--button-font-weight: 600;--button-gap: 8px;--button-shadow: 0 2px 5px rgba(0, 0, 0, .08);--button-disabled-background: #dddddd;--button-disabled-text: #999999;--button-disabled-border: #dddddd;--button-focus-ring: rgba(104, 71, 155, .2);--button-spinner-size: 16px;--button-spinner-border: 2px;display:inline-block;width:var(--button-width);box-sizing:border-box;font-family:var(--button-font-family)}.balina-button{width:100%;min-height:var(--button-height);padding:var(--button-padding);display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);box-sizing:border-box;border:var(--button-border);border-radius:var(--button-radius);background:var(--button-background);color:var(--button-text-color);font-family:var(--button-font-family);font-size:var(--button-font-size);font-weight:var(--button-font-weight);line-height:1;box-shadow:var(--button-shadow);outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .2s ease,border-color .2s ease,box-shadow .2s ease,transform .1s ease,color .2s ease}:host(.button-clickable) .balina-button{cursor:pointer}:host(:not(.button-clickable)) .balina-button{cursor:default}:host(.button-clickable) .balina-button:hover{background:var(--button-background-hover)}:host(.button-clickable) .balina-button:active{background:var(--button-background-active);transform:translateY(1px)}.balina-button:focus-visible{box-shadow:0 0 0 3px var(--button-focus-ring)}:host(.button-disabled) .balina-button{background:var(--button-disabled-background);color:var(--button-disabled-text);border-color:var(--button-disabled-border);cursor:not-allowed;box-shadow:none;transform:none}.balina-button[aria-busy=true]{cursor:wait}.button-label{display:inline-flex;align-items:center;justify-content:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.button-loading-text{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap}.button-spinner{width:var(--button-spinner-size);height:var(--button-spinner-size);flex:0 0 auto;box-sizing:border-box;border:var(--button-spinner-border) solid rgba(255,255,255,.4);border-top-color:var(--button-text-color);border-radius:50%;animation:button-spin .7s linear infinite}@keyframes button-spin{to{transform:rotate(360deg)}}@media(max-width:768px){:host{width:var(--button-width)}.balina-button{min-height:var(--button-height);font-size:var(--button-font-size)}}\n"] });
132
155
  }
133
156
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Button, decorators: [{
134
157
  type: Component,
135
- args: [{ selector: 'lib-button', standalone: true, imports: [], template: "<button\n class=\"balina-button\"\n [class]=\"customClass\"\n [type]=\"type\"\n [disabled]=\"disabled || loading\"\n [attr.aria-busy]=\"loading\"\n (click)=\"onClick($event)\"\n>\n\n @if (loading) {\n\n <span\n class=\"button-spinner\"\n aria-hidden=\"true\"\n ></span>\n\n <span>\n Loading...\n </span>\n\n } @else {\n\n <span class=\"button-label\">\n {{ label }}\n </span>\n\n }\n\n</button>", styles: [".balina-button{--button-width: auto;--button-height: 44px;--button-padding: 0 22px;--button-background: #68479b;--button-background-hover: #593b84;--button-background-active: #4b326f;--button-text-color: #ffffff;--button-border: 1px solid #68479b;--button-radius: 6px;--button-font-family: Arial, Helvetica, sans-serif;--button-font-size: 14px;--button-font-weight: 600;--button-gap: 8px;--button-shadow: 0 2px 5px rgba(0, 0, 0, .08);--button-disabled-background: #dddddd;--button-disabled-text: #999999;--button-disabled-border: #dddddd;width:var(--button-width);min-height:var(--button-height);padding:var(--button-padding);display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);box-sizing:border-box;border:var(--button-border);border-radius:var(--button-radius);background:var(--button-background);color:var(--button-text-color);font-family:var(--button-font-family);font-size:var(--button-font-size);font-weight:var(--button-font-weight);box-shadow:var(--button-shadow);outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s ease,border-color .2s ease,box-shadow .2s ease,transform .1s ease}.balina-button:hover:not(:disabled){background:var(--button-background-hover)}.balina-button:active:not(:disabled){background:var(--button-background-active);transform:translateY(1px)}.balina-button:focus-visible{box-shadow:0 0 0 3px #68479b33}.balina-button:disabled{background:var(--button-disabled-background);color:var(--button-disabled-text);border-color:var(--button-disabled-border);cursor:not-allowed;box-shadow:none;transform:none}.balina-button[aria-busy=true]{cursor:wait}.button-spinner{width:16px;height:16px;box-sizing:border-box;border:2px solid rgba(255,255,255,.4);border-top-color:var(--button-text-color);border-radius:50%;animation:button-spin .7s linear infinite}@keyframes button-spin{to{transform:rotate(360deg)}}.button-label{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap}@media(max-width:768px){.balina-button{min-height:var(--button-height);font-size:var(--button-font-size)}}\n"] }]
158
+ args: [{ selector: 'lib-button', standalone: true, imports: [], template: "<button\n class=\"balina-button\"\n\n [type]=\"type\"\n\n [disabled]=\"\n disabled ||\n loading ||\n !clickable\n \"\n\n [attr.aria-disabled]=\"\n disabled ||\n loading ||\n !clickable\n \"\n\n [attr.aria-busy]=\"loading\"\n\n (click)=\"onClick($event)\"\n>\n\n <!-- =====================================\n LOADING\n ===================================== -->\n\n @if (loading) {\n\n <span\n class=\"button-spinner\"\n aria-hidden=\"true\"\n ></span>\n\n <span class=\"button-loading-text\">\n Loading...\n </span>\n\n }\n\n <!-- =====================================\n NORMAL\n ===================================== -->\n\n @else {\n\n <span class=\"button-label\">\n {{ label }}\n </span>\n\n }\n\n</button>", styles: [":host{--button-width: auto;--button-height: 44px;--button-padding: 0 22px;--button-background: #68479b;--button-background-hover: #593b84;--button-background-active: #4b326f;--button-text-color: #ffffff;--button-border: 1px solid #68479b;--button-radius: 6px;--button-font-family: Arial, Helvetica, sans-serif;--button-font-size: 14px;--button-font-weight: 600;--button-gap: 8px;--button-shadow: 0 2px 5px rgba(0, 0, 0, .08);--button-disabled-background: #dddddd;--button-disabled-text: #999999;--button-disabled-border: #dddddd;--button-focus-ring: rgba(104, 71, 155, .2);--button-spinner-size: 16px;--button-spinner-border: 2px;display:inline-block;width:var(--button-width);box-sizing:border-box;font-family:var(--button-font-family)}.balina-button{width:100%;min-height:var(--button-height);padding:var(--button-padding);display:inline-flex;align-items:center;justify-content:center;gap:var(--button-gap);box-sizing:border-box;border:var(--button-border);border-radius:var(--button-radius);background:var(--button-background);color:var(--button-text-color);font-family:var(--button-font-family);font-size:var(--button-font-size);font-weight:var(--button-font-weight);line-height:1;box-shadow:var(--button-shadow);outline:none;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .2s ease,border-color .2s ease,box-shadow .2s ease,transform .1s ease,color .2s ease}:host(.button-clickable) .balina-button{cursor:pointer}:host(:not(.button-clickable)) .balina-button{cursor:default}:host(.button-clickable) .balina-button:hover{background:var(--button-background-hover)}:host(.button-clickable) .balina-button:active{background:var(--button-background-active);transform:translateY(1px)}.balina-button:focus-visible{box-shadow:0 0 0 3px var(--button-focus-ring)}:host(.button-disabled) .balina-button{background:var(--button-disabled-background);color:var(--button-disabled-text);border-color:var(--button-disabled-border);cursor:not-allowed;box-shadow:none;transform:none}.balina-button[aria-busy=true]{cursor:wait}.button-label{display:inline-flex;align-items:center;justify-content:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.button-loading-text{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap}.button-spinner{width:var(--button-spinner-size);height:var(--button-spinner-size);flex:0 0 auto;box-sizing:border-box;border:var(--button-spinner-border) solid rgba(255,255,255,.4);border-top-color:var(--button-text-color);border-radius:50%;animation:button-spin .7s linear infinite}@keyframes button-spin{to{transform:rotate(360deg)}}@media(max-width:768px){:host{width:var(--button-width)}.balina-button{min-height:var(--button-height);font-size:var(--button-font-size)}}\n"] }]
136
159
  }], propDecorators: { label: [{
137
160
  type: Input
138
161
  }], type: [{
@@ -141,35 +164,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
141
164
  type: Input
142
165
  }], loading: [{
143
166
  type: Input
167
+ }], clickable: [{
168
+ type: Input
144
169
  }], customClass: [{
145
170
  type: Input
171
+ }, {
172
+ type: HostBinding,
173
+ args: ['class']
174
+ }], buttonClickable: [{
175
+ type: HostBinding,
176
+ args: ['class.button-clickable']
177
+ }], buttonDisabled: [{
178
+ type: HostBinding,
179
+ args: ['class.button-disabled']
146
180
  }], clicked: [{
147
181
  type: Output
148
182
  }] } });
149
183
 
150
184
  class Card {
185
+ // =========================================
186
+ // INPUTS
187
+ // =========================================
151
188
  title = 'Last 24 hours';
152
189
  month = 'Jul';
153
190
  day = 9;
154
191
  description = '156 flights were monitored with 96.8% on-time performance. Minor delays occurred due to weather and air traffic.';
155
- cardClick = new EventEmitter();
192
+ /**
193
+ * Developer custom CSS class.
194
+ */
156
195
  customClass = '';
157
- clickable = false;
196
+ /**
197
+ * Controls whether the card is clickable.
198
+ */
199
+ clickable = true;
200
+ /**
201
+ * Disables card interaction.
202
+ */
158
203
  disabled = false;
204
+ // =========================================
205
+ // OUTPUTS
206
+ // =========================================
207
+ /**
208
+ * Legacy / specific card click event.
209
+ */
210
+ cardClick = new EventEmitter();
211
+ /**
212
+ * Standard Balina UI developer click event.
213
+ */
159
214
  clicked = new EventEmitter();
215
+ // =========================================
216
+ // CLICK HANDLER
217
+ // =========================================
160
218
  onClick(event) {
161
219
  if (!this.clickable ||
162
220
  this.disabled) {
221
+ event.preventDefault();
222
+ event.stopPropagation();
163
223
  return;
164
224
  }
225
+ // Standard developer event
165
226
  this.clicked.emit(event);
227
+ // Card-specific event
228
+ this.cardClick.emit();
166
229
  }
167
230
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Card, deps: [], target: i0.ɵɵFactoryTarget.Component });
168
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: Card, isStandalone: true, selector: "lib-card", inputs: { title: "title", month: "month", day: "day", description: "description", customClass: "customClass", clickable: "clickable", disabled: "disabled" }, outputs: { cardClick: "cardClick", clicked: "clicked" }, ngImport: i0, template: "<div\n class=\"balina-card\"\n [class]=\"customClass\"\n [class.card-clickable]=\"clickable\"\n [class.card-disabled]=\"disabled\"\n (click)=\"onClick($event)\"\n>\n\n <!-- Title -->\n <h2 class=\"card-title\">\n {{ title }}\n </h2>\n\n <!-- Card Content -->\n <div class=\"card-content\">\n\n <!-- Date -->\n <div class=\"card-date\">\n\n <div class=\"card-month\">\n {{ month }}\n </div>\n\n <div class=\"card-line\"></div>\n\n <div class=\"card-day\">\n {{ day }}\n </div>\n\n </div>\n\n <!-- Description -->\n <div class=\"card-description\">\n {{ description }}\n </div>\n\n </div>\n\n</div>", styles: [".balina-card{--card-width: 100%;--card-height: 280px;--card-padding: 28px;--card-background: #ffffff;--card-border: 3px solid #dddddd;--card-radius: 12px;--card-title-color: #000000;--card-description-color: #222222;--card-date-background: #eef5ff;--card-month-color: #2962ff;--card-line-color: #b7b7b7;--card-title-size: 38px;--card-description-size: 20px;--card-month-size: 24px;--card-day-size: 24px;--card-font-family: Arial, Helvetica, sans-serif;width:var(--card-width);min-height:var(--card-height);box-sizing:border-box;padding:var(--card-padding);background:var(--card-background);border:var(--card-border);border-radius:var(--card-radius);font-family:var(--card-font-family);cursor:pointer;transition:box-shadow .2s ease,transform .2s ease}.balina-card:hover{box-shadow:0 4px 12px #00000014;transform:translateY(-1px)}.card-title{margin:0;color:var(--card-title-color);font-size:var(--card-title-size);font-weight:700;line-height:1.2}.card-content{display:flex;align-items:center;gap:28px;margin-top:36px}.card-date{width:92px;height:96px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;padding:8px;background:var(--card-date-background);border-radius:10px}.card-month{color:var(--card-month-color);font-size:var(--card-month-size);font-weight:700;line-height:1}.card-line{width:80%;height:2px;margin:7px 0;background:var(--card-line-color)}.card-day{color:#000;font-size:var(--card-day-size);font-weight:600;line-height:1}.card-description{flex:1;color:var(--card-description-color);font-size:var(--card-description-size);font-weight:600;line-height:1.4}@media(max-width:768px){.balina-card{--card-title-size: 30px;--card-description-size: 17px;--card-height: auto;padding:22px}.card-content{gap:18px;margin-top:28px}.card-date{width:80px;height:88px}}@media(max-width:480px){.balina-card{--card-title-size: 26px;--card-description-size: 15px;padding:18px}.card-content{align-items:flex-start;gap:14px}.card-date{width:70px;height:80px}.card-month,.card-day{font-size:20px}}\n"] });
231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: Card, isStandalone: true, selector: "lib-card", inputs: { title: "title", month: "month", day: "day", description: "description", customClass: "customClass", clickable: "clickable", disabled: "disabled" }, outputs: { cardClick: "cardClick", clicked: "clicked" }, ngImport: i0, template: "<div\n class=\"balina-card\"\n\n [class]=\"customClass\"\n\n [class.card-clickable]=\"clickable\"\n\n [class.card-disabled]=\"disabled\"\n\n (click)=\"onClick($event)\"\n>\n\n <!-- =====================================\n TITLE\n ===================================== -->\n\n <h2 class=\"card-title\">\n {{ title }}\n </h2>\n\n\n <!-- =====================================\n CONTENT\n ===================================== -->\n\n <div class=\"card-content\">\n\n <!-- ===================================\n DATE\n =================================== -->\n\n <div class=\"card-date\">\n\n <div class=\"card-month\">\n {{ month }}\n </div>\n\n <div class=\"card-line\"></div>\n\n <div class=\"card-day\">\n {{ day }}\n </div>\n\n </div>\n\n\n <!-- ===================================\n DESCRIPTION\n =================================== -->\n\n <div class=\"card-description\">\n\n {{ description }}\n\n </div>\n\n </div>\n\n</div>", styles: [".balina-card{--card-width: 100%;--card-height: 280px;--card-padding: 28px;--card-background: #ffffff;--card-border: 3px solid #dddddd;--card-radius: 12px;--card-title-color: #000000;--card-description-color: #222222;--card-date-background: #eef5ff;--card-month-color: #2962ff;--card-day-color: #000000;--card-line-color: #b7b7b7;--card-title-size: 38px;--card-description-size: 20px;--card-month-size: 24px;--card-day-size: 24px;--card-font-family: Arial, Helvetica, sans-serif;--card-font-weight: 600;--card-hover-shadow: 0 4px 12px rgba(0, 0, 0, .08);--card-hover-transform: translateY(-1px);--card-disabled-background: #f5f5f5;--card-disabled-border: #d0d0d0;--card-disabled-text: #999999;width:var(--card-width);min-height:var(--card-height);padding:var(--card-padding);background:var(--card-background);border:var(--card-border);border-radius:var(--card-radius);font-family:var(--card-font-family);font-weight:var(--card-font-weight);transition:box-shadow .2s ease,transform .2s ease,background-color .2s ease,border-color .2s ease;box-sizing:border-box}.balina-card.card-clickable{cursor:pointer}.balina-card:not(.card-clickable){cursor:default}.balina-card.card-clickable:hover{box-shadow:var(--card-hover-shadow);transform:var(--card-hover-transform)}.balina-card.card-clickable:active{transform:translateY(0)}.card-title{margin:0;color:var(--card-title-color);font-size:var(--card-title-size);font-weight:700;line-height:1.2}.card-content{display:flex;align-items:center;gap:28px;margin-top:36px}.card-date{width:92px;height:96px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;padding:8px;background:var(--card-date-background);border-radius:10px}.card-month{color:var(--card-month-color);font-size:var(--card-month-size);font-weight:700;line-height:1}.card-line{width:80%;height:2px;margin:7px 0;background:var(--card-line-color)}.card-day{color:var(--card-day-color);font-size:var(--card-day-size);font-weight:600;line-height:1}.card-description{flex:1;color:var(--card-description-color);font-size:var(--card-description-size);font-weight:600;line-height:1.4}.balina-card.card-disabled{background:var(--card-disabled-background);border-color:var(--card-disabled-border);color:var(--card-disabled-text);cursor:not-allowed;box-shadow:none;transform:none}.balina-card.card-disabled:hover{box-shadow:none;transform:none}.balina-card.card-disabled .card-title,.balina-card.card-disabled .card-description{color:var(--card-disabled-text)}@media(max-width:768px){.balina-card{--card-title-size: 30px;--card-description-size: 17px;--card-height: auto;padding:22px}.card-content{gap:18px;margin-top:28px}.card-date{width:80px;height:88px}}@media(max-width:480px){.balina-card{--card-title-size: 26px;--card-description-size: 15px;padding:18px}.card-content{align-items:flex-start;gap:14px}.card-date{width:70px;height:80px}.card-month,.card-day{font-size:20px}}\n"] });
169
232
  }
170
233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Card, decorators: [{
171
234
  type: Component,
172
- args: [{ selector: 'lib-card', standalone: true, imports: [], template: "<div\n class=\"balina-card\"\n [class]=\"customClass\"\n [class.card-clickable]=\"clickable\"\n [class.card-disabled]=\"disabled\"\n (click)=\"onClick($event)\"\n>\n\n <!-- Title -->\n <h2 class=\"card-title\">\n {{ title }}\n </h2>\n\n <!-- Card Content -->\n <div class=\"card-content\">\n\n <!-- Date -->\n <div class=\"card-date\">\n\n <div class=\"card-month\">\n {{ month }}\n </div>\n\n <div class=\"card-line\"></div>\n\n <div class=\"card-day\">\n {{ day }}\n </div>\n\n </div>\n\n <!-- Description -->\n <div class=\"card-description\">\n {{ description }}\n </div>\n\n </div>\n\n</div>", styles: [".balina-card{--card-width: 100%;--card-height: 280px;--card-padding: 28px;--card-background: #ffffff;--card-border: 3px solid #dddddd;--card-radius: 12px;--card-title-color: #000000;--card-description-color: #222222;--card-date-background: #eef5ff;--card-month-color: #2962ff;--card-line-color: #b7b7b7;--card-title-size: 38px;--card-description-size: 20px;--card-month-size: 24px;--card-day-size: 24px;--card-font-family: Arial, Helvetica, sans-serif;width:var(--card-width);min-height:var(--card-height);box-sizing:border-box;padding:var(--card-padding);background:var(--card-background);border:var(--card-border);border-radius:var(--card-radius);font-family:var(--card-font-family);cursor:pointer;transition:box-shadow .2s ease,transform .2s ease}.balina-card:hover{box-shadow:0 4px 12px #00000014;transform:translateY(-1px)}.card-title{margin:0;color:var(--card-title-color);font-size:var(--card-title-size);font-weight:700;line-height:1.2}.card-content{display:flex;align-items:center;gap:28px;margin-top:36px}.card-date{width:92px;height:96px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;padding:8px;background:var(--card-date-background);border-radius:10px}.card-month{color:var(--card-month-color);font-size:var(--card-month-size);font-weight:700;line-height:1}.card-line{width:80%;height:2px;margin:7px 0;background:var(--card-line-color)}.card-day{color:#000;font-size:var(--card-day-size);font-weight:600;line-height:1}.card-description{flex:1;color:var(--card-description-color);font-size:var(--card-description-size);font-weight:600;line-height:1.4}@media(max-width:768px){.balina-card{--card-title-size: 30px;--card-description-size: 17px;--card-height: auto;padding:22px}.card-content{gap:18px;margin-top:28px}.card-date{width:80px;height:88px}}@media(max-width:480px){.balina-card{--card-title-size: 26px;--card-description-size: 15px;padding:18px}.card-content{align-items:flex-start;gap:14px}.card-date{width:70px;height:80px}.card-month,.card-day{font-size:20px}}\n"] }]
235
+ args: [{ selector: 'lib-card', standalone: true, imports: [], template: "<div\n class=\"balina-card\"\n\n [class]=\"customClass\"\n\n [class.card-clickable]=\"clickable\"\n\n [class.card-disabled]=\"disabled\"\n\n (click)=\"onClick($event)\"\n>\n\n <!-- =====================================\n TITLE\n ===================================== -->\n\n <h2 class=\"card-title\">\n {{ title }}\n </h2>\n\n\n <!-- =====================================\n CONTENT\n ===================================== -->\n\n <div class=\"card-content\">\n\n <!-- ===================================\n DATE\n =================================== -->\n\n <div class=\"card-date\">\n\n <div class=\"card-month\">\n {{ month }}\n </div>\n\n <div class=\"card-line\"></div>\n\n <div class=\"card-day\">\n {{ day }}\n </div>\n\n </div>\n\n\n <!-- ===================================\n DESCRIPTION\n =================================== -->\n\n <div class=\"card-description\">\n\n {{ description }}\n\n </div>\n\n </div>\n\n</div>", styles: [".balina-card{--card-width: 100%;--card-height: 280px;--card-padding: 28px;--card-background: #ffffff;--card-border: 3px solid #dddddd;--card-radius: 12px;--card-title-color: #000000;--card-description-color: #222222;--card-date-background: #eef5ff;--card-month-color: #2962ff;--card-day-color: #000000;--card-line-color: #b7b7b7;--card-title-size: 38px;--card-description-size: 20px;--card-month-size: 24px;--card-day-size: 24px;--card-font-family: Arial, Helvetica, sans-serif;--card-font-weight: 600;--card-hover-shadow: 0 4px 12px rgba(0, 0, 0, .08);--card-hover-transform: translateY(-1px);--card-disabled-background: #f5f5f5;--card-disabled-border: #d0d0d0;--card-disabled-text: #999999;width:var(--card-width);min-height:var(--card-height);padding:var(--card-padding);background:var(--card-background);border:var(--card-border);border-radius:var(--card-radius);font-family:var(--card-font-family);font-weight:var(--card-font-weight);transition:box-shadow .2s ease,transform .2s ease,background-color .2s ease,border-color .2s ease;box-sizing:border-box}.balina-card.card-clickable{cursor:pointer}.balina-card:not(.card-clickable){cursor:default}.balina-card.card-clickable:hover{box-shadow:var(--card-hover-shadow);transform:var(--card-hover-transform)}.balina-card.card-clickable:active{transform:translateY(0)}.card-title{margin:0;color:var(--card-title-color);font-size:var(--card-title-size);font-weight:700;line-height:1.2}.card-content{display:flex;align-items:center;gap:28px;margin-top:36px}.card-date{width:92px;height:96px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;box-sizing:border-box;padding:8px;background:var(--card-date-background);border-radius:10px}.card-month{color:var(--card-month-color);font-size:var(--card-month-size);font-weight:700;line-height:1}.card-line{width:80%;height:2px;margin:7px 0;background:var(--card-line-color)}.card-day{color:var(--card-day-color);font-size:var(--card-day-size);font-weight:600;line-height:1}.card-description{flex:1;color:var(--card-description-color);font-size:var(--card-description-size);font-weight:600;line-height:1.4}.balina-card.card-disabled{background:var(--card-disabled-background);border-color:var(--card-disabled-border);color:var(--card-disabled-text);cursor:not-allowed;box-shadow:none;transform:none}.balina-card.card-disabled:hover{box-shadow:none;transform:none}.balina-card.card-disabled .card-title,.balina-card.card-disabled .card-description{color:var(--card-disabled-text)}@media(max-width:768px){.balina-card{--card-title-size: 30px;--card-description-size: 17px;--card-height: auto;padding:22px}.card-content{gap:18px;margin-top:28px}.card-date{width:80px;height:88px}}@media(max-width:480px){.balina-card{--card-title-size: 26px;--card-description-size: 15px;padding:18px}.card-content{align-items:flex-start;gap:14px}.card-date{width:70px;height:80px}.card-month,.card-day{font-size:20px}}\n"] }]
173
236
  }], propDecorators: { title: [{
174
237
  type: Input
175
238
  }], month: [{
@@ -178,51 +241,137 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
178
241
  type: Input
179
242
  }], description: [{
180
243
  type: Input
181
- }], cardClick: [{
182
- type: Output
183
244
  }], customClass: [{
184
245
  type: Input
185
246
  }], clickable: [{
186
247
  type: Input
187
248
  }], disabled: [{
188
249
  type: Input
250
+ }], cardClick: [{
251
+ type: Output
189
252
  }], clicked: [{
190
253
  type: Output
191
254
  }] } });
192
255
 
193
256
  class TimeFilter {
257
+ // =========================================
258
+ // INPUTS
259
+ // =========================================
194
260
  label = '';
195
- value;
196
- selectedValue = '';
261
+ /**
262
+ * Unique value of this time filter.
263
+ */
264
+ value = '';
265
+ /**
266
+ * Optional external selected value.
267
+ *
268
+ * If the developer connects this input,
269
+ * multiple TimeFilters can work as a group.
270
+ *
271
+ * If it is not provided, the component
272
+ * works independently.
273
+ */
274
+ selectedValue = null;
275
+ /**
276
+ * Developer custom CSS class.
277
+ */
278
+ customClass = '';
279
+ /**
280
+ * Allows the developer to enable/disable
281
+ * click interaction.
282
+ */
283
+ clickable = true;
284
+ /**
285
+ * Disables the component.
286
+ */
287
+ disabled = false;
288
+ // =========================================
289
+ // OUTPUTS
290
+ // =========================================
291
+ /**
292
+ * Emits selected value when the component
293
+ * is used as a connected/group component.
294
+ */
197
295
  selectedValueChange = new EventEmitter();
296
+ /**
297
+ * Developer click event.
298
+ */
198
299
  clicked = new EventEmitter();
300
+ // =========================================
301
+ // INTERNAL STATE
302
+ // =========================================
303
+ /**
304
+ * Used when the component is working
305
+ * independently.
306
+ */
307
+ internalSelected = false;
308
+ // =========================================
309
+ // SELECTED STATE
310
+ // =========================================
199
311
  get selected() {
200
- return this.value === this.selectedValue;
312
+ /*
313
+ * If selectedValue is provided,
314
+ * use the external value.
315
+ */
316
+ if (this.selectedValue !== null &&
317
+ this.selectedValue !== undefined) {
318
+ return this.value === this.selectedValue;
319
+ }
320
+ /*
321
+ * Otherwise use internal state.
322
+ */
323
+ return this.internalSelected;
201
324
  }
325
+ // =========================================
326
+ // CLICK
327
+ // =========================================
202
328
  onClick(event) {
203
- // If already selected → unselect
329
+ if (this.disabled ||
330
+ !this.clickable) {
331
+ return;
332
+ }
333
+ // =====================================
334
+ // INDEPENDENT MODE
335
+ // =====================================
336
+ if (this.selectedValue === null ||
337
+ this.selectedValue === undefined) {
338
+ this.internalSelected =
339
+ !this.internalSelected;
340
+ this.clicked.emit(event);
341
+ return;
342
+ }
343
+ // =====================================
344
+ // CONNECTED / GROUP MODE
345
+ // =====================================
204
346
  if (this.selected) {
205
347
  this.selectedValueChange.emit('');
206
348
  }
207
- // If not selected → select
208
349
  else {
209
350
  this.selectedValueChange.emit(this.value);
210
351
  }
211
- // Developer custom event
352
+ // =====================================
353
+ // DEVELOPER CLICK EVENT
354
+ // =====================================
212
355
  this.clicked.emit(event);
213
356
  }
214
357
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TimeFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
215
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: TimeFilter, isStandalone: true, selector: "lib-time-filter", inputs: { label: "label", value: "value", selectedValue: "selectedValue" }, outputs: { selectedValueChange: "selectedValueChange", clicked: "clicked" }, ngImport: i0, template: "<div class=\"time-filter\" (click)=\"onClick($event)\" role=\"button\" tabindex=\"0\">\n <div class=\"radio-circle\" [class.selected]=\"selected\">\n <div class=\"radio-dot\"></div>\n </div>\n\n <span class=\"label\">\n {{ label }}\n </span>\n</div>", styles: [".time-filter{display:flex;align-items:center;gap:16px;width:fit-content;cursor:pointer;-webkit-user-select:none;user-select:none}.radio-circle{width:24px;height:24px;border:2px solid #a6a6a6;border-radius:50%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;transition:border-color .2s ease}.radio-dot{width:12px;height:12px;border-radius:50%;background:transparent;transition:background .2s ease}.radio-circle.selected{border-color:#1976d2}.radio-circle.selected .radio-dot{background:#1976d2}.label{font-size:20px;font-weight:400;color:#000;line-height:1}.label:hover{color:#333}\n"] });
358
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.2", type: TimeFilter, isStandalone: true, selector: "lib-time-filter", inputs: { label: "label", value: "value", selectedValue: "selectedValue", customClass: "customClass", clickable: "clickable", disabled: "disabled" }, outputs: { selectedValueChange: "selectedValueChange", clicked: "clicked" }, ngImport: i0, template: "<div\n class=\"time-filter\"\n [class]=\"customClass\"\n\n [class.time-filter-clickable]=\"clickable\"\n\n [class.time-filter-disabled]=\"disabled\"\n\n [class.time-filter-selected]=\"selected\"\n\n (click)=\"onClick($event)\"\n\n role=\"button\"\n\n tabindex=\"0\"\n>\n\n <!-- =====================================\n RADIO\n ===================================== -->\n\n <div\n class=\"radio-circle\"\n [class.selected]=\"selected\"\n >\n\n <div class=\"radio-dot\"></div>\n\n </div>\n\n\n <!-- =====================================\n LABEL\n ===================================== -->\n\n <span class=\"label\">\n {{ label }}\n </span>\n\n</div>", styles: [".time-filter{--time-filter-gap: 16px;--time-filter-circle-size: 24px;--time-filter-circle-border-width: 2px;--time-filter-dot-size: 12px;--time-filter-border-color: #a6a6a6;--time-filter-selected-color: #1976d2;--time-filter-text-color: #000000;--time-filter-text-hover: #333333;--time-filter-font-family: Arial, Helvetica, sans-serif;--time-filter-font-size: 20px;--time-filter-font-weight: 400;--time-filter-disabled-color: #aaaaaa;display:flex;align-items:center;gap:var(--time-filter-gap);width:fit-content;box-sizing:border-box;font-family:var(--time-filter-font-family);-webkit-user-select:none;user-select:none;transition:opacity .2s ease}.time-filter-clickable{cursor:pointer}.radio-circle{width:var(--time-filter-circle-size);height:var(--time-filter-circle-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border:var(--time-filter-circle-border-width) solid var(--time-filter-border-color);border-radius:50%;transition:border-color .2s ease,background-color .2s ease}.radio-dot{width:var(--time-filter-dot-size);height:var(--time-filter-dot-size);border-radius:50%;background:transparent;transition:background-color .2s ease,transform .2s ease}.radio-circle.selected{border-color:var(--time-filter-selected-color)}.radio-circle.selected .radio-dot{background:var(--time-filter-selected-color);transform:scale(1)}.label{color:var(--time-filter-text-color);font-size:var(--time-filter-font-size);font-weight:var(--time-filter-font-weight);line-height:1;transition:color .2s ease}.time-filter-clickable:hover .label{color:var(--time-filter-text-hover)}.time-filter-clickable:hover .radio-circle{border-color:var(--time-filter-selected-color)}.time-filter-disabled{opacity:.6;cursor:not-allowed}.time-filter-disabled .radio-circle{border-color:var(--time-filter-disabled-color)}.time-filter-disabled .label{color:var(--time-filter-disabled-color)}.time-filter-disabled .radio-circle.selected{border-color:var(--time-filter-disabled-color)}.time-filter-disabled .radio-circle.selected .radio-dot{background:var(--time-filter-disabled-color)}@media(max-width:768px){.time-filter{--time-filter-font-size: 18px;--time-filter-circle-size: 22px;--time-filter-dot-size: 10px;--time-filter-gap: 12px}}@media(max-width:480px){.time-filter{--time-filter-font-size: 16px;--time-filter-circle-size: 20px;--time-filter-dot-size: 9px;--time-filter-gap: 10px}}\n"] });
216
359
  }
217
360
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TimeFilter, decorators: [{
218
361
  type: Component,
219
- args: [{ selector: 'lib-time-filter', standalone: true, imports: [], template: "<div class=\"time-filter\" (click)=\"onClick($event)\" role=\"button\" tabindex=\"0\">\n <div class=\"radio-circle\" [class.selected]=\"selected\">\n <div class=\"radio-dot\"></div>\n </div>\n\n <span class=\"label\">\n {{ label }}\n </span>\n</div>", styles: [".time-filter{display:flex;align-items:center;gap:16px;width:fit-content;cursor:pointer;-webkit-user-select:none;user-select:none}.radio-circle{width:24px;height:24px;border:2px solid #a6a6a6;border-radius:50%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;transition:border-color .2s ease}.radio-dot{width:12px;height:12px;border-radius:50%;background:transparent;transition:background .2s ease}.radio-circle.selected{border-color:#1976d2}.radio-circle.selected .radio-dot{background:#1976d2}.label{font-size:20px;font-weight:400;color:#000;line-height:1}.label:hover{color:#333}\n"] }]
362
+ args: [{ selector: 'lib-time-filter', standalone: true, imports: [], template: "<div\n class=\"time-filter\"\n [class]=\"customClass\"\n\n [class.time-filter-clickable]=\"clickable\"\n\n [class.time-filter-disabled]=\"disabled\"\n\n [class.time-filter-selected]=\"selected\"\n\n (click)=\"onClick($event)\"\n\n role=\"button\"\n\n tabindex=\"0\"\n>\n\n <!-- =====================================\n RADIO\n ===================================== -->\n\n <div\n class=\"radio-circle\"\n [class.selected]=\"selected\"\n >\n\n <div class=\"radio-dot\"></div>\n\n </div>\n\n\n <!-- =====================================\n LABEL\n ===================================== -->\n\n <span class=\"label\">\n {{ label }}\n </span>\n\n</div>", styles: [".time-filter{--time-filter-gap: 16px;--time-filter-circle-size: 24px;--time-filter-circle-border-width: 2px;--time-filter-dot-size: 12px;--time-filter-border-color: #a6a6a6;--time-filter-selected-color: #1976d2;--time-filter-text-color: #000000;--time-filter-text-hover: #333333;--time-filter-font-family: Arial, Helvetica, sans-serif;--time-filter-font-size: 20px;--time-filter-font-weight: 400;--time-filter-disabled-color: #aaaaaa;display:flex;align-items:center;gap:var(--time-filter-gap);width:fit-content;box-sizing:border-box;font-family:var(--time-filter-font-family);-webkit-user-select:none;user-select:none;transition:opacity .2s ease}.time-filter-clickable{cursor:pointer}.radio-circle{width:var(--time-filter-circle-size);height:var(--time-filter-circle-size);flex-shrink:0;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border:var(--time-filter-circle-border-width) solid var(--time-filter-border-color);border-radius:50%;transition:border-color .2s ease,background-color .2s ease}.radio-dot{width:var(--time-filter-dot-size);height:var(--time-filter-dot-size);border-radius:50%;background:transparent;transition:background-color .2s ease,transform .2s ease}.radio-circle.selected{border-color:var(--time-filter-selected-color)}.radio-circle.selected .radio-dot{background:var(--time-filter-selected-color);transform:scale(1)}.label{color:var(--time-filter-text-color);font-size:var(--time-filter-font-size);font-weight:var(--time-filter-font-weight);line-height:1;transition:color .2s ease}.time-filter-clickable:hover .label{color:var(--time-filter-text-hover)}.time-filter-clickable:hover .radio-circle{border-color:var(--time-filter-selected-color)}.time-filter-disabled{opacity:.6;cursor:not-allowed}.time-filter-disabled .radio-circle{border-color:var(--time-filter-disabled-color)}.time-filter-disabled .label{color:var(--time-filter-disabled-color)}.time-filter-disabled .radio-circle.selected{border-color:var(--time-filter-disabled-color)}.time-filter-disabled .radio-circle.selected .radio-dot{background:var(--time-filter-disabled-color)}@media(max-width:768px){.time-filter{--time-filter-font-size: 18px;--time-filter-circle-size: 22px;--time-filter-dot-size: 10px;--time-filter-gap: 12px}}@media(max-width:480px){.time-filter{--time-filter-font-size: 16px;--time-filter-circle-size: 20px;--time-filter-dot-size: 9px;--time-filter-gap: 10px}}\n"] }]
220
363
  }], propDecorators: { label: [{
221
364
  type: Input
222
365
  }], value: [{
223
366
  type: Input
224
367
  }], selectedValue: [{
225
368
  type: Input
369
+ }], customClass: [{
370
+ type: Input
371
+ }], clickable: [{
372
+ type: Input
373
+ }], disabled: [{
374
+ type: Input
226
375
  }], selectedValueChange: [{
227
376
  type: Output
228
377
  }], clicked: [{
@@ -337,6 +486,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
337
486
  }] } });
338
487
 
339
488
  class DateInput {
489
+ elementRef;
340
490
  // =========================================
341
491
  // INPUTS
342
492
  // =========================================
@@ -345,19 +495,55 @@ class DateInput {
345
495
  value = '';
346
496
  disabled = false;
347
497
  readonly = false;
498
+ /**
499
+ * Developer custom CSS class.
500
+ *
501
+ * Example:
502
+ *
503
+ * customClass="flight-date"
504
+ */
348
505
  customClass = '';
349
506
  // =========================================
350
507
  // OUTPUTS
351
508
  // =========================================
509
+ /**
510
+ * Emits the selected date as:
511
+ *
512
+ * YYYY-MM-DD
513
+ */
352
514
  valueChange = new EventEmitter();
515
+ /**
516
+ * Emits the selected Date object.
517
+ */
353
518
  dateChange = new EventEmitter();
519
+ /**
520
+ * Emits when the calendar opens/closes.
521
+ */
354
522
  openedChange = new EventEmitter();
523
+ /**
524
+ * Developer click event.
525
+ */
355
526
  clicked = new EventEmitter();
356
527
  // =========================================
357
528
  // INTERNAL STATE
358
529
  // =========================================
359
530
  isOpen = false;
360
531
  currentDate = new Date();
532
+ constructor(elementRef) {
533
+ this.elementRef = elementRef;
534
+ }
535
+ // =========================================
536
+ // INITIALIZE VALUE
537
+ // =========================================
538
+ ngOnInit() {
539
+ if (this.value) {
540
+ const parsedDate = this.parseDate(this.value);
541
+ if (parsedDate) {
542
+ this.currentDate =
543
+ new Date(parsedDate.getFullYear(), parsedDate.getMonth(), 1);
544
+ }
545
+ }
546
+ }
361
547
  // =========================================
362
548
  // CALENDAR DAYS
363
549
  // =========================================
@@ -367,14 +553,28 @@ class DateInput {
367
553
  const firstDay = new Date(year, month, 1);
368
554
  const lastDay = new Date(year, month + 1, 0);
369
555
  const days = [];
556
+ /*
557
+ * Sunday = 0
558
+ * Monday = 1
559
+ * ...
560
+ * Saturday = 6
561
+ */
370
562
  const startDay = firstDay.getDay();
563
+ // =====================================
564
+ // PREVIOUS MONTH DAYS
565
+ // =====================================
371
566
  for (let i = 0; i < startDay; i++) {
372
- const previousDate = new Date(year, month, i - startDay + 1);
373
- days.push(previousDate);
567
+ days.push(new Date(year, month, i - startDay + 1));
374
568
  }
569
+ // =====================================
570
+ // CURRENT MONTH DAYS
571
+ // =====================================
375
572
  for (let day = 1; day <= lastDay.getDate(); day++) {
376
573
  days.push(new Date(year, month, day));
377
574
  }
575
+ // =====================================
576
+ // NEXT MONTH DAYS
577
+ // =====================================
378
578
  const remaining = 42 - days.length;
379
579
  for (let i = 1; i <= remaining; i++) {
380
580
  days.push(new Date(year, month + 1, i));
@@ -389,11 +589,14 @@ class DateInput {
389
589
  month: 'long'
390
590
  });
391
591
  }
592
+ // =========================================
593
+ // YEAR
594
+ // =========================================
392
595
  get year() {
393
596
  return this.currentDate.getFullYear();
394
597
  }
395
598
  // =========================================
396
- // OPEN / CLOSE
599
+ // TOGGLE CALENDAR
397
600
  // =========================================
398
601
  toggleCalendar(event) {
399
602
  if (this.disabled ||
@@ -403,20 +606,23 @@ class DateInput {
403
606
  if (event) {
404
607
  this.clicked.emit(event);
405
608
  }
406
- this.isOpen = !this.isOpen;
609
+ this.isOpen =
610
+ !this.isOpen;
407
611
  this.openedChange.emit(this.isOpen);
408
612
  }
409
613
  // =========================================
410
614
  // PREVIOUS MONTH
411
615
  // =========================================
412
- previousMonth() {
616
+ previousMonth(event) {
617
+ event?.stopPropagation();
413
618
  this.currentDate =
414
619
  new Date(this.year, this.currentDate.getMonth() - 1, 1);
415
620
  }
416
621
  // =========================================
417
622
  // NEXT MONTH
418
623
  // =========================================
419
- nextMonth() {
624
+ nextMonth(event) {
625
+ event?.stopPropagation();
420
626
  this.currentDate =
421
627
  new Date(this.year, this.currentDate.getMonth() + 1, 1);
422
628
  }
@@ -424,14 +630,29 @@ class DateInput {
424
630
  // SELECT DATE
425
631
  // =========================================
426
632
  selectDate(date, event) {
427
- if (this.disabled) {
633
+ event.stopPropagation();
634
+ if (this.disabled ||
635
+ this.readonly) {
428
636
  return;
429
637
  }
638
+ /*
639
+ * Create a clean Date object.
640
+ */
641
+ const selectedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
642
+ // =====================================
643
+ // UPDATE VALUE
644
+ // =====================================
430
645
  this.value =
431
- this.formatDate(date);
646
+ this.formatDate(selectedDate);
647
+ // =====================================
648
+ // EMIT EVENTS
649
+ // =====================================
432
650
  this.valueChange.emit(this.value);
433
- this.dateChange.emit(date);
651
+ this.dateChange.emit(selectedDate);
434
652
  this.clicked.emit(event);
653
+ // =====================================
654
+ // CLOSE
655
+ // =====================================
435
656
  this.isOpen = false;
436
657
  this.openedChange.emit(false);
437
658
  }
@@ -439,15 +660,40 @@ class DateInput {
439
660
  // FORMAT DATE
440
661
  // =========================================
441
662
  formatDate(date) {
442
- const day = String(date.getDate())
443
- .padStart(2, '0');
444
- const month = String(date.getMonth() + 1)
445
- .padStart(2, '0');
663
+ const day = String(date.getDate()).padStart(2, '0');
664
+ const month = String(date.getMonth() + 1).padStart(2, '0');
446
665
  const year = date.getFullYear();
447
666
  return `${year}-${month}-${day}`;
448
667
  }
449
668
  // =========================================
450
- // CHECK CURRENT MONTH
669
+ // PARSE DATE
670
+ // =========================================
671
+ parseDate(value) {
672
+ const parts = value.split('-');
673
+ if (parts.length !== 3) {
674
+ return null;
675
+ }
676
+ const year = Number(parts[0]);
677
+ const month = Number(parts[1]);
678
+ const day = Number(parts[2]);
679
+ if (!year ||
680
+ !month ||
681
+ !day) {
682
+ return null;
683
+ }
684
+ const date = new Date(year, month - 1, day);
685
+ /*
686
+ * Validate the date.
687
+ */
688
+ if (date.getFullYear() !== year ||
689
+ date.getMonth() !== month - 1 ||
690
+ date.getDate() !== day) {
691
+ return null;
692
+ }
693
+ return date;
694
+ }
695
+ // =========================================
696
+ // CURRENT MONTH
451
697
  // =========================================
452
698
  isCurrentMonth(date) {
453
699
  return (date.getMonth() ===
@@ -456,16 +702,19 @@ class DateInput {
456
702
  this.currentDate.getFullYear());
457
703
  }
458
704
  // =========================================
459
- // CHECK TODAY
705
+ // TODAY
460
706
  // =========================================
461
707
  isToday(date) {
462
708
  const today = new Date();
463
- return (date.getDate() === today.getDate() &&
464
- date.getMonth() === today.getMonth() &&
465
- date.getFullYear() === today.getFullYear());
709
+ return (date.getDate() ===
710
+ today.getDate() &&
711
+ date.getMonth() ===
712
+ today.getMonth() &&
713
+ date.getFullYear() ===
714
+ today.getFullYear());
466
715
  }
467
716
  // =========================================
468
- // CHECK SELECTED DATE
717
+ // SELECTED DATE
469
718
  // =========================================
470
719
  isSelected(date) {
471
720
  return (this.value ===
@@ -475,21 +724,32 @@ class DateInput {
475
724
  // OUTSIDE CLICK
476
725
  // =========================================
477
726
  onDocumentClick(event) {
727
+ if (!this.isOpen) {
728
+ return;
729
+ }
478
730
  const target = event.target;
479
- const dateInput = target.closest('lib-date-input');
480
- if (!dateInput &&
481
- this.isOpen) {
482
- this.isOpen = false;
483
- this.openedChange.emit(false);
731
+ const host = this.elementRef.nativeElement;
732
+ if (!host.contains(target)) {
733
+ this.closeCalendar();
734
+ }
735
+ }
736
+ // =========================================
737
+ // CLOSE CALENDAR
738
+ // =========================================
739
+ closeCalendar() {
740
+ if (!this.isOpen) {
741
+ return;
484
742
  }
743
+ this.isOpen = false;
744
+ this.openedChange.emit(false);
485
745
  }
486
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DateInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
487
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: DateInput, isStandalone: true, selector: "lib-date-input", inputs: { label: "label", placeholder: "placeholder", value: "value", disabled: "disabled", readonly: "readonly", customClass: "customClass" }, outputs: { valueChange: "valueChange", dateChange: "dateChange", openedChange: "openedChange", clicked: "clicked" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<div\n class=\"date-input-container\"\n [class]=\"customClass\"\n [class.date-input-disabled]=\"disabled\"\n [class.date-input-open]=\"isOpen\"\n>\n\n <!-- INPUT -->\n\n <div\n class=\"date-input-field\"\n (click)=\"toggleCalendar($event)\"\n >\n\n @if (label) {\n <label class=\"date-input-label\">\n {{ label }}\n </label>\n }\n\n <span\n class=\"date-input-value\"\n [class.date-input-placeholder]=\"!value\"\n >\n {{ value || placeholder }}\n </span>\n\n <!-- Calendar Icon -->\n\n <span class=\"calendar-icon\">\n\n <span class=\"calendar-top\"></span>\n\n <span class=\"calendar-body\"></span>\n\n </span>\n\n </div>\n\n\n <!-- DATE PICKER -->\n\n @if (isOpen) {\n\n <div\n class=\"date-picker\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- HEADER -->\n\n <div class=\"date-picker-header\">\n\n <button\n type=\"button\"\n class=\"month-button\"\n (click)=\"previousMonth()\"\n >\n \u2039\n </button>\n\n <span class=\"month-title\">\n {{ monthName }} {{ year }}\n </span>\n\n <button\n type=\"button\"\n class=\"month-button\"\n (click)=\"nextMonth()\"\n >\n \u203A\n </button>\n\n </div>\n\n\n <!-- WEEK DAYS -->\n\n <div class=\"week-days\">\n\n <span>Sun</span>\n <span>Mon</span>\n <span>Tue</span>\n <span>Wed</span>\n <span>Thu</span>\n <span>Fri</span>\n <span>Sat</span>\n\n </div>\n\n\n <!-- DAYS -->\n\n <div class=\"calendar-days\">\n\n @for (\n date of calendarDays;\n track date.getTime()\n ) {\n\n <button\n type=\"button\"\n\n class=\"calendar-day\"\n\n [class.other-month]=\"\n !isCurrentMonth(date)\n \"\n\n [class.today]=\"\n isToday(date)\n \"\n\n [class.selected]=\"\n isSelected(date)\n \"\n\n (click)=\"\n selectDate(date, $event)\n \"\n >\n\n {{ date.getDate() }}\n\n </button>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [".date-input-container{--date-width: 300px;--date-height: 44px;--date-background: #ffffff;--date-border: 1px solid #a9a9a9;--date-border-hover: #777777;--date-border-focus: #68479b;--date-radius: 6px;--date-text: #222222;--date-placeholder: #777777;--date-primary: #68479b;--date-hover: #f3f0f7;--date-selected: #68479b;--date-selected-text: #ffffff;--date-font-family: Arial, Helvetica, sans-serif;--date-font-size: 14px;--date-shadow: 0 4px 12px rgba(0, 0, 0, .12);position:relative;width:var(--date-width);font-family:var(--date-font-family);box-sizing:border-box}.date-input-field{position:relative;width:100%;height:var(--date-height);display:flex;align-items:center;box-sizing:border-box;padding:0 50px 0 18px;border:var(--date-border);border-radius:var(--date-radius);background:var(--date-background);color:var(--date-text);font-size:var(--date-font-size);font-weight:600;cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.date-input-field:hover{border-color:var(--date-border-hover)}.date-input-open .date-input-field{border-color:var(--date-border-focus);box-shadow:0 0 0 1px #68479b1f}.date-input-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--date-background);color:var(--date-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.date-input-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.date-input-placeholder{color:var(--date-placeholder)}.calendar-icon{position:absolute;right:17px;width:18px;height:18px;box-sizing:border-box;border:1.8px solid #555;border-radius:3px}.calendar-top{position:absolute;left:0;top:4px;width:100%;border-top:1.8px solid #555}.calendar-icon:before,.calendar-icon:after{content:\"\";position:absolute;top:-4px;width:2px;height:6px;background:#555;border-radius:2px}.calendar-icon:before{left:4px}.calendar-icon:after{right:4px}.date-picker{position:absolute;top:calc(100% + 6px);left:0;width:100%;min-width:300px;z-index:1000;padding:14px;box-sizing:border-box;background:#fff;border:1px solid #d5d5d5;border-radius:var(--date-radius);box-shadow:var(--date-shadow)}.date-picker-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.month-title{font-size:15px;font-weight:700;color:var(--date-text)}.month-button{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#555;font-size:24px;line-height:1;cursor:pointer}.month-button:hover{background:var(--date-hover);color:var(--date-primary)}.week-days{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:6px;text-align:center;color:#777;font-size:12px;font-weight:600}.calendar-days{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}.calendar-day{width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:var(--date-text);font-size:13px;cursor:pointer;transition:background-color .15s ease,color .15s ease}.calendar-day:hover{background:var(--date-hover)}.calendar-day.other-month{color:silver}.calendar-day.today{border:1px solid var(--date-primary);color:var(--date-primary)}.calendar-day.selected{background:var(--date-selected);color:var(--date-selected-text);border:none}.date-input-disabled .date-input-field{background:#f5f5f5;border-color:#d0d0d0;color:#999;cursor:not-allowed}@media(max-width:768px){.date-input-container{width:100%}.date-picker{min-width:100%}}\n"] });
746
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DateInput, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
747
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: DateInput, isStandalone: true, selector: "lib-date-input", inputs: { label: "label", placeholder: "placeholder", value: "value", disabled: "disabled", readonly: "readonly", customClass: "customClass" }, outputs: { valueChange: "valueChange", dateChange: "dateChange", openedChange: "openedChange", clicked: "clicked" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.customClass" } }, ngImport: i0, template: "<div\n class=\"date-input-container\"\n [class.date-input-disabled]=\"disabled\"\n [class.date-input-open]=\"isOpen\"\n>\n\n <!-- =====================================\n INPUT FIELD\n ===================================== -->\n\n <div\n class=\"date-input-field\"\n (click)=\"toggleCalendar($event)\"\n >\n\n <!-- LABEL -->\n\n @if (label) {\n\n <label class=\"date-input-label\">\n\n {{ label }}\n\n </label>\n\n }\n\n\n <!-- VALUE -->\n\n <span\n class=\"date-input-value\"\n [class.date-input-placeholder]=\"!value\"\n >\n\n {{ value || placeholder }}\n\n </span>\n\n\n <!-- CALENDAR ICON -->\n\n <span class=\"calendar-icon\">\n\n <span class=\"calendar-top\"></span>\n\n <span class=\"calendar-body\"></span>\n\n </span>\n\n </div>\n\n\n <!-- =====================================\n DATE PICKER\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"date-picker\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- =================================\n HEADER\n ================================= -->\n\n <div class=\"date-picker-header\">\n\n <button\n type=\"button\"\n class=\"month-button\"\n\n aria-label=\"Previous month\"\n\n (click)=\"previousMonth($event)\"\n >\n\n \u2039\n\n </button>\n\n\n <span class=\"month-title\">\n\n {{ monthName }} {{ year }}\n\n </span>\n\n\n <button\n type=\"button\"\n class=\"month-button\"\n\n aria-label=\"Next month\"\n\n (click)=\"nextMonth($event)\"\n >\n\n \u203A\n\n </button>\n\n </div>\n\n\n <!-- =================================\n WEEK DAYS\n ================================= -->\n\n <div class=\"week-days\">\n\n <span>Sun</span>\n\n <span>Mon</span>\n\n <span>Tue</span>\n\n <span>Wed</span>\n\n <span>Thu</span>\n\n <span>Fri</span>\n\n <span>Sat</span>\n\n </div>\n\n\n <!-- =================================\n CALENDAR DAYS\n ================================= -->\n\n <div class=\"calendar-days\">\n\n @for (\n date of calendarDays;\n track date.getTime()\n ) {\n\n <button\n type=\"button\"\n\n class=\"calendar-day\"\n\n [class.other-month]=\"\n !isCurrentMonth(date)\n \"\n\n [class.today]=\"\n isToday(date)\n \"\n\n [class.selected]=\"\n isSelected(date)\n \"\n\n (click)=\"\n selectDate(date, $event)\n \"\n >\n\n {{ date.getDate() }}\n\n </button>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [":host{--date-width: 300px;--date-height: 44px;--date-background: #ffffff;--date-border: 1px solid #a9a9a9;--date-border-hover: #777777;--date-border-focus: #68479b;--date-radius: 6px;--date-text: #222222;--date-placeholder: #777777;--date-primary: #68479b;--date-hover: #f3f0f7;--date-selected: #68479b;--date-selected-text: #ffffff;--date-font-family: Arial, Helvetica, sans-serif;--date-font-size: 14px;--date-font-weight: 600;--date-shadow: 0 4px 12px rgba(0, 0, 0, .12);--date-icon-color: #555555;--date-disabled-background: #f5f5f5;--date-disabled-border: #d0d0d0;--date-disabled-text: #999999;display:block;position:relative;width:var(--date-width);box-sizing:border-box;font-family:var(--date-font-family)}.date-input-container{position:relative;width:100%;box-sizing:border-box}.date-input-field{position:relative;width:100%;height:var(--date-height);display:flex;align-items:center;box-sizing:border-box;padding:0 50px 0 18px;border:var(--date-border);border-radius:var(--date-radius);background:var(--date-background);color:var(--date-text);font-size:var(--date-font-size);font-weight:var(--date-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease,background-color .2s ease}.date-input-field:hover{border-color:var(--date-border-hover)}.date-input-open .date-input-field{border-color:var(--date-border-focus);box-shadow:0 0 0 1px #68479b1f}.date-input-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--date-background);color:var(--date-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.date-input-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--date-text)}.date-input-placeholder{color:var(--date-placeholder)}.calendar-icon{position:absolute;right:17px;width:18px;height:18px;box-sizing:border-box;border:1.8px solid var(--date-icon-color);border-radius:3px}.calendar-top{position:absolute;left:0;top:4px;width:100%;border-top:1.8px solid var(--date-icon-color)}.calendar-icon:before,.calendar-icon:after{content:\"\";position:absolute;top:-4px;width:2px;height:6px;background:var(--date-icon-color);border-radius:2px}.calendar-icon:before{left:4px}.calendar-icon:after{right:4px}.date-picker{position:absolute;top:calc(100% + 6px);left:0;width:100%;min-width:300px;z-index:1000;padding:14px;box-sizing:border-box;background:#fff;border:1px solid #d5d5d5;border-radius:var(--date-radius);box-shadow:var(--date-shadow)}.date-picker-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.month-title{font-size:15px;font-weight:700;color:var(--date-text)}.month-button{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#555;font-size:24px;line-height:1;cursor:pointer;transition:background-color .15s ease,color .15s ease}.month-button:hover{background:var(--date-hover);color:var(--date-primary)}.week-days{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:6px;text-align:center;color:#777;font-size:12px;font-weight:600}.calendar-days{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}.calendar-day{width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:var(--date-text);font-family:var(--date-font-family);font-size:13px;cursor:pointer;transition:background-color .15s ease,color .15s ease}.calendar-day:hover{background:var(--date-hover)}.calendar-day.other-month{color:silver}.calendar-day.today{border:1px solid var(--date-primary);color:var(--date-primary)}.calendar-day.selected{background:var(--date-selected);color:var(--date-selected-text);border:none}.calendar-day.selected.today{background:var(--date-selected);color:var(--date-selected-text)}.date-input-disabled .date-input-field{background:var(--date-disabled-background);border-color:var(--date-disabled-border);color:var(--date-disabled-text);cursor:not-allowed}@media(max-width:768px){:host{width:100%}.date-picker{min-width:100%}}\n"] });
488
748
  }
489
749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: DateInput, decorators: [{
490
750
  type: Component,
491
- args: [{ selector: 'lib-date-input', standalone: true, imports: [], template: "<div\n class=\"date-input-container\"\n [class]=\"customClass\"\n [class.date-input-disabled]=\"disabled\"\n [class.date-input-open]=\"isOpen\"\n>\n\n <!-- INPUT -->\n\n <div\n class=\"date-input-field\"\n (click)=\"toggleCalendar($event)\"\n >\n\n @if (label) {\n <label class=\"date-input-label\">\n {{ label }}\n </label>\n }\n\n <span\n class=\"date-input-value\"\n [class.date-input-placeholder]=\"!value\"\n >\n {{ value || placeholder }}\n </span>\n\n <!-- Calendar Icon -->\n\n <span class=\"calendar-icon\">\n\n <span class=\"calendar-top\"></span>\n\n <span class=\"calendar-body\"></span>\n\n </span>\n\n </div>\n\n\n <!-- DATE PICKER -->\n\n @if (isOpen) {\n\n <div\n class=\"date-picker\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- HEADER -->\n\n <div class=\"date-picker-header\">\n\n <button\n type=\"button\"\n class=\"month-button\"\n (click)=\"previousMonth()\"\n >\n \u2039\n </button>\n\n <span class=\"month-title\">\n {{ monthName }} {{ year }}\n </span>\n\n <button\n type=\"button\"\n class=\"month-button\"\n (click)=\"nextMonth()\"\n >\n \u203A\n </button>\n\n </div>\n\n\n <!-- WEEK DAYS -->\n\n <div class=\"week-days\">\n\n <span>Sun</span>\n <span>Mon</span>\n <span>Tue</span>\n <span>Wed</span>\n <span>Thu</span>\n <span>Fri</span>\n <span>Sat</span>\n\n </div>\n\n\n <!-- DAYS -->\n\n <div class=\"calendar-days\">\n\n @for (\n date of calendarDays;\n track date.getTime()\n ) {\n\n <button\n type=\"button\"\n\n class=\"calendar-day\"\n\n [class.other-month]=\"\n !isCurrentMonth(date)\n \"\n\n [class.today]=\"\n isToday(date)\n \"\n\n [class.selected]=\"\n isSelected(date)\n \"\n\n (click)=\"\n selectDate(date, $event)\n \"\n >\n\n {{ date.getDate() }}\n\n </button>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [".date-input-container{--date-width: 300px;--date-height: 44px;--date-background: #ffffff;--date-border: 1px solid #a9a9a9;--date-border-hover: #777777;--date-border-focus: #68479b;--date-radius: 6px;--date-text: #222222;--date-placeholder: #777777;--date-primary: #68479b;--date-hover: #f3f0f7;--date-selected: #68479b;--date-selected-text: #ffffff;--date-font-family: Arial, Helvetica, sans-serif;--date-font-size: 14px;--date-shadow: 0 4px 12px rgba(0, 0, 0, .12);position:relative;width:var(--date-width);font-family:var(--date-font-family);box-sizing:border-box}.date-input-field{position:relative;width:100%;height:var(--date-height);display:flex;align-items:center;box-sizing:border-box;padding:0 50px 0 18px;border:var(--date-border);border-radius:var(--date-radius);background:var(--date-background);color:var(--date-text);font-size:var(--date-font-size);font-weight:600;cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.date-input-field:hover{border-color:var(--date-border-hover)}.date-input-open .date-input-field{border-color:var(--date-border-focus);box-shadow:0 0 0 1px #68479b1f}.date-input-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--date-background);color:var(--date-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.date-input-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.date-input-placeholder{color:var(--date-placeholder)}.calendar-icon{position:absolute;right:17px;width:18px;height:18px;box-sizing:border-box;border:1.8px solid #555;border-radius:3px}.calendar-top{position:absolute;left:0;top:4px;width:100%;border-top:1.8px solid #555}.calendar-icon:before,.calendar-icon:after{content:\"\";position:absolute;top:-4px;width:2px;height:6px;background:#555;border-radius:2px}.calendar-icon:before{left:4px}.calendar-icon:after{right:4px}.date-picker{position:absolute;top:calc(100% + 6px);left:0;width:100%;min-width:300px;z-index:1000;padding:14px;box-sizing:border-box;background:#fff;border:1px solid #d5d5d5;border-radius:var(--date-radius);box-shadow:var(--date-shadow)}.date-picker-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.month-title{font-size:15px;font-weight:700;color:var(--date-text)}.month-button{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#555;font-size:24px;line-height:1;cursor:pointer}.month-button:hover{background:var(--date-hover);color:var(--date-primary)}.week-days{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:6px;text-align:center;color:#777;font-size:12px;font-weight:600}.calendar-days{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}.calendar-day{width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:var(--date-text);font-size:13px;cursor:pointer;transition:background-color .15s ease,color .15s ease}.calendar-day:hover{background:var(--date-hover)}.calendar-day.other-month{color:silver}.calendar-day.today{border:1px solid var(--date-primary);color:var(--date-primary)}.calendar-day.selected{background:var(--date-selected);color:var(--date-selected-text);border:none}.date-input-disabled .date-input-field{background:#f5f5f5;border-color:#d0d0d0;color:#999;cursor:not-allowed}@media(max-width:768px){.date-input-container{width:100%}.date-picker{min-width:100%}}\n"] }]
492
- }], propDecorators: { label: [{
751
+ args: [{ selector: 'lib-date-input', standalone: true, imports: [], template: "<div\n class=\"date-input-container\"\n [class.date-input-disabled]=\"disabled\"\n [class.date-input-open]=\"isOpen\"\n>\n\n <!-- =====================================\n INPUT FIELD\n ===================================== -->\n\n <div\n class=\"date-input-field\"\n (click)=\"toggleCalendar($event)\"\n >\n\n <!-- LABEL -->\n\n @if (label) {\n\n <label class=\"date-input-label\">\n\n {{ label }}\n\n </label>\n\n }\n\n\n <!-- VALUE -->\n\n <span\n class=\"date-input-value\"\n [class.date-input-placeholder]=\"!value\"\n >\n\n {{ value || placeholder }}\n\n </span>\n\n\n <!-- CALENDAR ICON -->\n\n <span class=\"calendar-icon\">\n\n <span class=\"calendar-top\"></span>\n\n <span class=\"calendar-body\"></span>\n\n </span>\n\n </div>\n\n\n <!-- =====================================\n DATE PICKER\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"date-picker\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- =================================\n HEADER\n ================================= -->\n\n <div class=\"date-picker-header\">\n\n <button\n type=\"button\"\n class=\"month-button\"\n\n aria-label=\"Previous month\"\n\n (click)=\"previousMonth($event)\"\n >\n\n \u2039\n\n </button>\n\n\n <span class=\"month-title\">\n\n {{ monthName }} {{ year }}\n\n </span>\n\n\n <button\n type=\"button\"\n class=\"month-button\"\n\n aria-label=\"Next month\"\n\n (click)=\"nextMonth($event)\"\n >\n\n \u203A\n\n </button>\n\n </div>\n\n\n <!-- =================================\n WEEK DAYS\n ================================= -->\n\n <div class=\"week-days\">\n\n <span>Sun</span>\n\n <span>Mon</span>\n\n <span>Tue</span>\n\n <span>Wed</span>\n\n <span>Thu</span>\n\n <span>Fri</span>\n\n <span>Sat</span>\n\n </div>\n\n\n <!-- =================================\n CALENDAR DAYS\n ================================= -->\n\n <div class=\"calendar-days\">\n\n @for (\n date of calendarDays;\n track date.getTime()\n ) {\n\n <button\n type=\"button\"\n\n class=\"calendar-day\"\n\n [class.other-month]=\"\n !isCurrentMonth(date)\n \"\n\n [class.today]=\"\n isToday(date)\n \"\n\n [class.selected]=\"\n isSelected(date)\n \"\n\n (click)=\"\n selectDate(date, $event)\n \"\n >\n\n {{ date.getDate() }}\n\n </button>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [":host{--date-width: 300px;--date-height: 44px;--date-background: #ffffff;--date-border: 1px solid #a9a9a9;--date-border-hover: #777777;--date-border-focus: #68479b;--date-radius: 6px;--date-text: #222222;--date-placeholder: #777777;--date-primary: #68479b;--date-hover: #f3f0f7;--date-selected: #68479b;--date-selected-text: #ffffff;--date-font-family: Arial, Helvetica, sans-serif;--date-font-size: 14px;--date-font-weight: 600;--date-shadow: 0 4px 12px rgba(0, 0, 0, .12);--date-icon-color: #555555;--date-disabled-background: #f5f5f5;--date-disabled-border: #d0d0d0;--date-disabled-text: #999999;display:block;position:relative;width:var(--date-width);box-sizing:border-box;font-family:var(--date-font-family)}.date-input-container{position:relative;width:100%;box-sizing:border-box}.date-input-field{position:relative;width:100%;height:var(--date-height);display:flex;align-items:center;box-sizing:border-box;padding:0 50px 0 18px;border:var(--date-border);border-radius:var(--date-radius);background:var(--date-background);color:var(--date-text);font-size:var(--date-font-size);font-weight:var(--date-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease,background-color .2s ease}.date-input-field:hover{border-color:var(--date-border-hover)}.date-input-open .date-input-field{border-color:var(--date-border-focus);box-shadow:0 0 0 1px #68479b1f}.date-input-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--date-background);color:var(--date-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.date-input-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--date-text)}.date-input-placeholder{color:var(--date-placeholder)}.calendar-icon{position:absolute;right:17px;width:18px;height:18px;box-sizing:border-box;border:1.8px solid var(--date-icon-color);border-radius:3px}.calendar-top{position:absolute;left:0;top:4px;width:100%;border-top:1.8px solid var(--date-icon-color)}.calendar-icon:before,.calendar-icon:after{content:\"\";position:absolute;top:-4px;width:2px;height:6px;background:var(--date-icon-color);border-radius:2px}.calendar-icon:before{left:4px}.calendar-icon:after{right:4px}.date-picker{position:absolute;top:calc(100% + 6px);left:0;width:100%;min-width:300px;z-index:1000;padding:14px;box-sizing:border-box;background:#fff;border:1px solid #d5d5d5;border-radius:var(--date-radius);box-shadow:var(--date-shadow)}.date-picker-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.month-title{font-size:15px;font-weight:700;color:var(--date-text)}.month-button{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:#555;font-size:24px;line-height:1;cursor:pointer;transition:background-color .15s ease,color .15s ease}.month-button:hover{background:var(--date-hover);color:var(--date-primary)}.week-days{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:6px;text-align:center;color:#777;font-size:12px;font-weight:600}.calendar-days{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}.calendar-day{width:100%;aspect-ratio:1;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:transparent;color:var(--date-text);font-family:var(--date-font-family);font-size:13px;cursor:pointer;transition:background-color .15s ease,color .15s ease}.calendar-day:hover{background:var(--date-hover)}.calendar-day.other-month{color:silver}.calendar-day.today{border:1px solid var(--date-primary);color:var(--date-primary)}.calendar-day.selected{background:var(--date-selected);color:var(--date-selected-text);border:none}.calendar-day.selected.today{background:var(--date-selected);color:var(--date-selected-text)}.date-input-disabled .date-input-field{background:var(--date-disabled-background);border-color:var(--date-disabled-border);color:var(--date-disabled-text);cursor:not-allowed}@media(max-width:768px){:host{width:100%}.date-picker{min-width:100%}}\n"] }]
752
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{
493
753
  type: Input
494
754
  }], placeholder: [{
495
755
  type: Input
@@ -501,6 +761,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
501
761
  type: Input
502
762
  }], customClass: [{
503
763
  type: Input
764
+ }, {
765
+ type: HostBinding,
766
+ args: ['class']
504
767
  }], valueChange: [{
505
768
  type: Output
506
769
  }], dateChange: [{
@@ -516,6 +779,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
516
779
  }] } });
517
780
 
518
781
  class Dropdown {
782
+ elementRef;
519
783
  // =========================================
520
784
  // INPUTS
521
785
  // =========================================
@@ -529,27 +793,18 @@ class Dropdown {
529
793
  // =========================================
530
794
  // OUTPUTS
531
795
  // =========================================
532
- /**
533
- * Emits the selected value.
534
- */
535
796
  valueChange = new EventEmitter();
536
- /**
537
- * Emits the complete selected option.
538
- */
539
797
  selectionChange = new EventEmitter();
540
- /**
541
- * Emits when the dropdown opens/closes.
542
- */
543
798
  openedChange = new EventEmitter();
544
- /**
545
- * Developer click event.
546
- */
547
799
  clicked = new EventEmitter();
548
800
  // =========================================
549
- // INTERNAL STATE
801
+ // STATE
550
802
  // =========================================
551
803
  isOpen = false;
552
804
  searchText = '';
805
+ constructor(elementRef) {
806
+ this.elementRef = elementRef;
807
+ }
553
808
  // =========================================
554
809
  // FILTERED OPTIONS
555
810
  // =========================================
@@ -571,7 +826,7 @@ class Dropdown {
571
826
  return this.options.find(option => option.value === this.value);
572
827
  }
573
828
  // =========================================
574
- // OPEN / CLOSE
829
+ // TOGGLE
575
830
  // =========================================
576
831
  toggleDropdown(event) {
577
832
  if (this.disabled) {
@@ -580,35 +835,52 @@ class Dropdown {
580
835
  if (event) {
581
836
  this.clicked.emit(event);
582
837
  }
583
- this.isOpen = !this.isOpen;
838
+ this.isOpen =
839
+ !this.isOpen;
584
840
  this.openedChange.emit(this.isOpen);
585
841
  if (!this.isOpen) {
586
842
  this.searchText = '';
587
843
  }
588
844
  }
845
+ // =========================================
846
+ // DOCUMENT CLICK
847
+ // =========================================
589
848
  onDocumentClick(event) {
849
+ if (!this.isOpen) {
850
+ return;
851
+ }
590
852
  const target = event.target;
591
- const dropdown = target.closest('lib-dropdown');
592
- if (!dropdown && this.isOpen) {
593
- this.isOpen = false;
594
- this.searchText = '';
595
- this.openedChange.emit(false);
853
+ const host = this.elementRef.nativeElement;
854
+ if (!host.contains(target)) {
855
+ this.closeDropdown();
596
856
  }
597
857
  }
598
858
  // =========================================
599
- // SELECT OPTION
859
+ // CLOSE
860
+ // =========================================
861
+ closeDropdown() {
862
+ if (!this.isOpen) {
863
+ return;
864
+ }
865
+ this.isOpen = false;
866
+ this.searchText = '';
867
+ this.openedChange.emit(false);
868
+ }
869
+ // =========================================
870
+ // SELECT
600
871
  // =========================================
601
872
  selectOption(option, event) {
602
873
  if (option.disabled) {
603
874
  return;
604
875
  }
605
- this.value = option.value;
876
+ this.value =
877
+ option.value;
606
878
  this.valueChange.emit(option.value);
607
879
  this.selectionChange.emit(option);
608
880
  this.clicked.emit(event);
609
881
  this.isOpen = false;
610
- this.openedChange.emit(false);
611
882
  this.searchText = '';
883
+ this.openedChange.emit(false);
612
884
  }
613
885
  // =========================================
614
886
  // SEARCH
@@ -618,13 +890,13 @@ class Dropdown {
618
890
  this.searchText =
619
891
  input.value;
620
892
  }
621
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
622
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: Dropdown, isStandalone: true, selector: "lib-dropdown", inputs: { label: "label", placeholder: "placeholder", options: "options", value: "value", disabled: "disabled", searchable: "searchable", customClass: "customClass" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange", openedChange: "openedChange", clicked: "clicked" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<div\n class=\"dropdown-container\"\n [class]=\"customClass\"\n [class.dropdown-disabled]=\"disabled\"\n [class.dropdown-open]=\"isOpen\"\n>\n\n <!-- =====================================\n DROPDOWN FIELD\n ===================================== -->\n\n <div\n class=\"dropdown-field\"\n (click)=\"toggleDropdown($event)\"\n >\n\n <!-- Label -->\n\n @if (label) {\n <label class=\"dropdown-label\">\n {{ label }}\n </label>\n }\n\n\n <!-- Selected value -->\n\n <span\n class=\"dropdown-value\"\n [class.dropdown-placeholder]=\"!selectedOption\"\n >\n {{\n selectedOption?.label ||\n placeholder\n }}\n </span>\n\n\n <!-- Arrow -->\n\n <span\n class=\"dropdown-arrow\"\n [class.arrow-up]=\"isOpen\"\n >\n </span>\n\n </div>\n\n\n <!-- =====================================\n DROPDOWN MENU\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- Search -->\n\n @if (searchable) {\n\n <div class=\"dropdown-search\">\n\n <input\n type=\"text\"\n class=\"dropdown-search-input\"\n\n placeholder=\"Search...\"\n\n [value]=\"searchText\"\n\n (input)=\"onSearch($event)\"\n />\n\n </div>\n\n }\n\n\n <!-- Options -->\n\n <div class=\"dropdown-options\">\n\n @for (\n option of filteredOptions;\n track option.value\n ) {\n\n <div\n class=\"dropdown-option\"\n\n [class.option-disabled]=\"\n option.disabled\n \"\n\n [class.option-selected]=\"\n option.value === value\n \"\n\n (click)=\"\n selectOption(option, $event)\n \"\n >\n\n {{ option.label }}\n\n </div>\n\n }\n\n\n <!-- No result -->\n\n @if (filteredOptions.length === 0) {\n\n <div class=\"dropdown-no-result\">\n No results found\n </div>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [".dropdown-container{--dropdown-width: 300px;--dropdown-height: 44px;--dropdown-background: #ffffff;--dropdown-border: 1px solid #a9a9a9;--dropdown-border-hover: #777777;--dropdown-border-focus: #68479b;--dropdown-radius: 6px;--dropdown-text-color: #222222;--dropdown-placeholder-color: #777777;--dropdown-menu-background: #ffffff;--dropdown-option-hover: #f3f0f7;--dropdown-option-selected: #eee8f5;--dropdown-disabled: #f5f5f5;--dropdown-font-family: Arial, Helvetica, sans-serif;--dropdown-font-size: 14px;--dropdown-font-weight: 600;--dropdown-shadow: 0 4px 12px rgba(0, 0, 0, .12);position:relative;width:var(--dropdown-width);font-family:var(--dropdown-font-family);box-sizing:border-box}.dropdown-field{position:relative;width:100%;height:var(--dropdown-height);display:flex;align-items:center;box-sizing:border-box;padding:0 48px 0 18px;border:var(--dropdown-border);border-radius:var(--dropdown-radius);background:var(--dropdown-background);color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);font-weight:var(--dropdown-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.dropdown-field:hover{border-color:var(--dropdown-border-hover)}.dropdown-open .dropdown-field{border-color:var(--dropdown-border-focus);box-shadow:0 0 0 1px #68479b1f}.dropdown-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--dropdown-background);color:var(--dropdown-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.dropdown-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--dropdown-text-color)}.dropdown-placeholder{color:var(--dropdown-placeholder-color)}.dropdown-arrow{position:absolute;right:18px;top:50%;width:9px;height:9px;border-right:2px solid #555;border-bottom:2px solid #555;transform:translateY(-65%) rotate(45deg);transition:transform .2s ease}.dropdown-arrow.arrow-up{transform:translateY(-25%) rotate(225deg)}.dropdown-menu{position:absolute;top:calc(100% + 6px);left:0;width:100%;z-index:1000;overflow:hidden;background:var(--dropdown-menu-background);border:1px solid #d0d0d0;border-radius:var(--dropdown-radius);box-shadow:var(--dropdown-shadow)}.dropdown-search{padding:8px;border-bottom:1px solid #eeeeee}.dropdown-search-input{width:100%;height:38px;box-sizing:border-box;padding:0 12px;border:1px solid #cfcfcf;border-radius:5px;outline:none;background:#fff;font-family:var(--dropdown-font-family);font-size:14px}.dropdown-search-input:focus{border-color:var(--dropdown-border-focus)}.dropdown-options{max-height:240px;overflow-y:auto}.dropdown-option{min-height:42px;display:flex;align-items:center;padding:0 14px;box-sizing:border-box;color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);cursor:pointer;transition:background-color .15s ease}.dropdown-option:hover{background:var(--dropdown-option-hover)}.dropdown-option.option-selected{background:var(--dropdown-option-selected);color:var(--dropdown-border-focus);font-weight:600}.dropdown-option.option-disabled{color:#aaa;cursor:not-allowed;background:transparent}.dropdown-no-result{min-height:50px;display:flex;align-items:center;justify-content:center;padding:0 15px;color:#888;font-size:13px}.dropdown-disabled .dropdown-field{background:var(--dropdown-disabled);color:#999;border-color:#d0d0d0;cursor:not-allowed}.dropdown-options::-webkit-scrollbar{width:6px}.dropdown-options::-webkit-scrollbar-thumb{background:#c5c5c5;border-radius:10px}@media(max-width:768px){.dropdown-container{width:100%}.dropdown-field{height:var(--dropdown-height)}}\n"] });
893
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Dropdown, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
894
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: Dropdown, isStandalone: true, selector: "lib-dropdown", inputs: { label: "label", placeholder: "placeholder", options: "options", value: "value", disabled: "disabled", searchable: "searchable", customClass: "customClass" }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange", openedChange: "openedChange", clicked: "clicked" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, properties: { "class": "this.customClass" } }, ngImport: i0, template: "<div\n class=\"dropdown-container\"\n [class.dropdown-disabled]=\"disabled\"\n [class.dropdown-open]=\"isOpen\"\n>\n\n <!-- =====================================\n FIELD\n ===================================== -->\n\n <div\n class=\"dropdown-field\"\n (click)=\"toggleDropdown($event)\"\n >\n\n <!-- Label -->\n\n @if (label) {\n\n <label class=\"dropdown-label\">\n {{ label }}\n </label>\n\n }\n\n\n <!-- Selected Value -->\n\n <span\n class=\"dropdown-value\"\n [class.dropdown-placeholder]=\"\n !selectedOption\n \"\n >\n\n {{\n selectedOption?.label ||\n placeholder\n }}\n\n </span>\n\n\n <!-- Arrow -->\n\n <span\n class=\"dropdown-arrow\"\n [class.arrow-up]=\"isOpen\"\n ></span>\n\n </div>\n\n\n <!-- =====================================\n MENU\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- SEARCH -->\n\n @if (searchable) {\n\n <div class=\"dropdown-search\">\n\n <input\n type=\"text\"\n class=\"dropdown-search-input\"\n\n placeholder=\"Search...\"\n\n [value]=\"searchText\"\n\n (input)=\"onSearch($event)\"\n />\n\n </div>\n\n }\n\n\n <!-- OPTIONS -->\n\n <div class=\"dropdown-options\">\n\n @for (\n option of filteredOptions;\n track option.value\n ) {\n\n <div\n class=\"dropdown-option\"\n\n [class.option-disabled]=\"\n option.disabled\n \"\n\n [class.option-selected]=\"\n option.value === value\n \"\n\n (click)=\"\n selectOption(option, $event)\n \"\n >\n\n {{ option.label }}\n\n </div>\n\n }\n\n\n <!-- NO RESULTS -->\n\n @if (\n filteredOptions.length === 0\n ) {\n\n <div class=\"dropdown-no-result\">\n\n No results found\n\n </div>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [":host{--dropdown-width: 300px;--dropdown-height: 44px;--dropdown-background: #ffffff;--dropdown-border: 1px solid #a9a9a9;--dropdown-border-hover: #777777;--dropdown-border-focus: #68479b;--dropdown-radius: 6px;--dropdown-text-color: #222222;--dropdown-placeholder-color: #777777;--dropdown-menu-background: #ffffff;--dropdown-option-hover: #f3f0f7;--dropdown-option-selected: #eee8f5;--dropdown-disabled: #f5f5f5;--dropdown-font-family: Arial, Helvetica, sans-serif;--dropdown-font-size: 14px;--dropdown-font-weight: 600;--dropdown-shadow: 0 4px 12px rgba(0, 0, 0, .12);--dropdown-label-color: #68479b;--dropdown-arrow-color: #555555;--dropdown-search-border: #cfcfcf;display:block;position:relative;width:var(--dropdown-width);box-sizing:border-box}.dropdown-container{position:relative;width:100%;font-family:var(--dropdown-font-family);box-sizing:border-box}.dropdown-field{position:relative;width:100%;height:var(--dropdown-height);display:flex;align-items:center;box-sizing:border-box;padding:0 48px 0 18px;border:var(--dropdown-border);border-radius:var(--dropdown-radius);background:var(--dropdown-background);color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);font-weight:var(--dropdown-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.dropdown-field:hover{border-color:var(--dropdown-border-hover)}.dropdown-open .dropdown-field{border-color:var(--dropdown-border-focus);box-shadow:0 0 0 1px #68479b1f}.dropdown-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--dropdown-background);color:var(--dropdown-label-color);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.dropdown-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--dropdown-text-color)}.dropdown-placeholder{color:var(--dropdown-placeholder-color)}.dropdown-arrow{position:absolute;right:18px;top:50%;width:9px;height:9px;border-right:2px solid var(--dropdown-arrow-color);border-bottom:2px solid var(--dropdown-arrow-color);transform:translateY(-65%) rotate(45deg);transition:transform .2s ease}.dropdown-arrow.arrow-up{transform:translateY(-25%) rotate(225deg)}.dropdown-menu{position:absolute;top:calc(100% + 6px);left:0;width:100%;z-index:1000;overflow:hidden;background:var(--dropdown-menu-background);border:1px solid #d0d0d0;border-radius:var(--dropdown-radius);box-shadow:var(--dropdown-shadow)}.dropdown-search{padding:8px;border-bottom:1px solid #eeeeee}.dropdown-search-input{width:100%;height:38px;box-sizing:border-box;padding:0 12px;border:1px solid var(--dropdown-search-border);border-radius:5px;outline:none;background:#fff;color:var(--dropdown-text-color);font-family:var(--dropdown-font-family);font-size:14px}.dropdown-search-input:focus{border-color:var(--dropdown-border-focus)}.dropdown-options{max-height:240px;overflow-y:auto}.dropdown-option{min-height:42px;display:flex;align-items:center;padding:0 14px;box-sizing:border-box;color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);cursor:pointer;transition:background-color .15s ease}.dropdown-option:hover{background:var(--dropdown-option-hover)}.dropdown-option.option-selected{background:var(--dropdown-option-selected);color:var(--dropdown-border-focus);font-weight:600}.dropdown-option.option-disabled{color:#aaa;cursor:not-allowed;background:transparent}.dropdown-no-result{min-height:50px;display:flex;align-items:center;justify-content:center;padding:0 15px;color:#888;font-size:13px}.dropdown-disabled .dropdown-field{background:var(--dropdown-disabled);color:#999;border-color:#d0d0d0;cursor:not-allowed}.dropdown-options::-webkit-scrollbar{width:6px}.dropdown-options::-webkit-scrollbar-thumb{background:#c5c5c5;border-radius:10px}@media(max-width:768px){:host{width:100%}.dropdown-field{height:var(--dropdown-height)}}\n"] });
623
895
  }
624
896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: Dropdown, decorators: [{
625
897
  type: Component,
626
- args: [{ selector: 'lib-dropdown', standalone: true, imports: [], template: "<div\n class=\"dropdown-container\"\n [class]=\"customClass\"\n [class.dropdown-disabled]=\"disabled\"\n [class.dropdown-open]=\"isOpen\"\n>\n\n <!-- =====================================\n DROPDOWN FIELD\n ===================================== -->\n\n <div\n class=\"dropdown-field\"\n (click)=\"toggleDropdown($event)\"\n >\n\n <!-- Label -->\n\n @if (label) {\n <label class=\"dropdown-label\">\n {{ label }}\n </label>\n }\n\n\n <!-- Selected value -->\n\n <span\n class=\"dropdown-value\"\n [class.dropdown-placeholder]=\"!selectedOption\"\n >\n {{\n selectedOption?.label ||\n placeholder\n }}\n </span>\n\n\n <!-- Arrow -->\n\n <span\n class=\"dropdown-arrow\"\n [class.arrow-up]=\"isOpen\"\n >\n </span>\n\n </div>\n\n\n <!-- =====================================\n DROPDOWN MENU\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- Search -->\n\n @if (searchable) {\n\n <div class=\"dropdown-search\">\n\n <input\n type=\"text\"\n class=\"dropdown-search-input\"\n\n placeholder=\"Search...\"\n\n [value]=\"searchText\"\n\n (input)=\"onSearch($event)\"\n />\n\n </div>\n\n }\n\n\n <!-- Options -->\n\n <div class=\"dropdown-options\">\n\n @for (\n option of filteredOptions;\n track option.value\n ) {\n\n <div\n class=\"dropdown-option\"\n\n [class.option-disabled]=\"\n option.disabled\n \"\n\n [class.option-selected]=\"\n option.value === value\n \"\n\n (click)=\"\n selectOption(option, $event)\n \"\n >\n\n {{ option.label }}\n\n </div>\n\n }\n\n\n <!-- No result -->\n\n @if (filteredOptions.length === 0) {\n\n <div class=\"dropdown-no-result\">\n No results found\n </div>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [".dropdown-container{--dropdown-width: 300px;--dropdown-height: 44px;--dropdown-background: #ffffff;--dropdown-border: 1px solid #a9a9a9;--dropdown-border-hover: #777777;--dropdown-border-focus: #68479b;--dropdown-radius: 6px;--dropdown-text-color: #222222;--dropdown-placeholder-color: #777777;--dropdown-menu-background: #ffffff;--dropdown-option-hover: #f3f0f7;--dropdown-option-selected: #eee8f5;--dropdown-disabled: #f5f5f5;--dropdown-font-family: Arial, Helvetica, sans-serif;--dropdown-font-size: 14px;--dropdown-font-weight: 600;--dropdown-shadow: 0 4px 12px rgba(0, 0, 0, .12);position:relative;width:var(--dropdown-width);font-family:var(--dropdown-font-family);box-sizing:border-box}.dropdown-field{position:relative;width:100%;height:var(--dropdown-height);display:flex;align-items:center;box-sizing:border-box;padding:0 48px 0 18px;border:var(--dropdown-border);border-radius:var(--dropdown-radius);background:var(--dropdown-background);color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);font-weight:var(--dropdown-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.dropdown-field:hover{border-color:var(--dropdown-border-hover)}.dropdown-open .dropdown-field{border-color:var(--dropdown-border-focus);box-shadow:0 0 0 1px #68479b1f}.dropdown-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--dropdown-background);color:var(--dropdown-border-focus);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.dropdown-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--dropdown-text-color)}.dropdown-placeholder{color:var(--dropdown-placeholder-color)}.dropdown-arrow{position:absolute;right:18px;top:50%;width:9px;height:9px;border-right:2px solid #555;border-bottom:2px solid #555;transform:translateY(-65%) rotate(45deg);transition:transform .2s ease}.dropdown-arrow.arrow-up{transform:translateY(-25%) rotate(225deg)}.dropdown-menu{position:absolute;top:calc(100% + 6px);left:0;width:100%;z-index:1000;overflow:hidden;background:var(--dropdown-menu-background);border:1px solid #d0d0d0;border-radius:var(--dropdown-radius);box-shadow:var(--dropdown-shadow)}.dropdown-search{padding:8px;border-bottom:1px solid #eeeeee}.dropdown-search-input{width:100%;height:38px;box-sizing:border-box;padding:0 12px;border:1px solid #cfcfcf;border-radius:5px;outline:none;background:#fff;font-family:var(--dropdown-font-family);font-size:14px}.dropdown-search-input:focus{border-color:var(--dropdown-border-focus)}.dropdown-options{max-height:240px;overflow-y:auto}.dropdown-option{min-height:42px;display:flex;align-items:center;padding:0 14px;box-sizing:border-box;color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);cursor:pointer;transition:background-color .15s ease}.dropdown-option:hover{background:var(--dropdown-option-hover)}.dropdown-option.option-selected{background:var(--dropdown-option-selected);color:var(--dropdown-border-focus);font-weight:600}.dropdown-option.option-disabled{color:#aaa;cursor:not-allowed;background:transparent}.dropdown-no-result{min-height:50px;display:flex;align-items:center;justify-content:center;padding:0 15px;color:#888;font-size:13px}.dropdown-disabled .dropdown-field{background:var(--dropdown-disabled);color:#999;border-color:#d0d0d0;cursor:not-allowed}.dropdown-options::-webkit-scrollbar{width:6px}.dropdown-options::-webkit-scrollbar-thumb{background:#c5c5c5;border-radius:10px}@media(max-width:768px){.dropdown-container{width:100%}.dropdown-field{height:var(--dropdown-height)}}\n"] }]
627
- }], propDecorators: { label: [{
898
+ args: [{ selector: 'lib-dropdown', standalone: true, imports: [], template: "<div\n class=\"dropdown-container\"\n [class.dropdown-disabled]=\"disabled\"\n [class.dropdown-open]=\"isOpen\"\n>\n\n <!-- =====================================\n FIELD\n ===================================== -->\n\n <div\n class=\"dropdown-field\"\n (click)=\"toggleDropdown($event)\"\n >\n\n <!-- Label -->\n\n @if (label) {\n\n <label class=\"dropdown-label\">\n {{ label }}\n </label>\n\n }\n\n\n <!-- Selected Value -->\n\n <span\n class=\"dropdown-value\"\n [class.dropdown-placeholder]=\"\n !selectedOption\n \"\n >\n\n {{\n selectedOption?.label ||\n placeholder\n }}\n\n </span>\n\n\n <!-- Arrow -->\n\n <span\n class=\"dropdown-arrow\"\n [class.arrow-up]=\"isOpen\"\n ></span>\n\n </div>\n\n\n <!-- =====================================\n MENU\n ===================================== -->\n\n @if (isOpen) {\n\n <div\n class=\"dropdown-menu\"\n (click)=\"$event.stopPropagation()\"\n >\n\n <!-- SEARCH -->\n\n @if (searchable) {\n\n <div class=\"dropdown-search\">\n\n <input\n type=\"text\"\n class=\"dropdown-search-input\"\n\n placeholder=\"Search...\"\n\n [value]=\"searchText\"\n\n (input)=\"onSearch($event)\"\n />\n\n </div>\n\n }\n\n\n <!-- OPTIONS -->\n\n <div class=\"dropdown-options\">\n\n @for (\n option of filteredOptions;\n track option.value\n ) {\n\n <div\n class=\"dropdown-option\"\n\n [class.option-disabled]=\"\n option.disabled\n \"\n\n [class.option-selected]=\"\n option.value === value\n \"\n\n (click)=\"\n selectOption(option, $event)\n \"\n >\n\n {{ option.label }}\n\n </div>\n\n }\n\n\n <!-- NO RESULTS -->\n\n @if (\n filteredOptions.length === 0\n ) {\n\n <div class=\"dropdown-no-result\">\n\n No results found\n\n </div>\n\n }\n\n </div>\n\n </div>\n\n }\n\n</div>", styles: [":host{--dropdown-width: 300px;--dropdown-height: 44px;--dropdown-background: #ffffff;--dropdown-border: 1px solid #a9a9a9;--dropdown-border-hover: #777777;--dropdown-border-focus: #68479b;--dropdown-radius: 6px;--dropdown-text-color: #222222;--dropdown-placeholder-color: #777777;--dropdown-menu-background: #ffffff;--dropdown-option-hover: #f3f0f7;--dropdown-option-selected: #eee8f5;--dropdown-disabled: #f5f5f5;--dropdown-font-family: Arial, Helvetica, sans-serif;--dropdown-font-size: 14px;--dropdown-font-weight: 600;--dropdown-shadow: 0 4px 12px rgba(0, 0, 0, .12);--dropdown-label-color: #68479b;--dropdown-arrow-color: #555555;--dropdown-search-border: #cfcfcf;display:block;position:relative;width:var(--dropdown-width);box-sizing:border-box}.dropdown-container{position:relative;width:100%;font-family:var(--dropdown-font-family);box-sizing:border-box}.dropdown-field{position:relative;width:100%;height:var(--dropdown-height);display:flex;align-items:center;box-sizing:border-box;padding:0 48px 0 18px;border:var(--dropdown-border);border-radius:var(--dropdown-radius);background:var(--dropdown-background);color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);font-weight:var(--dropdown-font-weight);cursor:pointer;transition:border-color .2s ease,box-shadow .2s ease}.dropdown-field:hover{border-color:var(--dropdown-border-hover)}.dropdown-open .dropdown-field{border-color:var(--dropdown-border-focus);box-shadow:0 0 0 1px #68479b1f}.dropdown-label{position:absolute;top:0;left:14px;transform:translateY(-50%);padding:0 6px;background:var(--dropdown-background);color:var(--dropdown-label-color);font-size:13px;font-weight:500;line-height:1;pointer-events:none}.dropdown-value{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--dropdown-text-color)}.dropdown-placeholder{color:var(--dropdown-placeholder-color)}.dropdown-arrow{position:absolute;right:18px;top:50%;width:9px;height:9px;border-right:2px solid var(--dropdown-arrow-color);border-bottom:2px solid var(--dropdown-arrow-color);transform:translateY(-65%) rotate(45deg);transition:transform .2s ease}.dropdown-arrow.arrow-up{transform:translateY(-25%) rotate(225deg)}.dropdown-menu{position:absolute;top:calc(100% + 6px);left:0;width:100%;z-index:1000;overflow:hidden;background:var(--dropdown-menu-background);border:1px solid #d0d0d0;border-radius:var(--dropdown-radius);box-shadow:var(--dropdown-shadow)}.dropdown-search{padding:8px;border-bottom:1px solid #eeeeee}.dropdown-search-input{width:100%;height:38px;box-sizing:border-box;padding:0 12px;border:1px solid var(--dropdown-search-border);border-radius:5px;outline:none;background:#fff;color:var(--dropdown-text-color);font-family:var(--dropdown-font-family);font-size:14px}.dropdown-search-input:focus{border-color:var(--dropdown-border-focus)}.dropdown-options{max-height:240px;overflow-y:auto}.dropdown-option{min-height:42px;display:flex;align-items:center;padding:0 14px;box-sizing:border-box;color:var(--dropdown-text-color);font-size:var(--dropdown-font-size);cursor:pointer;transition:background-color .15s ease}.dropdown-option:hover{background:var(--dropdown-option-hover)}.dropdown-option.option-selected{background:var(--dropdown-option-selected);color:var(--dropdown-border-focus);font-weight:600}.dropdown-option.option-disabled{color:#aaa;cursor:not-allowed;background:transparent}.dropdown-no-result{min-height:50px;display:flex;align-items:center;justify-content:center;padding:0 15px;color:#888;font-size:13px}.dropdown-disabled .dropdown-field{background:var(--dropdown-disabled);color:#999;border-color:#d0d0d0;cursor:not-allowed}.dropdown-options::-webkit-scrollbar{width:6px}.dropdown-options::-webkit-scrollbar-thumb{background:#c5c5c5;border-radius:10px}@media(max-width:768px){:host{width:100%}.dropdown-field{height:var(--dropdown-height)}}\n"] }]
899
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{
628
900
  type: Input
629
901
  }], placeholder: [{
630
902
  type: Input
@@ -638,6 +910,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
638
910
  type: Input
639
911
  }], customClass: [{
640
912
  type: Input
913
+ }, {
914
+ type: HostBinding,
915
+ args: ['class']
641
916
  }], valueChange: [{
642
917
  type: Output
643
918
  }], selectionChange: [{
@@ -648,7 +923,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
648
923
  type: Output
649
924
  }], onDocumentClick: [{
650
925
  type: HostListener,
651
- args: ['document:click', ['$event']]
926
+ args: ['document:click',
927
+ ['$event']]
652
928
  }] } });
653
929
 
654
930
  /*