@tolle_/tolle-ui 18.2.16 → 18.2.18

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.
@@ -2886,13 +2886,19 @@ class MaskedInputComponent {
2886
2886
  size = 'default';
2887
2887
  returnRaw = false;
2888
2888
  hideHintOnFocus = true;
2889
+ externalFocused;
2890
+ mergedPosition = 'none';
2889
2891
  inputEl;
2890
2892
  hasPrefix = false;
2891
2893
  hasSuffix = false;
2892
2894
  displayValue = '';
2893
2895
  isFocused = false;
2894
2896
  tokens = {
2895
- '0': /\d/, '9': /\d/, 'a': /[a-z]/i, 'A': /[a-z]/i, '*': /[a-z0-9]/i
2897
+ '0': /\d/,
2898
+ '9': /\d/,
2899
+ a: /[a-z]/i,
2900
+ A: /[a-z]/i,
2901
+ '*': /[a-z0-9]/i,
2896
2902
  };
2897
2903
  onChange = () => { };
2898
2904
  onTouched = () => { };
@@ -2910,58 +2916,52 @@ class MaskedInputComponent {
2910
2916
  }
2911
2917
  }
2912
2918
  get computedLabelClass() {
2913
- return cn("text-sm font-medium text-foreground leading-none transition-opacity duration-200", this.disabled && "opacity-50");
2919
+ return cn('text-sm font-medium text-foreground leading-none transition-opacity duration-200', this.disabled && 'opacity-50');
2914
2920
  }
2915
2921
  get computedContainerClass() {
2922
+ const focused = this.externalFocused !== undefined ? this.externalFocused : this.isFocused;
2916
2923
  return cn(
2917
2924
  // Base styles
2918
- "group relative flex items-center w-full rounded-md border transition-all duration-200", "bg-background",
2925
+ 'group relative flex items-center w-full border transition-all duration-200', 'bg-background',
2919
2926
  // Border and shadow
2920
- "border-input shadow-sm",
2927
+ 'border-input shadow-sm',
2921
2928
  // Sizing
2922
- this.size === 'xs' && "h-8 px-2 gap-1.5 text-xs", this.size === 'sm' && "h-9 px-3 gap-2 text-sm", this.size === 'default' && "h-10 px-3 gap-2 text-sm", this.size === 'lg' && "h-11 px-4 gap-3 text-base",
2923
- // Focus state - SIMPLE LIKE ZARDUI
2924
- !(this.readonly || this.disabled) && [
2925
- "focus-within:border-primary/80",
2926
- "focus-within:ring-4",
2927
- "focus-within:ring-ring/30",
2928
- "focus-within:ring-offset-0",
2929
- "focus-within:shadow-none",
2929
+ this.size === 'xs' && 'h-8 px-2 gap-1.5 text-xs', this.size === 'sm' && 'h-9 px-3 gap-2 text-sm', this.size === 'default' && 'h-10 px-3 gap-2 text-sm', this.size === 'lg' && 'h-11 px-4 gap-3 text-base',
2930
+ // Merged position - handle border radius
2931
+ this.mergedPosition === 'left' && 'rounded-l-md rounded-r-none border-r-0 pr-0', this.mergedPosition === 'right' && 'rounded-r-md rounded-l-none border-l-0 pl-0', this.mergedPosition === 'none' && 'rounded-md',
2932
+ // Focus state
2933
+ !(this.readonly || this.disabled) &&
2934
+ focused && [
2935
+ 'ring-4',
2936
+ 'ring-ring/30',
2937
+ 'ring-offset-0',
2938
+ 'shadow-none',
2939
+ this.error ? 'border-destructive/80' : 'border-primary/80',
2930
2940
  ],
2931
2941
  // Error state
2932
2942
  this.error && [
2933
- "border-destructive",
2934
- !(this.readonly || this.disabled) && [
2935
- "focus-within:border-destructive/80",
2936
- "focus-within:ring-destructive/30"
2937
- ]
2943
+ 'border-destructive',
2944
+ !(this.readonly || this.disabled) && focused && 'ring-destructive/30',
2938
2945
  ],
2939
2946
  // Disabled state
2940
- this.disabled && [
2941
- "cursor-not-allowed opacity-50",
2942
- "border-opacity-50"
2943
- ],
2947
+ this.disabled && ['cursor-not-allowed opacity-50', 'border-opacity-50'],
2944
2948
  // Readonly state
2945
- this.readonly && [
2946
- "cursor-default",
2947
- "border-dashed",
2948
- !this.disabled && "focus-within:ring-0 focus-within:border-opacity-100"
2949
- ], this.containerClass);
2949
+ this.readonly && ['cursor-default', 'border-dashed', !this.disabled && !focused && 'ring-0'], this.containerClass);
2950
2950
  }
