fn-input 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/fn-input.mjs +14 -158
- package/fesm2022/fn-input.mjs.map +1 -1
- package/lib/fn-input.component.d.ts +2 -8
- package/lib/fn-input.component.d.ts.map +1 -1
- package/package.json +2 -4
- package/esm2022/fn-input.mjs +0 -5
- package/esm2022/lib/fn-input.component.mjs +0 -1192
- package/esm2022/lib/fn-input.types.mjs +0 -12
- package/esm2022/public-api.mjs +0 -3
package/fesm2022/fn-input.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, EventEmitter, signal,
|
|
2
|
+
import { InjectionToken, inject, EventEmitter, signal, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import * as i2 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
@@ -9,6 +9,7 @@ import { TranslateService, TranslateModule } from '@ngx-translate/core';
|
|
|
9
9
|
import { Subscription, merge } from 'rxjs';
|
|
10
10
|
import { startWith } from 'rxjs/operators';
|
|
11
11
|
import { FNLabel } from 'fn-label';
|
|
12
|
+
import { FNIconComponent } from '@arpudhabotupload/fn-icon-angular';
|
|
12
13
|
|
|
13
14
|
const FN_TOAST_SERVICE = new InjectionToken('FN_TOAST_SERVICE');
|
|
14
15
|
const DEFAULT_CURRENCY_META = [
|
|
@@ -21,17 +22,6 @@ const DEFAULT_CURRENCY_META = [
|
|
|
21
22
|
{ code: 'INR', locale: 'en-IN', symbol: '₹', digit: '1.2-2' },
|
|
22
23
|
];
|
|
23
24
|
|
|
24
|
-
const CORE_ICONS = {
|
|
25
|
-
'eye-open': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 12s4-8 10-8 10 8 10 8-4 8-10 8-10-8-10-8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
26
|
-
'eye-close': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a11 11 0 0 0 20 0" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M5 13l-1 2M12 14v2M19 13l1 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
27
|
-
'two-square': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="8" y="4" width="12" height="12" rx="2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M4 8v10a2 2 0 0 0 2 2h10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
28
|
-
'round-arrow-top-left': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
29
|
-
'check-circle': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/><path d="M8 12l3 3 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
30
|
-
'info-circle': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="16" x2="12" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="8" x2="12.01" y2="8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
31
|
-
'alert-circle': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="8" x2="12" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="16" x2="12.01" y2="16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
32
|
-
'exclamation-circle': `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="8" x2="12" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="16" x2="12.01" y2="16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
33
|
-
cross: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
|
|
34
|
-
};
|
|
35
25
|
class FNInput {
|
|
36
26
|
cdr;
|
|
37
27
|
field = {};
|
|
@@ -49,7 +39,6 @@ class FNInput {
|
|
|
49
39
|
translateService = inject(TranslateService);
|
|
50
40
|
hasFocus = signal(false);
|
|
51
41
|
textareaElement;
|
|
52
|
-
iconContainers;
|
|
53
42
|
// Internal Toast State
|
|
54
43
|
toasts = signal([]);
|
|
55
44
|
// Removed HttpClient injection
|
|
@@ -112,11 +101,6 @@ class FNInput {
|
|
|
112
101
|
ngOnDestroy() {
|
|
113
102
|
this.subs.unsubscribe();
|
|
114
103
|
}
|
|
115
|
-
ngOnChanges(changes) {
|
|
116
|
-
if (changes['field'] || changes['form']) {
|
|
117
|
-
this.updateAllIcons();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
104
|
setupFieldMessageListener() {
|
|
121
105
|
if (!this.control)
|
|
122
106
|
return;
|
|
@@ -142,12 +126,6 @@ class FNInput {
|
|
|
142
126
|
if (this.field.type === 'number' && this.field.isCurrency && this.control?.value) {
|
|
143
127
|
this.formatInitialCurrencyValue();
|
|
144
128
|
}
|
|
145
|
-
this.setupIconContainersListener();
|
|
146
|
-
}
|
|
147
|
-
setupIconContainersListener() {
|
|
148
|
-
this.subs.add(this.iconContainers.changes.subscribe(() => {
|
|
149
|
-
this.updateAllIcons();
|
|
150
|
-
}));
|
|
151
129
|
}
|
|
152
130
|
enforceLowercase(event) {
|
|
153
131
|
const input = event.target;
|
|
@@ -326,7 +304,6 @@ class FNInput {
|
|
|
326
304
|
}
|
|
327
305
|
togglePasswordVisibility() {
|
|
328
306
|
this.showPassword = !this.showPassword;
|
|
329
|
-
this.updateAllIcons();
|
|
330
307
|
}
|
|
331
308
|
handleInput(event) {
|
|
332
309
|
const target = event.target;
|
|
@@ -345,17 +322,7 @@ class FNInput {
|
|
|
345
322
|
// BUT assuming `handleInput` is the generic handler:
|
|
346
323
|
if (!this.isAlphanumeric) {
|
|
347
324
|
// Regex to allow: a-z, A-Z, 0-9, and: ) {} [] ; , . " ' = + - * / < > ! & | % _ ( - @ $ ? :
|
|
348
|
-
// Note: User list:
|
|
349
|
-
// Combined unique special chars: ) { } [ ] ; , . " ' = + - * / < > ! & | % _ ( @ $ ? :
|
|
350
|
-
// Also space is implied? usually yes.
|
|
351
|
-
// And NO EMOJIS.
|
|
352
|
-
// easier to just replace anything NOT in the allowed set.
|
|
353
|
-
// Allowed chars regex pattern:
|
|
354
|
-
// A-Za-z0-9
|
|
355
|
-
// Space: \s
|
|
356
|
-
// Special: (){}[\];,."='+\-*/<>!&|%_@$?:
|
|
357
|
-
// Note: - inside [] needs to be escaped or at end. ] needs escape. \ needs escape? (not in list).
|
|
358
|
-
// User list has: `.` `"` `'` `?` (wait, ? not in list?)
|
|
325
|
+
// Note: User list: `.` `"` `'` `?` (wait, ? not in list?)
|
|
359
326
|
// User list: `) {} [] ; , . " ' = + - * / < > ! & | % &_( )-/.;@!, $%`
|
|
360
327
|
// Uniques: ) { } [ ] ; , . " ' = + - * / < > ! & | % _ ( @ $
|
|
361
328
|
// (The `&` is repeated, `.` repeated, `,` repeated etc.)
|
|
@@ -974,8 +941,6 @@ class FNInput {
|
|
|
974
941
|
if (service) {
|
|
975
942
|
this.subs.add(service.toasts$.subscribe((toasts) => {
|
|
976
943
|
this.toasts.set(toasts);
|
|
977
|
-
// Wait for DOM to update then render toast icons
|
|
978
|
-
this.updateAllIcons();
|
|
979
944
|
}));
|
|
980
945
|
}
|
|
981
946
|
}
|
|
@@ -1031,119 +996,6 @@ class FNInput {
|
|
|
1031
996
|
? '.'
|
|
1032
997
|
: '';
|
|
1033
998
|
}
|
|
1034
|
-
// --- ICON LOGIC ---
|
|
1035
|
-
updateAllIcons() {
|
|
1036
|
-
// We'll call this after ViewInit or when inputs change
|
|
1037
|
-
setTimeout(() => {
|
|
1038
|
-
this.iconContainers?.forEach((container) => {
|
|
1039
|
-
const ds = container.nativeElement.dataset;
|
|
1040
|
-
const name = ds['iconName'];
|
|
1041
|
-
const variant = ds['iconVariant'] || 'Line';
|
|
1042
|
-
const size = (ds['iconSize'] || 'medium');
|
|
1043
|
-
const color = ds['iconColor'];
|
|
1044
|
-
const disabled = ds['iconDisabled'] === 'true';
|
|
1045
|
-
if (name) {
|
|
1046
|
-
this.loadIconToContainer(container.nativeElement, name, variant, size, color, disabled);
|
|
1047
|
-
}
|
|
1048
|
-
});
|
|
1049
|
-
}, 0);
|
|
1050
|
-
}
|
|
1051
|
-
loadIconToContainer(container, name, variant, sizeName, color, disabled) {
|
|
1052
|
-
const size = this.sizeMap[sizeName];
|
|
1053
|
-
// 1. Check if raw SVG string is provided
|
|
1054
|
-
const trimmedName = name.trim();
|
|
1055
|
-
if (trimmedName.startsWith('<svg')) {
|
|
1056
|
-
this.renderSvgToContainer(container, trimmedName, color, size, 'custom', disabled);
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
// 1b. Check if it's just raw SVG inner content (like `<path ...>`)
|
|
1060
|
-
if (trimmedName.startsWith('<path') ||
|
|
1061
|
-
trimmedName.startsWith('<g') ||
|
|
1062
|
-
trimmedName.startsWith('<circle') ||
|
|
1063
|
-
trimmedName.startsWith('<rect') ||
|
|
1064
|
-
trimmedName.startsWith('<polygon')) {
|
|
1065
|
-
const wrappedSvg = `<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">${trimmedName}</svg>`;
|
|
1066
|
-
this.renderSvgToContainer(container, wrappedSvg, color, size, 'custom', disabled);
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
// 2. Check built-in icons registry
|
|
1070
|
-
// Strip possible --size suffix if it was passed by mistake from old logic
|
|
1071
|
-
const baseName = trimmedName.split('--')[0].trim();
|
|
1072
|
-
if (CORE_ICONS[baseName]) {
|
|
1073
|
-
this.renderSvgToContainer(container, CORE_ICONS[baseName], color, size, baseName, disabled);
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
// No fallback to HTTP
|
|
1077
|
-
console.warn(`[fn-input] Icon "${name}" not found in core registry and no raw SVG provided.`);
|
|
1078
|
-
container.innerHTML = '';
|
|
1079
|
-
this.cdr.detectChanges();
|
|
1080
|
-
}
|
|
1081
|
-
renderSvgToContainer(container, raw, color, size, name, disabled) {
|
|
1082
|
-
try {
|
|
1083
|
-
const parser = new DOMParser();
|
|
1084
|
-
const doc = parser.parseFromString(raw, 'image/svg+xml');
|
|
1085
|
-
const svg = doc.querySelector('svg');
|
|
1086
|
-
if (!svg) {
|
|
1087
|
-
container.innerHTML = '';
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
// SVG Normalization logic
|
|
1091
|
-
for (const s of svg.querySelectorAll('style'))
|
|
1092
|
-
s.remove();
|
|
1093
|
-
if (color) {
|
|
1094
|
-
for (const el of svg.querySelectorAll('*')) {
|
|
1095
|
-
const style = el.getAttribute('style');
|
|
1096
|
-
if (style) {
|
|
1097
|
-
const cleaned = style
|
|
1098
|
-
.replaceAll(/fill\s*:\s*[^;]+;?/gi, '')
|
|
1099
|
-
.replaceAll(/stroke\s*:\s*[^;]+;?/gi, '');
|
|
1100
|
-
if (cleaned)
|
|
1101
|
-
el.setAttribute('style', cleaned);
|
|
1102
|
-
else
|
|
1103
|
-
el.removeAttribute('style');
|
|
1104
|
-
}
|
|
1105
|
-
const fill = el.getAttribute('fill');
|
|
1106
|
-
if (fill && fill !== 'none' && !fill.startsWith('url(')) {
|
|
1107
|
-
el.setAttribute('fill', 'currentColor');
|
|
1108
|
-
}
|
|
1109
|
-
const stroke = el.getAttribute('stroke');
|
|
1110
|
-
if (stroke && stroke !== 'none' && !stroke.startsWith('url(')) {
|
|
1111
|
-
el.setAttribute('stroke', 'currentColor');
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
svg.removeAttribute('width');
|
|
1116
|
-
svg.removeAttribute('height');
|
|
1117
|
-
if (!svg.getAttribute('viewBox')) {
|
|
1118
|
-
svg.setAttribute('viewBox', `0 0 ${size} ${size}`);
|
|
1119
|
-
}
|
|
1120
|
-
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
1121
|
-
svg.setAttribute('focusable', 'false');
|
|
1122
|
-
svg.setAttribute('aria-hidden', 'true');
|
|
1123
|
-
svg.setAttribute('width', '100%');
|
|
1124
|
-
svg.setAttribute('height', '100%');
|
|
1125
|
-
const style = `;display:block;max-width:100%;max-height:100%;`;
|
|
1126
|
-
svg.setAttribute('style', (svg.getAttribute('style') || '') + style);
|
|
1127
|
-
container.innerHTML = '';
|
|
1128
|
-
if (color) {
|
|
1129
|
-
container.style.color = color;
|
|
1130
|
-
}
|
|
1131
|
-
if (disabled) {
|
|
1132
|
-
container.style.opacity = '0.5';
|
|
1133
|
-
container.style.cursor = 'not-allowed';
|
|
1134
|
-
}
|
|
1135
|
-
else {
|
|
1136
|
-
container.style.opacity = '1';
|
|
1137
|
-
container.style.cursor = '';
|
|
1138
|
-
}
|
|
1139
|
-
container.appendChild(svg);
|
|
1140
|
-
this.cdr.detectChanges();
|
|
1141
|
-
}
|
|
1142
|
-
catch (error) {
|
|
1143
|
-
console.error(`[fn-input] Error parsing SVG for icon "${name}":`, error);
|
|
1144
|
-
container.innerHTML = '';
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
999
|
// Removed getIconPath as HTTP loading is disabled
|
|
1148
1000
|
// --- MESSAGE LOGIC ---
|
|
1149
1001
|
getFieldMessage() {
|
|
@@ -1170,12 +1022,19 @@ class FNInput {
|
|
|
1170
1022
|
get isSuccess() {
|
|
1171
1023
|
return !!(this.control?.valid && this.control?.touched);
|
|
1172
1024
|
}
|
|
1173
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1174
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FNInput, isStandalone: true, selector: "fn-input", inputs: { field: "field", helperHandle: "helperHandle", toastService: "toastService", currencyMeta: "currencyMeta", defaultLocale: "defaultLocale", form: "form" }, outputs: { valueChange: "valueChange", fieldBlur: "fieldBlur" }, viewQueries: [{ propertyName: "textareaElement", first: true, predicate: ["fnTextarea"], descendants: true }, { propertyName: "iconContainers", predicate: ["iconContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (field.name && isVisible() && !field.hidden) {\r\n <div class=\"fn-input-container\">\r\n @if (field.type! !== 'hidden') {\r\n <fn-label\r\n [for]=\"uniqueId\"\r\n [label]=\"field.label\"\r\n [required]=\"field.required || false\"\r\n [hideOptional]=\"field.hideOptional || false\"\r\n [color]=\"field.color || '#03182b'\"\r\n [variant]=\"field.labelVariant || 'p1'\"\r\n [statusLabel]=\"field.statusLabel\"\r\n ></fn-label>\r\n }\r\n\r\n <ng-container>\r\n @switch (field.type) {\r\n <!-- Textarea Field -->\r\n @case ('textarea') {\r\n <textarea\r\n #fnTextarea\r\n style=\"resize: none\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [rows]=\"field.rows || 1\"\r\n [readonly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"handleTextArea($event)\"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n ></textarea>\r\n }\r\n\r\n <!-- Password Field -->\r\n @case ('password') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"showPassword ? 'text' : 'password'\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [disabled]=\"field.disabled || false\"\r\n [formControl]=\"control\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [readonly]=\"field.readOnly || false\"\r\n (input)=\"isAlphanumeric ? handleAlphanumericInput($event) : handleInput($event)\"\r\n (focus)=\"onPasswordFocus()\"\r\n (blur)=\"handlePasswordBlur($event)\"\r\n class=\"fn-input-field overflow-hidden text-ellipsis\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-12':\r\n (field.toggleMask && !field.isCopyText) ||\r\n (!field.toggleMask && field.isCopyText),\r\n 'pr-24': field.toggleMask && field.isCopyText && !field.hasGenerateKey,\r\n 'pr-32': field.toggleMask && field.isCopyText && field.hasGenerateKey,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.toggleMask && !field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n } @else if (field.isCopyText && !field.toggleMask) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n } @else if (field.toggleMask && field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n [ngClass]=\"{ 'opacity-50 cursor-not-allowed': !control.value }\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (field.feedback && isPasswordFocused) {\r\n <div class=\"mt-2 space-y-1\">\r\n <div class=\"flex items-center justify-between text-xs\">\r\n <span class=\"font-medium\" [ngClass]=\"strengthClass\">\r\n {{ passwordStrengthLabel | translate }}\r\n </span>\r\n <div class=\"flex items-center gap-1\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"\r\n passwordStrengthPercent > 50 ? 'check-circle' : 'info-circle'\r\n \"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"extrasmall\"\r\n [attr.data-icon-color]=\"\r\n passwordStrengthPercent <= 25\r\n ? '#ef3e42'\r\n : passwordStrengthPercent <= 75\r\n ? '#ff9f00'\r\n : '#6cc24a'\r\n \"\r\n ></div>\r\n </div>\r\n </div>\r\n <div class=\"h-1.5 w-full bg-[#eef0f2] rounded-full overflow-hidden\">\r\n <div\r\n class=\"h-full transition-all duration-300 rounded-full\"\r\n [ngClass]=\"{\r\n 'bg-[#ef3e42]': passwordStrengthPercent <= 25,\r\n 'bg-[#ff9f00]': passwordStrengthPercent > 25 && passwordStrengthPercent <= 75,\r\n 'bg-[#6cc24a]': passwordStrengthPercent > 75,\r\n }\"\r\n [style.width.%]=\"passwordStrengthPercent\"\r\n ></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Number Field -->\r\n @case ('number') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type === 'number' && field.isCurrency ? 'text' : 'number'\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"isDisabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n [min]=\"field.type === 'number' && !field.isCurrency ? 0 : null\"\r\n [max]=\"field.type === 'number' && !field.isCurrency ? field.max : null\"\r\n [step]=\"field.type === 'number' && !field.isCurrency ? field.step || 1 : null\"\r\n (input)=\"handleNumberInput($event, field.minFractionDigits || 2)\"\r\n (paste)=\"handleNumberPaste($event, field.minFractionDigits || 2)\"\r\n (blur)=\"handleBlur($event, field.minFractionDigits || 2)\"\r\n (keydown)=\"handleNumberKeydown($event, field.isCurrency || false)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10': field.suffix?.icon && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'text-[24px] font-bold': field.isCurrency,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Default Input Field (text, email, etc.) -->\r\n @default {\r\n <div class=\"relative z-0\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"\r\n isEmailField\r\n ? handleEmailInput($event)\r\n : field.type === 'text' && isAlphanumeric\r\n ? handleAlphanumericInput($event)\r\n : handleInput($event)\r\n \"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10':\r\n (field.icon || field.suffix?.icon) && !field.isCopyText && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'pr-20':\r\n field.isCopyText && !field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-30':\r\n field.isCopyText && field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-32':\r\n field.isCopyText && field.hasGenerateKey && (field.icon || field.suffix?.icon),\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n }\r\n\r\n @if (field.icon || field.suffix?.icon || field.suffix?.text) {\r\n @if (field.suffix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"suffixClick($event)\"\r\n (keydown.enter)=\"suffixClick($event)\"\r\n [attr.tabindex]=\"field.suffix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.suffix?.onClick\"\r\n >{{ field.suffix!.text }}</span\r\n >\r\n }\r\n @if (field.icon || field.suffix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.suffix?.icon || field.icon!.name\"\r\n [attr.data-icon-variant]=\"field.icon?.variant || 'Line'\"\r\n [attr.data-icon-size]=\"field.icon ? getIconSizeName(field.icon.size) : 'medium'\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </ng-container>\r\n\r\n @if (showFormFieldMessage(control, helperText)) {\r\n <div class=\"fn-field-message-container\">\r\n <span\r\n class=\"fn-field-message-text\"\r\n [ngClass]=\"{\r\n error: isError,\r\n success: isSuccess,\r\n }\"\r\n >\r\n {{ getFieldMessage() | translate }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Integrated Toast Notifications -->\r\n <div class=\"toast-container\">\r\n @for (toast of toasts(); track toast.id) {\r\n <div class=\"toast toast-{{ toast.type }}\" [class.toast-enter]=\"toast.isVisible\">\r\n <span class=\"toast-icon\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"getToastIconName(toast)\"\r\n data-icon-variant=\"Colour\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"\r\n toast.type === 'success'\r\n ? '#6cc24a'\r\n : toast.type === 'warn'\r\n ? '#ff9f00'\r\n : toast.type === 'error'\r\n ? '#ef3e42'\r\n : '#03182b'\r\n \"\r\n ></div>\r\n </span>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-message\">\r\n {{ getTranslatedMessage(toast.message) | translate }}\r\n </span>\r\n </div>\r\n <button class=\"toast-close\" (click)=\"removeToast(toast.id)\" aria-label=\"Close\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"cross\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n data-icon-color=\"#ffffff\"\r\n ></div>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".fn-input-container{display:flex;flex-direction:column;width:100%}.fn-label{font-family:Outfit,sans-serif;font-weight:500;margin-bottom:8px;display:flex;align-items:center}.fn-label span{color:#c4cdd2;font-size:14px;font-weight:400}.fn-input-field{width:100%;height:48px;padding:0 16px;background-color:#f7f8f9;border-bottom:1.5px solid #c4cdd2;font-family:Outfit,sans-serif;font-size:16px;color:#03182b;transition:all .2s ease;border-radius:4px 4px 0 0}.fn-input-field::placeholder{color:#c4cdd2}.fn-input-field:focus{outline:none;background-color:#fff;border-bottom-color:#03182b}.fn-input-field.error{border-bottom-color:#ef3e42}.fn-input-field.success{border-bottom-color:#6cc24a}.fn-input-field.disabled{background-color:#eef0f2;color:#c4cdd2;cursor:not-allowed}.fn-input-field.pl-10{padding-left:40px!important}.fn-input-field.has-prefix-text{padding-left:100px!important}.fn-input-field.pr-10{padding-right:40px!important}.fn-input-field.has-suffix-text{padding-right:120px!important}.fn-input-field.pr-12{padding-right:44px!important}.fn-input-field.pr-20{padding-right:72px!important}.fn-input-field.pr-24{padding-right:88px!important}.fn-input-field.pr-30{padding-right:104px!important}.fn-input-field.pr-32{padding-right:120px!important}.fn-field-message-container{margin-top:4px}.fn-field-message-text{font-family:Outfit,sans-serif;font-size:12px;color:#c4cdd2}.fn-field-message-text.error{color:#ef3e42}.fn-field-message-text.success{color:#6cc24a}.icon-container{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}.relative{position:relative}.absolute{position:absolute}.right-0{right:0}.left-0{left:0}.top-1\\/2{top:50%}.-translate-y-1\\/2{transform:translateY(-50%)}.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.mt-2{margin-top:.5rem}.space-y-1>:not([hidden])~:not([hidden]){margin-top:.25rem}.text-xs{font-size:.75rem}.font-medium{font-weight:500}.h-1\\.5{height:.375rem}.w-full{width:100%}.bg-\\[\\#eef0f2\\]{background-color:#eef0f2}.rounded-full{border-radius:9999px}.overflow-hidden{overflow:hidden}.transition-all{transition-property:all}.duration-300{transition-duration:.3s}.cursor-pointer{cursor:pointer}.opacity-50{opacity:.5}.cursor-not-allowed{cursor:not-allowed}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.toast-container{position:fixed;top:24px;left:50%;transform:translate(-50%);z-index:9999;width:100%;max-width:336px;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{display:flex;align-items:center;background:#25333f;border-radius:8px;padding:12px;width:336px;max-width:100%;opacity:0;transform:translateY(-20px);transition:all .3s cubic-bezier(.4,0,.2,1);pointer-events:auto}.toast.toast-enter{opacity:1;transform:translateY(0)}.toast.toast-success{border-left:4px solid #6cc24a}.toast.toast-warn{border-left:4px solid #ff9f00}.toast.toast-error{border-left:4px solid #ef3e42}.toast.toast-info{border-left:4px solid #3b82f6}.toast .toast-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;margin-right:8px;flex-shrink:0}.toast .toast-content{flex:1;min-width:0}.toast .toast-message{font-size:14px;line-height:20px;color:#fff;display:block}.toast .toast-close{background:none;border:none;font-size:20px;color:#fff;opacity:.7;cursor:pointer;padding:0;margin-left:8px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .2s;pointer-events:auto}.toast .toast-close:hover{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: FNLabel, selector: "fn-label", inputs: ["field", "label", "required", "className", "class", "for", "color", "variant", "statusLabel", "ngClass", "hideOptional"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1025
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: FNInput, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1026
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.19", type: FNInput, isStandalone: true, selector: "fn-input", inputs: { field: "field", helperHandle: "helperHandle", toastService: "toastService", currencyMeta: "currencyMeta", defaultLocale: "defaultLocale", form: "form" }, outputs: { valueChange: "valueChange", fieldBlur: "fieldBlur" }, viewQueries: [{ propertyName: "textareaElement", first: true, predicate: ["fnTextarea"], descendants: true }], ngImport: i0, template: "@if (field.name && isVisible() && !field.hidden) {\r\n <div class=\"fn-input-container\">\r\n @if (field.type! !== 'hidden') {\r\n <fn-label\r\n [for]=\"uniqueId\"\r\n [label]=\"field.label\"\r\n [required]=\"field.required || false\"\r\n [hideOptional]=\"field.hideOptional || false\"\r\n [color]=\"field.color || '#03182b'\"\r\n [variant]=\"field.labelVariant || 'p1'\"\r\n [statusLabel]=\"field.statusLabel\"\r\n ></fn-label>\r\n }\r\n\r\n <ng-container>\r\n @switch (field.type) {\r\n <!-- Textarea Field -->\r\n @case ('textarea') {\r\n <textarea\r\n #fnTextarea\r\n style=\"resize: none\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [rows]=\"field.rows || 1\"\r\n [readonly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"handleTextArea($event)\"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n ></textarea>\r\n }\r\n\r\n <!-- Password Field -->\r\n @case ('password') {\r\n <div class=\"relative w-full\">\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n <input\r\n [type]=\"showPassword ? 'text' : 'password'\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [disabled]=\"field.disabled || false\"\r\n [formControl]=\"control\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [readonly]=\"field.readOnly || false\"\r\n (input)=\"isAlphanumeric ? handleAlphanumericInput($event) : handleInput($event)\"\r\n (focus)=\"onPasswordFocus()\"\r\n (blur)=\"handlePasswordBlur($event)\"\r\n class=\"fn-input-field overflow-hidden text-ellipsis\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-12':\r\n (field.toggleMask && !field.isCopyText) ||\r\n (!field.toggleMask && field.isCopyText),\r\n 'pr-24': field.toggleMask && field.isCopyText && !field.hasGenerateKey,\r\n 'pr-32': field.toggleMask && field.isCopyText && field.hasGenerateKey,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.toggleMask && !field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <fn-icon\r\n [name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n } @else if (field.isCopyText && !field.toggleMask) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <fn-icon\r\n [name]=\"'round-arrow-top-left'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n }\r\n } @else if (field.toggleMask && field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <fn-icon\r\n [name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n [ngClass]=\"{ 'opacity-50 cursor-not-allowed': !control.value }\"\r\n >\r\n <fn-icon [name]=\"'round-arrow-top-left'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (field.feedback && isPasswordFocused) {\r\n <div class=\"mt-2 space-y-1\">\r\n <div class=\"flex items-center justify-between text-xs\">\r\n <span class=\"font-medium\" [ngClass]=\"strengthClass\">\r\n {{ passwordStrengthLabel | translate }}\r\n </span>\r\n <div class=\"flex items-center gap-1\">\r\n <fn-icon\r\n [name]=\"passwordStrengthPercent > 50 ? 'check-circle' : 'info-circle'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'extrasmall'\"\r\n [color]=\"\r\n passwordStrengthPercent <= 25\r\n ? '#ef3e42'\r\n : passwordStrengthPercent <= 75\r\n ? '#ff9f00'\r\n : '#6cc24a'\r\n \"\r\n ></fn-icon>\r\n </div>\r\n </div>\r\n <div class=\"h-1.5 w-full bg-[#eef0f2] rounded-full overflow-hidden\">\r\n <div\r\n class=\"h-full transition-all duration-300 rounded-full\"\r\n [ngClass]=\"{\r\n 'bg-[#ef3e42]': passwordStrengthPercent <= 25,\r\n 'bg-[#ff9f00]': passwordStrengthPercent > 25 && passwordStrengthPercent <= 75,\r\n 'bg-[#6cc24a]': passwordStrengthPercent > 75,\r\n }\"\r\n [style.width.%]=\"passwordStrengthPercent\"\r\n ></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Number Field -->\r\n @case ('number') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type === 'number' && field.isCurrency ? 'text' : 'number'\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"isDisabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n [min]=\"field.type === 'number' && !field.isCurrency ? 0 : null\"\r\n [max]=\"field.type === 'number' && !field.isCurrency ? field.max : null\"\r\n [step]=\"field.type === 'number' && !field.isCurrency ? field.step || 1 : null\"\r\n (input)=\"handleNumberInput($event, field.minFractionDigits || 2)\"\r\n (paste)=\"handleNumberPaste($event, field.minFractionDigits || 2)\"\r\n (blur)=\"handleBlur($event, field.minFractionDigits || 2)\"\r\n (keydown)=\"handleNumberKeydown($event, field.isCurrency || false)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10': field.suffix?.icon && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'text-[24px] font-bold': field.isCurrency,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Default Input Field (text, email, etc.) -->\r\n @default {\r\n <div class=\"relative z-0\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"\r\n isEmailField\r\n ? handleEmailInput($event)\r\n : field.type === 'text' && isAlphanumeric\r\n ? handleAlphanumericInput($event)\r\n : handleInput($event)\r\n \"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10':\r\n (field.icon || field.suffix?.icon) && !field.isCopyText && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'pr-20':\r\n field.isCopyText && !field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-30':\r\n field.isCopyText && field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-32':\r\n field.isCopyText && field.hasGenerateKey && (field.icon || field.suffix?.icon),\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <fn-icon [name]=\"'round-arrow-top-left'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n }\r\n }\r\n\r\n @if (field.icon || field.suffix?.icon || field.suffix?.text) {\r\n @if (field.suffix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"suffixClick($event)\"\r\n (keydown.enter)=\"suffixClick($event)\"\r\n [attr.tabindex]=\"field.suffix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.suffix?.onClick\"\r\n >{{ field.suffix!.text }}</span\r\n >\r\n }\r\n @if (field.icon || field.suffix?.icon) {\r\n <fn-icon\r\n [name]=\"field.suffix?.icon || field.icon?.name || ''\"\r\n [variant]=\"field.icon?.variant || 'Line'\"\r\n [size]=\"field.icon ? getIconSizeName(field.icon.size) : 'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </ng-container>\r\n\r\n @if (showFormFieldMessage(control, helperText)) {\r\n <div class=\"fn-field-message-container\">\r\n <span\r\n class=\"fn-field-message-text\"\r\n [ngClass]=\"{\r\n error: isError,\r\n success: isSuccess,\r\n }\"\r\n >\r\n {{ getFieldMessage() | translate }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Integrated Toast Notifications -->\r\n <div class=\"toast-container\">\r\n @for (toast of toasts(); track toast.id) {\r\n <div class=\"toast toast-{{ toast.type }}\" [class.toast-enter]=\"toast.isVisible\">\r\n <span class=\"toast-icon\">\r\n <fn-icon\r\n [name]=\"getToastIconName(toast)\"\r\n [variant]=\"'Colour'\"\r\n [size]=\"'medium'\"\r\n [color]=\"\r\n toast.type === 'success'\r\n ? '#6cc24a'\r\n : toast.type === 'warn'\r\n ? '#ff9f00'\r\n : toast.type === 'error'\r\n ? '#ef3e42'\r\n : '#03182b'\r\n \"\r\n ></fn-icon>\r\n </span>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-message\">\r\n {{ getTranslatedMessage(toast.message) | translate }}\r\n </span>\r\n </div>\r\n <button class=\"toast-close\" (click)=\"removeToast(toast.id)\" aria-label=\"Close\">\r\n <fn-icon\r\n [name]=\"'cross'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"'#ffffff'\"\r\n ></fn-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".fn-input-container{display:flex;flex-direction:column;width:100%}.fn-label{font-family:Outfit,sans-serif;font-weight:500;margin-bottom:8px;display:flex;align-items:center}.fn-label span{color:#c4cdd2;font-size:14px;font-weight:400}.fn-input-field{width:100%;height:48px;padding:0 16px;background-color:#f7f8f9;border-bottom:1.5px solid #c4cdd2;font-family:Outfit,sans-serif;font-size:16px;color:#03182b;transition:all .2s ease;border-radius:4px 4px 0 0}.fn-input-field::placeholder{color:#c4cdd2}.fn-input-field:focus{outline:none;background-color:#fff;border-bottom-color:#03182b}.fn-input-field.error{border-bottom-color:#ef3e42}.fn-input-field.success{border-bottom-color:#6cc24a}.fn-input-field.disabled{background-color:#eef0f2;color:#c4cdd2;cursor:not-allowed}.fn-input-field.pl-10{padding-left:40px!important}.fn-input-field.has-prefix-text{padding-left:100px!important}.fn-input-field.pr-10{padding-right:40px!important}.fn-input-field.has-suffix-text{padding-right:120px!important}.fn-input-field.pr-12{padding-right:44px!important}.fn-input-field.pr-20{padding-right:72px!important}.fn-input-field.pr-24{padding-right:88px!important}.fn-input-field.pr-30{padding-right:104px!important}.fn-input-field.pr-32{padding-right:120px!important}.fn-field-message-container{margin-top:4px}.fn-field-message-text{font-family:Outfit,sans-serif;font-size:12px;color:#c4cdd2}.fn-field-message-text.error{color:#ef3e42}.fn-field-message-text.success{color:#6cc24a}.icon-container{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}.relative{position:relative}.absolute{position:absolute}.right-0{right:0}.left-0{left:0}.top-1\\/2{top:50%}.-translate-y-1\\/2{transform:translateY(-50%)}.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.mt-2{margin-top:.5rem}.space-y-1>:not([hidden])~:not([hidden]){margin-top:.25rem}.text-xs{font-size:.75rem}.font-medium{font-weight:500}.h-1\\.5{height:.375rem}.w-full{width:100%}.bg-\\[\\#eef0f2\\]{background-color:#eef0f2}.rounded-full{border-radius:9999px}.overflow-hidden{overflow:hidden}.transition-all{transition-property:all}.duration-300{transition-duration:.3s}.cursor-pointer{cursor:pointer}.opacity-50{opacity:.5}.cursor-not-allowed{cursor:not-allowed}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.toast-container{position:fixed;top:24px;left:50%;transform:translate(-50%);z-index:9999;width:100%;max-width:336px;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{display:flex;align-items:center;background:#25333f;border-radius:8px;padding:12px;width:336px;max-width:100%;opacity:0;transform:translateY(-20px);transition:all .3s cubic-bezier(.4,0,.2,1);pointer-events:auto}.toast.toast-enter{opacity:1;transform:translateY(0)}.toast.toast-success{border-left:4px solid #6cc24a}.toast.toast-warn{border-left:4px solid #ff9f00}.toast.toast-error{border-left:4px solid #ef3e42}.toast.toast-info{border-left:4px solid #3b82f6}.toast .toast-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;margin-right:8px;flex-shrink:0}.toast .toast-content{flex:1;min-width:0}.toast .toast-message{font-size:14px;line-height:20px;color:#fff;display:block}.toast .toast-close{background:none;border:none;font-size:20px;color:#fff;opacity:.7;cursor:pointer;padding:0;margin-left:8px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .2s;pointer-events:auto}.toast .toast-close:hover{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: FNLabel, selector: "fn-label", inputs: ["field", "label", "required", "className", "class", "for", "color", "variant", "statusLabel", "ngClass", "hideOptional"] }, { kind: "component", type: FNIconComponent, selector: "fn-icon", inputs: ["name", "variant", "size", "color", "strokeWidth", "disabled", "className", "basePath"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1175
1027
|
}
|
|
1176
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1028
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.19", ngImport: i0, type: FNInput, decorators: [{
|
|
1177
1029
|
type: Component,
|
|
1178
|
-
args: [{ selector: 'fn-input', standalone: true, imports: [FormsModule, CommonModule, ReactiveFormsModule, TranslateModule, FNLabel], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (field.name && isVisible() && !field.hidden) {\r\n <div class=\"fn-input-container\">\r\n @if (field.type! !== 'hidden') {\r\n <fn-label\r\n [for]=\"uniqueId\"\r\n [label]=\"field.label\"\r\n [required]=\"field.required || false\"\r\n [hideOptional]=\"field.hideOptional || false\"\r\n [color]=\"field.color || '#03182b'\"\r\n [variant]=\"field.labelVariant || 'p1'\"\r\n [statusLabel]=\"field.statusLabel\"\r\n ></fn-label>\r\n }\r\n\r\n <ng-container>\r\n @switch (field.type) {\r\n <!-- Textarea Field -->\r\n @case ('textarea') {\r\n <textarea\r\n #fnTextarea\r\n style=\"resize: none\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [rows]=\"field.rows || 1\"\r\n [readonly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"handleTextArea($event)\"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n ></textarea>\r\n }\r\n\r\n <!-- Password Field -->\r\n @case ('password') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"showPassword ? 'text' : 'password'\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [disabled]=\"field.disabled || false\"\r\n [formControl]=\"control\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [readonly]=\"field.readOnly || false\"\r\n (input)=\"isAlphanumeric ? handleAlphanumericInput($event) : handleInput($event)\"\r\n (focus)=\"onPasswordFocus()\"\r\n (blur)=\"handlePasswordBlur($event)\"\r\n class=\"fn-input-field overflow-hidden text-ellipsis\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-12':\r\n (field.toggleMask && !field.isCopyText) ||\r\n (!field.toggleMask && field.isCopyText),\r\n 'pr-24': field.toggleMask && field.isCopyText && !field.hasGenerateKey,\r\n 'pr-32': field.toggleMask && field.isCopyText && field.hasGenerateKey,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.toggleMask && !field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n } @else if (field.isCopyText && !field.toggleMask) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n } @else if (field.toggleMask && field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"large\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n [ngClass]=\"{ 'opacity-50 cursor-not-allowed': !control.value }\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (field.feedback && isPasswordFocused) {\r\n <div class=\"mt-2 space-y-1\">\r\n <div class=\"flex items-center justify-between text-xs\">\r\n <span class=\"font-medium\" [ngClass]=\"strengthClass\">\r\n {{ passwordStrengthLabel | translate }}\r\n </span>\r\n <div class=\"flex items-center gap-1\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"\r\n passwordStrengthPercent > 50 ? 'check-circle' : 'info-circle'\r\n \"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"extrasmall\"\r\n [attr.data-icon-color]=\"\r\n passwordStrengthPercent <= 25\r\n ? '#ef3e42'\r\n : passwordStrengthPercent <= 75\r\n ? '#ff9f00'\r\n : '#6cc24a'\r\n \"\r\n ></div>\r\n </div>\r\n </div>\r\n <div class=\"h-1.5 w-full bg-[#eef0f2] rounded-full overflow-hidden\">\r\n <div\r\n class=\"h-full transition-all duration-300 rounded-full\"\r\n [ngClass]=\"{\r\n 'bg-[#ef3e42]': passwordStrengthPercent <= 25,\r\n 'bg-[#ff9f00]': passwordStrengthPercent > 25 && passwordStrengthPercent <= 75,\r\n 'bg-[#6cc24a]': passwordStrengthPercent > 75,\r\n }\"\r\n [style.width.%]=\"passwordStrengthPercent\"\r\n ></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Number Field -->\r\n @case ('number') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type === 'number' && field.isCurrency ? 'text' : 'number'\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"isDisabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n [min]=\"field.type === 'number' && !field.isCurrency ? 0 : null\"\r\n [max]=\"field.type === 'number' && !field.isCurrency ? field.max : null\"\r\n [step]=\"field.type === 'number' && !field.isCurrency ? field.step || 1 : null\"\r\n (input)=\"handleNumberInput($event, field.minFractionDigits || 2)\"\r\n (paste)=\"handleNumberPaste($event, field.minFractionDigits || 2)\"\r\n (blur)=\"handleBlur($event, field.minFractionDigits || 2)\"\r\n (keydown)=\"handleNumberKeydown($event, field.isCurrency || false)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10': field.suffix?.icon && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'text-[24px] font-bold': field.isCurrency,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Default Input Field (text, email, etc.) -->\r\n @default {\r\n <div class=\"relative z-0\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.prefix!.icon\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"\r\n isEmailField\r\n ? handleEmailInput($event)\r\n : field.type === 'text' && isAlphanumeric\r\n ? handleAlphanumericInput($event)\r\n : handleInput($event)\r\n \"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10':\r\n (field.icon || field.suffix?.icon) && !field.isCopyText && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'pr-20':\r\n field.isCopyText && !field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-30':\r\n field.isCopyText && field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-32':\r\n field.isCopyText && field.hasGenerateKey && (field.icon || field.suffix?.icon),\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"two-square\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"round-arrow-top-left\"\r\n data-icon-color=\"#03182b\"\r\n ></div>\r\n </span>\r\n }\r\n }\r\n\r\n @if (field.icon || field.suffix?.icon || field.suffix?.text) {\r\n @if (field.suffix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"suffixClick($event)\"\r\n (keydown.enter)=\"suffixClick($event)\"\r\n [attr.tabindex]=\"field.suffix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.suffix?.onClick\"\r\n >{{ field.suffix!.text }}</span\r\n >\r\n }\r\n @if (field.icon || field.suffix?.icon) {\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"field.suffix?.icon || field.icon!.name\"\r\n [attr.data-icon-variant]=\"field.icon?.variant || 'Line'\"\r\n [attr.data-icon-size]=\"field.icon ? getIconSizeName(field.icon.size) : 'medium'\"\r\n [attr.data-icon-color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </ng-container>\r\n\r\n @if (showFormFieldMessage(control, helperText)) {\r\n <div class=\"fn-field-message-container\">\r\n <span\r\n class=\"fn-field-message-text\"\r\n [ngClass]=\"{\r\n error: isError,\r\n success: isSuccess,\r\n }\"\r\n >\r\n {{ getFieldMessage() | translate }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Integrated Toast Notifications -->\r\n <div class=\"toast-container\">\r\n @for (toast of toasts(); track toast.id) {\r\n <div class=\"toast toast-{{ toast.type }}\" [class.toast-enter]=\"toast.isVisible\">\r\n <span class=\"toast-icon\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n [attr.data-icon-name]=\"getToastIconName(toast)\"\r\n data-icon-variant=\"Colour\"\r\n data-icon-size=\"medium\"\r\n [attr.data-icon-color]=\"\r\n toast.type === 'success'\r\n ? '#6cc24a'\r\n : toast.type === 'warn'\r\n ? '#ff9f00'\r\n : toast.type === 'error'\r\n ? '#ef3e42'\r\n : '#03182b'\r\n \"\r\n ></div>\r\n </span>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-message\">\r\n {{ getTranslatedMessage(toast.message) | translate }}\r\n </span>\r\n </div>\r\n <button class=\"toast-close\" (click)=\"removeToast(toast.id)\" aria-label=\"Close\">\r\n <div\r\n #iconContainer\r\n class=\"icon-container\"\r\n data-icon-name=\"cross\"\r\n data-icon-variant=\"Line\"\r\n data-icon-size=\"medium\"\r\n data-icon-color=\"#ffffff\"\r\n ></div>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".fn-input-container{display:flex;flex-direction:column;width:100%}.fn-label{font-family:Outfit,sans-serif;font-weight:500;margin-bottom:8px;display:flex;align-items:center}.fn-label span{color:#c4cdd2;font-size:14px;font-weight:400}.fn-input-field{width:100%;height:48px;padding:0 16px;background-color:#f7f8f9;border-bottom:1.5px solid #c4cdd2;font-family:Outfit,sans-serif;font-size:16px;color:#03182b;transition:all .2s ease;border-radius:4px 4px 0 0}.fn-input-field::placeholder{color:#c4cdd2}.fn-input-field:focus{outline:none;background-color:#fff;border-bottom-color:#03182b}.fn-input-field.error{border-bottom-color:#ef3e42}.fn-input-field.success{border-bottom-color:#6cc24a}.fn-input-field.disabled{background-color:#eef0f2;color:#c4cdd2;cursor:not-allowed}.fn-input-field.pl-10{padding-left:40px!important}.fn-input-field.has-prefix-text{padding-left:100px!important}.fn-input-field.pr-10{padding-right:40px!important}.fn-input-field.has-suffix-text{padding-right:120px!important}.fn-input-field.pr-12{padding-right:44px!important}.fn-input-field.pr-20{padding-right:72px!important}.fn-input-field.pr-24{padding-right:88px!important}.fn-input-field.pr-30{padding-right:104px!important}.fn-input-field.pr-32{padding-right:120px!important}.fn-field-message-container{margin-top:4px}.fn-field-message-text{font-family:Outfit,sans-serif;font-size:12px;color:#c4cdd2}.fn-field-message-text.error{color:#ef3e42}.fn-field-message-text.success{color:#6cc24a}.icon-container{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}.relative{position:relative}.absolute{position:absolute}.right-0{right:0}.left-0{left:0}.top-1\\/2{top:50%}.-translate-y-1\\/2{transform:translateY(-50%)}.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.mt-2{margin-top:.5rem}.space-y-1>:not([hidden])~:not([hidden]){margin-top:.25rem}.text-xs{font-size:.75rem}.font-medium{font-weight:500}.h-1\\.5{height:.375rem}.w-full{width:100%}.bg-\\[\\#eef0f2\\]{background-color:#eef0f2}.rounded-full{border-radius:9999px}.overflow-hidden{overflow:hidden}.transition-all{transition-property:all}.duration-300{transition-duration:.3s}.cursor-pointer{cursor:pointer}.opacity-50{opacity:.5}.cursor-not-allowed{cursor:not-allowed}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.toast-container{position:fixed;top:24px;left:50%;transform:translate(-50%);z-index:9999;width:100%;max-width:336px;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{display:flex;align-items:center;background:#25333f;border-radius:8px;padding:12px;width:336px;max-width:100%;opacity:0;transform:translateY(-20px);transition:all .3s cubic-bezier(.4,0,.2,1);pointer-events:auto}.toast.toast-enter{opacity:1;transform:translateY(0)}.toast.toast-success{border-left:4px solid #6cc24a}.toast.toast-warn{border-left:4px solid #ff9f00}.toast.toast-error{border-left:4px solid #ef3e42}.toast.toast-info{border-left:4px solid #3b82f6}.toast .toast-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;margin-right:8px;flex-shrink:0}.toast .toast-content{flex:1;min-width:0}.toast .toast-message{font-size:14px;line-height:20px;color:#fff;display:block}.toast .toast-close{background:none;border:none;font-size:20px;color:#fff;opacity:.7;cursor:pointer;padding:0;margin-left:8px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .2s;pointer-events:auto}.toast .toast-close:hover{opacity:1}\n"] }]
|
|
1030
|
+
args: [{ selector: 'fn-input', standalone: true, imports: [
|
|
1031
|
+
FormsModule,
|
|
1032
|
+
CommonModule,
|
|
1033
|
+
ReactiveFormsModule,
|
|
1034
|
+
TranslateModule,
|
|
1035
|
+
FNLabel,
|
|
1036
|
+
FNIconComponent,
|
|
1037
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (field.name && isVisible() && !field.hidden) {\r\n <div class=\"fn-input-container\">\r\n @if (field.type! !== 'hidden') {\r\n <fn-label\r\n [for]=\"uniqueId\"\r\n [label]=\"field.label\"\r\n [required]=\"field.required || false\"\r\n [hideOptional]=\"field.hideOptional || false\"\r\n [color]=\"field.color || '#03182b'\"\r\n [variant]=\"field.labelVariant || 'p1'\"\r\n [statusLabel]=\"field.statusLabel\"\r\n ></fn-label>\r\n }\r\n\r\n <ng-container>\r\n @switch (field.type) {\r\n <!-- Textarea Field -->\r\n @case ('textarea') {\r\n <textarea\r\n #fnTextarea\r\n style=\"resize: none\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [rows]=\"field.rows || 1\"\r\n [readonly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"handleTextArea($event)\"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n ></textarea>\r\n }\r\n\r\n <!-- Password Field -->\r\n @case ('password') {\r\n <div class=\"relative w-full\">\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n <input\r\n [type]=\"showPassword ? 'text' : 'password'\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [disabled]=\"field.disabled || false\"\r\n [formControl]=\"control\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [readonly]=\"field.readOnly || false\"\r\n (input)=\"isAlphanumeric ? handleAlphanumericInput($event) : handleInput($event)\"\r\n (focus)=\"onPasswordFocus()\"\r\n (blur)=\"handlePasswordBlur($event)\"\r\n class=\"fn-input-field overflow-hidden text-ellipsis\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-12':\r\n (field.toggleMask && !field.isCopyText) ||\r\n (!field.toggleMask && field.isCopyText),\r\n 'pr-24': field.toggleMask && field.isCopyText && !field.hasGenerateKey,\r\n 'pr-32': field.toggleMask && field.isCopyText && field.hasGenerateKey,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.toggleMask && !field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <fn-icon\r\n [name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n } @else if (field.isCopyText && !field.toggleMask) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <fn-icon\r\n [name]=\"'round-arrow-top-left'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n }\r\n } @else if (field.toggleMask && field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"togglePasswordVisibility()\"\r\n (keydown)=\"togglePasswordVisibility()\"\r\n >\r\n <fn-icon\r\n [name]=\"showPassword ? 'eye-open' : 'eye-close'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'large'\"\r\n [color]=\"'#03182b'\"\r\n ></fn-icon>\r\n </span>\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n [ngClass]=\"{ 'opacity-50 cursor-not-allowed': !control.value }\"\r\n >\r\n <fn-icon [name]=\"'round-arrow-top-left'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n }\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (field.feedback && isPasswordFocused) {\r\n <div class=\"mt-2 space-y-1\">\r\n <div class=\"flex items-center justify-between text-xs\">\r\n <span class=\"font-medium\" [ngClass]=\"strengthClass\">\r\n {{ passwordStrengthLabel | translate }}\r\n </span>\r\n <div class=\"flex items-center gap-1\">\r\n <fn-icon\r\n [name]=\"passwordStrengthPercent > 50 ? 'check-circle' : 'info-circle'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'extrasmall'\"\r\n [color]=\"\r\n passwordStrengthPercent <= 25\r\n ? '#ef3e42'\r\n : passwordStrengthPercent <= 75\r\n ? '#ff9f00'\r\n : '#6cc24a'\r\n \"\r\n ></fn-icon>\r\n </div>\r\n </div>\r\n <div class=\"h-1.5 w-full bg-[#eef0f2] rounded-full overflow-hidden\">\r\n <div\r\n class=\"h-full transition-all duration-300 rounded-full\"\r\n [ngClass]=\"{\r\n 'bg-[#ef3e42]': passwordStrengthPercent <= 25,\r\n 'bg-[#ff9f00]': passwordStrengthPercent > 25 && passwordStrengthPercent <= 75,\r\n 'bg-[#6cc24a]': passwordStrengthPercent > 75,\r\n }\"\r\n [style.width.%]=\"passwordStrengthPercent\"\r\n ></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Number Field -->\r\n @case ('number') {\r\n <div class=\"relative w-full\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type === 'number' && field.isCurrency ? 'text' : 'number'\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"isDisabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n [min]=\"field.type === 'number' && !field.isCurrency ? 0 : null\"\r\n [max]=\"field.type === 'number' && !field.isCurrency ? field.max : null\"\r\n [step]=\"field.type === 'number' && !field.isCurrency ? field.step || 1 : null\"\r\n (input)=\"handleNumberInput($event, field.minFractionDigits || 2)\"\r\n (paste)=\"handleNumberPaste($event, field.minFractionDigits || 2)\"\r\n (blur)=\"handleBlur($event, field.minFractionDigits || 2)\"\r\n (keydown)=\"handleNumberKeydown($event, field.isCurrency || false)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10': field.suffix?.icon && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'text-[24px] font-bold': field.isCurrency,\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n </div>\r\n }\r\n\r\n <!-- Default Input Field (text, email, etc.) -->\r\n @default {\r\n <div class=\"relative z-0\">\r\n @if (field.prefix?.icon || field.prefix?.text) {\r\n <div\r\n class=\"absolute left-0 top-1/2 -translate-y-1/2 pl-3 flex items-center gap-2\"\r\n style=\"z-index: 1\"\r\n >\r\n @if (field.prefix?.icon) {\r\n <fn-icon\r\n [name]=\"field.prefix?.icon || ''\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n @if (field.prefix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"prefixClick($event)\"\r\n (keydown.enter)=\"prefixClick($event)\"\r\n [attr.tabindex]=\"field.prefix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.prefix?.onClick\"\r\n >{{ field.prefix!.text }}</span\r\n >\r\n }\r\n </div>\r\n }\r\n <input\r\n [type]=\"field.type\"\r\n [name]=\"field.name\"\r\n [id]=\"uniqueId\"\r\n [required]=\"field.required || false\"\r\n [placeholder]=\"field.placeholder || '' | translate\"\r\n [disabled]=\"field.disabled || false\"\r\n [readOnly]=\"field.readOnly || false\"\r\n [formControl]=\"control\"\r\n (input)=\"\r\n isEmailField\r\n ? handleEmailInput($event)\r\n : field.type === 'text' && isAlphanumeric\r\n ? handleAlphanumericInput($event)\r\n : handleInput($event)\r\n \"\r\n (blur)=\"handleBlur($event)\"\r\n class=\"fn-input-field\"\r\n [ngClass]=\"{\r\n disabled: control.disabled || field.readOnly,\r\n error: control.touched && control.errors,\r\n success: field['hasSuccessBorder'] && control.valid && !control.errors,\r\n 'pl-10': field.prefix?.icon && !field.prefix?.text,\r\n 'has-prefix-text': !!field.prefix?.text,\r\n 'pr-10':\r\n (field.icon || field.suffix?.icon) && !field.isCopyText && !field.suffix?.text,\r\n 'has-suffix-text': !!field.suffix?.text,\r\n 'pr-20':\r\n field.isCopyText && !field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-30':\r\n field.isCopyText && field.hasGenerateKey && !(field.icon || field.suffix?.icon),\r\n 'pr-32':\r\n field.isCopyText && field.hasGenerateKey && (field.icon || field.suffix?.icon),\r\n }\"\r\n [ngStyle]=\"{\r\n color: field.valueColor || 'inherit',\r\n 'font-size': field.valueSize || '',\r\n }\"\r\n (focus)=\"onFocus()\"\r\n />\r\n\r\n <div class=\"absolute right-0 top-1/2 -translate-y-1/2 flex items-center pr-3 gap-2\">\r\n @if (field.isCopyText) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"copyText($event)\"\r\n (keydown)=\"copyText($event)\"\r\n >\r\n <fn-icon [name]=\"'two-square'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n @if (field.hasGenerateKey) {\r\n <span\r\n class=\"cursor-pointer\"\r\n (click)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n (keydown)=\"field.onGenerateKey && field.onGenerateKey()\"\r\n >\r\n <fn-icon [name]=\"'round-arrow-top-left'\" [color]=\"'#03182b'\"></fn-icon>\r\n </span>\r\n }\r\n }\r\n\r\n @if (field.icon || field.suffix?.icon || field.suffix?.text) {\r\n @if (field.suffix?.text) {\r\n <span\r\n class=\"text-sm font-medium whitespace-nowrap\"\r\n [ngStyle]=\"{ color: control.disabled ? '#c4cdd2' : '#03182b' }\"\r\n (click)=\"suffixClick($event)\"\r\n (keydown.enter)=\"suffixClick($event)\"\r\n [attr.tabindex]=\"field.suffix?.onClick ? 0 : null\"\r\n [class.cursor-pointer]=\"!!field.suffix?.onClick\"\r\n >{{ field.suffix!.text }}</span\r\n >\r\n }\r\n @if (field.icon || field.suffix?.icon) {\r\n <fn-icon\r\n [name]=\"field.suffix?.icon || field.icon?.name || ''\"\r\n [variant]=\"field.icon?.variant || 'Line'\"\r\n [size]=\"field.icon ? getIconSizeName(field.icon.size) : 'medium'\"\r\n [color]=\"control.disabled ? '#c4cdd2' : '#03182b'\"\r\n ></fn-icon>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </ng-container>\r\n\r\n @if (showFormFieldMessage(control, helperText)) {\r\n <div class=\"fn-field-message-container\">\r\n <span\r\n class=\"fn-field-message-text\"\r\n [ngClass]=\"{\r\n error: isError,\r\n success: isSuccess,\r\n }\"\r\n >\r\n {{ getFieldMessage() | translate }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- Integrated Toast Notifications -->\r\n <div class=\"toast-container\">\r\n @for (toast of toasts(); track toast.id) {\r\n <div class=\"toast toast-{{ toast.type }}\" [class.toast-enter]=\"toast.isVisible\">\r\n <span class=\"toast-icon\">\r\n <fn-icon\r\n [name]=\"getToastIconName(toast)\"\r\n [variant]=\"'Colour'\"\r\n [size]=\"'medium'\"\r\n [color]=\"\r\n toast.type === 'success'\r\n ? '#6cc24a'\r\n : toast.type === 'warn'\r\n ? '#ff9f00'\r\n : toast.type === 'error'\r\n ? '#ef3e42'\r\n : '#03182b'\r\n \"\r\n ></fn-icon>\r\n </span>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-message\">\r\n {{ getTranslatedMessage(toast.message) | translate }}\r\n </span>\r\n </div>\r\n <button class=\"toast-close\" (click)=\"removeToast(toast.id)\" aria-label=\"Close\">\r\n <fn-icon\r\n [name]=\"'cross'\"\r\n [variant]=\"'Line'\"\r\n [size]=\"'medium'\"\r\n [color]=\"'#ffffff'\"\r\n ></fn-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".fn-input-container{display:flex;flex-direction:column;width:100%}.fn-label{font-family:Outfit,sans-serif;font-weight:500;margin-bottom:8px;display:flex;align-items:center}.fn-label span{color:#c4cdd2;font-size:14px;font-weight:400}.fn-input-field{width:100%;height:48px;padding:0 16px;background-color:#f7f8f9;border-bottom:1.5px solid #c4cdd2;font-family:Outfit,sans-serif;font-size:16px;color:#03182b;transition:all .2s ease;border-radius:4px 4px 0 0}.fn-input-field::placeholder{color:#c4cdd2}.fn-input-field:focus{outline:none;background-color:#fff;border-bottom-color:#03182b}.fn-input-field.error{border-bottom-color:#ef3e42}.fn-input-field.success{border-bottom-color:#6cc24a}.fn-input-field.disabled{background-color:#eef0f2;color:#c4cdd2;cursor:not-allowed}.fn-input-field.pl-10{padding-left:40px!important}.fn-input-field.has-prefix-text{padding-left:100px!important}.fn-input-field.pr-10{padding-right:40px!important}.fn-input-field.has-suffix-text{padding-right:120px!important}.fn-input-field.pr-12{padding-right:44px!important}.fn-input-field.pr-20{padding-right:72px!important}.fn-input-field.pr-24{padding-right:88px!important}.fn-input-field.pr-30{padding-right:104px!important}.fn-input-field.pr-32{padding-right:120px!important}.fn-field-message-container{margin-top:4px}.fn-field-message-text{font-family:Outfit,sans-serif;font-size:12px;color:#c4cdd2}.fn-field-message-text.error{color:#ef3e42}.fn-field-message-text.success{color:#6cc24a}.icon-container{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}.relative{position:relative}.absolute{position:absolute}.right-0{right:0}.left-0{left:0}.top-1\\/2{top:50%}.-translate-y-1\\/2{transform:translateY(-50%)}.flex{display:flex}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.mt-2{margin-top:.5rem}.space-y-1>:not([hidden])~:not([hidden]){margin-top:.25rem}.text-xs{font-size:.75rem}.font-medium{font-weight:500}.h-1\\.5{height:.375rem}.w-full{width:100%}.bg-\\[\\#eef0f2\\]{background-color:#eef0f2}.rounded-full{border-radius:9999px}.overflow-hidden{overflow:hidden}.transition-all{transition-property:all}.duration-300{transition-duration:.3s}.cursor-pointer{cursor:pointer}.opacity-50{opacity:.5}.cursor-not-allowed{cursor:not-allowed}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.toast-container{position:fixed;top:24px;left:50%;transform:translate(-50%);z-index:9999;width:100%;max-width:336px;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{display:flex;align-items:center;background:#25333f;border-radius:8px;padding:12px;width:336px;max-width:100%;opacity:0;transform:translateY(-20px);transition:all .3s cubic-bezier(.4,0,.2,1);pointer-events:auto}.toast.toast-enter{opacity:1;transform:translateY(0)}.toast.toast-success{border-left:4px solid #6cc24a}.toast.toast-warn{border-left:4px solid #ff9f00}.toast.toast-error{border-left:4px solid #ef3e42}.toast.toast-info{border-left:4px solid #3b82f6}.toast .toast-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;margin-right:8px;flex-shrink:0}.toast .toast-content{flex:1;min-width:0}.toast .toast-message{font-size:14px;line-height:20px;color:#fff;display:block}.toast .toast-close{background:none;border:none;font-size:20px;color:#fff;opacity:.7;cursor:pointer;padding:0;margin-left:8px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:opacity .2s;pointer-events:auto}.toast .toast-close:hover{opacity:1}\n"] }]
|
|
1179
1038
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { field: [{
|
|
1180
1039
|
type: Input
|
|
1181
1040
|
}], helperHandle: [{
|
|
@@ -1195,9 +1054,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1195
1054
|
}], textareaElement: [{
|
|
1196
1055
|
type: ViewChild,
|
|
1197
1056
|
args: ['fnTextarea']
|
|
1198
|
-
}], iconContainers: [{
|
|
1199
|
-
type: ViewChildren,
|
|
1200
|
-
args: ['iconContainer']
|
|
1201
1057
|
}] } });
|
|
1202
1058
|
|
|
1203
1059
|
/**
|