diff --git a/complete.ts b/complete.ts new file mode 100644 index 0000000..393fc2b --- /dev/null +++ b/complete.ts @@ -0,0 +1,78 @@ +import { validateInput } from '@repo/validate'; + +export class AddressComplete { + private primary?: HTMLInputElement; + address2?: HTMLInputElement; + city?: HTMLInputElement; + province?: HTMLInputElement; + postalCode?: HTMLInputElement; + country?: HTMLInputElement; + + bind(primary: HTMLInputElement) { + this.primary = primary; + addListener(this.primary, this); + } +} + +const addListener = (node: HTMLInputElement, completer: AddressComplete) => { + const googleOptions: google.maps.places.AutocompleteOptions = { + // componentRestrictions: { country: ['ca'] }, + fields: ['address_components'], + strictBounds: false + }; + + const autocomplete = new google.maps.places.Autocomplete(node, googleOptions); + + autocomplete.addListener('place_changed', () => { + let address1 = ''; + let postalCode = ''; + + const place = autocomplete.getPlace(); + // Sort each component + for (const component of place.address_components as google.maps.GeocoderAddressComponent[]) { + const componentType = component.types[0]; + + switch (componentType) { + case 'street_number': { + address1 = `${component.long_name} ${address1}`; + break; + } + case 'route': { + address1 += component.short_name; + break; + } + case 'postal_code': { + postalCode = `${component.long_name}${postalCode}`; + break; + } + case 'postal_code_suffix': { + postalCode = `${postalCode}-${component.long_name}`; + break; + } + case 'locality': { + if (completer.city) completer.city.value = component.long_name; + break; + } + case 'administrative_area_level_1': { + // completer.province?.setValStr(component.short_name); + if (completer.province) completer.province.value = component.short_name; + break; + } + case 'country': { + // completer.country?.setValStr(component.short_name); + if (completer.country) completer.country.value = component.short_name; + break; + } + } + } + + node.value = address1; + if (completer.postalCode) completer.postalCode.value = postalCode; + completer.address2?.focus(); + + // Trigger rechecks on each field + validateInput(node); + if (completer.city !== undefined) validateInput(completer.city); + if (completer.postalCode !== undefined) validateInput(completer.postalCode); + }); +}; diff --git a/package.json b/package.json index 02144e2..b0e906d 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,14 @@ "module": "index.ts", "main": "index.ts", "exports": { + "./complete": { + "types": "./complete.ts", + "svelte": "./complete.ts" + }, + "./sanitize": { + "types": "./sanitize.ts", + "svelte": "./sanitize.ts" + }, ".": { "types": "./index.ts", "svelte": "./index.ts" diff --git a/sanitize.ts b/sanitize.ts new file mode 100644 index 0000000..c2581ac --- /dev/null +++ b/sanitize.ts @@ -0,0 +1,17 @@ +export function sanitize(data: FormData, key: string): string; +export function sanitize( + data: FormData, + key: string, + fallback: Fallback +): string | Fallback; +export function sanitize( + data: FormData, + key: string, + fallback?: Fallback +): string | Fallback { + const val = data.get(key); + if (val !== null) return val.toString().trim(); + + if (fallback !== undefined) return fallback; + throw new Error(`Missing required field: ${key}`); +}