2951
2951
  get computedInputClass() {
2952
2952
  return cn(
2953
2953
  // Base styles
2954
- "flex-1 bg-transparent border-none p-0", "placeholder:text-muted-foreground",
2954
+ 'flex-1 bg-transparent border-none p-0', 'placeholder:text-muted-foreground',
2955
2955
  // Remove all default focus styles
2956
- "focus:outline-none focus:ring-0 focus:shadow-none",
2956
+ 'focus:outline-none focus:ring-0 focus:shadow-none',
2957
2957
  // Text sizing
2958
- this.size === 'xs' && "text-xs", this.size === 'sm' && "text-sm", this.size === 'default' && "text-sm", this.size === 'lg' && "text-base",
2958
+ this.size === 'xs' && 'text-xs', this.size === 'sm' && 'text-sm', this.size === 'default' && 'text-sm', this.size === 'lg' && 'text-base',
2959
2959
  // Cursor states
2960
- this.disabled && "cursor-not-allowed", this.readonly && "cursor-default",
2960
+ this.disabled && 'cursor-not-allowed', this.readonly && 'cursor-default',
2961
2961
  // Text color
2962
- "text-foreground",
2962
+ 'text-foreground',
2963
2963
  // Selection color
2964
- "selection:bg-primary/20 selection:text-foreground", this.class);
2964
+ 'selection:bg-primary/20 selection:text-foreground', this.class);
2965
2965
  }
2966
2966
  focusInput() {
2967
2967
  if (!this.disabled && this.inputEl) {
@@ -3031,28 +3031,22 @@ class MaskedInputComponent {
3031
3031
  }
3032
3032
  cn = cn;
3033
3033
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskedInputComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3034
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MaskedInputComponent, isStandalone: true, selector: "tolle-masked-input", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", mask: "mask", placeholder: "placeholder", type: "type", disabled: "disabled", readonly: "readonly", class: "class", containerClass: "containerClass", error: "error", size: "size", returnRaw: "returnRaw", hideHintOnFocus: "hideHintOnFocus" }, providers: [
3034
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MaskedInputComponent, isStandalone: true, selector: "tolle-masked-input", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", mask: "mask", placeholder: "placeholder", type: "type", disabled: "disabled", readonly: "readonly", class: "class", containerClass: "containerClass", error: "error", size: "size", returnRaw: "returnRaw", hideHintOnFocus: "hideHintOnFocus", externalFocused: "externalFocused", mergedPosition: "mergedPosition" }, providers: [
3035
3035
  {
3036
3036
  provide: NG_VALUE_ACCESSOR,
3037
3037
  useExisting: forwardRef(() => MaskedInputComponent),
3038
- multi: true
3039
- }
3038
+ multi: true,
3039
+ },
3040
3040
  ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }], ngImport: i0, template: `
3041
- <div class="flex flex-col gap-1.5 w-full">
3042
- <label
3043
- *ngIf="label"
3044
- [for]="id"
3045
- [class]="computedLabelClass"
3046
- >
3041
+ <div class="flex w-full flex-col gap-1.5">
3042
+ <label *ngIf="label && mergedPosition === 'none'" [for]="id" [class]="computedLabelClass">
3047
3043
  {{ label }}
3048
3044
  </label>
3049
3045
 
3050
- <div
3051
- [class]="computedContainerClass"
3052
- (click)="focusInput()"
3053
- >
3046
+ <div [class]="computedContainerClass" (click)="focusInput()">
3054
3047
  <!-- Prefix Icon -->
3055
- <div class="h-full flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
3048
+ <div
3049
+ class="flex h-full items-center text-muted-foreground transition-colors duration-200 group-focus-within:text-primary">
3056
3050
  <ng-content select="[prefix]"></ng-content>
3057
3051
  </div>
3058
3052
 
@@ -3069,28 +3063,23 @@ class MaskedInputComponent {
3069
3063
  (focus)="onFocus()"
3070
3064
  [class]="computedInputClass"
3071
3065
  [attr.aria-invalid]="error"
3072
- [attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
3073
- />
3066
+ [attr.aria-describedby]="error && errorMessage ? id + '-error' : null" />
3074
3067
 
3075
3068
  <!-- Suffix Icon -->
3076
- <div class="h-full flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
3069
+ <div
3070
+ class="flex h-full items-center text-muted-foreground transition-colors duration-200 group-focus-within:text-primary">
3077
3071
  <ng-content select="[suffix]"></ng-content>
3078
3072
  </div>
3079
3073
  </div>
3080
3074
 
3081
- <ng-container *ngIf="!disabled">
3075
+ <ng-container *ngIf="!disabled && mergedPosition === 'none'">
3082
3076
  <p
3083
3077
  *ngIf="hint && !error"
3084
- class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
3085
- [class.opacity-0]="isFocused && hideHintOnFocus"
3086
- >
3078
+ class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
3079
+ [class.opacity-0]="isFocused && hideHintOnFocus">
3087
3080
  {{ hint }}
3088
3081
  </p>
3089
- <p
3090
- *ngIf="error && errorMessage"
3091
- [id]="id + '-error'"
3092
- class="text-xs text-destructive px-1"
3093
- >
3082
+ <p *ngIf="error && errorMessage" [id]="id + '-error'" class="px-1 text-xs text-destructive">
3094
3083
  {{ errorMessage }}
3095
3084
  </p>
3096
3085
  </ng-container>
@@ -3107,25 +3096,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3107
3096
  {
3108
3097
  provide: NG_VALUE_ACCESSOR,
3109
3098
  useExisting: forwardRef(() => MaskedInputComponent),
3110
- multi: true
3111
- }
3099
+ multi: true,
3100
+ },
3112
3101
  ],
3113
3102
  template: `
