places-autocomplete-svelte 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,17 +2,20 @@
2
2
 
3
3
  This Svelte component leverages the [Google Maps Places Autocomplete API](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview) to provide a user-friendly way to search for and retrieve detailed address information within your [SvelteKit](https://kit.svelte.dev) applications.
4
4
 
5
- Preview this package [Demo](https://places-autocomplete-demo.pages.dev/)
6
5
 
7
6
  ## Features:
8
7
 
9
8
  - **Seamless Integration:** Easily integrate the component into your SvelteKit projects.
10
- - **Autocomplete Suggestions:** Provide users with real-time suggestions as they type, enhancing the search experience.
9
+ - **Autocomplete Suggestions:** Provides real-time address suggestions as the user types.
11
10
  - **Detailed Address Retrieval:** Retrieve comprehensive address information, including street address, city, region, postal code, and country.
12
- - **Country/Region Selection:** Allow users to specify a region for more targeted results.
13
- - **Customizable:** Tailor the component's appearance and behavior using language settings, placeholder text.
11
+ - **Country/Region Filtering:** Refine search results by specifying countries or regions.
12
+ - **Customizable:** Tailor the component's appearance (placeholder, language) and data retrieved (`fetchFields`).
14
13
  - **Accessible:** Supports keyboard navigation for selecting suggestions.
15
14
 
15
+ ## Demo
16
+
17
+ See a live demo of the component in action: [Demo](https://places-autocomplete-demo.pages.dev/)
18
+
16
19
  ![Places Autocomplete Svelte](places-autocomplete-svelte.gif)
17
20
 
18
21
  ## Requirements
@@ -55,36 +58,95 @@ npm i places-autocomplete-svelte@1.0.1
55
58
  <p>Response Object: {JSON.stringify(fullResponse, null, 2)}</p>
56
59
  ```
57
60
 
58
- ## Countries/Regions
59
61
 
60
- Use optional `countries` property to refine search by region:
62
+
63
+ ## Customization
64
+
65
+ - `countries`: Use countries property to refine search by region
66
+ - `placeholder`: Use the placeholder property to customize the input field's placeholder text.
67
+ - `autocomplete`: Use to disable the HTML `<input>` autocomplete attribute.
68
+ - `requestParams` (autocomplete request):
69
+ - `language`: Use the language property to set the language of the autocomplete results.
70
+ - `region`: Use the region property to bias the results toward a particular region. If the countries array is provided the region will be used from the selected country.
71
+ - `fetchFields`: Use to control the Place response
61
72
 
62
73
  ```svelte
63
74
  <script>
64
75
  // ... other imports
65
76
 
77
+ /**
78
+ * @type array optional
79
+ */
66
80
  let countries = [
67
- { name: 'United Kingdom', region: 'GB', language: 'en-GB' },
68
- { name: 'United States', region: 'US', language: 'en-US' }
81
+ { name: 'United Kingdom', region: 'GB'},
82
+ { name: 'United States', region: 'US' }
69
83
  // ... more countries
70
84
  ];
85
+ /**
86
+ * @type string optional
87
+ */
88
+ const placeholder = 'Search...';
89
+ /**
90
+ * @type string optional
91
+ * The <input> HTML autocomplete attribute.
92
+ * if ommited defaults to 'off'
93
+ * */
94
+ const autocompete = 'off';
95
+ /**
96
+ * @type object optional
97
+ * List of accepted AutocompleteRequest properties
98
+ */
99
+ const requestParams = {
100
+ /**
101
+ * @type string optional
102
+ */
103
+ language : 'en-GB',
104
+ /**
105
+ * @type string optional
106
+ */
107
+ region : 'GB',
108
+ }
109
+
110
+ /**
111
+ * @type array optional
112
+ */
113
+ const fetchFields = ['formattedAddress', 'addressComponents'];
71
114
  </script>
72
115
 
73
- <PlaceAutocomplete {onResponse} {PUBLIC_GOOGLE_MAPS_API_KEY} bind:countries/>
116
+ <PlaceAutocomplete
117
+ {onError}
118
+ {onResponse}
119
+ {PUBLIC_GOOGLE_MAPS_API_KEY}
120
+ bind:countries
121
+ {placeholder}
122
+ {autocompete}
123
+ {fetchFields}/>
124
+
74
125
  ```
75
126
 
76
- - The `region` code follows the [CLDR two-character format](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest).
77
- - The `language` in which to return results. [See details](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest.language)
127
+ - `region` code follows the [CLDR two-character format](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest). The selected country region overwrites the `region` value in `requestParams`
128
+ - `language` in which to return results. If ommited defaults to the browser's language preference. [See details](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest.language)
129
+ - `requestParams` list of accepted [AutocompleteRequest properties](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest)
130
+ - `fetchFields` the [types](https://developers.google.com/maps/documentation/javascript/place-class-data-fields) of Place data to return when requesting place details. If omitted defaults to `['formattedAddress', 'addressComponents']`
131
+
132
+
133
+ ## Component Properties
134
+ | Property | Type | Description | Required | Default Value |
135
+ |--------------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------|
136
+ | `PUBLIC_GOOGLE_MAPS_API_KEY` | `String` | Your Google Maps Places API Key. | Yes | |
137
+ | `onResponse` | `CustomEvent` | Dispatched when a place is selected, containing the place details. | Yes | |
138
+ | `onError` | `CustomEvent` | Dispatched when an error occurs. | No | |
139
+ | `countries` | `Array` | Array of countries/regions to filter results. | No | `[]` |
140
+ | `placeholder` | `String` | Placeholder text for the input field. | No | `"Search..."` |
141
+ | `autocomplete` | `string` | HTML `autocomplete` attribute for the input field. Set to "off" to disable browser autocomplete. | No | `"off"` |
142
+ | `requestParams` | `Object` | Object for additional request parameters (e.g., `types`, `bounds`). See [AutocompleteRequest](https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest). | No | `{}` |
143
+ | `fetchFields` | `Array` | Array of place data fields to return. See [Supported Fields](https://developers.google.com/maps/documentation/javascript/reference/places-service#PlaceResult) | No | `['formattedAddress', 'addressComponents']` |
78
144
 
79
145
 
80
146
  ## Error Handling
81
147
 
82
- The component will throw an error if:
83
-
84
- - The Google Maps API key is invalid or missing.
85
- - There are network issues connecting to the Google Maps service.
148
+ The `onError` event will be dispatched if there is an issue with the Google Maps API or the autocomplete request.
86
149
 
87
- Handle these errors gracefully in your application:
88
150
 
89
151
  ```svelte
90
152
  <script>
@@ -95,36 +157,19 @@ Handle these errors gracefully in your application:
95
157
  let onError = (error: string) => {
96
158
  console.error(error);
97
159
  pacError = error;
98
- };
160
+ };
99
161
  </script>
100
162
 
101
- <PlaceAutocomplete {onError} {onResponse} {PUBLIC_GOOGLE_MAPS_API_KEY} />
163
+ <PlaceAutocomplete
164
+ {onResponse}
165
+ {onError}
166
+ {PUBLIC_GOOGLE_MAPS_API_KEY} />
102
167
 
103
168
  {#if pacError}
104
169
  <p class="error">{pacError}</p>
105
170
  {/if}
106
171
  ```
107
172
 
108
- ## Customization
109
-
110
- - Placeholder: Use the placeholder property to customize the input field's placeholder text.
111
- - Language: Use the language property to set the language of the autocomplete results.
112
- - Region: Use the region property to bias the results toward a particular region. If the countries array is provided the region will be used from the selected country.
113
- - Autocomplete: Use to disable the input autocomplete.
114
-
115
- ```svelte
116
- <script>
117
- // ... other imports
118
- const placeholder = 'Search...';
119
- const language = 'en-GB';
120
- const region = 'GB';
121
- const autocompete = 'off';
122
- </script>
123
-
124
- <PlaceAutocomplete {onError} {onResponse} {PUBLIC_GOOGLE_MAPS_API_KEY} bind:countries {placeholder} {language} {region} {autocomplete}/>
125
-
126
- ```
127
-
128
173
  ## Contributing
129
174
 
130
175
  Contributions are welcome! Please open an issue or submit a pull request on the [GitHub repository](https://github.com/alexpechkarev/places-autocomplete-svelte/).
@@ -132,3 +177,5 @@ Contributions are welcome! Please open an issue or submit a pull request on the
132
177
  ## License
133
178
 
134
179
  [MIT](LICENSE)
180
+
181
+
@@ -1,35 +1,26 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
3
  import * as GMaps from '@googlemaps/js-api-loader';
4
+ import type { Props } from './interfaces.js';
5
+ import {
6
+ validateRequestParams,
7
+ requestParamsDefault,
8
+ } from './helpers.js';
4
9
  const { Loader } = GMaps;
5
10
 
6
- // Props Interface
7
- interface Props {
8
- PUBLIC_GOOGLE_MAPS_API_KEY: string;
9
- fetchFields?: string[];
10
- countries: { name: string; region: string }[];
11
- placeholder?: string;
12
- language?: string;
13
- region?: string;
14
- autocomplete?: AutoFill;
15
- onResponse: (e: Event) => void;
16
- onError: (error: string) => void;
17
- }
18
- // Bindable Props
19
11
  let {
20
- PUBLIC_GOOGLE_MAPS_API_KEY = $bindable(''),
21
12
  /**
22
13
  * By default using SKU: Place Detals (Location Only) - 0.005 USD per each
23
14
  * @see https://developers.google.com/maps/documentation/javascript/usage-and-billing#location-placedetails
24
15
  */
16
+ PUBLIC_GOOGLE_MAPS_API_KEY,
25
17
  fetchFields = $bindable(['formattedAddress', 'addressComponents']),
26
18
  countries = $bindable([]),
27
- placeholder = $bindable('Search...'),
28
- language = $bindable('en-GB'),
29
- region = $bindable('GB'),
30
- autocomplete = $bindable('off'),
19
+ placeholder = 'Search...',
20
+ autocompete = 'off',
31
21
  onResponse = $bindable((e: Event) => {}),
32
- onError = $bindable((error: string) => {})
22
+ onError = $bindable((error: string) => {}),
23
+ requestParams
33
24
  }: Props = $props();
34
25
 
35
26
  // Check if countries are available
@@ -39,16 +30,12 @@
39
30
  let currentSuggestion = $state(-1);
40
31
  let title: string = $state('');
41
32
  let results: any[] = $state([]);
42
- let token;
43
33
  let loader: GMaps.Loader;
44
34
  let placesApi: { [key: string]: any } = {};
45
- //https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data#AutocompleteRequest.includedPrimaryTypes
46
- let request = $state({
47
- input: '',
48
- language: language,
49
- region: region,
50
- sessionToken: ''
51
- });
35
+ //https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
36
+ // validate and merge requestParams with requestParamsDefault
37
+ let request = $state(validateRequestParams(Object.assign(requestParamsDefault, requestParams)));
38
+
52
39
 
53
40
  $effect(() => {
54
41
  if (request.input == '') {
@@ -63,7 +50,7 @@
63
50
  currentSuggestion = -1;
64
51
  request.input = '';
65
52
  results = [];
66
- refreshToken(request);
53
+ setSessionToken();
67
54
  };
68
55
 
69
56
  /**
@@ -80,16 +67,25 @@
80
67
  results = [];
81
68
  return;
82
69
  }
70
+ /**
71
+ * Prevent making request if the inputOffset is greater than the length of the input
72
+ * The input lenght should be greater than the inputOffset before making a request and displaying suggestions
73
+ */
74
+ if (request.inputOffset && request.inputOffset >= target.value.length) {
75
+ return;
76
+ }
83
77
 
78
+ // set request input
84
79
  request.input = target.value;
85
-
80
+
81
+ // attempt to get autocomplete suggestions
86
82
  try {
87
83
  const { suggestions } =
88
84
  await placesApi.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
89
85
  results = [];
90
86
  // iterate suggestions and add results to an array
91
87
  for (const suggestion of suggestions) {
92
- // add suggexstions to results
88
+ // add suggestions to results
93
89
  results.push({
94
90
  to_pace: suggestion.placePrediction.toPlace(),
95
91
  text: suggestion.placePrediction.text.toString()
@@ -122,35 +118,29 @@
122
118
  );
123
119
  }
124
120
 
125
- // reset srarch input and results
121
+ // reset search input and results
126
122
  reset();
127
123
  };
128
124
 
129
125
  /**
130
- * Helper function to refresh the session token.
131
- * @param request
126
+ * Helper function to set the session token.
132
127
  */
133
- const refreshToken = async (request: {
134
- input?: string;
135
- language?: string;
136
- region?: string;
137
- sessionToken?: any;
138
- }): Promise<{ input?: string; language?: string; region?: string; sessionToken?: any }> => {
128
+ const setSessionToken = () => {
139
129
  try {
140
- token = new placesApi.AutocompleteSessionToken();
141
- request.sessionToken = token;
142
- return request;
130
+ request.sessionToken = new placesApi.AutocompleteSessionToken();
143
131
  } catch (e: any) {
144
132
  onError((e.name || 'An error occurred') + ' - ' + (e.message || 'error fetch token'));
145
- return request;
146
- }
133
+ }
147
134
  };
135
+
148
136
  /**
149
137
  * Initialize the Google Maps JavaScript API Loader.
150
138
  */
151
139
  onMount(async (): Promise<void> => {
140
+ // focus on the input
152
141
  inputRef.focus();
153
142
 
143
+ // load the Google Maps API
154
144
  try {
155
145
  loader = new Loader({
156
146
  apiKey: PUBLIC_GOOGLE_MAPS_API_KEY,
@@ -162,8 +152,7 @@
162
152
  await loader.importLibrary('places');
163
153
  placesApi.AutocompleteSessionToken = AutocompleteSessionToken;
164
154
  placesApi.AutocompleteSuggestion = AutocompleteSuggestion;
165
- token = new placesApi.AutocompleteSessionToken();
166
- request.sessionToken = token;
155
+ setSessionToken();
167
156
  } catch (e: any) {
168
157
  onError(
169
158
  (e.name || 'An error occurred') + ' - ' + (e.message || 'Error loading Google Maps API')
@@ -217,8 +206,8 @@
217
206
  name="search"
218
207
  bind:this={inputRef}
219
208
  class="border-1 w-full rounded-md border-0 shadow-sm bg-gray-100 px-4 py-2.5 pl-10 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 sm:text-sm"
220
- placeholder={placeholder}
221
- autocomplete={autocomplete}
209
+ {placeholder}
210
+ autocomplete={autocompete}
222
211
  aria-controls="options"
223
212
  bind:value={request.input}
224
213
  oninput={makeAcRequest}
@@ -283,10 +272,559 @@
283
272
  </select>
284
273
  </div>
285
274
  </div>
286
-
287
-
288
275
  </div>
289
276
  </section>
277
+
290
278
  <style>
291
- input,select,ul{margin:0;padding:0}.absolute,.sr-only{position:absolute}.block,svg{display:block}*,.border-gray-300{--tw-border-opacity:1}.text-gray-500,.text-gray-900,.text-white{--tw-text-opacity:1}*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb;--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }:after,:before{--tw-content:''}:host,section{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:system-ui,sans-serif,apple color emoji,segoe ui emoji,Segoe UI Symbol,noto color emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}kbd{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}input,select{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}ul{list-style:none}.cursor-default,:disabled{cursor:default}svg{vertical-align:middle}[type=text],input:where(:not([type])),select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[type=text]:focus,input:where(:not([type])):focus,select:focus{outline:transparent solid 2px;outline-offset:2px;--tw-ring-inset:var(--tw-empty,);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder{color:#6b7280;opacity:1}select{text-transform:none;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAyMCAyMCc+PHBhdGggc3Ryb2tlPScjNmI3MjgwJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1saW5lam9pbj0ncm91bmQnIHN0cm9rZS13aWR0aD0nMS41JyBkPSdNNiA4bDQgNCA0LTQnLz48L3N2Zz4=);background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}:root{--background:0 0% 100%;--foreground:222.2 84% 4.9%;--muted:210 40% 96.1%;--muted-foreground:215.4 16.3% 46.9%;--popover:0 0% 100%;--popover-foreground:222.2 84% 4.9%;--card:0 0% 100%;--card-foreground:222.2 84% 4.9%;--border:214.3 31.8% 91.4%;--input:214.3 31.8% 91.4%;--primary:222.2 47.4% 11.2%;--primary-foreground:210 40% 98%;--secondary:210 40% 96.1%;--secondary-foreground:222.2 47.4% 11.2%;--accent:210 40% 96.1%;--accent-foreground:222.2 47.4% 11.2%;--destructive:0 72.2% 50.6%;--destructive-foreground:210 40% 98%;--ring:222.2 84% 4.9%;--radius:0.5rem}*{border-color:hsl(var(--border) / var(--tw-border-opacity))}::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.sr-only{width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.relative{position:relative}.inset-y-0{top:0;bottom:0}.left-0{left:0}.right-0{right:0}.z-50{z-index:50}.-mb-2{margin-bottom:-.5rem}.mr-1{margin-right:.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-10{height:2.5rem}.h-5{height:1.25rem}.max-h-60{max-height:15rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr));grid-template-columns:1fr}.items-center{align-items:center}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem;row-gap:1rem}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-gray-300{border-color:rgb(209 213 219 / var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-indigo-500,.hover\:bg-indigo-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241 / var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.px-1{padding-left:.25rem;padding-right:.25rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-1\.5{padding-right:.375rem}.pr-20{padding-right:5rem}.pr-7{padding-right:1.75rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.text-gray-500{color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-900{color:rgb(17 24 39 / var(--tw-text-opacity))}.text-white{color:rgb(255 255 255 / var(--tw-text-opacity))}.shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0 / 0.1),0 4px 6px -4px rgb(0 0 0 / 0.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:ring-2:focus,.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0 / var(--tw-ring-opacity))}.focus\:outline-none:focus{outline:transparent solid 2px;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-inset:focus{--tw-ring-inset:inset}@media (min-width:640px){.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}}@keyframes swipe-out{0%{transform:translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));opacity:1}to{transform:translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));opacity:0}}.grid{display:grid}@media (min-width:768px){.lg\:grid-cols-6{grid-template-columns:repeat(6,1fr)}.lg\:col-span-6{grid-column-start:span 6}.lg\:col-span-4{grid-column-start:span 4}.lg\:col-span-2{grid-column-start:span 2}}.my-10{margin-top:2.5rem;margin-bottom:2.5rem} .justify-center {justify-content: center;} .hover\:text-white:hover{color:rgb(255 255 255 / var(--tw-text-opacity))}
292
- </style>
279
+ input,
280
+ select,
281
+ ul {
282
+ margin: 0;
283
+ padding: 0;
284
+ }
285
+ .absolute,
286
+ .sr-only {
287
+ position: absolute;
288
+ }
289
+ .block,
290
+ svg {
291
+ display: block;
292
+ }
293
+ *,
294
+ .border-gray-300 {
295
+ --tw-border-opacity: 1;
296
+ }
297
+ .text-gray-500,
298
+ .text-gray-900,
299
+ .text-white {
300
+ --tw-text-opacity: 1;
301
+ }
302
+ *,
303
+ :after,
304
+ :before {
305
+ box-sizing: border-box;
306
+ border: 0 solid #e5e7eb;
307
+ --tw-border-spacing-x: 0;
308
+ --tw-border-spacing-y: 0;
309
+ --tw-translate-x: 0;
310
+ --tw-translate-y: 0;
311
+ --tw-rotate: 0;
312
+ --tw-skew-x: 0;
313
+ --tw-skew-y: 0;
314
+ --tw-scale-x: 1;
315
+ --tw-scale-y: 1;
316
+ --tw-pan-x: ;
317
+ --tw-pan-y: ;
318
+ --tw-pinch-zoom: ;
319
+ --tw-scroll-snap-strictness: proximity;
320
+ --tw-gradient-from-position: ;
321
+ --tw-gradient-via-position: ;
322
+ --tw-gradient-to-position: ;
323
+ --tw-ordinal: ;
324
+ --tw-slashed-zero: ;
325
+ --tw-numeric-figure: ;
326
+ --tw-numeric-spacing: ;
327
+ --tw-numeric-fraction: ;
328
+ --tw-ring-inset: ;
329
+ --tw-ring-offset-width: 0px;
330
+ --tw-ring-offset-color: #fff;
331
+ --tw-ring-color: rgb(59 130 246 / 0.5);
332
+ --tw-ring-offset-shadow: 0 0 #0000;
333
+ --tw-ring-shadow: 0 0 #0000;
334
+ --tw-shadow: 0 0 #0000;
335
+ --tw-shadow-colored: 0 0 #0000;
336
+ --tw-blur: ;
337
+ --tw-brightness: ;
338
+ --tw-contrast: ;
339
+ --tw-grayscale: ;
340
+ --tw-hue-rotate: ;
341
+ --tw-invert: ;
342
+ --tw-saturate: ;
343
+ --tw-sepia: ;
344
+ --tw-drop-shadow: ;
345
+ --tw-backdrop-blur: ;
346
+ --tw-backdrop-brightness: ;
347
+ --tw-backdrop-contrast: ;
348
+ --tw-backdrop-grayscale: ;
349
+ --tw-backdrop-hue-rotate: ;
350
+ --tw-backdrop-invert: ;
351
+ --tw-backdrop-opacity: ;
352
+ --tw-backdrop-saturate: ;
353
+ --tw-backdrop-sepia: ;
354
+ --tw-contain-size: ;
355
+ --tw-contain-layout: ;
356
+ --tw-contain-paint: ;
357
+ --tw-contain-style: ;
358
+ }
359
+ :after,
360
+ :before {
361
+ --tw-content: '';
362
+ }
363
+ :host,
364
+ section {
365
+ line-height: 1.5;
366
+ -webkit-text-size-adjust: 100%;
367
+ -moz-tab-size: 4;
368
+ -o-tab-size: 4;
369
+ tab-size: 4;
370
+ font-family:
371
+ system-ui,
372
+ sans-serif,
373
+ apple color emoji,
374
+ segoe ui emoji,
375
+ Segoe UI Symbol,
376
+ noto color emoji;
377
+ font-feature-settings: normal;
378
+ font-variation-settings: normal;
379
+ -webkit-tap-highlight-color: transparent;
380
+ }
381
+ kbd {
382
+ font-family:
383
+ ui-monospace,
384
+ SFMono-Regular,
385
+ Menlo,
386
+ Monaco,
387
+ Consolas,
388
+ Liberation Mono,
389
+ Courier New,
390
+ monospace;
391
+ font-feature-settings: normal;
392
+ font-variation-settings: normal;
393
+ font-size: 1em;
394
+ }
395
+ input,
396
+ select {
397
+ font-family: inherit;
398
+ font-feature-settings: inherit;
399
+ font-variation-settings: inherit;
400
+ font-size: 100%;
401
+ font-weight: inherit;
402
+ line-height: inherit;
403
+ letter-spacing: inherit;
404
+ color: inherit;
405
+ }
406
+ :-moz-focusring {
407
+ outline: auto;
408
+ }
409
+ :-moz-ui-invalid {
410
+ box-shadow: none;
411
+ }
412
+ ::-webkit-inner-spin-button,
413
+ ::-webkit-outer-spin-button {
414
+ height: auto;
415
+ }
416
+ ::-webkit-search-decoration {
417
+ -webkit-appearance: none;
418
+ }
419
+ ::-webkit-file-upload-button {
420
+ -webkit-appearance: button;
421
+ font: inherit;
422
+ }
423
+ ul {
424
+ list-style: none;
425
+ }
426
+ .cursor-default,
427
+ :disabled {
428
+ cursor: default;
429
+ }
430
+ svg {
431
+ vertical-align: middle;
432
+ }
433
+ [type='text'],
434
+ input:where(:not([type])),
435
+ select {
436
+ -webkit-appearance: none;
437
+ -moz-appearance: none;
438
+ appearance: none;
439
+ background-color: #fff;
440
+ border-color: #6b7280;
441
+ border-width: 1px;
442
+ border-radius: 0;
443
+ padding: 0.5rem 0.75rem;
444
+ font-size: 1rem;
445
+ line-height: 1.5rem;
446
+ --tw-shadow: 0 0 #0000;
447
+ }
448
+ [type='text']:focus,
449
+ input:where(:not([type])):focus,
450
+ select:focus {
451
+ outline: transparent solid 2px;
452
+ outline-offset: 2px;
453
+ --tw-ring-inset: var(--tw-empty,);
454
+ --tw-ring-offset-width: 0px;
455
+ --tw-ring-offset-color: #fff;
456
+ --tw-ring-color: #2563eb;
457
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
458
+ var(--tw-ring-offset-color);
459
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width))
460
+ var(--tw-ring-color);
461
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
462
+ border-color: #2563eb;
463
+ }
464
+ input::-moz-placeholder {
465
+ color: #6b7280;
466
+ opacity: 1;
467
+ }
468
+ input::placeholder {
469
+ color: #6b7280;
470
+ opacity: 1;
471
+ }
472
+ select {
473
+ text-transform: none;
474
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGZpbGw9J25vbmUnIHZpZXdCb3g9JzAgMCAyMCAyMCc+PHBhdGggc3Ryb2tlPScjNmI3MjgwJyBzdHJva2UtbGluZWNhcD0ncm91bmQnIHN0cm9rZS1saW5lam9pbj0ncm91bmQnIHN0cm9rZS13aWR0aD0nMS41JyBkPSdNNiA4bDQgNCA0LTQnLz48L3N2Zz4=);
475
+ background-position: right 0.5rem center;
476
+ background-repeat: no-repeat;
477
+ background-size: 1.5em 1.5em;
478
+ padding-right: 2.5rem;
479
+ -webkit-print-color-adjust: exact;
480
+ print-color-adjust: exact;
481
+ }
482
+ :root {
483
+ --background: 0 0% 100%;
484
+ --foreground: 222.2 84% 4.9%;
485
+ --muted: 210 40% 96.1%;
486
+ --muted-foreground: 215.4 16.3% 46.9%;
487
+ --popover: 0 0% 100%;
488
+ --popover-foreground: 222.2 84% 4.9%;
489
+ --card: 0 0% 100%;
490
+ --card-foreground: 222.2 84% 4.9%;
491
+ --border: 214.3 31.8% 91.4%;
492
+ --input: 214.3 31.8% 91.4%;
493
+ --primary: 222.2 47.4% 11.2%;
494
+ --primary-foreground: 210 40% 98%;
495
+ --secondary: 210 40% 96.1%;
496
+ --secondary-foreground: 222.2 47.4% 11.2%;
497
+ --accent: 210 40% 96.1%;
498
+ --accent-foreground: 222.2 47.4% 11.2%;
499
+ --destructive: 0 72.2% 50.6%;
500
+ --destructive-foreground: 210 40% 98%;
501
+ --ring: 222.2 84% 4.9%;
502
+ --radius: 0.5rem;
503
+ }
504
+ * {
505
+ border-color: hsl(var(--border) / var(--tw-border-opacity));
506
+ }
507
+ ::backdrop {
508
+ --tw-border-spacing-x: 0;
509
+ --tw-border-spacing-y: 0;
510
+ --tw-translate-x: 0;
511
+ --tw-translate-y: 0;
512
+ --tw-rotate: 0;
513
+ --tw-skew-x: 0;
514
+ --tw-skew-y: 0;
515
+ --tw-scale-x: 1;
516
+ --tw-scale-y: 1;
517
+ --tw-pan-x: ;
518
+ --tw-pan-y: ;
519
+ --tw-pinch-zoom: ;
520
+ --tw-scroll-snap-strictness: proximity;
521
+ --tw-gradient-from-position: ;
522
+ --tw-gradient-via-position: ;
523
+ --tw-gradient-to-position: ;
524
+ --tw-ordinal: ;
525
+ --tw-slashed-zero: ;
526
+ --tw-numeric-figure: ;
527
+ --tw-numeric-spacing: ;
528
+ --tw-numeric-fraction: ;
529
+ --tw-ring-inset: ;
530
+ --tw-ring-offset-width: 0px;
531
+ --tw-ring-offset-color: #fff;
532
+ --tw-ring-color: rgb(59 130 246 / 0.5);
533
+ --tw-ring-offset-shadow: 0 0 #0000;
534
+ --tw-ring-shadow: 0 0 #0000;
535
+ --tw-shadow: 0 0 #0000;
536
+ --tw-shadow-colored: 0 0 #0000;
537
+ --tw-blur: ;
538
+ --tw-brightness: ;
539
+ --tw-contrast: ;
540
+ --tw-grayscale: ;
541
+ --tw-hue-rotate: ;
542
+ --tw-invert: ;
543
+ --tw-saturate: ;
544
+ --tw-sepia: ;
545
+ --tw-drop-shadow: ;
546
+ --tw-backdrop-blur: ;
547
+ --tw-backdrop-brightness: ;
548
+ --tw-backdrop-contrast: ;
549
+ --tw-backdrop-grayscale: ;
550
+ --tw-backdrop-hue-rotate: ;
551
+ --tw-backdrop-invert: ;
552
+ --tw-backdrop-opacity: ;
553
+ --tw-backdrop-saturate: ;
554
+ --tw-backdrop-sepia: ;
555
+ --tw-contain-size: ;
556
+ --tw-contain-layout: ;
557
+ --tw-contain-paint: ;
558
+ --tw-contain-style: ;
559
+ }
560
+ .sr-only {
561
+ width: 1px;
562
+ height: 1px;
563
+ padding: 0;
564
+ margin: -1px;
565
+ overflow: hidden;
566
+ clip: rect(0, 0, 0, 0);
567
+ white-space: nowrap;
568
+ border-width: 0;
569
+ }
570
+ .pointer-events-none {
571
+ pointer-events: none;
572
+ }
573
+ .relative {
574
+ position: relative;
575
+ }
576
+ .inset-y-0 {
577
+ top: 0;
578
+ bottom: 0;
579
+ }
580
+ .left-0 {
581
+ left: 0;
582
+ }
583
+ .right-0 {
584
+ right: 0;
585
+ }
586
+ .z-50 {
587
+ z-index: 50;
588
+ }
589
+ .-mb-2 {
590
+ margin-bottom: -0.5rem;
591
+ }
592
+ .mr-1 {
593
+ margin-right: 0.25rem;
594
+ }
595
+ .mt-4 {
596
+ margin-top: 1rem;
597
+ }
598
+ .flex {
599
+ display: flex;
600
+ }
601
+ .inline-flex {
602
+ display: inline-flex;
603
+ }
604
+ .hidden {
605
+ display: none;
606
+ }
607
+ .h-10 {
608
+ height: 2.5rem;
609
+ }
610
+ .h-5 {
611
+ height: 1.25rem;
612
+ }
613
+ .max-h-60 {
614
+ max-height: 15rem;
615
+ }
616
+ .w-5 {
617
+ width: 1.25rem;
618
+ }
619
+ .w-6 {
620
+ width: 1.5rem;
621
+ }
622
+ .w-8 {
623
+ width: 2rem;
624
+ }
625
+ .w-full {
626
+ width: 100%;
627
+ }
628
+ .transform {
629
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate))
630
+ skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x))
631
+ scaleY(var(--tw-scale-y));
632
+ }
633
+ .select-none {
634
+ -webkit-user-select: none;
635
+ -moz-user-select: none;
636
+ user-select: none;
637
+ }
638
+ .grid-cols-1 {
639
+ grid-template-columns: repeat(1, minmax(0, 1fr));
640
+ grid-template-columns: 1fr;
641
+ }
642
+ .items-center {
643
+ align-items: center;
644
+ }
645
+ .gap-x-4 {
646
+ -moz-column-gap: 1rem;
647
+ column-gap: 1rem;
648
+ row-gap: 1rem;
649
+ }
650
+ .rounded-md {
651
+ border-radius: calc(var(--radius) - 2px);
652
+ }
653
+ .rounded-xl {
654
+ border-radius: 0.75rem;
655
+ }
656
+ .border {
657
+ border-width: 1px;
658
+ }
659
+ .border-gray-300 {
660
+ border-color: rgb(209 213 219 / var(--tw-border-opacity));
661
+ }
662
+ .bg-gray-100 {
663
+ --tw-bg-opacity: 1;
664
+ background-color: rgb(243 244 246 / var(--tw-bg-opacity));
665
+ }
666
+ .bg-indigo-500,
667
+ .hover\:bg-indigo-500:hover {
668
+ --tw-bg-opacity: 1;
669
+ background-color: rgb(99 102 241 / var(--tw-bg-opacity));
670
+ }
671
+ .bg-transparent {
672
+ background-color: transparent;
673
+ }
674
+ .bg-white {
675
+ --tw-bg-opacity: 1;
676
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
677
+ }
678
+ .px-1 {
679
+ padding-left: 0.25rem;
680
+ padding-right: 0.25rem;
681
+ }
682
+ .px-4 {
683
+ padding-left: 1rem;
684
+ padding-right: 1rem;
685
+ }
686
+ .py-0 {
687
+ padding-top: 0;
688
+ padding-bottom: 0;
689
+ }
690
+ .py-1 {
691
+ padding-top: 0.25rem;
692
+ padding-bottom: 0.25rem;
693
+ }
694
+ .py-1\.5 {
695
+ padding-top: 0.375rem;
696
+ padding-bottom: 0.375rem;
697
+ }
698
+ .py-2 {
699
+ padding-top: 0.5rem;
700
+ padding-bottom: 0.5rem;
701
+ }
702
+ .py-2\.5 {
703
+ padding-top: 0.625rem;
704
+ padding-bottom: 0.625rem;
705
+ }
706
+ .pl-10 {
707
+ padding-left: 2.5rem;
708
+ }
709
+ .pl-2 {
710
+ padding-left: 0.5rem;
711
+ }
712
+ .pl-3 {
713
+ padding-left: 0.75rem;
714
+ }
715
+ .pl-4 {
716
+ padding-left: 1rem;
717
+ }
718
+ .pr-1\.5 {
719
+ padding-right: 0.375rem;
720
+ }
721
+ .pr-20 {
722
+ padding-right: 5rem;
723
+ }
724
+ .pr-7 {
725
+ padding-right: 1.75rem;
726
+ }
727
+ .text-base {
728
+ font-size: 1rem;
729
+ line-height: 1.5rem;
730
+ }
731
+ .text-sm {
732
+ font-size: 0.875rem;
733
+ line-height: 1.25rem;
734
+ }
735
+ .text-xs {
736
+ font-size: 0.75rem;
737
+ line-height: 1rem;
738
+ }
739
+ .text-gray-500 {
740
+ color: rgb(107 114 128 / var(--tw-text-opacity));
741
+ }
742
+ .text-gray-900 {
743
+ color: rgb(17 24 39 / var(--tw-text-opacity));
744
+ }
745
+ .text-white {
746
+ color: rgb(255 255 255 / var(--tw-text-opacity));
747
+ }
748
+ .shadow-lg {
749
+ --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
750
+ --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color),
751
+ 0 4px 6px -4px var(--tw-shadow-color);
752
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
753
+ var(--tw-shadow);
754
+ }
755
+ .focus\:ring-2:focus,
756
+ .ring-1 {
757
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width)
758
+ var(--tw-ring-offset-color);
759
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
760
+ }
761
+ .ring-1 {
762
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width))
763
+ var(--tw-ring-color);
764
+ }
765
+ .ring-black {
766
+ --tw-ring-opacity: 1;
767
+ --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity));
768
+ }
769
+ .focus\:outline-none:focus {
770
+ outline: transparent solid 2px;
771
+ outline-offset: 2px;
772
+ }
773
+ .focus\:ring-2:focus {
774
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width))
775
+ var(--tw-ring-color);
776
+ }
777
+ .focus\:ring-inset:focus {
778
+ --tw-ring-inset: inset;
779
+ }
780
+ @media (min-width: 640px) {
781
+ .sm\:text-sm {
782
+ font-size: 0.875rem;
783
+ line-height: 1.25rem;
784
+ }
785
+ }
786
+ @media (min-width: 1024px) {
787
+ .lg\:col-span-2 {
788
+ grid-column: span 2 / span 2;
789
+ }
790
+ }
791
+ @keyframes swipe-out {
792
+ 0% {
793
+ transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));
794
+ opacity: 1;
795
+ }
796
+ to {
797
+ transform: translateY(
798
+ calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%)
799
+ );
800
+ opacity: 0;
801
+ }
802
+ }
803
+ .grid {
804
+ display: grid;
805
+ }
806
+ @media (min-width: 768px) {
807
+ .lg\:grid-cols-6 {
808
+ grid-template-columns: repeat(6, 1fr);
809
+ }
810
+ .lg\:col-span-6 {
811
+ grid-column-start: span 6;
812
+ }
813
+ .lg\:col-span-4 {
814
+ grid-column-start: span 4;
815
+ }
816
+ .lg\:col-span-2 {
817
+ grid-column-start: span 2;
818
+ }
819
+ }
820
+ .my-10 {
821
+ margin-top: 2.5rem;
822
+ margin-bottom: 2.5rem;
823
+ }
824
+ .justify-center {
825
+ justify-content: center;
826
+ }
827
+ .hover\:text-white:hover {
828
+ color: rgb(255 255 255 / var(--tw-text-opacity));
829
+ }
830
+ </style>
@@ -1,15 +1,4 @@
1
- declare const PlaceAutocomplete: import("svelte").Component<{
2
- PUBLIC_GOOGLE_MAPS_API_KEY: string;
3
- fetchFields?: string[];
4
- countries: {
5
- name: string;
6
- region: string;
7
- }[];
8
- placeholder?: string;
9
- language?: string;
10
- region?: string;
11
- autocomplete?: AutoFill;
12
- onResponse: (e: Event) => void;
13
- onError: (error: string) => void;
14
- }, {}, "PUBLIC_GOOGLE_MAPS_API_KEY" | "fetchFields" | "countries" | "placeholder" | "language" | "region" | "autocomplete" | "onResponse" | "onError">;
1
+ import type { Props } from './interfaces.js';
2
+ declare const PlaceAutocomplete: import("svelte").Component<Props, {}, "fetchFields" | "countries" | "onResponse" | "onError">;
3
+ type PlaceAutocomplete = ReturnType<typeof PlaceAutocomplete>;
15
4
  export default PlaceAutocomplete;
@@ -0,0 +1,7 @@
1
+ import type { RequestParams } from './interfaces.js';
2
+ export declare const requestParamsDefault: RequestParams;
3
+ /**
4
+ * Validate and cast request parameters
5
+ * @param requestParams
6
+ */
7
+ export declare const validateRequestParams: (requestParams: RequestParams) => RequestParams;
@@ -0,0 +1,183 @@
1
+ export const requestParamsDefault = {
2
+ /**
3
+ * @type string required
4
+ * The text string on which to search.
5
+ */
6
+ input: '',
7
+ /**
8
+ * @type Array<string>
9
+ * A Place is only returned if its primary type is included in this list.
10
+ * Up to 5 values can be specified.
11
+ * If no types are specified, all Place types are returned.
12
+ * https://developers.google.com/maps/documentation/javascript/place-types
13
+ *
14
+ * ['postal_code','premise','street_address','route']
15
+ * FY83DD 72
16
+ */
17
+ //includedPrimaryTypes: ['postal_code','premise','street_address','route'],
18
+ includedPrimaryTypes: [],
19
+ /**
20
+ * @type Array<string> optional
21
+ * Only include results in the specified regions, specified as up to 15 CLDR two-character region codes.
22
+ * An empty set will not restrict the results.
23
+ * If both locationRestriction and includedRegionCodes are set, the results will be located in the area of intersection.
24
+ */
25
+ includedRegionCodes: ['GB'],
26
+ /**
27
+ * @type number optional
28
+ * A zero-based Unicode character offset of input indicating the cursor position in input.
29
+ * The cursor position may influence what predictions are returned. If not specified, defaults to the length of input.
30
+ */
31
+ inputOffset: 0,
32
+ /**
33
+ * @type string optional
34
+ * The language in which to return results. Will default to the browser's language preference.
35
+ *
36
+ */
37
+ language: 'en-GB',
38
+ /**
39
+ * @type LocationBias optional
40
+ * Bias results to a specified location.
41
+ *
42
+ * At most one of locationBias or locationRestriction should be set.
43
+ * If neither are set, the results will be biased by IP address, meaning the IP address
44
+ * will be mapped to an imprecise location and used as a biasing signal.
45
+ */
46
+ locationBias: null,
47
+ /**
48
+ * @param LocationRestriction optional
49
+ * Restrict results to a specified location.
50
+ *
51
+ * At most one of locationBias or locationRestriction should be set.
52
+ * If neither are set, the results will be biased by IP address, meaning the IP address will
53
+ * be mapped to an imprecise location and used as a biasing signal.
54
+ */
55
+ locationRestriction: {
56
+ west: 0,
57
+ south: 0,
58
+ east: 0,
59
+ north: 0
60
+ },
61
+ /**
62
+ * @type LatLng|LatLngLiteral optional
63
+ * The point around which you wish to retrieve place information.
64
+ */
65
+ origin: {
66
+ lat: 0,
67
+ lng: 0
68
+ },
69
+ /**
70
+ * @type string optional
71
+ * The region code, specified as a CLDR two-character region code. T
72
+ * his affects address formatting, result ranking, and may influence what results are returned.
73
+ * This does not restrict results to the specified region.
74
+ */
75
+ region: 'GB',
76
+ /**
77
+ * @type AutocompleteSessionToken optional
78
+ *
79
+ * A token which identifies an Autocomplete session for billing purposes.
80
+ * Generate a new session token via AutocompleteSessionToken The session begins when the user starts typing a query, and concludes
81
+ * when they select a place and call Place.fetchFields. Each session can have multiple queries, followed by one fetchFields call.
82
+ * The credentials used for each request within a session must belong to the same Google Cloud Console project. Once a session has
83
+ * concluded, the token is no longer valid; your app must generate a fresh token for each session. If the sessionToken parameter is
84
+ * omitted, or if you reuse a session token, the session is charged as if no session token was provided (each request is billed
85
+ * separately).
86
+ *
87
+ * We recommend the following guidelines:
88
+ * Use session tokens for all Place Autocomplete calls.
89
+ * Generate a fresh token for each session.
90
+ * Be sure to pass a unique session token for each new session. Using the same token for more than one session will result in each
91
+ * request being billed individually.
92
+ */
93
+ sessionToken: ''
94
+ };
95
+ /**
96
+ * Validate and cast request parameters
97
+ * @param requestParams
98
+ */
99
+ export const validateRequestParams = (requestParams) => {
100
+ // https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
101
+ /**
102
+ * If requestParams is not an object, set it to an empty object
103
+ */
104
+ if (typeof requestParams !== 'object') {
105
+ requestParams = {
106
+ input: String(''),
107
+ sessionToken: String(''),
108
+ };
109
+ return requestParams;
110
+ }
111
+ // Remove any keys that are not in requestParamsDefault
112
+ for (const key in requestParams) {
113
+ if (!(key in requestParamsDefault)) {
114
+ delete requestParams[key];
115
+ }
116
+ }
117
+ // Reset sessionToken to empty string if passed to the component
118
+ if (requestParams.sessionToken) {
119
+ requestParams.sessionToken = String('');
120
+ }
121
+ /**
122
+ * If requestParams.input is not a string, set it to an empty string
123
+ */
124
+ if (typeof requestParams.input !== 'string') {
125
+ requestParams.input = String('');
126
+ }
127
+ /**
128
+ * If requestParams.includedPrimaryTypes is not an array or is an empty array, remove it
129
+ * If requestParams.includedPrimaryTypes is an array and has more than 5 items, slice it to 5
130
+ */
131
+ if (!Array.isArray(requestParams.includedPrimaryTypes)
132
+ || (Array.isArray(requestParams.includedPrimaryTypes) && requestParams.includedPrimaryTypes.length === 0)) {
133
+ delete requestParams.includedPrimaryTypes;
134
+ }
135
+ else if (Array.isArray(requestParams.includedPrimaryTypes) && requestParams.includedPrimaryTypes.length > 5) {
136
+ requestParams.includedPrimaryTypes = requestParams.includedPrimaryTypes.slice(0, 5);
137
+ }
138
+ /**
139
+ * If requestParams.includedRegionCodes is not an array or is an empty array, remove it
140
+ * If requestParams.includedRegionCodes is an array and has more than 15 items, slice it to 15
141
+ */
142
+ if (!Array.isArray(requestParams.includedRegionCodes)
143
+ || (Array.isArray(requestParams.includedPrimaryTypes) && requestParams.includedPrimaryTypes.length === 0)) {
144
+ delete requestParams.includedRegionCodes;
145
+ }
146
+ else if (Array.isArray(requestParams.includedRegionCodes) && requestParams.includedRegionCodes.length > 15) {
147
+ requestParams.includedRegionCodes = requestParams.includedRegionCodes.slice(0, 15);
148
+ }
149
+ /**
150
+ * If requestParams.inputOffset is not a number or is less than 1, remove it
151
+ */
152
+ if (typeof requestParams.inputOffset !== 'number'
153
+ || (typeof requestParams.inputOffset === 'number' && requestParams.inputOffset < 1)) {
154
+ delete requestParams.inputOffset;
155
+ }
156
+ // If language is not a string, remove it
157
+ if (typeof requestParams.language !== 'string') {
158
+ delete requestParams.language;
159
+ }
160
+ // If locationBias is not a string, remove it
161
+ if (typeof requestParams.locationBias !== 'string') {
162
+ delete requestParams.locationBias;
163
+ }
164
+ /**
165
+ * If locationRestriction is not set, remove it
166
+ */
167
+ if (requestParams.locationRestriction?.east === 0
168
+ && requestParams.locationRestriction?.north === 0
169
+ && requestParams.locationRestriction?.south === 0
170
+ && requestParams.locationRestriction?.west === 0) {
171
+ delete requestParams.locationRestriction;
172
+ }
173
+ // If origin is not set, remove it
174
+ if (requestParams.origin?.lat === 0 && requestParams.origin?.lng === 0) {
175
+ delete requestParams.origin;
176
+ }
177
+ // If region is not a string, remove it
178
+ if (typeof requestParams.region !== 'string') {
179
+ delete requestParams.region;
180
+ }
181
+ //console.log('requestParams:', Object.keys(requestParams));
182
+ return requestParams;
183
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @interface RequestParams
3
+ * https://developers.google.com/maps/documentation/javascript/reference/autocomplete-data
4
+ */
5
+ export interface RequestParams {
6
+ input: string;
7
+ includedPrimaryTypes?: string[];
8
+ includedRegionCodes?: string[];
9
+ inputOffset?: number;
10
+ language?: string;
11
+ locationBias?: unknown | string;
12
+ locationRestriction?: {
13
+ west: number;
14
+ south: number;
15
+ east: number;
16
+ north: number;
17
+ };
18
+ origin?: {
19
+ lat: number;
20
+ lng: number;
21
+ };
22
+ region?: string;
23
+ sessionToken?: string;
24
+ }
25
+ export interface Props {
26
+ PUBLIC_GOOGLE_MAPS_API_KEY: string;
27
+ fetchFields?: string[];
28
+ countries: {
29
+ name: string;
30
+ region: string;
31
+ }[];
32
+ placeholder?: string;
33
+ language?: string;
34
+ region?: string;
35
+ autocompete?: AutoFill;
36
+ requestParams: RequestParams;
37
+ onResponse: (e: Event) => void;
38
+ onError: (error: string) => void;
39
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "places-autocomplete-svelte",
3
3
  "license": "MIT",
4
- "version": "2.0.9",
4
+ "version": "2.1.0",
5
5
  "description": "A lightweight and customizable Svelte component for easy integration of Google Maps Places Autocomplete (New API) in your Svelte/SvelteKit applications. Provides accessible autocomplete suggestions and detailed address retrieval.",
6
6
  "keywords": [
7
7
  "svelte",
@@ -15,10 +15,10 @@
15
15
  "address",
16
16
  "address-input",
17
17
  "input",
18
- "search",
18
+ "search",
19
19
  "new api"
20
20
  ],
21
- "homepage": "https://github.com/alexpechkarev",
21
+ "homepage": "https://places-autocomplete-demo.pages.dev",
22
22
  "repository": {
23
23
  "type": "git",
24
24
  "url": "git+https://github.com/alexpechkarev/places-autocomplete-svelte.git"
@@ -69,26 +69,26 @@
69
69
  "devDependencies": {
70
70
  "@sveltejs/adapter-auto": "^3.3.1",
71
71
  "@sveltejs/adapter-cloudflare": "^4.7.4",
72
- "@sveltejs/kit": "^2.7.3",
72
+ "@sveltejs/kit": "^2.8.0",
73
73
  "@sveltejs/package": "^2.3.7",
74
74
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
75
75
  "@tailwindcss/typography": "^0.5.15",
76
76
  "@types/eslint": "^9.6.1",
77
77
  "autoprefixer": "^10.4.20",
78
- "eslint": "^9.13.0",
78
+ "eslint": "^9.14.0",
79
79
  "eslint-config-prettier": "^9.1.0",
80
80
  "eslint-plugin-svelte": "^2.46.0",
81
- "globals": "^15.11.0",
81
+ "globals": "^15.12.0",
82
82
  "postcss": "^8.4.47",
83
83
  "prettier": "^3.3.3",
84
84
  "prettier-plugin-svelte": "^3.2.7",
85
85
  "publint": "^0.2.12",
86
- "svelte": "^5.1.4",
87
- "svelte-check": "^4.0.5",
86
+ "svelte": "^5.1.12",
87
+ "svelte-check": "^4.0.6",
88
88
  "tailwindcss": "^3.4.14",
89
- "tslib": "^2.8.0",
89
+ "tslib": "^2.8.1",
90
90
  "typescript": "^5.6.3",
91
- "typescript-eslint": "^8.12.2",
91
+ "typescript-eslint": "^8.13.0",
92
92
  "vite": "^5.4.10"
93
93
  },
94
94
  "svelte": "./dist/index.js",