UNPKG

@taiga-ui/kit

Version:

Taiga UI Angular main components kit

201 lines (195 loc) 22.4 kB
import { TuiLabel } from '@taiga-ui/core/components/label'; import * as i6 from '@taiga-ui/core/components/textfield'; import { TUI_TEXTFIELD_OPTIONS, TuiTextfieldContent, TuiTextfieldComponent, TuiTextfieldOptionsDirective } from '@taiga-ui/core/components/textfield'; import * as i7 from '@taiga-ui/core/portals/dropdown'; import { TuiDropdownOpen, tuiDropdownEnabled, TuiDropdownContent } from '@taiga-ui/core/portals/dropdown'; import { NgTemplateOutlet } from '@angular/common'; import * as i0 from '@angular/core'; import { viewChildren, ElementRef, inject, effect, signal, computed, input, model, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core'; import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop'; import * as i4 from '@angular/forms'; import { FormsModule } from '@angular/forms'; import * as i1 from '@maskito/angular'; import { MaskitoDirective } from '@maskito/angular'; import { maskitoTransform, MASKITO_DEFAULT_OPTIONS, maskitoInitialCalibrationPlugin } from '@maskito/core'; import { maskitoPhoneOptionsGenerator, maskitoGetCountryFromNumber } from '@maskito/phone'; import { WA_IS_IOS } from '@ng-web-apis/platform'; import { TuiControl, tuiAsControl } from '@taiga-ui/cdk/classes'; import { TUI_DEFAULT_MATCHER, CHAR_PLUS } from '@taiga-ui/cdk/constants'; import { TuiActiveZone } from '@taiga-ui/cdk/directives/active-zone'; import { TuiAutoFocus, tuiAutoFocusOptionsProvider } from '@taiga-ui/cdk/directives/auto-focus'; import { tuiFallbackValueProvider } from '@taiga-ui/cdk/tokens'; import { tuiInjectElement, tuiValue, tuiIsInputEvent } from '@taiga-ui/cdk/utils/dom'; import { TuiButton } from '@taiga-ui/core/components/button'; import * as i5 from '@taiga-ui/core/components/data-list'; import { TuiOption, TuiDataList } from '@taiga-ui/core/components/data-list'; import * as i2 from '@taiga-ui/core/components/input'; import { TuiWithInput, TuiInput } from '@taiga-ui/core/components/input'; import { TuiTitle } from '@taiga-ui/core/components/title'; import { TUI_COMMON_ICONS } from '@taiga-ui/core/tokens'; import * as i3 from '@taiga-ui/kit/directives/appearance-proxy'; import { TuiAppearanceProxy } from '@taiga-ui/kit/directives/appearance-proxy'; import { TuiChevron } from '@taiga-ui/kit/directives/chevron'; import { TuiFlagPipe } from '@taiga-ui/kit/pipes/flag'; import { TUI_INTERNATIONAL_SEARCH, TUI_COUNTRIES } from '@taiga-ui/kit/tokens'; import { tuiMaskito } from '@taiga-ui/kit/utils'; import { validatePhoneNumberLength, getCountryCallingCode } from 'libphonenumber-js/core'; import { of, from, filter } from 'rxjs'; import { tuiCreateOptions } from '@taiga-ui/cdk/utils/di'; const TUI_INPUT_PHONE_INTERNATIONAL_DEFAULT_OPTIONS = { countries: [], countrySearch: false, countryIsoCode: 'RU', metadata: of({ version: 4, countries: {}, country_calling_codes: {}, nonGeographic: {}, }), separator: '-', }; /** * Default parameters for input phone international component */ const [TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS, tuiInputPhoneInternationalOptionsProvider,] = tuiCreateOptions(TUI_INPUT_PHONE_INTERNATIONAL_DEFAULT_OPTIONS); const NOT_FORM_CONTROL_SYMBOLS = /[^+\d]/g; class TuiInputPhoneInternationalComponent extends TuiControl { constructor() { super(...arguments); this.list = viewChildren(TuiOption, { read: ElementRef }); this.el = tuiInjectElement(); this.ios = inject(WA_IS_IOS); this.icons = inject(TUI_COMMON_ICONS); this.options = inject(TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS); this.label = inject(TUI_INTERNATIONAL_SEARCH); this.metadata = toSignal(from(this.options.metadata)); this.names = inject(TUI_COUNTRIES); this.open = inject(TuiDropdownOpen).open; this.dropdownEnabled = tuiDropdownEnabled(this.interactive); this.change = effect(() => this.onChange(this.unmask(this.masked()))); this.search = signal(''); this.size = inject(TUI_TEXTFIELD_OPTIONS).size; this.masked = tuiValue(this.el); this.mask = tuiMaskito(computed(() => this.computeMask(this.countryIsoCode(), this.metadata()))); this.filtered = computed(() => this.countries() .map((iso) => ({ iso, name: this.names()?.[iso] || '', code: getCallingCode(iso, this.metadata()), })) .filter(({ name, code }) => TUI_DEFAULT_MATCHER(`${name}${code}`, this.search()))); this.$ = inject(TuiActiveZone) .tuiActiveZoneChange.pipe(filter(() => !this.readOnly()), takeUntilDestroyed()) .subscribe((active) => { const prefix = `${getCallingCode(this.countryIsoCode(), this.metadata())} `; this.search.set(''); this.masked.update((value) => { const fallback = active ? value || prefix : value; return value === prefix ? '' : fallback; }); }); this.countrySearch = input(this.options.countrySearch); this.countryIsoCode = model(this.options.countryIsoCode); this.countries = input(this.options.countries); } writeValue(unmasked) { const code = this.getCountryCode(unmasked ?? ''); if (code) { this.countryIsoCode.set(code); } super.writeValue(unmasked); this.masked.set(maskitoTransform(this.value() ?? '', this.mask() || MASKITO_DEFAULT_OPTIONS)); } onPaste(event) { const data = tuiIsInputEvent(event) && event.data; if (!data || (!event.inputType.includes('Drop') && !event.inputType.includes('Paste'))) { return; } const code = this.getCountryCode(data); if (code) { this.countryIsoCode.set(code); } } onItemClick(code) { this.el.focus(); this.open.set(false); this.countryIsoCode.set(code); this.search.set(''); this.masked.set(maskitoTransform(this.value() || getCallingCode(code, this.metadata()), this.mask() || MASKITO_DEFAULT_OPTIONS)); } computeMask(countryIsoCode, metadata) { if (!metadata) { return MASKITO_DEFAULT_OPTIONS; } const { plugins, ...options } = maskitoPhoneOptionsGenerator({ countryIsoCode, metadata, separator: this.options.separator, }); return { ...options, plugins: [...plugins, maskitoInitialCalibrationPlugin()], }; } unmask(maskedValue) { const value = maskedValue.replaceAll(NOT_FORM_CONTROL_SYMBOLS, ''); const code = getCallingCode(this.countryIsoCode(), this.metadata()); return value === code ? '' : value; } getCountryCode(value) { const metadata = this.metadata(); const phone = value.startsWith(CHAR_PLUS) ? value : `${CHAR_PLUS}${value}`; return metadata && validatePhoneNumberLength(phone, metadata) !== 'TOO_SHORT' ? (maskitoGetCountryFromNumber(phone, metadata) ?? null) : null; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiInputPhoneInternationalComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: TuiInputPhoneInternationalComponent, isStandalone: true, selector: "input[tuiInputPhoneInternational]", inputs: { countrySearch: { classPropertyName: "countrySearch", publicName: "countrySearch", isSignal: true, isRequired: false, transformFunction: null }, countryIsoCode: { classPropertyName: "countryIsoCode", publicName: "countryIsoCode", isSignal: true, isRequired: false, transformFunction: null }, countries: { classPropertyName: "countries", publicName: "countries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { countryIsoCode: "countryIsoCodeChange" }, host: { attributes: { "ngSkipHydration": "true", "type": "tel" }, listeners: { "beforeinput.capture": "onPaste($event)", "click": "open.set(false)", "input": "masked.set($event.target.value)" }, properties: { "attr.inputmode": "!ios && open() ? \"none\" : null", "disabled": "disabled()" } }, providers: [ tuiAsControl(TuiInputPhoneInternationalComponent), tuiFallbackValueProvider(''), tuiAutoFocusOptionsProvider({ preventScroll: true }), ], viewQueries: [{ propertyName: "list", predicate: TuiOption, descendants: true, read: ElementRef, isSignal: true }], usesInheritance: true, hostDirectives: [{ directive: i1.MaskitoDirective }, { directive: i2.TuiWithInput }, { directive: i3.TuiAppearanceProxy }], ngImport: i0, template: "<ng-container *tuiTextfieldContent>\n <button\n appearance=\"textfield\"\n tabindex=\"-1\"\n tuiButton\n tuiChevron\n type=\"button\"\n class=\"t-ipi-select\"\n [attr.data-mode]=\"mode()\"\n [class.t-ipi-select_readonly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [size]=\"size()\"\n [tuiAppearanceFocus]=\"open()\"\n (click.prevent)=\"interactive() && open.set(!open())\"\n (pointerdown.stop)=\"(0)\"\n >\n <img\n class=\"t-ipi-flag\"\n [alt]=\"names()[countryIsoCode()]\"\n [class.t-ipi-flag_small]=\"size() === 's'\"\n [src]=\"countryIsoCode() | tuiFlag\"\n />\n </button>\n</ng-container>\n<ng-template #filter>\n <div class=\"t-ipi-search\">\n <tui-textfield\n [iconStart]=\"icons.search\"\n [tuiTextfieldSize]=\"size() === 's' ? 's' : 'm'\"\n >\n <input\n autocomplete=\"off\"\n tuiInput\n type=\"text\"\n [ngModel]=\"search()\"\n [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"label()\"\n [tuiAutoFocus]=\"!ios\"\n (keydown.arrowDown)=\"list()[0]?.nativeElement?.focus()\"\n (ngModelChange)=\"search.set($event)\"\n />\n </tui-textfield>\n </div>\n</ng-template>\n<ng-container *tuiDropdown>\n <!-- To keep it under local injector -->\n @if (countrySearch()) {\n <ng-container *ngTemplateOutlet=\"filter\" />\n }\n <tui-data-list class=\"t-ipi-options\">\n @for (item of filtered(); track $index) {\n <button\n tuiOption\n type=\"button\"\n (click)=\"onItemClick(item.iso)\"\n >\n <img\n alt=\"\"\n class=\"t-ipi-flag\"\n [class.t-ipi-flag_small]=\"size() === 's'\"\n [src]=\"item.iso | tuiFlag\"\n />\n <span tuiTitle>{{ item.name }}</span>\n <span class=\"t-ipi-code\">{{ item.code }}</span>\n </button>\n }\n </tui-data-list>\n</ng-container>\n", styles: ["[tuiInputPhoneInternational][tuiInputPhoneInternational]:where(*[data-tui-version=\"5.7.0\"]){inset-inline-start:var(--t-offset);border-top-left-radius:0;border-bottom-left-radius:0;inline-size:calc(100% - var(--t-offset))}[tuiInputPhoneInternational][tuiInputPhoneInternational]:where(*[data-tui-version=\"5.7.0\"])+[tuiLabel][tuiLabel]{padding-inline-start:var(--t-offset)}tui-textfield[data-size=s]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 4.125rem}tui-textfield[data-size=s]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 .1875rem}tui-textfield[data-size=m]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 4.875rem}tui-textfield[data-size=m]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 -.1875rem}tui-textfield[data-size=l]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 5.25rem}tui-textfield[data-size=l]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 -.1875rem}[data-tui-version=\"5.7.0\"] .t-ipi-select{position:absolute;inset-inline-start:0;border-radius:inherit;border-top-right-radius:0;border-bottom-right-radius:0}[data-tui-version=\"5.7.0\"] .t-ipi-select_readonly{pointer-events:none}[data-tui-version=\"5.7.0\"] .t-ipi-flag{inline-size:1.75rem;block-size:1.75rem;border-radius:100%}[data-tui-version=\"5.7.0\"] .t-ipi-flag_small{inline-size:1.25rem;block-size:1.25rem}[data-tui-version=\"5.7.0\"] .t-ipi-code{color:var(--tui-text-secondary)}[data-tui-version=\"5.7.0\"] .t-ipi-search{position:sticky;z-index:1;inset-block-start:0;background:var(--tui-background-elevation-3);padding:.375rem .375rem 0}@supports (-webkit-touch-callout: none){[data-tui-version=\"5.7.0\"] .t-ipi-search input:focus{animation:tuiPreventIOSScroll 1ms}}@keyframes tuiPreventIOSScroll{0%{opacity:0}to{opacity:1}}[data-tui-version=\"5.7.0\"] tui-dropdown-mobile .t-ipi-search,[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-search{background:var(--tui-background-elevation-1)}[data-tui-version=\"5.7.0\"] tui-dropdown-mobile .t-ipi-options:not(:first-child),[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-options:not(:first-child){min-block-size:calc(100 * var(--tui-viewport-vh) - 8.75rem)}[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-search{inset-block-start:4.5rem;padding-inline-start:0;padding-inline-end:0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.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: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TuiAutoFocus, selector: "[tuiAutoFocus]", inputs: ["tuiAutoFocus"] }, { kind: "directive", type: TuiButton, selector: "a[tuiButton],button[tuiButton],a[tuiIconButton],button[tuiIconButton]", inputs: ["size"] }, { kind: "directive", type: TuiChevron, selector: "[tuiChevron]", inputs: ["tuiChevron"] }, { kind: "component", type: i5.TuiDataListComponent, selector: "tui-data-list", inputs: ["emptyContent", "size"] }, { kind: "directive", type: i5.TuiOption, selector: "button[tuiOption], a[tuiOption], label[tuiOption]", inputs: ["disabled"] }, { kind: "pipe", type: TuiFlagPipe, name: "tuiFlag" }, { kind: "component", type: i6.TuiTextfieldComponent, selector: "tui-textfield:not([multi])", inputs: ["content", "filler"] }, { kind: "directive", type: i6.TuiTextfieldOptionsDirective, selector: "[tuiTextfieldAppearance],[tuiTextfieldSize],[tuiTextfieldCleaner]", inputs: ["tuiTextfieldAppearance", "tuiTextfieldSize", "tuiTextfieldCleaner"] }, { kind: "directive", type: i7.TuiDropdownContent, selector: "ng-template[tuiDropdown]" }, { kind: "directive", type: i2.TuiInputDirective, selector: "input[tuiInput]", inputs: ["readOnly", "invalid", "focused", "state"] }, { kind: "directive", type: TuiTextfieldContent, selector: "ng-template[tuiTextfieldContent]" }, { kind: "directive", type: TuiTitle, selector: "[tuiTitle]", inputs: ["tuiTitle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiInputPhoneInternationalComponent, decorators: [{ type: Component, args: [{ selector: 'input[tuiInputPhoneInternational]', imports: [ FormsModule, NgTemplateOutlet, TuiAutoFocus, TuiButton, TuiChevron, TuiDataList, TuiFlagPipe, TuiInput, TuiTextfieldContent, TuiTitle, ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [ tuiAsControl(TuiInputPhoneInternationalComponent), tuiFallbackValueProvider(''), tuiAutoFocusOptionsProvider({ preventScroll: true }), ], hostDirectives: [MaskitoDirective, TuiWithInput, TuiAppearanceProxy], host: { ngSkipHydration: 'true', type: 'tel', '[attr.inputmode]': '!ios && open() ? "none" : null', '[disabled]': 'disabled()', '(beforeinput.capture)': 'onPaste($event)', '(click)': 'open.set(false)', '(input)': 'masked.set($event.target.value)', }, template: "<ng-container *tuiTextfieldContent>\n <button\n appearance=\"textfield\"\n tabindex=\"-1\"\n tuiButton\n tuiChevron\n type=\"button\"\n class=\"t-ipi-select\"\n [attr.data-mode]=\"mode()\"\n [class.t-ipi-select_readonly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [size]=\"size()\"\n [tuiAppearanceFocus]=\"open()\"\n (click.prevent)=\"interactive() && open.set(!open())\"\n (pointerdown.stop)=\"(0)\"\n >\n <img\n class=\"t-ipi-flag\"\n [alt]=\"names()[countryIsoCode()]\"\n [class.t-ipi-flag_small]=\"size() === 's'\"\n [src]=\"countryIsoCode() | tuiFlag\"\n />\n </button>\n</ng-container>\n<ng-template #filter>\n <div class=\"t-ipi-search\">\n <tui-textfield\n [iconStart]=\"icons.search\"\n [tuiTextfieldSize]=\"size() === 's' ? 's' : 'm'\"\n >\n <input\n autocomplete=\"off\"\n tuiInput\n type=\"text\"\n [ngModel]=\"search()\"\n [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"label()\"\n [tuiAutoFocus]=\"!ios\"\n (keydown.arrowDown)=\"list()[0]?.nativeElement?.focus()\"\n (ngModelChange)=\"search.set($event)\"\n />\n </tui-textfield>\n </div>\n</ng-template>\n<ng-container *tuiDropdown>\n <!-- To keep it under local injector -->\n @if (countrySearch()) {\n <ng-container *ngTemplateOutlet=\"filter\" />\n }\n <tui-data-list class=\"t-ipi-options\">\n @for (item of filtered(); track $index) {\n <button\n tuiOption\n type=\"button\"\n (click)=\"onItemClick(item.iso)\"\n >\n <img\n alt=\"\"\n class=\"t-ipi-flag\"\n [class.t-ipi-flag_small]=\"size() === 's'\"\n [src]=\"item.iso | tuiFlag\"\n />\n <span tuiTitle>{{ item.name }}</span>\n <span class=\"t-ipi-code\">{{ item.code }}</span>\n </button>\n }\n </tui-data-list>\n</ng-container>\n", styles: ["[tuiInputPhoneInternational][tuiInputPhoneInternational]:where(*[data-tui-version=\"5.7.0\"]){inset-inline-start:var(--t-offset);border-top-left-radius:0;border-bottom-left-radius:0;inline-size:calc(100% - var(--t-offset))}[tuiInputPhoneInternational][tuiInputPhoneInternational]:where(*[data-tui-version=\"5.7.0\"])+[tuiLabel][tuiLabel]{padding-inline-start:var(--t-offset)}tui-textfield[data-size=s]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 4.125rem}tui-textfield[data-size=s]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 .1875rem}tui-textfield[data-size=m]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 4.875rem}tui-textfield[data-size=m]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 -.1875rem}tui-textfield[data-size=l]:where(*[data-tui-version=\"5.7.0\"]){--t-offset: 5.25rem}tui-textfield[data-size=l]:where(*[data-tui-version=\"5.7.0\"]) .t-ipi-flag{margin:0 -.1875rem}[data-tui-version=\"5.7.0\"] .t-ipi-select{position:absolute;inset-inline-start:0;border-radius:inherit;border-top-right-radius:0;border-bottom-right-radius:0}[data-tui-version=\"5.7.0\"] .t-ipi-select_readonly{pointer-events:none}[data-tui-version=\"5.7.0\"] .t-ipi-flag{inline-size:1.75rem;block-size:1.75rem;border-radius:100%}[data-tui-version=\"5.7.0\"] .t-ipi-flag_small{inline-size:1.25rem;block-size:1.25rem}[data-tui-version=\"5.7.0\"] .t-ipi-code{color:var(--tui-text-secondary)}[data-tui-version=\"5.7.0\"] .t-ipi-search{position:sticky;z-index:1;inset-block-start:0;background:var(--tui-background-elevation-3);padding:.375rem .375rem 0}@supports (-webkit-touch-callout: none){[data-tui-version=\"5.7.0\"] .t-ipi-search input:focus{animation:tuiPreventIOSScroll 1ms}}@keyframes tuiPreventIOSScroll{0%{opacity:0}to{opacity:1}}[data-tui-version=\"5.7.0\"] tui-dropdown-mobile .t-ipi-search,[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-search{background:var(--tui-background-elevation-1)}[data-tui-version=\"5.7.0\"] tui-dropdown-mobile .t-ipi-options:not(:first-child),[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-options:not(:first-child){min-block-size:calc(100 * var(--tui-viewport-vh) - 8.75rem)}[data-tui-version=\"5.7.0\"] tui-sheet-dialog .t-ipi-search{inset-block-start:4.5rem;padding-inline-start:0;padding-inline-end:0}\n"] }] }] }); function getCallingCode(iso, metadata) { return metadata ? CHAR_PLUS + getCountryCallingCode(iso, metadata) : ''; } const TuiInputPhoneInternational = [ TuiInputPhoneInternationalComponent, TuiLabel, TuiTextfieldComponent, TuiTextfieldOptionsDirective, TuiDropdownContent, ]; /** * Generated bundle index. Do not edit. */ export { TUI_INPUT_PHONE_INTERNATIONAL_DEFAULT_OPTIONS, TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS, TuiInputPhoneInternational, TuiInputPhoneInternationalComponent, tuiInputPhoneInternationalOptionsProvider }; //# sourceMappingURL=taiga-ui-kit-components-input-phone-international.mjs.map