3114
- <div class="flex flex-col gap-1.5 w-full">
3115
- <label
3116
- *ngIf="label"
3117
- [for]="id"
3118
- [class]="computedLabelClass"
3119
- >
3103
+ <div class="flex w-full flex-col gap-1.5">
3104
+ <label *ngIf="label && mergedPosition === 'none'" [for]="id" [class]="computedLabelClass">
3120
3105
  {{ label }}
3121
3106
  </label>
3122
3107
 
3123
- <div
3124
- [class]="computedContainerClass"
3125
- (click)="focusInput()"
3126
- >
3108
+ <div [class]="computedContainerClass" (click)="focusInput()">
3127
3109
  <!-- Prefix Icon -->
3128
- <div class="h-full flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
3110
+ <div
3111
+ class="flex h-full items-center text-muted-foreground transition-colors duration-200 group-focus-within:text-primary">
3129
3112
  <ng-content select="[prefix]"></ng-content>
3130
3113
  </div>
3131
3114
 
@@ -3142,33 +3125,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3142
3125
  (focus)="onFocus()"
3143
3126
  [class]="computedInputClass"
3144
3127
  [attr.aria-invalid]="error"
3145
- [attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
3146
- />
3128
+ [attr.aria-describedby]="error && errorMessage ? id + '-error' : null" />
3147
3129
 
3148
3130
  <!-- Suffix Icon -->
3149
- <div class="h-full flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
3131
+ <div
3132
+ class="flex h-full items-center text-muted-foreground transition-colors duration-200 group-focus-within:text-primary">
3150
3133
  <ng-content select="[suffix]"></ng-content>
3151
3134
  </div>
3152
3135
  </div>
3153
3136
 
3154
- <ng-container *ngIf="!disabled">
3137
+ <ng-container *ngIf="!disabled && mergedPosition === 'none'">
3155
3138
  <p
3156
3139
  *ngIf="hint && !error"
3157
- class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
3158
- [class.opacity-0]="isFocused && hideHintOnFocus"
3159
- >
3140
+ class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
3141
+ [class.opacity-0]="isFocused && hideHintOnFocus">
3160
3142
  {{ hint }}
3161
3143
  </p>
3162
- <p
3163
- *ngIf="error && errorMessage"
3164
- [id]="id + '-error'"
3165
- class="text-xs text-destructive px-1"
3166
- >
3144
+ <p *ngIf="error && errorMessage" [id]="id + '-error'" class="px-1 text-xs text-destructive">
3167
3145
  {{ errorMessage }}
3168
3146
  </p>
3169
3147
  </ng-container>
3170
3148
  </div>
3171
- `
3149
+ `,
3172
3150
  }]
