UNPKG

@firestitch/address

Version:
1 lines 179 kB
{"version":3,"file":"firestitch-address.mjs","sources":["../../src/app/enums/address-format.enum.ts","../../src/app/helpers/address-is-empty.ts","../../src/app/helpers/create-empty-address.ts","../../src/app/helpers/extract-unit.ts","../../src/app/helpers/google-place-to-address.ts","../../src/app/components/address-autocomplete/address-autocomplete.component.ts","../../src/app/components/address-autocomplete/address-autocomplete.component.html","../../src/app/consts/countries.const.ts","../../src/app/enums/country.enum.ts","../../src/app/components/address-region/address-region.component.ts","../../src/app/components/address-region/address-region.component.html","../../src/app/helpers/address-format.ts","../../src/app/helpers/address-one-line-format.ts","../../src/app/helpers/address-two-line-format.ts","../../src/app/helpers/address-summary-format.ts","../../src/app/helpers/search-country-regions.ts","../../src/app/components/address-country/address-country.component.ts","../../src/app/components/address-country/address-country.component.html","../../src/app/components/address/address.component.ts","../../src/app/components/address/address.component.html","../../src/app/components/address-dialog/address-dialog.component.ts","../../src/app/components/address-dialog/address-dialog.component.html","../../src/app/components/address-format/address-format.component.ts","../../src/app/components/address-format/address-format.component.html","../../src/app/components/address-search/address-search.component.ts","../../src/app/components/address-search/address-search.component.html","../../src/app/components/address-picker/address-picker.component.ts","../../src/app/components/address-picker/address-picker.component.html","../../src/app/consts/inject-token-countries.ts","../../src/app/fs-address-countries.module.ts","../../src/app/fs-address-country.module.ts","../../src/app/fs-address-region.module.ts","../../src/app/fs-address.module.ts","../../src/app/components/address-region-country/address-region-country.component.ts","../../src/app/components/address-region-country/address-region-country.component.html","../../src/app/fs-address-region-country.module.ts","../../src/app/consts/inject-token-google-map-key.ts","../../src/app/services/address-geocoder.ts","../../src/public_api.ts","../../src/firestitch-address.ts"],"sourcesContent":["export enum AddressFormat {\n OneLine = 'oneline',\n TwoLine = 'twoline',\n Summary = 'summary',\n}","import { FsAddress } from '../interfaces/address.interface';\n\nexport function addressIsEmpty(value: FsAddress): boolean {\n return !value\n || (!value.name\n && !value.street\n && !value.city\n && !value.region\n && !value.zip\n && !value.country\n && !value.address2\n && !value.address3\n );\n}\n","import { FsAddress } from '../interfaces/address.interface';\n\n\nexport function createEmptyAddress(): FsAddress {\n return {\n name: '',\n description: '',\n country: '',\n region: '',\n city: '',\n street: '',\n address2: '',\n address3: '',\n zip: '',\n lat: null,\n lng: null,\n };\n}\n","export function extractUnit(text: string) {\n const primaryUnitRegex = /((unit|apt|#|apartment|building|floor|suite|room|department|po\\s*box)\\s?#?\\d+([,.])?(\\w)?([,.])?)/gi;\n const secondaryUnitRegex = /-\\s?\\d+/gi;\n const nonWordOrDigitChar = /^[^a-z\\d]*|[^a-z\\d]*$/gi;\n\n let unit = [\n ...(text.match(primaryUnitRegex) || []),\n ...(text.match(secondaryUnitRegex) || []),\n ][0];\n\n if (unit) {\n text = text\n .replace(unit, '')\n .trim();\n\n unit = unit\n .replace(nonWordOrDigitChar, '')\n .replace('unit', 'Unit')\n .trim();\n }\n\n text = text.replace(nonWordOrDigitChar, '').trim();\n\n return {\n text,\n unit,\n };\n}\n","import { createEmptyAddress } from './create-empty-address';\nimport { FsAddressConfig } from '../interfaces/address-config.interface';\nimport { FsAddress } from '../interfaces/address.interface';\n\n\nexport function googlePlaceToFsAddress(\n result: google.maps.places.Place,\n config: FsAddressConfig,\n): FsAddress {\n const address = createEmptyAddress();\n\n let countryLongName: string, regionLongName: string, streetShortName: string;\n\n address.lat = result.location.lat();\n address.lng = result.location.lng();\n address.description = result.formattedAddress;\n\n // Finding different parts of address\n result.addressComponents.forEach((item) => {\n if (item.types.some(type => type === 'country')) {\n address.country = item.shortText;\n countryLongName = item.longText;\n }\n\n if (item.types.some(type => type === 'administrative_area_level_1')) {\n address.region = item.shortText;\n regionLongName = item.longText;\n }\n\n if (item.types.some(type => type === 'locality' || type === 'political')) {\n address.city = item.longText;\n }\n\n if (item.types.some(type => type === 'postal_code')) {\n address.zip = item.longText;\n }\n });\n\n // Address.Street consists from number and street\n const streetNumber = result.addressComponents\n .find(el => el.types.some(type => type === 'street_number'));\n\n if (streetNumber) {\n address.street = streetNumber.longText + ' ';\n streetShortName = streetNumber.longText + ' ';\n } else {\n const match = address.description.match(/^[\\d-]+/);\n if (match) {\n address.street = match[0] + ' ';\n streetShortName = match[0] + ' ';\n }\n }\n\n const streetAddress = result.addressComponents\n .find(el => el.types.some(type => type === 'route'));\n\n if (streetAddress) {\n if (!address.street) {\n address.street = streetAddress.longText;\n streetShortName = streetAddress.shortText;\n } else {\n address.street += streetAddress.longText;\n streetShortName += streetAddress.shortText;\n }\n }\n\n // Checking correct place NAME\n if (\n address.country !== result.displayName\n && countryLongName !== result.displayName\n && address.region !== result.displayName\n && regionLongName !== result.displayName\n && address.city !== result.displayName\n && streetShortName !== result.displayName\n && address.zip !== result.displayName\n && address.street !== result.displayName\n ) {\n if (config.name && config.name.visible !== false) {\n address.name = result.displayName;\n }\n\n } else {\n address.name = '';\n }\n\n return address;\n}\n","import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DestroyRef,\n ElementRef,\n EventEmitter,\n forwardRef,\n HostBinding,\n inject,\n Input,\n NgZone,\n OnInit,\n Output,\n ViewChild,\n} from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, FormsModule } from '@angular/forms';\n\nimport { FocusMonitor } from '@angular/cdk/a11y';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport {\n MatAutocomplete,\n MatAutocompleteSelectedEvent,\n MatAutocompleteTrigger,\n MatOption,\n} from '@angular/material/autocomplete';\n\nimport { guid } from '@firestitch/common';\nimport { FsMap } from '@firestitch/map';\n\nimport { from, fromEvent, Observable, of } from 'rxjs';\nimport {\n debounceTime,\n distinctUntilChanged,\n filter,\n map,\n switchMap,\n tap,\n} from 'rxjs/operators';\n\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { AddressFormat } from '../../enums/address-format.enum';\nimport { addressIsEmpty } from '../../helpers/address-is-empty';\nimport { createEmptyAddress } from '../../helpers/create-empty-address';\nimport { extractUnit } from '../../helpers/extract-unit';\nimport { googlePlaceToFsAddress } from '../../helpers/google-place-to-address';\nimport { FsAddressConfig } from '../../interfaces/address-config.interface';\nimport { FsAddress } from '../../interfaces/address.interface';\nimport { MatFormField, MatLabel, MatHint } from '@angular/material/form-field';\nimport { MatInput } from '@angular/material/input';\nimport { FsFormModule } from '@firestitch/form';\nimport { FsClearModule } from '@firestitch/clear';\nimport { MatOption as MatOption_1 } from '@angular/material/core';\n\n\n@Component({\n selector: 'fs-address-autocomplete',\n templateUrl: './address-autocomplete.component.html',\n styleUrls: ['./address-autocomplete.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => FsAddressAutocompleteComponent),\n multi: true,\n },\n {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => FsAddressAutocompleteComponent),\n multi: true,\n },\n ],\n standalone: true,\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n FormsModule,\n MatAutocompleteTrigger,\n FsFormModule,\n FsClearModule,\n MatAutocomplete,\n MatOption_1,\n MatHint,\n ],\n})\nexport class FsAddressAutocompleteComponent implements OnInit, ControlValueAccessor, Validator {\n private _map = inject(FsMap);\n private _ngZone = inject(NgZone);\n private _fm = inject(FocusMonitor);\n private _elementRef = inject(ElementRef);\n private _cdRef = inject(ChangeDetectorRef);\n\n\n public static nextId = 0;\n\n @Input()\n public format = AddressFormat.TwoLine;\n\n @Input()\n public readonly = false;\n\n @Input()\n public showClear = true;\n\n @Input()\n public suggestions = false;\n\n @Input()\n public set config(value: FsAddressConfig) {\n this._config = value;\n if (this._config) {\n this.required =\n ((this.config.name && this.config.name.required) ||\n (this.config.country && this.config.country.required) ||\n (this.config.region && this.config.region.required) ||\n (this.config.city && this.config.city.required) ||\n (this.config.street && this.config.street.required) ||\n (this.config.address2 && this.config.address2.required) ||\n (this.config.address3 && this.config.address3.required) ||\n (this.config.zip && this.config.zip.required));\n }\n }\n\n public get config(): FsAddressConfig {\n return this._config;\n }\n\n @Output()\n public readonly addressChange = new EventEmitter();\n\n @Output()\n public readonly addressManual = new EventEmitter<string>();\n\n @ViewChild('searchInput', { static: true, read: ElementRef })\n public readonly searchElement: ElementRef;\n\n @ViewChild(MatAutocomplete, { static: true })\n public readonly autoCompleteRef: MatAutocomplete;\n\n @ViewChild(MatAutocompleteTrigger, { static: true })\n public readonly autocompleteTrigger: MatAutocompleteTrigger;\n\n @HostBinding()\n public id = `fs-address-autocomplete-${FsAddressAutocompleteComponent.nextId++}`;\n\n public inputAddress: FsAddress = this._defaultInputAddress();\n public googleSuggestions: google.maps.places.AutocompleteSuggestion[] = [];\n public googlePlace: google.maps.places.Place = null;\n public onChange: (data: any) => void;\n public onTouched: () => void;\n public focused = false;\n public readonly autocompleteName = `search-${guid('xxxxxxxx')}`;\n\n private _config: FsAddressConfig = {};\n private _address: FsAddress = {};\n private _searchText = '';\n private _disabled = false;\n private _required = false;\n private _placeholder: string;\n\n private _destroyRef = inject(DestroyRef);\n\n public set value(value: FsAddress) {\n this._address = value;\n this.onChange(this._address);\n }\n\n public get value(): FsAddress {\n return this._address;\n }\n\n @Input()\n public get disabled(): boolean {\n return this._disabled;\n }\n\n public set disabled(value: boolean) {\n this._disabled = coerceBooleanProperty(value);\n }\n\n @Input()\n public get required() {\n return this._required;\n }\n\n public set required(req) {\n this._required = coerceBooleanProperty(req);\n }\n\n @Input()\n public get placeholder() {\n return this._placeholder;\n }\n public set placeholder(plh) {\n this._placeholder = plh;\n }\n\n @HostBinding('class.floating')\n public get shouldLabelFloat() {\n return this.focused;\n }\n\n public get empty(): boolean {\n return addressIsEmpty(this.value);\n }\n\n public ngOnInit() {\n this._initGoogleMap();\n this._listenUserTyping();\n this._listenAutocompleteSelection();\n this._registerFocusMonitor();\n }\n\n public writeValue(value: FsAddress | null) {\n this._address = value;\n this.inputAddress = value;\n this._cdRef.markForCheck();\n }\n\n public onContainerClick(event: MouseEvent) {\n if ((event.target as Element).tagName.toLowerCase() !== 'input') {\n this.searchElement.nativeElement.focus();\n this._elementRef.nativeElement.querySelector('input').focus();\n }\n }\n\n public registerOnChange(fn: (data: any) => void): void {\n this.onChange = fn;\n }\n\n public registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n public displayWith = (value: FsAddress) => {\n if (value && typeof value === 'object') {\n return this.value?.street;\n } else if (!this.empty) {\n return '';\n }\n };\n\n public validate(control: AbstractControl): ValidationErrors | null {\n const validationErrors: ValidationErrors = {};\n const requiredField = [];\n const parts = ['name', 'street', 'city', 'region', 'zip', 'country', 'lat', 'lng'];\n\n if (this.required && this.empty) {\n validationErrors.required = true;\n }\n\n if (!this.empty) {\n parts.forEach((part) => {\n if (this.config[part] && this.config[part].required && !this.value[part]) {\n requiredField.push([part]);\n }\n });\n\n if (((this.config.lat && this.config.lat.required) ||\n (this.config.lng && this.config.lng.required)) &&\n (!this.value.lat || !this.value.lat)) {\n validationErrors.invalid = 'position on map';\n }\n\n if (requiredField.length) {\n if (requiredField.length === 1) {\n validationErrors.invalid = `The ${requiredField[0]} is required`;\n } else {\n const last = requiredField.pop();\n validationErrors.invalid = `The ${requiredField.join(', ')} and ${last} are required`;\n }\n }\n }\n\n return validationErrors;\n }\n\n public clear(): void {\n this.inputAddress = this._defaultInputAddress();\n this.value = createEmptyAddress();\n this.addressChange.emit(null);\n this._clearPredictions();\n setTimeout(() => {\n this.autocompleteTrigger.openPanel();\n });\n }\n\n public manual(value: string): void {\n this.addressManual.emit(value);\n }\n\n // Search input can't be null. We implemented required validation to show asterisk if needed\n // But general validation placed in another level and not depends of this input\n // This hack allow us to show asterisk but disable extra validation\n private _defaultInputAddress() {\n return null;\n }\n\n private _listenUserTyping(): void {\n this._ngZone.runOutsideAngular(() => {\n\n fromEvent(this.searchElement.nativeElement, 'keydown')\n .pipe(\n filter((event: KeyboardEvent) => event.code === 'Tab'),\n map(() => this.autocompleteTrigger.activeOption?.value),\n filter((place) => !!place && this.googleSuggestions.length !== 0),\n switchMap((place) => this._placeToAddress(place)),\n takeUntilDestroyed(this._destroyRef),\n )\n .subscribe((address: FsAddress) => {\n this._selectAddress(address);\n this._clearPredictions();\n });\n\n fromEvent(this.searchElement.nativeElement, 'keyup')\n .pipe(\n debounceTime(200),\n filter((event: KeyboardEvent) => {\n return event.code !== 'Enter' && event.code !== 'Tab';\n }),\n map((event: KeyboardEvent) => {\n return (event.target as HTMLInputElement).value;\n }),\n tap((text) => {\n if (!text) {\n this._clearPredictions();\n }\n }),\n filter((value) => !!value),\n tap((value) => {\n this._searchText = value;\n if (!value) {\n this._address = {\n ...this._address,\n street: value,\n };\n\n this._selectAddress(this._address);\n }\n }),\n distinctUntilChanged(),\n switchMap((text: string) => {\n return this._getPlaceSuggestions(text);\n }),\n takeUntilDestroyed(this._destroyRef),\n )\n .subscribe((suggestions: google.maps.places.AutocompleteSuggestion[]) => {\n this._ngZone.run(() => {\n this.googleSuggestions = [\n ...suggestions,\n ];\n\n this._cdRef.markForCheck();\n });\n });\n });\n }\n\n private _clearPredictions() {\n this.googleSuggestions = [];\n this._cdRef.markForCheck();\n }\n\n private _selectAddress(address) {\n this.value = address;\n this.addressChange.emit(address);\n }\n\n private _placeToAddress(suggestion: google.maps.places.AutocompleteSuggestion): Observable<FsAddress> {\n if (!suggestion || !this.googlePlace) {\n return of(null);\n }\n\n const place = suggestion.placePrediction.toPlace();\n const fetchFieldsRequestOptions: google.maps.places.FetchFieldsRequest = {\n fields: [\n 'displayName',\n 'location',\n 'addressComponents',\n 'formattedAddress',\n ],\n };\n\n return from(place.fetchFields(fetchFieldsRequestOptions))\n .pipe(\n map(({ place }: {place: google.maps.places.Place}): FsAddress => {\n if (!place) {\n return {};\n }\n\n return googlePlaceToFsAddress(place, this.config);\n }),\n );\n }\n\n private _listenAutocompleteSelection(): void {\n this.autoCompleteRef.optionSelected\n .pipe(\n map((event: MatAutocompleteSelectedEvent) => event.option),\n // used to get the value from input when \"manual\" option selected\n filter((option: MatOption<{ manual: boolean, value: string} | google.maps.places.AutocompleteSuggestion>) => {\n if (option.value instanceof google.maps.places.AutocompleteSuggestion) {\n return true;\n }\n\n this.manual(option.value.value);\n\n return false;\n }),\n map((option) => {\n return option.value;\n }),\n switchMap((value: google.maps.places.AutocompleteSuggestion) => this._placeToAddress(value)),\n takeUntilDestroyed(this._destroyRef),\n )\n .subscribe((address: FsAddress) => {\n this._ngZone.run(() => {\n this.searchElement.nativeElement.blur();\n this.value = address;\n\n const { unit } = extractUnit(this._searchText);\n if (unit) {\n address.address2 = unit;\n }\n\n this.addressChange.emit(address);\n this.inputAddress = address;\n\n this._cdRef.markForCheck();\n });\n });\n }\n\n private _initGoogleMap() {\n this._ngZone.runOutsideAngular(() => {\n this._map.loaded$\n .pipe(\n takeUntilDestroyed(this._destroyRef),\n )\n .subscribe(() => {\n this.googlePlace = new google.maps.places.Place({ id: this.id });\n });\n });\n }\n\n private _getPlaceSuggestions(address: string): Promise<google.maps.places.AutocompleteSuggestion[]> {\n const { text } = extractUnit(address);\n const placesRequest = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(\n { input: text },\n );\n\n return placesRequest\n .then((result) => {\n return result.suggestions;\n })\n .catch(() => {\n return [];\n });\n }\n\n private _registerFocusMonitor(): void {\n this._fm.monitor(this._elementRef, true)\n .pipe(\n filter(() => !this.disabled),\n takeUntilDestroyed(this._destroyRef),\n )\n .subscribe((origin) => {\n this.focused = !!origin;\n });\n }\n}\n","<mat-form-field [floatLabel]=\"empty ? 'auto' : 'always'\">\n <mat-label>\n {{ placeholder }}\n </mat-label>\n <ng-content></ng-content>\n <input\n matInput\n type=\"text\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputAddress\"\n [matAutocomplete]=\"autocomplete\"\n [name]=\"autocompleteName\"\n [disabled]=\"disabled\"\n [fsClear]=\"showClear && !empty && !disabled && !readonly\"\n (cleared)=\"clear()\"\n #searchInput=\"ngModel\">\n <mat-autocomplete\n [displayWith]=\"displayWith\"\n autoActiveFirstOption\n [class]=\"'fs-autocomplete-pane'\"\n #autocomplete=\"matAutocomplete\">\n @for (option of googleSuggestions; track option) {\n <mat-option [value]=\"option\">\n {{ option.placePrediction.text.text }}\n </mat-option>\n }\n @if (!config.hideEnterManually) {\n <div class=\"static-options\">\n <mat-option [value]=\"{ manual: true, value: searchInput.value }\">\n Enter address manually\n </mat-option>\n </div>\n }\n </mat-autocomplete>\n @if (config.hint) {\n <mat-hint>\n {{ config.hint }}\n </mat-hint>\n }\n</mat-form-field>","import { IAddressCountry } from '../interfaces';\n\nexport const Countries: IAddressCountry[] = [\n { code: 'AF', name: 'Afghanistan' },\n { code: 'AL', name: 'Albania' },\n { code: 'DZ', name: 'Algeria' },\n { code: 'AS', name: 'American Samoa' },\n { code: 'AD', name: 'Andorra' },\n { code: 'AO', name: 'Angola' },\n { code: 'AI', name: 'Anguilla' },\n { code: 'AQ', name: 'Antarctica' },\n { code: 'AG', name: 'Antigua and Barbuda' },\n { code: 'AR', name: 'Argentina' },\n { code: 'AM', name: 'Armenia' },\n { code: 'AW', name: 'Aruba' },\n { code: 'AU', name: 'Australia' },\n { code: 'AT', name: 'Austria' },\n { code: 'AZ', name: 'Azerbaijan' },\n { code: 'BS', name: 'Bahamas' },\n { code: 'BH', name: 'Bahrain' },\n { code: 'BD', name: 'Bangladesh' },\n { code: 'BB', name: 'Barbados' },\n { code: 'BY', name: 'Belarus' },\n { code: 'BE', name: 'Belgium' },\n { code: 'BZ', name: 'Belize' },\n { code: 'BJ', name: 'Benin' },\n { code: 'BM', name: 'Bermuda' },\n { code: 'BT', name: 'Bhutan' },\n { code: 'BO', name: 'Bolivia' },\n { code: 'BA', name: 'Bosnia and Herzegovina' },\n { code: 'BW', name: 'Botswana' },\n { code: 'BV', name: 'Bouvet Island' },\n { code: 'BR', name: 'Brazil' },\n { code: 'IO', name: 'British Indian Ocean Territory' },\n { code: 'BN', name: 'Brunei Darussalam' },\n { code: 'BG', name: 'Bulgaria' },\n { code: 'BF', name: 'Burkina Faso' },\n { code: 'BI', name: 'Burundi' },\n { code: 'KH', name: 'Cambodia' },\n { code: 'CM', name: 'Cameroon' },\n {\n code: 'CA', name: 'Canada',\n regionLabel: 'Province',\n regions: [\n { code: 'AB', name: 'Alberta' },\n { code: 'BC', name: 'British Columbia' },\n { code: 'MB', name: 'Manitoba' },\n { code: 'NB', name: 'New Brunswick' },\n { code: 'NL', name: 'Newfoundland and Labrador' },\n { code: 'NT', name: 'Northwest Territories' },\n { code: 'NS', name: 'Nova Scotia' },\n { code: 'NU', name: 'Nunavut' },\n { code: 'ON', name: 'Ontario' },\n { code: 'PE', name: 'Prince Edward Island' },\n { code: 'QC', name: 'Quebec' },\n { code: 'SK', name: 'Saskatchewan' },\n { code: 'YT', name: 'Yukon Territory' },\n ],\n },\n { code: 'CV', name: 'Cape Verde' },\n { code: 'KY', name: 'Cayman Islands' },\n { code: 'CF', name: 'Central African Republic' },\n { code: 'TD', name: 'Chad' },\n { code: 'CL', name: 'Chile' },\n { code: 'CN', name: 'China' },\n { code: 'CX', name: 'Christmas Island' },\n { code: 'CC', name: 'Cocos (Keeling) Islands' },\n { code: 'CO', name: 'Colombia' },\n { code: 'KM', name: 'Comoros' },\n { code: 'CG', name: 'Congo' },\n { code: 'CD', name: 'Congo, the Democratic Republic of the' },\n { code: 'CK', name: 'Cook Islands' },\n { code: 'CR', name: 'Costa Rica' },\n { code: 'CI', name: 'Cote D\\'Ivoire' },\n { code: 'HR', name: 'Croatia' },\n { code: 'CU', name: 'Cuba' },\n { code: 'CY', name: 'Cyprus' },\n { code: 'CZ', name: 'Czech Republic' },\n { code: 'DK', name: 'Denmark' },\n { code: 'DJ', name: 'Djibouti' },\n { code: 'DM', name: 'Dominica' },\n { code: 'DO', name: 'Dominican Republic' },\n { code: 'EC', name: 'Ecuador' },\n { code: 'EG', name: 'Egypt' },\n { code: 'SV', name: 'El Salvador' },\n { code: 'GQ', name: 'Equatorial Guinea' },\n { code: 'ER', name: 'Eritrea' },\n { code: 'EE', name: 'Estonia' },\n { code: 'ET', name: 'Ethiopia' },\n { code: 'FK', name: 'Falkland Islands (Malvinas)' },\n { code: 'FO', name: 'Faroe Islands' },\n { code: 'FJ', name: 'Fiji' },\n { code: 'FI', name: 'Finland' },\n { code: 'FR', name: 'France' },\n { code: 'GF', name: 'French Guiana' },\n { code: 'PF', name: 'French Polynesia' },\n { code: 'TF', name: 'French Southern Territories' },\n { code: 'GA', name: 'Gabon' },\n { code: 'GM', name: 'Gambia' },\n { code: 'GE', name: 'Georgia' },\n { code: 'DE', name: 'Germany' },\n { code: 'GH', name: 'Ghana' },\n { code: 'GI', name: 'Gibraltar' },\n { code: 'GR', name: 'Greece' },\n { code: 'GL', name: 'Greenland' },\n { code: 'GD', name: 'Grenada' },\n { code: 'GP', name: 'Guadeloupe' },\n { code: 'GU', name: 'Guam' },\n { code: 'GT', name: 'Guatemala' },\n { code: 'GN', name: 'Guinea' },\n { code: 'GW', name: 'Guinea-Bissau' },\n { code: 'GY', name: 'Guyana' },\n { code: 'HT', name: 'Haiti' },\n { code: 'HM', name: 'Heard Island and Mcdonald Islands' },\n { code: 'VA', name: 'Holy See (Vatican City State)' },\n { code: 'HN', name: 'Honduras' },\n { code: 'HK', name: 'Hong Kong' },\n { code: 'HU', name: 'Hungary' },\n { code: 'IS', name: 'Iceland' },\n { code: 'IN', name: 'India' },\n { code: 'ID', name: 'Indonesia' },\n { code: 'IR', name: 'Iran, Islamic Republic of' },\n { code: 'IQ', name: 'Iraq' },\n { code: 'IE', name: 'Ireland' },\n { code: 'IL', name: 'Israel' },\n { code: 'IT', name: 'Italy' },\n { code: 'JM', name: 'Jamaica' },\n { code: 'JP', name: 'Japan' },\n { code: 'JO', name: 'Jordan' },\n { code: 'KZ', name: 'Kazakhstan' },\n { code: 'KE', name: 'Kenya' },\n { code: 'KI', name: 'Kiribati' },\n { code: 'KP', name: 'Korea, Democratic People\\'s Republic of' },\n { code: 'KR', name: 'Korea, Republic of' },\n { code: 'KW', name: 'Kuwait' },\n { code: 'KG', name: 'Kyrgyzstan' },\n { code: 'LA', name: 'Lao People\\'s Democratic Republic' },\n { code: 'LV', name: 'Latvia' },\n { code: 'LB', name: 'Lebanon' },\n { code: 'LS', name: 'Lesotho' },\n { code: 'LR', name: 'Liberia' },\n { code: 'LY', name: 'Libyan Arab Jamahiriya' },\n { code: 'LI', name: 'Liechtenstein' },\n { code: 'LT', name: 'Lithuania' },\n { code: 'LU', name: 'Luxembourg' },\n { code: 'MO', name: 'Macao' },\n { code: 'MK', name: 'Macedonia' },\n { code: 'MG', name: 'Madagascar' },\n { code: 'MW', name: 'Malawi' },\n { code: 'MY', name: 'Malaysia' },\n { code: 'MV', name: 'Maldives' },\n { code: 'ML', name: 'Mali' },\n { code: 'MT', name: 'Malta' },\n { code: 'MH', name: 'Marshall Islands' },\n { code: 'MQ', name: 'Martinique' },\n { code: 'MR', name: 'Mauritania' },\n { code: 'MU', name: 'Mauritius' },\n { code: 'YT', name: 'Mayotte' },\n { code: 'MX', name: 'Mexico' },\n { code: 'FM', name: 'Micronesia, Federated States of' },\n { code: 'MD', name: 'Moldova, Republic of' },\n { code: 'MC', name: 'Monaco' },\n { code: 'MN', name: 'Mongolia' },\n { code: 'MS', name: 'Montserrat' },\n { code: 'MA', name: 'Morocco' },\n { code: 'MZ', name: 'Mozambique' },\n { code: 'MM', name: 'Myanmar' },\n { code: 'NA', name: 'Namibia' },\n { code: 'NR', name: 'Nauru' },\n { code: 'NP', name: 'Nepal' },\n { code: 'NL', name: 'Netherlands' },\n { code: 'AN', name: 'Netherlands Antilles' },\n { code: 'NC', name: 'New Caledonia' },\n { code: 'NZ', name: 'New Zealand' },\n { code: 'NI', name: 'Nicaragua' },\n { code: 'NE', name: 'Niger' },\n { code: 'NG', name: 'Nigeria' },\n { code: 'NU', name: 'Niue' },\n { code: 'NF', name: 'Norfolk Island' },\n { code: 'MP', name: 'Northern Mariana Islands' },\n { code: 'NO', name: 'Norway' },\n { code: 'OM', name: 'Oman' },\n { code: 'PK', name: 'Pakistan' },\n { code: 'PW', name: 'Palau' },\n { code: 'PS', name: 'Palestinian Territory, Occupied' },\n { code: 'PA', name: 'Panama' },\n { code: 'PG', name: 'Papua New Guinea' },\n { code: 'PY', name: 'Paraguay' },\n { code: 'PE', name: 'Peru' },\n { code: 'PH', name: 'Philippines' },\n { code: 'PN', name: 'Pitcairn' },\n { code: 'PL', name: 'Poland' },\n { code: 'PT', name: 'Portugal' },\n { code: 'PR', name: 'Puerto Rico' },\n { code: 'QA', name: 'Qatar' },\n { code: 'RE', name: 'Reunion' },\n { code: 'RO', name: 'Romania' },\n { code: 'RU', name: 'Russian Federation' },\n { code: 'RW', name: 'Rwanda' },\n { code: 'SH', name: 'Saint Helena' },\n { code: 'KN', name: 'Saint Kitts and Nevis' },\n { code: 'LC', name: 'Saint Lucia' },\n { code: 'PM', name: 'Saint Pierre and Miquelon' },\n { code: 'VC', name: 'Saint Vincent and the Grenadines' },\n { code: 'WS', name: 'Samoa' },\n { code: 'SM', name: 'San Marino' },\n { code: 'ST', name: 'Sao Tome and Principe' },\n { code: 'SA', name: 'Saudi Arabia' },\n { code: 'SN', name: 'Senegal' },\n { code: 'ME', name: 'Montenegro' },\n { code: 'RS', name: 'Serbia' },\n { code: 'SC', name: 'Seychelles' },\n { code: 'SL', name: 'Sierra Leone' },\n { code: 'SG', name: 'Singapore' },\n { code: 'SK', name: 'Slovakia' },\n { code: 'SI', name: 'Slovenia' },\n { code: 'SB', name: 'Solomon Islands' },\n { code: 'SO', name: 'Somalia' },\n { code: 'ZA', name: 'South Africa' },\n { code: 'GS', name: 'South Georgia and Sandwich Isles' },\n { code: 'ES', name: 'Spain' },\n { code: 'LK', name: 'Sri Lanka' },\n { code: 'SD', name: 'Sudan' },\n { code: 'SR', name: 'Suriname' },\n { code: 'SJ', name: 'Svalbard and Jan Mayen' },\n { code: 'SZ', name: 'Swaziland' },\n { code: 'SE', name: 'Sweden' },\n { code: 'CH', name: 'Switzerland' },\n { code: 'SY', name: 'Syrian Arab Republic' },\n { code: 'TW', name: 'Taiwan (ROC)' },\n { code: 'TJ', name: 'Tajikistan' },\n { code: 'TZ', name: 'Tanzania, United Republic of' },\n { code: 'TH', name: 'Thailand' },\n { code: 'TL', name: 'Timor-Leste' },\n { code: 'TG', name: 'Togo' },\n { code: 'TK', name: 'Tokelau' },\n { code: 'TO', name: 'Tonga' },\n { code: 'TT', name: 'Trinidad and Tobago' },\n { code: 'TN', name: 'Tunisia' },\n { code: 'TR', name: 'Turkey' },\n { code: 'TM', name: 'Turkmenistan' },\n { code: 'TC', name: 'Turks and Caicos Islands' },\n { code: 'TV', name: 'Tuvalu' },\n { code: 'UG', name: 'Uganda' },\n { code: 'UA', name: 'Ukraine' },\n { code: 'AE', name: 'United Arab Emirates' },\n { code: 'GB', name: 'United Kingdom' },\n {\n code: 'US',\n name: 'United States',\n regionLabel: 'State',\n regions: [\n { code: 'AK', name: 'Alaska' },\n { code: 'AL', name: 'Alabama' },\n { code: 'AR', name: 'Arkansas' },\n { code: 'AS', name: 'American Samoa' },\n { code: 'AZ', name: 'Arizona' },\n { code: 'CA', name: 'California' },\n { code: 'CO', name: 'Colorado' },\n { code: 'CT', name: 'Connecticut' },\n { code: 'DE', name: 'Delaware' },\n { code: 'FL', name: 'Florida' },\n { code: 'GA', name: 'Georgia' },\n { code: 'HI', name: 'Hawaii' },\n { code: 'IA', name: 'Iowa' },\n { code: 'ID', name: 'Idaho' },\n { code: 'IL', name: 'Illinois' },\n { code: 'IN', name: 'Indiana' },\n { code: 'KS', name: 'Kansas' },\n { code: 'KY', name: 'Kentucky' },\n { code: 'LA', name: 'Louisiana' },\n { code: 'MA', name: 'Massachusetts' },\n { code: 'MD', name: 'Maryland' },\n { code: 'ME', name: 'Maine' },\n { code: 'MI', name: 'Michigan' },\n { code: 'MN', name: 'Minnesota' },\n { code: 'MO', name: 'Missouri' },\n { code: 'MP', name: 'Northern Mariana Islands' },\n { code: 'MS', name: 'Mississippi' },\n { code: 'MT', name: 'Montana' },\n { code: 'NC', name: 'North Carolina' },\n { code: 'ND', name: 'North Dakota' },\n { code: 'NE', name: 'Nebraska' },\n { code: 'NH', name: 'New Hampshire' },\n { code: 'NJ', name: 'New Jersey' },\n { code: 'NM', name: 'New Mexico' },\n { code: 'NV', name: 'Nevada' },\n { code: 'NY', name: 'New York' },\n { code: 'OH', name: 'Ohio' },\n { code: 'OK', name: 'Oklahoma' },\n { code: 'OR', name: 'Oregon' },\n { code: 'PA', name: 'Pennsylvania' },\n { code: 'PR', name: 'Puerto Rico' },\n { code: 'RI', name: 'Rhode Island' },\n { code: 'SC', name: 'South Carolina' },\n { code: 'SD', name: 'South Dakota' },\n { code: 'TN', name: 'Tennessee' },\n { code: 'TX', name: 'Texas' },\n { code: 'UT', name: 'Utah' },\n { code: 'VA', name: 'Virginia' },\n { code: 'VI', name: 'Virgin Islands' },\n { code: 'VT', name: 'Vermont' },\n { code: 'WA', name: 'Washington' },\n { code: 'DC', name: 'Washington (District of Columbia)' },\n { code: 'WI', name: 'Wisconsin' },\n { code: 'WV', name: 'West Virginia' },\n { code: 'WY', name: 'Wyoming' },\n ],\n },\n { code: 'UM', name: 'United States Minor Outlying Islands' },\n { code: 'UY', name: 'Uruguay' },\n { code: 'UZ', name: 'Uzbekistan' },\n { code: 'VU', name: 'Vanuatu' },\n { code: 'VE', name: 'Venezuela' },\n { code: 'VN', name: 'Viet Nam' },\n { code: 'VG', name: 'Virgin Islands, British' },\n { code: 'VI', name: 'Virgin Islands, U.s.' },\n { code: 'WF', name: 'Wallis and Futuna' },\n { code: 'EH', name: 'Western Sahara' },\n { code: 'YE', name: 'Yemen' },\n { code: 'ZM', name: 'Zambia' },\n { code: 'ZW', name: 'Zimbabwe' },\n];\n","export enum Country {\n Canada = 'CA',\n UnitedStates = 'US',\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Optional, Output, ViewChild, inject } from '@angular/core';\nimport { ControlContainer, NgForm, NgModel, FormsModule } from '@angular/forms';\n\n\nimport { FsAutocompleteComponent, FsAutocompleteModule } from '@firestitch/autocomplete';\nimport { guid } from '@firestitch/common';\nimport { controlContainerFactory } from '@firestitch/core';\n\nimport { Subject, of } from 'rxjs';\nimport { map, takeUntil } from 'rxjs/operators';\n\nimport { Countries } from '../../consts';\nimport { Country } from '../../enums/country.enum';\nimport { IAddressCountry } from '../../interfaces/address-country.interface';\nimport { IAddressRegion } from '../../interfaces/address-region.interface';\nimport { FsFormModule } from '@firestitch/form';\n\n\n@Component({\n selector: 'fs-address-region',\n templateUrl: './address-region.component.html',\n styleUrls: ['./address-region.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n viewProviders: [\n {\n provide: ControlContainer,\n useFactory: controlContainerFactory,\n deps: [[new Optional(), NgForm]],\n },\n ],\n standalone: true,\n imports: [\n FsAutocompleteModule,\n FormsModule,\n FsFormModule,\n ],\n})\nexport class FsAddressRegionComponent implements OnInit, OnDestroy {\n private _cdRef = inject(ChangeDetectorRef);\n\n\n @ViewChild(FsAutocompleteComponent, { read: NgModel, static: true })\n public autocompleteModel: NgModel;\n\n @Input() public set region(regionCode: string) {\n const region = this.addressCountries\n .reduce((accum, addressCountry) => {\n return [\n ...accum,\n ...(addressCountry.regions || [])\n .filter((addressRegion) => (\n addressRegion.code === regionCode &&\n (!this.country || this.country === addressCountry.code)),\n ),\n ];\n }, [])[0];\n\n this.regionModel = (region ? region : (regionCode ? { name: regionCode } : null));\n }\n\n public get region() {\n return this.regionModel?.code;\n }\n\n @Input() public disabled = false;\n @Input() public country: Country | string;\n @Input() public label;\n @Input() public required = false;\n @Input() public regionCountryOrder = [Country.Canada, Country.UnitedStates];\n @Input() public set countries(countryCodes: (string | Country)[]) {\n countryCodes = countryCodes || [Country.Canada, Country.UnitedStates];\n this._countries = countryCodes\n .map((countryCode: string) => {\n return Countries.find((country) => country.code === countryCode);\n });\n\n this.updateCountryRegionLabels();\n }\n\n public get addressCountries() {\n return this._countries;\n }\n\n @Output() public regionChange = new EventEmitter<string>();\n\n public regionModel: IAddressRegion;\n public controlName = `region${guid('xxxxxx')}`;\n public regionLabel: string;\n public countryEnum = Country;\n\n private _countries: IAddressCountry[] = [];\n private _destroy$ = new Subject<void>();\n\n constructor() {\n this.countries = [Country.Canada, Country.UnitedStates];\n }\n\n public ngOnInit() {\n this.updateCountryRegionLabels();\n this._listenControlStateChanges();\n }\n\n public clear() {\n this.regionModel = null;\n }\n\n public ngOnDestroy(): void {\n this._destroy$.next(null);\n this._destroy$.complete();\n }\n\n public fetch = (keyword: string) => {\n keyword = keyword.toLowerCase();\n\n return of(null)\n .pipe(\n map(() => {\n const regions: IAddressRegion[] = this._countries\n .reduce((accum, country) => {\n const countryRegions = (country.regions || [])\n .filter((region) => {\n const regionName = region.name.toLowerCase().trim();\n\n return regionName.indexOf(keyword) !== -1;\n });\n\n if (countryRegions.length ) {\n console.log(country, keyword, countryRegions);\n }\n\n return [\n ...accum,\n ...countryRegions\n .map((countryRegion) => {\n return {\n ...countryRegion,\n country: country.name,\n };\n }),\n ];\n }, []);\n console.log(regions, keyword);\n\n return regions;\n }),\n );\n };\n\n public displayWith = (data) => {\n return data?.name;\n };\n\n public selectUserOption(keyword) {\n this.regionModel = {\n code: keyword,\n name: keyword,\n };\n\n this.autocompleteModel.control.markAsDirty();\n\n this.regionChange.emit(keyword);\n }\n\n public regionChanged() {\n this.regionChange.emit(this.regionModel?.code);\n }\n\n public justUseShow = (keyword) => {\n return !!keyword;\n };\n\n public updateCountryRegionLabels() {\n this.regionLabel = this.label ? this.label : Object.keys(\n this._countries\n .reduce((accum, country) => {\n return {\n ...accum,\n [country.regionLabel || 'Province']: true,\n };\n }, {}),\n )\n .join('/');\n }\n\n // we need this to get updated ng-(invalid/dirty) classes\n private _listenControlStateChanges(): void {\n this.autocompleteModel\n .control\n .statusChanges\n .pipe(\n takeUntil(this._destroy$),\n )\n .subscribe(() => {\n this._cdRef.markForCheck();\n });\n }\n\n}\n","<fs-autocomplete\n [fetch]=\"fetch\"\n [displayWith]=\"displayWith\"\n [fetchOnFocus]=\"true\"\n [(ngModel)]=\"regionModel\"\n (ngModelChange)=\"regionChanged()\"\n [placeholder]=\"regionLabel\"\n [disabled]=\"disabled\"\n [fsFormRequired]=\"required\"\n [name]=\"controlName\">\n <ng-template\n fsAutocompleteTemplate\n let-data=\"data\">\n <span class=\"country-region\">\n <span>\n {{ data.name }}\n </span>\n @if (!regionModel && addressCountries.length > 1) {\n <span>\n {{ data.country }}\n </span>\n }\n </span>\n </ng-template>\n <ng-template\n fsAutocompleteStatic\n let-keyword\n (selected)=\"selectUserOption($event)\"\n [show]=\"justUseShow\">\n Just Use \"{{ keyword }}\"\n </ng-template>\n <ng-template fsAutocompleteNoResults></ng-template>\n</fs-autocomplete>","import { AddressFormat } from './../enums/address-format.enum';\n\n\nexport function addressFormat(\n address,\n options: {\n format?: AddressFormat;\n includeFirst?: number;\n } = {}): string {\n\n options = {\n format: AddressFormat.OneLine,\n ...options,\n };\n\n const parts = ['name', 'street', 'address2', 'address3', 'city', 'region', 'zip', 'country'];\n let addressParts = [];\n let lines = [];\n\n if (address) {\n parts.forEach((part) => {\n if (address[part]) {\n addressParts.push(address[part]);\n }\n });\n }\n\n if (options.includeFirst) {\n addressParts = addressParts.slice(0, options.includeFirst);\n }\n\n if (addressParts.length) {\n if (options.format === AddressFormat.TwoLine) {\n lines = [[addressParts.shift()]];\n }\n\n lines.push(addressParts);\n }\n\n return lines\n .map((line) => {\n return line.join(', ');\n })\n .join('\\n');\n}\n","import { AddressFormat } from '../enums/address-format.enum';\n\nimport { addressFormat } from './address-format';\n\n\nexport function addressOneLineFormat(address, options: { includeFirst?: number } = {}) {\n\n const addressOptions: any = {\n format: AddressFormat.OneLine,\n ...options,\n };\n\n return addressFormat(address, addressOptions);\n}\n","import { AddressFormat } from '../enums/address-format.enum';\n\nimport { addressFormat } from './address-format';\n\n\nexport function addressTwoLineFormat(address, options: { includeFirst?: number } = {}) {\n\n const addressOptions: any = {\n format: AddressFormat.TwoLine,\n ...options,\n };\n\n return addressFormat(address, addressOptions);\n}\n","\nexport function addressSummaryFormat(address) {\n\n const parts = ['name', 'street', 'address2', 'address3', 'city', 'region', 'country'];\n const addressParts = [];\n\n if (address) {\n for ( let i = 0; i < parts.length; i++) {\n const field = parts[i];\n const part = address[field];\n\n if (field === 'name' && part) {\n addressParts.push(part);\n\n } else if (part && field !== 'name') {\n addressParts.push(part);\n\n const nextPart = address[parts[i + 1]];\n if (nextPart) {\n addressParts.push(nextPart);\n }\n }\n }\n }\n\n return addressParts.join(', ');\n}\n","import { IAddressCountry } from '../interfaces/address-country.interface';\nimport { IAddressRegion } from '../interfaces/address-region.interface';\n\n\nexport function searchCountryRegions(\n text: string,\n regions: IAddressRegion[] | IAddressCountry[],\n limit?: number,\n): IAddressRegion[] {\n let matches = [];\n\n text = text.toLowerCase().trim();\n regions.forEach((region) => {\n const regionName = region.name.toLowerCase().trim();\n const index = regionName.indexOf(text);\n\n if (index > -1) {\n matches.push({\n index,\n region,\n });\n }\n });\n\n matches.sort((a, b) => {\n if (a.index < b.index) {\n return -1;\n } else if (a.index > b.index) {\n return 1;\n }\n\n return 0;\n\n });\n\n if (limit) {\n matches = matches.slice(0, limit);\n }\n\n return matches.map((match) => match.region);\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Optional, Output, SimpleChanges, forwardRef, inject } from '@angular/core';\nimport { ControlContainer, ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms';\n\nimport { FsAutocompleteChipsModule } from '@firestitch/autocomplete-chips';\nimport { guid } from '@firestitch/common';\nimport { controlContainerFactory } from '@firestitch/core';\nimport { FsFormModule } from '@firestitch/form';\n\nimport { of } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\nimport { Countries } from '../../consts/countries.const';\nimport { searchCountryRegions } from '../../helpers';\n\n\n@Component({\n selector: 'fs-address-country',\n templateUrl: './address-country.component.html',\n styleUrls: ['./address-country.component.scss'],\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => FsAddressCountryComponent),\n }],\n viewProviders: [\n {\n provide: ControlContainer,\n useFactory: controlContainerFactory,\n deps: [[new Optional(), NgForm]],\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n FsAutocompleteChipsModule,\n FormsModule,\n FsFormModule,\n ],\n})\nexport class FsAddressCountryComponent implements OnChanges, ControlValueAccessor {\n\n @Input() public disabled = false;\n @Input() public required = false;\n @Input() public excludeCountries: string[];\n @Input() public countries = Countries;\n @Input() public label = 'Country';\n \n\n @Output() public selectionChange = new EventEmitter<any>();\n\n public country;\n public name = `addressCountry${guid()}`;\n public onChange: (data: any) => void;\n public onTouched: () => void;\n\n private _cdRef = inject(ChangeDetectorRef);\n\n public fetch = (keyword: string) => {\n return of(keyword)\n .pipe(\n map((kw) => {\n return searchCountryRegions(kw, this.countries, 10);\n }),\n );\n };\n\n public writeValue(data: any): void {\n this.country = this.countries\n .find((country) => country.code === data);\n this._cdRef.markForCheck();\n }\n\n public changed(value) {\n const code = value?.code || null;\n this.onChange(code);\n this.selectionChange.emit(code);\n }\n\n public registerOnChange(fn: (data: any) => void): void {\n this.onChange = fn;\n }\n\n public registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n public ngOnChanges(changes: SimpleChanges): void {\n if (changes.excludeCountries && changes.excludeCountries.currentValue) {\n this.countries = this.countries.filter((country) => {\n return this.excludeCountries.indexOf(country.code) === -1;\n });\n }\n }\n}\n","<fs-autocomplete-chips\n [fetch]=\"fetch\"\n [fetchOnFocus]=\"true\"\n [(ngModel)]=\"country\"\n [size]=\"'small'\"\n (ngModelChange)=\"changed($event)\"\n [label]=\"label\"\n [multiple]=\"false\"\n [disabled]=\"disabled\"\n [fsFormRequired]=\"required\"\n [name]=\"name\">\n <ng-template\n fsAutocompleteChipsTemplate\n let-object=\"object\">\n {{ object.name }}\n </ng-template>\n</fs-autocomplete-chips>","import {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Optional,\n Output,\n ViewChild,\n} from '@angular/core';\nimport { ControlContainer, NgForm, FormsModule } from '@angular/forms';\n\nimport { guid } from '@firestitch/common';\nimport { controlContainerFactory } from '@firestitch/core';\nimport { FsMapComponent, FsMapModule } from '@firestitch/map';\n\nimport { Subject } from 'rxjs';\n\nimport { isObject } from 'lodash-es';\n\nimport { Countries } from '../../consts/countries.const';\nimport { Country } from '../../enums/country.enum';\nimport { FsAddressConfig } from '../../interfaces/address-config.interface';\nimport { FsAddressMapConfig } from '../../interfaces/address-map-config.interface';\nimport { FsAddress } from '../../interfaces/address.interface';\nimport { FsAddressRegionComponent } from '../address-region/address-region.component';\nimport { MatFormField, MatLabel } from '@angular/material/form-field';\nimport { MatInput } from '@angular/material/input';\nimport { FsFormModule } from '@firestitch/form';\nimport { FsAddressCountryComponent } from '../address-country/address-country.component';\n\n\n@Component({\n selector: 'fs-address',\n templateUrl: './address.component.html',\n styleUrls: ['./address.component.scss'],\n viewProviders: [\n {\n provide: ControlContainer,\n useFactory: controlContainerFactory,\n deps: [[new Optional(), NgForm]],\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n FormsModule,\n FsFormModule,\n FsAddressRegionComponent,\n FsAddressCountryComponent,\n FsMapModule,\n ],\n})\nexport class FsAddressComponent implements OnInit, OnChanges, OnDestroy {\n\n @ViewChild(FsAddressRegionComponent)\n public fsAddressRegionComponent: FsAddressRegionComponent;\n\n @ViewChild(FsMapComponent)\n public map: FsMapComponent;\n\n @Input() public address: FsAddress;\n @Input() public excludeCountries: string[];\n @Input() public regionCountryOrder = [Country.Canada, Country.UnitedStates];\n\n @Input('config') public set setConfig(config: FsAddressConfig) {\n config.search = config.search === undefined ? false : config.search;\n\n if (!isObject(config.map)) {\n config.map = { showMap: false };\n }\n\n