3173
3151
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { id: [{
3174
3152
  type: Input
@@ -3200,6 +3178,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3200
3178
  type: Input
3201
3179
  }], hideHintOnFocus: [{
3202
3180
  type: Input
3181
+ }], externalFocused: [{
3182
+ type: Input
3183
+ }], mergedPosition: [{
3184
+ type: Input
3203
3185
  }], inputEl: [{
3204
3186
  type: ViewChild,
3205
3187
  args: ['inputEl', { static: true }]
@@ -3430,7 +3412,7 @@ class DatePickerComponent {
3430
3412
  ></tolle-calendar>
3431
3413
  </div>
3432
3414
  </div>
3433
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "mode", "disablePastDates", "showQuickActions", "minDate", "maxDate", "formatMonthFn", "formatYearFn", "formatDateFn"], outputs: ["dateSelect"] }] });
3415
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus", "externalFocused", "mergedPosition"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "mode", "disablePastDates", "showQuickActions", "minDate", "maxDate", "formatMonthFn", "formatYearFn", "formatDateFn"], outputs: ["dateSelect"] }] });
3434
3416
  }
3435
3417
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, decorators: [{
3436
3418
  type: Component,
@@ -12930,7 +12912,10 @@ class CountrySelectorComponent {
12930
12912
  defaultCountryCode = 'GH';
12931
12913
  returnValue = 'isoAlpha2';
12932
12914
  showName = true;
12915
+ mergedPosition = 'none';
12933
12916
  onSelect = new EventEmitter();
12917
+ onFocusChange = new EventEmitter();
12918
+ onBlurChange = new EventEmitter();
12934
12919
  popover;
12935
12920
  searchInput;
12936
12921
  countryCodesService = inject(CountryCodesService);
@@ -12954,19 +12939,20 @@ class CountrySelectorComponent {
12954
12939
  }
12955
12940
  }
12956
12941
  get computedTriggerClass() {
12957
- return cn('flex w-full items-center justify-between rounded-md border transition-all duration-200', 'bg-background text-foreground', 'border-input shadow-sm', this.size === 'xs' && 'h-8 px-2 text-xs', this.size === 'sm' && 'h-9 px-3 text-sm', this.size === 'default' && 'h-10 px-3 text-sm', this.size === 'lg' && 'h-11 px-4 text-base', !(this.readonly || this.disabled) && [
12958
- 'focus:outline-none',
12959
- 'focus:ring-4',
12960
- 'focus:ring-ring/30',
12961
- 'focus:ring-offset-0',
12962
- 'focus:border-primary/80',
12963
- ], !(this.readonly || this.disabled) && 'hover:border-accent', this.error && [
12964
- 'border-destructive',
12965
- !(this.readonly || this.disabled) && [
12966
- 'focus:border-destructive/80',
12967
- 'focus:ring-destructive/30',
12968
- ],
12969
- ], this.disabled && 'cursor-not-allowed opacity-50 border-opacity-50', this.readonly && 'cursor-default border-dashed', this.class);
12942
+ return cn('flex w-full items-center justify-between border transition-all duration-200', 'bg-background text-foreground', 'border-input shadow-sm', this.size === 'xs' && 'h-8 px-2 text-xs', this.size === 'sm' && 'h-9 px-3 text-sm', this.size === 'default' && 'h-10 px-3 text-sm', this.size === 'lg' && 'h-11 px-4 text-base',
12943
+ // Merged position
12944
+ this.mergedPosition === 'left' && 'rounded-l-md rounded-r-none border-r-0', this.mergedPosition === 'none' && 'rounded-md',
12945
+ // Focus state
12946
+ !(this.readonly || this.disabled) &&
12947
+ this.isFocused && [
12948
+ 'ring-4',
12949
+ 'ring-ring/30',
12950
+ 'ring-offset-0',
12951
+ 'shadow-none',
12952
+ this.error ? 'border-destructive/80' : 'border-primary/80',
12953
+ ], !(this.readonly || this.disabled) && 'hover:border-accent',
12954
+ // Error state
12955
+ this.error && ['border-destructive', this.isFocused && 'ring-destructive/30'], this.disabled && 'cursor-not-allowed opacity-50 border-opacity-50', this.readonly && 'cursor-default border-dashed', this.class);
12970
12956
  }
12971
12957
  get computedLabelClass() {
12972
12958
  return cn('text-sm font-medium text-foreground leading-none transition-opacity duration-200', this.disabled && 'opacity-50');
@@ -13010,10 +12996,12 @@ class CountrySelectorComponent {
13010
12996
  }
13011
12997
  onFocus() {
13012
12998
  this.isFocused = true;
12999
+ this.onFocusChange.emit();
13013
13000
  }
13014
13001
  onBlur() {
13015
13002
  this.isFocused = false;
13016
13003
  this.onTouched();
13004
+ this.onBlurChange.emit();
13017
13005
  }
13018
13006
  onPopoverOpen() {
13019
13007
  setTimeout(() => {
@@ -13055,7 +13043,7 @@ class CountrySelectorComponent {
13055
13043
  this.disabled = isDisabled;
13056
13044
  }
13057
13045
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CountrySelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13058
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CountrySelectorComponent, isStandalone: true, selector: "tolle-country-selector", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", error: "error", hideHintOnFocus: "hideHintOnFocus", placeholder: "placeholder", class: "class", disabled: "disabled", readonly: "readonly", size: "size", defaultCountryCode: "defaultCountryCode", returnValue: "returnValue", showName: "showName" }, outputs: { onSelect: "onSelect" }, providers: [
13046
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CountrySelectorComponent, isStandalone: true, selector: "tolle-country-selector", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", error: "error", hideHintOnFocus: "hideHintOnFocus", placeholder: "placeholder", class: "class", disabled: "disabled", readonly: "readonly", size: "size", defaultCountryCode: "defaultCountryCode", returnValue: "returnValue", showName: "showName", mergedPosition: "mergedPosition" }, outputs: { onSelect: "onSelect", onFocusChange: "onFocusChange", onBlurChange: "onBlurChange" }, providers: [
13059
13047
  {
13060
13048
  provide: NG_VALUE_ACCESSOR,
13061
13049
  useExisting: forwardRef(() => CountrySelectorComponent),
@@ -13063,7 +13051,7 @@ class CountrySelectorComponent {
13063
13051
  },
13064
13052
  ], viewQueries: [{ propertyName: "popover", first: true, predicate: ["popover"], descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], ngImport: i0, template: `
13065
13053
  <div class="flex w-full flex-col gap-1.5">
13066
- <label *ngIf="label" [for]="id" [class]="computedLabelClass">
13054
+ <label *ngIf="label && mergedPosition === 'none'" [for]="id" [class]="computedLabelClass">
13067
13055
  {{ label }}
13068
13056
  </label>
13069
13057
 
@@ -13100,13 +13088,13 @@ class CountrySelectorComponent {
13100
13088
  </div>
13101
13089
 
13102
13090
  <div
13103
- class="flex min-w-[300px] max-w-[400px] flex-col overflow-hidden rounded-md border border-border bg-popover shadow-md">
13091
+ class="flex min-w-[300px] max-w-[400px] flex-col overflow-hidden rounded-md border border-border bg-popover shadow-md"
13092
+ (mousedown)="$event.stopPropagation()">
13104
13093
  <div class="sticky top-0 z-10 border-b border-border bg-popover p-2 shadow-sm">
13105
13094
  <tolle-input
13106
13095
  size="sm"
13107
13096
  placeholder="Search country..."
13108
13097
  [(ngModel)]="searchQuery"
13109
- (mousedown)="$event.stopPropagation()"
13110
13098
  (ngModelChange)="filterCountries($event)"
13111
13099
  class="w-full"
13112
13100
  #searchInput>
@@ -13140,7 +13128,7 @@ class CountrySelectorComponent {
13140
13128
  </div>
13141
13129
  </tolle-popover>
13142
13130
 
13143
- <ng-container *ngIf="!disabled">
13131
+ <ng-container *ngIf="!disabled && mergedPosition === 'none'">
13144
13132
  <p
13145
13133
  *ngIf="hint && !error"
13146
13134
  class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
@@ -13172,7 +13160,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
13172
13160
  ],
13173
13161
  template: `
13174
13162
  <div class="flex w-full flex-col gap-1.5">
13175
- <label *ngIf="label" [for]="id" [class]="computedLabelClass">
13163
+ <label *ngIf="label && mergedPosition === 'none'" [for]="id" [class]="computedLabelClass">
13176
13164
  {{ label }}
13177
13165
  </label>
13178
13166
 
@@ -13209,13 +13197,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
13209
13197
  </div>
13210
13198
 
13211
13199
  <div
13212
- class="flex min-w-[300px] max-w-[400px] flex-col overflow-hidden rounded-md border border-border bg-popover shadow-md">
13200
+ class="flex min-w-[300px] max-w-[400px] flex-col overflow-hidden rounded-md border border-border bg-popover shadow-md"
13201
+ (mousedown)="$event.stopPropagation()">
13213
13202
  <div class="sticky top-0 z-10 border-b border-border bg-popover p-2 shadow-sm">
13214
13203
  <tolle-input
13215
13204
  size="sm"
13216
13205
  placeholder="Search country..."
13217
13206
  [(ngModel)]="searchQuery"
13218
- (mousedown)="$event.stopPropagation()"
13219
13207
  (ngModelChange)="filterCountries($event)"
13220
13208
  class="w-full"
13221
13209
  #searchInput>
@@ -13249,7 +13237,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
13249
13237
  </div>
13250
13238
  </tolle-popover>
13251
13239
 
13252
- <ng-container *ngIf="!disabled">
13240
+ <ng-container *ngIf="!disabled && mergedPosition === 'none'">
13253
13241
  <p
13254
13242
  *ngIf="hint && !error"
13255
13243
  class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
@@ -13294,8 +13282,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
13294
13282
  type: Input
13295
13283
  }], showName: [{
13296
13284
  type: Input
13285
+ }], mergedPosition: [{
13286
+ type: Input
13297
13287
  }], onSelect: [{
13298
13288
  type: Output
13289
+ }], onFocusChange: [{
13290
+ type: Output
13291
+ }], onBlurChange: [{
13292
+ type: Output
13299
13293
  }], popover: [{
13300
13294
  type: ViewChild,
13301
13295
  args: ['popover']
@@ -13325,12 +13319,48 @@ class PhoneNumberInputComponent {
13325
13319
  displayValue = '';
13326
13320
  selectedIso = '';
13327
13321
  rawValue = '';
13322
+ isFocused = false;
13328
13323
  cn = cn;
13329
13324
  onChange = () => { };
13330
13325
  onTouched = () => { };
13331
13326
  constructor() {
13332
13327
  this.selectedIso = this.defaultCountryCode;
13333
13328
  }
13329
+ get computedLabelClass() {
13330
+ return cn('text-sm font-medium text-foreground leading-none transition-opacity duration-200', this.disabled && 'opacity-50');
13331
+ }
13332
+ get computedMergedClass() {
13333
+ return cn('flex rounded-md border transition-all duration-200', 'bg-background shadow-sm', this.size === 'xs' && 'h-8', this.size === 'sm' && 'h-9', this.size === 'default' && 'h-10', this.size === 'lg' && 'h-11',
13334
+ // Focus state
13335
+ !this.disabled &&
13336
+ !this.readonly &&
13337
+ this.isFocused && [
13338
+ 'ring-4',
13339
+ 'ring-ring/30',
13340
+ 'ring-offset-0',
13341
+ 'shadow-none',
13342
+ this.error ? 'border-destructive/80' : 'border-primary/80',
13343
+ ],
13344
+ // Error state
13345
+ this.error && ['border-destructive', this.isFocused && 'ring-destructive/30'],
13346
+ // Disabled state
13347
+ this.disabled && ['cursor-not-allowed opacity-50', 'border-opacity-50'],
13348
+ // Readonly state
13349
+ this.readonly && [
13350
+ 'cursor-default',
13351
+ 'border-dashed',
13352
+ !this.disabled && !this.isFocused && 'ring-0',
13353
+ ], this.class);
13354
+ }
13355
+ onFocusIn() {
13356
+ this.isFocused = true;
13357
+ this.cdr.markForCheck();
13358
+ }
13359
+ onFocusOut() {
13360
+ this.isFocused = false;
13361
+ this.onTouched();
13362
+ this.cdr.markForCheck();
13363
+ }
13334
13364
  writeValue(value) {
13335
13365
  if (value) {
13336
13366
  if (typeof value === 'object' && value.number) {
@@ -13404,75 +13434,103 @@ class PhoneNumberInputComponent {
13404
13434
  {
13405
13435
  provide: NG_VALUE_ACCESSOR,
13406
13436
  useExisting: forwardRef(() => PhoneNumberInputComponent),
13407
- multi: true
13408
- }
13437
+ multi: true,
13438
+ },
13409
13439
  ], ngImport: i0, template: `
13410
- <tolle-masked-input
13411
- [id]="id"
13412
- [label]="label"
13413
- [hint]="hint"
13414
- [error]="error"
13415
- [errorMessage]="errorMessage"
13416
- [hideHintOnFocus]="hideHintOnFocus"
13417
- [mask]="mask"
13418
- [size]="size"
13419
- [disabled]="disabled"
13420
- [readonly]="readonly"
13421
- [placeholder]="placeholder"
13422
- [(ngModel)]="displayValue"
13423
- (ngModelChange)="onMaskInputChange($event)"
13424
- [containerClass]="cn('pl-0', class)"
13425
- >
13426
- <tolle-country-selector
13427
- prefix
13428
- class="country-selector-override"
13429
- [showName]="false"
13430
- [size]="size"
13431
- [disabled]="disabled || !enableCountrySelector"
13432
- [readonly]="readonly"
13433
- [(ngModel)]="selectedIso"
13434
- (ngModelChange)="onCountryChange($event)"
13435
- ></tolle-country-selector>
13436
- </tolle-masked-input>
13437
- `, isInline: true, styles: [":host{display:block;width:100%}::ng-deep .country-selector-override{display:flex;align-items:center;border-right:1px solid var(--border, #e5e7eb)}::ng-deep .country-selector-override button{border:none!important;border-radius:0!important;border-top-left-radius:calc(var(--radius, .5rem) - 1px)!important;border-bottom-left-radius:calc(var(--radius, .5rem) - 1px)!important;background:transparent!important;box-shadow:none!important;padding-left:.75rem!important;padding-right:.5rem!important;display:flex!important;align-items:center!important;justify-content:center!important;gap:.25rem!important}::ng-deep .country-selector-override button .flex{gap:.25rem!important}::ng-deep .country-selector-override button i{margin-left:0!important;line-height:1!important;display:flex!important;align-items:center!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CountrySelectorComponent, selector: "tolle-country-selector", inputs: ["id", "label", "hint", "errorMessage", "error", "hideHintOnFocus", "placeholder", "class", "disabled", "readonly", "size", "defaultCountryCode", "returnValue", "showName"], outputs: ["onSelect"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus"] }] });
13440
+ <div class="flex w-full flex-col gap-1.5">
13441
+ <label *ngIf="label" [for]="id" [class]="computedLabelClass">
13442
+ {{ label }}
13443
+ </label>
13444
+
13445
+ <div [class]="computedMergedClass" (focusin)="onFocusIn()" (focusout)="onFocusOut()">
13446
+ <tolle-country-selector
13447
+ [showName]="false"
13448
+ [size]="size"
13449
+ [disabled]="disabled || !enableCountrySelector"
13450
+ [readonly]="readonly"
13451
+ [mergedPosition]="'left'"
13452
+ [(ngModel)]="selectedIso"
13453
+ (ngModelChange)="onCountryChange($event)"></tolle-country-selector>
13454
+
13455
+ <tolle-masked-input
13456
+ [id]="id"
13457
+ [mask]="mask"
13458
+ [size]="size"
13459
+ [disabled]="disabled"
13460
+ [readonly]="readonly"
13461
+ [placeholder]="placeholder"
13462
+ [error]="error"
13463
+ [externalFocused]="isFocused"
13464
+ [mergedPosition]="'right'"
13465
+ [(ngModel)]="displayValue"
13466
+ (ngModelChange)="onMaskInputChange($event)"></tolle-masked-input>
13467
+ </div>
13468
+
13469
+ <ng-container *ngIf="!disabled">
13470
+ <p
13471
+ *ngIf="hint && !error"
13472
+ class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
13473
+ [class.opacity-0]="isFocused && hideHintOnFocus">
13474
+ {{ hint }}
13475
+ </p>
13476
+ <p *ngIf="error && errorMessage" [id]="id + '-error'" class="px-1 text-xs text-destructive">
13477
+ {{ errorMessage }}
13478
+ </p>
13479
+ </ng-container>
13480
+ </div>
13481
+ `, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CountrySelectorComponent, selector: "tolle-country-selector", inputs: ["id", "label", "hint", "errorMessage", "error", "hideHintOnFocus", "placeholder", "class", "disabled", "readonly", "size", "defaultCountryCode", "returnValue", "showName", "mergedPosition"], outputs: ["onSelect", "onFocusChange", "onBlurChange"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus", "externalFocused", "mergedPosition"] }] });
13438
13482
  }
13439
13483
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PhoneNumberInputComponent, decorators: [{
13440
13484
  type: Component,
13441
13485
  args: [{ selector: 'tolle-phone-number-input', standalone: true, imports: [CommonModule, FormsModule, CountrySelectorComponent, MaskedInputComponent], template: `
13442
- <tolle-masked-input
13443
- [id]="id"
13444
- [label]="label"
13445
- [hint]="hint"
13446
- [error]="error"
13447
- [errorMessage]="errorMessage"
13448
- [hideHintOnFocus]="hideHintOnFocus"
13449
- [mask]="mask"
13450
- [size]="size"
13451
- [disabled]="disabled"
13452
- [readonly]="readonly"
13453
- [placeholder]="placeholder"
13454
- [(ngModel)]="displayValue"
13455
- (ngModelChange)="onMaskInputChange($event)"
13456
- [containerClass]="cn('pl-0', class)"
13457
- >
13458
- <tolle-country-selector
13459
- prefix
13460
- class="country-selector-override"
13461
- [showName]="false"
13462
- [size]="size"
13463
- [disabled]="disabled || !enableCountrySelector"
13464
- [readonly]="readonly"
13465
- [(ngModel)]="selectedIso"
13466
- (ngModelChange)="onCountryChange($event)"
13467
- ></tolle-country-selector>
13468
- </tolle-masked-input>
13486
+ <div class="flex w-full flex-col gap-1.5">
13487
+ <label *ngIf="label" [for]="id" [class]="computedLabelClass">
13488
+ {{ label }}
13489
+ </label>
13490
+
13491
+ <div [class]="computedMergedClass" (focusin)="onFocusIn()" (focusout)="onFocusOut()">
13492
+ <tolle-country-selector
13493
+ [showName]="false"
13494
+ [size]="size"
13495
+ [disabled]="disabled || !enableCountrySelector"
13496
+ [readonly]="readonly"
13497
+ [mergedPosition]="'left'"
13498
+ [(ngModel)]="selectedIso"
13499
+ (ngModelChange)="onCountryChange($event)"></tolle-country-selector>
13500
+
13501
+ <tolle-masked-input
13502
+ [id]="id"
13503
+ [mask]="mask"
13504
+ [size]="size"
13505
+ [disabled]="disabled"
13506
+ [readonly]="readonly"
13507
+ [placeholder]="placeholder"
13508
+ [error]="error"
13509
+ [externalFocused]="isFocused"
13510
+ [mergedPosition]="'right'"
13511
+ [(ngModel)]="displayValue"
13512
+ (ngModelChange)="onMaskInputChange($event)"></tolle-masked-input>
13513
+ </div>
13514
+
13515
+ <ng-container *ngIf="!disabled">
13516
+ <p
13517
+ *ngIf="hint && !error"
13518
+ class="px-1 text-xs text-muted-foreground transition-opacity duration-200"
13519
+ [class.opacity-0]="isFocused && hideHintOnFocus">
13520
+ {{ hint }}
13521
+ </p>
13522
+ <p *ngIf="error && errorMessage" [id]="id + '-error'" class="px-1 text-xs text-destructive">
13523
+ {{ errorMessage }}
13524
+ </p>
13525
+ </ng-container>
13526
+ </div>
13469
13527
  `, providers: [
13470
13528
  {
13471
13529
  provide: NG_VALUE_ACCESSOR,
13472
13530
  useExisting: forwardRef(() => PhoneNumberInputComponent),
13473
- multi: true
13474
- }
13475
- ], styles: [":host{display:block;width:100%}::ng-deep .country-selector-override{display:flex;align-items:center;border-right:1px solid var(--border, #e5e7eb)}::ng-deep .country-selector-override button{border:none!important;border-radius:0!important;border-top-left-radius:calc(var(--radius, .5rem) - 1px)!important;border-bottom-left-radius:calc(var(--radius, .5rem) - 1px)!important;background:transparent!important;box-shadow:none!important;padding-left:.75rem!important;padding-right:.5rem!important;display:flex!important;align-items:center!important;justify-content:center!important;gap:.25rem!important}::ng-deep .country-selector-override button .flex{gap:.25rem!important}::ng-deep .country-selector-override button i{margin-left:0!important;line-height:1!important;display:flex!important;align-items:center!important}\n"] }]
13531
+ multi: true,
13532
+ },
13533
+ ], styles: [":host{display:block;width:100%}\n"] }]
13476
13534
  }], ctorParameters: () => [], propDecorators: { id: [{
13477
13535
  type: Input
13478
13536
  }], label: [{