@pro6pp/infer-core 0.0.2-beta.5 → 0.0.2-beta.6
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/dist/index.cjs +3 -6
- package/dist/index.d.cts +90 -31
- package/dist/index.d.ts +90 -31
- package/dist/index.global.js +4 -7
- package/dist/index.js +3 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var c=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var
|
|
1
|
+
"use strict";var c=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var C=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var T=(n,e)=>{for(var t in e)c(n,t,{get:e[t],enumerable:!0})},w=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of v(e))!I.call(n,i)&&i!==t&&c(n,i,{get:()=>e[i],enumerable:!(s=y(e,i))||s.enumerable});return n};var _=n=>w(c({},"__esModule",{value:!0}),n);var o=(n,e,t)=>C(n,typeof e!="symbol"?e+"":e,t);var F={};T(F,{DEFAULT_STYLES:()=>b,INITIAL_STATE:()=>h,InferCore:()=>g});module.exports=_(F);var d={API_URL:"https://api.pro6pp.nl/v2",LIMIT:1e3,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50},m={DIGITS_1_3:/^[0-9]{1,3}$/},h={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,isError:!1,isLoading:!1,selectedSuggestionIndex:-1},g=class{constructor(e){o(this,"country");o(this,"authKey");o(this,"apiUrl");o(this,"limit");o(this,"fetcher");o(this,"onStateChange");o(this,"onSelect");o(this,"state");o(this,"abortController",null);o(this,"debouncedFetch");o(this,"isSelecting",!1);this.country=e.country,this.authKey=e.authKey,this.apiUrl=e.apiUrl||d.API_URL,this.limit=e.limit||d.LIMIT,this.fetcher=e.fetcher||((i,r)=>fetch(i,r)),this.onStateChange=e.onStateChange||(()=>{}),this.onSelect=e.onSelect||(()=>{}),this.state={...h};let t=e.debounceMs!==void 0?e.debounceMs:d.DEBOUNCE_MS,s=Math.max(t,d.MIN_DEBOUNCE_MS);this.debouncedFetch=this.debounce(i=>this.executeFetch(i),s)}handleInput(e){if(this.isSelecting){this.isSelecting=!1;return}let t=this.state.stage==="final"&&e!==this.state.query;this.updateState({query:e,isValid:!1,isLoading:!!e.trim(),selectedSuggestionIndex:-1}),t&&this.onSelect(null),this.debouncedFetch(e)}handleKeyDown(e){let t=e.target;if(!t)return;let s=this.state.cities.length+this.state.streets.length+this.state.suggestions.length;if(s>0){if(e.key==="ArrowDown"){e.preventDefault();let r=this.state.selectedSuggestionIndex+1;r>=s&&(r=0),this.updateState({selectedSuggestionIndex:r});return}if(e.key==="ArrowUp"){e.preventDefault();let r=this.state.selectedSuggestionIndex-1;r<0&&(r=s-1),this.updateState({selectedSuggestionIndex:r});return}if(e.key==="Enter"&&this.state.selectedSuggestionIndex>=0){e.preventDefault();let a=[...this.state.cities,...this.state.streets,...this.state.suggestions][this.state.selectedSuggestionIndex];a&&(this.selectItem(a),this.updateState({selectedSuggestionIndex:-1}));return}}let i=t.value;if(e.key===" "&&this.shouldAutoInsertComma(i)){e.preventDefault();let r=`${i.trim()}, `;this.updateQueryAndFetch(r)}}selectItem(e){this.debouncedFetch.cancel(),this.abortController&&this.abortController.abort();let t=typeof e=="string"?e:e.label,s=t;typeof e!="string"&&typeof e.value=="string"&&(s=e.value);let i=typeof e!="string"&&typeof e.value=="object"?e.value:void 0,r=!!i&&Object.keys(i).length>0;if(this.isSelecting=!0,this.state.stage==="final"||r){let p=t;if(i&&Object.keys(i).length>0){let{street:l,street_number:u,house_number:x,city:f}=i,S=u||x;l&&S&&f&&(p=`${l} ${S}, ${f}`)}this.finishSelection(p,i);return}let a=typeof e!="string"?e.subtitle:null;this.processSelection(s,a)}shouldAutoInsertComma(e){if(!e.includes(",")&&m.DIGITS_1_3.test(e.trim()))return!0;if(this.state.stage==="house_number"){let s=this.getCurrentFragment(e);return m.DIGITS_1_3.test(s)}return!1}finishSelection(e,t){this.updateState({query:e,suggestions:[],cities:[],streets:[],isValid:!0,stage:"final"}),this.onSelect(t||e),setTimeout(()=>{this.isSelecting=!1},0)}processSelection(e,t){let{stage:s,query:i}=this.state,r=i;if(t&&(s==="city"||s==="street"||s==="mixed")){if(s==="city")r=`${t}, ${e}, `;else{let u=this.getQueryPrefix(i);r=u?`${u} ${e}, ${t}, `:`${e}, ${t}, `}this.updateQueryAndFetch(r);return}if(s==="direct"||s==="addition"){this.finishSelection(e);return}!i.includes(",")&&(s==="city"||s==="street"||s==="house_number_first")?r=`${e}, `:(r=this.replaceLastSegment(i,e),s!=="house_number"&&(r+=", ")),this.updateQueryAndFetch(r)}executeFetch(e){let t=(e||"").toString();if(!t.trim()){this.abortController?.abort(),this.resetState();return}this.updateState({isError:!1}),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;let s=new URL(`${this.apiUrl}/infer/${this.country.toLowerCase()}`),i={authKey:this.authKey,query:t,limit:this.limit.toString()};s.search=new URLSearchParams(i).toString(),this.fetcher(s.toString(),{signal:this.abortController.signal}).then(r=>{if(!r.ok)throw new Error("Network error");return r.json()}).then(r=>this.mapResponseToState(r)).catch(r=>{r.name!=="AbortError"&&this.updateState({isError:!0,isLoading:!1})})}mapResponseToState(e){let t={stage:e.stage,isLoading:!1},s=!1,i=null,r=e.suggestions||[],a=[],p=new Set;for(let l of r){let u=`${l.label}|${l.subtitle||""}|${JSON.stringify(l.value||{})}`;p.has(u)||(p.add(u),a.push(l))}if(e.stage==="mixed"?(t.cities=e.cities||[],t.streets=e.streets||[],t.suggestions=[]):(t.suggestions=a,t.cities=[],t.streets=[],e.stage==="final"&&a.length===1&&(s=!0,i=a[0])),t.isValid=e.stage==="final",s&&i){t.query=i.label,t.suggestions=[],t.cities=[],t.streets=[],t.isValid=!0,this.updateState(t);let l=typeof i.value=="object"?i.value:i.label;this.onSelect(l)}else this.updateState(t)}updateQueryAndFetch(e){this.updateState({query:e,suggestions:[],cities:[],streets:[]}),this.updateState({isLoading:!0,isValid:!1}),this.debouncedFetch(e),setTimeout(()=>{this.isSelecting=!1},0)}replaceLastSegment(e,t){let s=e.lastIndexOf(",");return s===-1?t:`${e.slice(0,s+1)} ${t}`.trim()}getQueryPrefix(e){let t=e.lastIndexOf(",");return t===-1?"":e.slice(0,t+1).trimEnd()}getCurrentFragment(e){return(e.split(",").slice(-1)[0]??"").trim()}resetState(){this.updateState({...h,query:this.state.query})}updateState(e){this.state={...this.state,...e},this.onStateChange(this.state)}debounce(e,t){let s,i=(...r)=>{s&&clearTimeout(s),s=setTimeout(()=>e.apply(this,r),t)};return i.cancel=()=>{s&&(clearTimeout(s),s=void 0)},i}};var b=`
|
|
2
2
|
.pro6pp-wrapper {
|
|
3
3
|
position: relative;
|
|
4
4
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -38,21 +38,18 @@
|
|
|
38
38
|
list-style: none !important;
|
|
39
39
|
padding: 0 !important;
|
|
40
40
|
margin: 0 !important;
|
|
41
|
-
overflow: hidden;
|
|
42
41
|
}
|
|
43
42
|
.pro6pp-item {
|
|
44
|
-
padding:
|
|
43
|
+
padding: 10px 16px;
|
|
45
44
|
cursor: pointer;
|
|
46
45
|
display: flex;
|
|
47
46
|
flex-direction: row;
|
|
48
47
|
align-items: center;
|
|
49
48
|
color: #000000;
|
|
50
49
|
font-size: 14px;
|
|
51
|
-
line-height: 1;
|
|
50
|
+
line-height: 1.2;
|
|
52
51
|
white-space: nowrap;
|
|
53
52
|
overflow: hidden;
|
|
54
|
-
border-radius: 0 !important;
|
|
55
|
-
margin: 0 !important;
|
|
56
53
|
}
|
|
57
54
|
.pro6pp-item:hover, .pro6pp-item--active {
|
|
58
55
|
background-color: #f5f5f5;
|
package/dist/index.d.cts
CHANGED
|
@@ -4,106 +4,139 @@
|
|
|
4
4
|
type CountryCode = 'NL' | 'DE';
|
|
5
5
|
/**
|
|
6
6
|
* The current step in the address inference process.
|
|
7
|
+
* - `empty`: No input yet.
|
|
8
|
+
* - `mixed`: User is prompted to choose between cities and streets.
|
|
9
|
+
* - `street`: User is selecting a street.
|
|
10
|
+
* - `city`: User is selecting a city.
|
|
11
|
+
* - `postcode`: User is entering a postcode.
|
|
12
|
+
* - `house_number`: User is entering a house number.
|
|
13
|
+
* - `house_number_first`: Specialized mode where number is entered before street.
|
|
14
|
+
* - `addition`: Selecting a house number addition (e.g., 'A', 'III').
|
|
15
|
+
* - `direct`: Direct address hit (often via postcode).
|
|
16
|
+
* - `final`: A complete, valid address has been identified.
|
|
7
17
|
*/
|
|
8
18
|
type Stage = 'empty' | 'mixed' | 'street' | 'city' | 'postcode' | 'house_number' | 'house_number_first' | 'addition' | 'direct' | 'final';
|
|
9
19
|
/**
|
|
10
|
-
* The standardized address object returned upon selection.
|
|
20
|
+
* The standardized address object returned upon a successful final selection.
|
|
11
21
|
*/
|
|
12
22
|
interface AddressValue {
|
|
23
|
+
/** The name of the street. */
|
|
13
24
|
street: string;
|
|
25
|
+
/** The name of the city/locality. */
|
|
14
26
|
city: string;
|
|
27
|
+
/** The house number (formatted). */
|
|
15
28
|
street_number?: string | number;
|
|
29
|
+
/** The house number (numeric part). */
|
|
16
30
|
house_number?: string | number;
|
|
31
|
+
/** The postal code. */
|
|
17
32
|
postcode?: string;
|
|
33
|
+
/** The full postal code including letters (country-specific). */
|
|
18
34
|
postcode_full?: string;
|
|
35
|
+
/** The house number addition or suffix. */
|
|
19
36
|
addition?: string;
|
|
20
37
|
/** Allow for extra fields if API expands. */
|
|
21
38
|
[key: string]: unknown;
|
|
22
39
|
}
|
|
23
40
|
/**
|
|
24
|
-
* A single item in the
|
|
41
|
+
* A single item in the suggestion list.
|
|
25
42
|
*/
|
|
26
43
|
interface InferResult {
|
|
27
|
-
/** The text to display in the UI (e.g. "
|
|
44
|
+
/** The text to display in the UI (e.g. "Main Street"). */
|
|
28
45
|
label: string;
|
|
29
|
-
/** The actual address data.
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
/** The actual address data.
|
|
47
|
+
* Only present if this result completes an address or represents a specific entity.
|
|
48
|
+
*/
|
|
49
|
+
value?: AddressValue | string;
|
|
50
|
+
/** Secondary information (e.g., city name when suggesting a street). */
|
|
32
51
|
subtitle?: string | null;
|
|
33
|
-
/** Number of underlying results
|
|
52
|
+
/** Number of underlying results found for this suggestion. */
|
|
34
53
|
count?: number | string;
|
|
35
54
|
}
|
|
36
55
|
/**
|
|
37
|
-
* The complete state
|
|
56
|
+
* The complete UI state managed by InferCore.
|
|
38
57
|
*/
|
|
39
58
|
interface InferState {
|
|
40
|
-
/** The current value of the input
|
|
59
|
+
/** The current text value of the search input. */
|
|
41
60
|
query: string;
|
|
42
|
-
/** The current
|
|
61
|
+
/** The current logical stage of the address lookup. */
|
|
43
62
|
stage: Stage | null;
|
|
44
|
-
/** List of
|
|
63
|
+
/** List of city suggestions (used in `mixed` stage). */
|
|
45
64
|
cities: InferResult[];
|
|
46
|
-
/** List of
|
|
65
|
+
/** List of street suggestions (used in `mixed` stage). */
|
|
47
66
|
streets: InferResult[];
|
|
48
|
-
/** General suggestions
|
|
67
|
+
/** General list of suggestions for the current stage. */
|
|
49
68
|
suggestions: InferResult[];
|
|
50
|
-
/**
|
|
69
|
+
/** Flag indicating if the current selection is a complete, valid address. */
|
|
51
70
|
isValid: boolean;
|
|
52
|
-
/**
|
|
71
|
+
/** Flag indicating if the last API request failed. */
|
|
53
72
|
isError: boolean;
|
|
54
|
-
/**
|
|
73
|
+
/** Flag indicating if a network request is currently in progress. */
|
|
55
74
|
isLoading: boolean;
|
|
56
75
|
/**
|
|
57
|
-
* The index of the currently highlighted suggestion
|
|
58
|
-
* -
|
|
59
|
-
* -
|
|
76
|
+
* The index of the currently highlighted suggestion.
|
|
77
|
+
* - `0` to `n`: An item is highlighted via keyboard navigation.
|
|
78
|
+
* - `-1`: No item is highlighted.
|
|
60
79
|
*/
|
|
61
80
|
selectedSuggestionIndex: number;
|
|
62
81
|
}
|
|
63
82
|
/**
|
|
64
|
-
* Custom fetch implementation, compatible with
|
|
65
|
-
* Useful for
|
|
83
|
+
* Custom fetch implementation, compatible with the Web Fetch API.
|
|
84
|
+
* Useful for Node.js environments or proxying requests.
|
|
66
85
|
*/
|
|
67
86
|
type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
68
87
|
/**
|
|
69
|
-
* Configuration for Infer
|
|
88
|
+
* Configuration options for the Infer engine.
|
|
70
89
|
*/
|
|
71
90
|
interface InferConfig {
|
|
72
91
|
/**
|
|
73
|
-
* Pro6PP Authorization Key.
|
|
92
|
+
* Your Pro6PP Authorization Key.
|
|
74
93
|
*/
|
|
75
94
|
authKey: string;
|
|
76
95
|
/**
|
|
77
|
-
*
|
|
96
|
+
* The country to perform address lookups in.
|
|
78
97
|
*/
|
|
79
98
|
country: CountryCode;
|
|
80
99
|
/**
|
|
81
100
|
* Base URL for the Pro6PP API.
|
|
82
|
-
* Useful for proxying requests through your own backend.
|
|
83
101
|
* @default 'https://api.pro6pp.nl/v2'
|
|
84
102
|
*/
|
|
85
103
|
apiUrl?: string;
|
|
86
104
|
/**
|
|
87
|
-
* Custom fetch implementation.
|
|
88
|
-
*
|
|
105
|
+
* Custom fetch implementation for network requests.
|
|
106
|
+
* @default window.fetch
|
|
89
107
|
*/
|
|
90
108
|
fetcher?: Fetcher;
|
|
91
109
|
/**
|
|
92
|
-
* Maximum number of
|
|
110
|
+
* Maximum number of suggestions to request from the API.
|
|
93
111
|
* @default 1000
|
|
94
112
|
*/
|
|
95
113
|
limit?: number;
|
|
96
114
|
/**
|
|
97
|
-
*
|
|
115
|
+
* The delay in milliseconds before performing the API search.
|
|
116
|
+
* Note: A lower bound of 50ms is enforced to protect API stability.
|
|
117
|
+
* @default 150
|
|
118
|
+
*/
|
|
119
|
+
debounceMs?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Callback triggered whenever the internal state (suggestions, loading status, etc.) updates.
|
|
98
122
|
*/
|
|
99
123
|
onStateChange?: (state: InferState) => void;
|
|
100
124
|
/**
|
|
101
|
-
* Callback
|
|
125
|
+
* Callback triggered when a user selects an item.
|
|
126
|
+
* If the address is complete, returns an `AddressValue` object.
|
|
127
|
+
* If the selection is partial, returns a `string`.
|
|
102
128
|
*/
|
|
103
129
|
onSelect?: (selection: AddressValue | string | null) => void;
|
|
104
130
|
}
|
|
105
131
|
|
|
132
|
+
/**
|
|
133
|
+
* The initial state of the address inference engine.
|
|
134
|
+
*/
|
|
106
135
|
declare const INITIAL_STATE: InferState;
|
|
136
|
+
/**
|
|
137
|
+
* The core logic engine for Pro6PP Infer.
|
|
138
|
+
* Manages API communication, state transitions, and keyboard interaction logic.
|
|
139
|
+
*/
|
|
107
140
|
declare class InferCore {
|
|
108
141
|
private country;
|
|
109
142
|
private authKey;
|
|
@@ -112,17 +145,43 @@ declare class InferCore {
|
|
|
112
145
|
private fetcher;
|
|
113
146
|
private onStateChange;
|
|
114
147
|
private onSelect;
|
|
148
|
+
/**
|
|
149
|
+
* The current read-only state of the engine.
|
|
150
|
+
* Use `onStateChange` to react to updates.
|
|
151
|
+
*/
|
|
115
152
|
state: InferState;
|
|
116
153
|
private abortController;
|
|
117
154
|
private debouncedFetch;
|
|
118
155
|
private isSelecting;
|
|
156
|
+
/**
|
|
157
|
+
* Initializes a new instance of the Infer engine.
|
|
158
|
+
* @param config The configuration object including API keys and callbacks.
|
|
159
|
+
*/
|
|
119
160
|
constructor(config: InferConfig);
|
|
161
|
+
/**
|
|
162
|
+
* Processes new text input from the user.
|
|
163
|
+
* Triggers a debounced API request and updates the internal state.
|
|
164
|
+
* @param value The raw string from the input field.
|
|
165
|
+
*/
|
|
120
166
|
handleInput(value: string): void;
|
|
167
|
+
/**
|
|
168
|
+
* Handles keyboard events for the input field.
|
|
169
|
+
* Supports:
|
|
170
|
+
* - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
|
|
171
|
+
* - `Enter`: Select the currently highlighted suggestion.
|
|
172
|
+
* - `Space`: Automatically inserts a comma if a numeric house number is detected.
|
|
173
|
+
* @param event The keyboard event from the input element.
|
|
174
|
+
*/
|
|
121
175
|
handleKeyDown(event: KeyboardEvent | {
|
|
122
176
|
key: string;
|
|
123
177
|
target: EventTarget | null;
|
|
124
178
|
preventDefault: () => void;
|
|
125
179
|
}): void;
|
|
180
|
+
/**
|
|
181
|
+
* Manually selects a suggestion or a string value.
|
|
182
|
+
* This is typically called when a user clicks a suggestion in the UI.
|
|
183
|
+
* @param item The suggestion object or string to select.
|
|
184
|
+
*/
|
|
126
185
|
selectItem(item: InferResult | string): void;
|
|
127
186
|
private shouldAutoInsertComma;
|
|
128
187
|
private finishSelection;
|
|
@@ -138,6 +197,6 @@ declare class InferCore {
|
|
|
138
197
|
private debounce;
|
|
139
198
|
}
|
|
140
199
|
|
|
141
|
-
declare const DEFAULT_STYLES = "\n .pro6pp-wrapper {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n box-sizing: border-box;\n width: 100%;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1.5;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .pro6pp-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 9999;\n margin-top: 4px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n max-height: 300px;\n overflow-y: auto;\n list-style: none !important;\n padding: 0 !important;\n margin: 0 !important;\n
|
|
200
|
+
declare const DEFAULT_STYLES = "\n .pro6pp-wrapper {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n box-sizing: border-box;\n width: 100%;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1.5;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .pro6pp-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 9999;\n margin-top: 4px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n max-height: 300px;\n overflow-y: auto;\n list-style: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n .pro6pp-item {\n padding: 10px 16px;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n align-items: center;\n color: #000000;\n font-size: 14px;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n }\n .pro6pp-item:hover, .pro6pp-item--active {\n background-color: #f5f5f5;\n }\n .pro6pp-item__label {\n font-weight: 500;\n flex-shrink: 0;\n }\n .pro6pp-item__subtitle {\n font-size: 14px;\n color: #404040;\n overflow: hidden;\n text-overflow: ellipsis;\n flex-shrink: 1;\n }\n .pro6pp-item__chevron {\n margin-left: auto;\n display: flex;\n align-items: center;\n color: #a3a3a3;\n padding-left: 8px;\n }\n .pro6pp-no-results {\n padding: 12px;\n color: #555555;\n font-size: 14px;\n text-align: center;\n user-select: none;\n pointer-events: none;\n }\n .pro6pp-loader {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n width: 16px;\n height: 16px;\n border: 2px solid #e0e0e0;\n border-top-color: #404040;\n border-radius: 50%;\n animation: pro6pp-spin 0.6s linear infinite;\n pointer-events: none;\n }\n @keyframes pro6pp-spin {\n to { transform: translateY(-50%) rotate(360deg); }\n }\n";
|
|
142
201
|
|
|
143
202
|
export { type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, INITIAL_STATE, type InferConfig, InferCore, type InferResult, type InferState, type Stage };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,106 +4,139 @@
|
|
|
4
4
|
type CountryCode = 'NL' | 'DE';
|
|
5
5
|
/**
|
|
6
6
|
* The current step in the address inference process.
|
|
7
|
+
* - `empty`: No input yet.
|
|
8
|
+
* - `mixed`: User is prompted to choose between cities and streets.
|
|
9
|
+
* - `street`: User is selecting a street.
|
|
10
|
+
* - `city`: User is selecting a city.
|
|
11
|
+
* - `postcode`: User is entering a postcode.
|
|
12
|
+
* - `house_number`: User is entering a house number.
|
|
13
|
+
* - `house_number_first`: Specialized mode where number is entered before street.
|
|
14
|
+
* - `addition`: Selecting a house number addition (e.g., 'A', 'III').
|
|
15
|
+
* - `direct`: Direct address hit (often via postcode).
|
|
16
|
+
* - `final`: A complete, valid address has been identified.
|
|
7
17
|
*/
|
|
8
18
|
type Stage = 'empty' | 'mixed' | 'street' | 'city' | 'postcode' | 'house_number' | 'house_number_first' | 'addition' | 'direct' | 'final';
|
|
9
19
|
/**
|
|
10
|
-
* The standardized address object returned upon selection.
|
|
20
|
+
* The standardized address object returned upon a successful final selection.
|
|
11
21
|
*/
|
|
12
22
|
interface AddressValue {
|
|
23
|
+
/** The name of the street. */
|
|
13
24
|
street: string;
|
|
25
|
+
/** The name of the city/locality. */
|
|
14
26
|
city: string;
|
|
27
|
+
/** The house number (formatted). */
|
|
15
28
|
street_number?: string | number;
|
|
29
|
+
/** The house number (numeric part). */
|
|
16
30
|
house_number?: string | number;
|
|
31
|
+
/** The postal code. */
|
|
17
32
|
postcode?: string;
|
|
33
|
+
/** The full postal code including letters (country-specific). */
|
|
18
34
|
postcode_full?: string;
|
|
35
|
+
/** The house number addition or suffix. */
|
|
19
36
|
addition?: string;
|
|
20
37
|
/** Allow for extra fields if API expands. */
|
|
21
38
|
[key: string]: unknown;
|
|
22
39
|
}
|
|
23
40
|
/**
|
|
24
|
-
* A single item in the
|
|
41
|
+
* A single item in the suggestion list.
|
|
25
42
|
*/
|
|
26
43
|
interface InferResult {
|
|
27
|
-
/** The text to display in the UI (e.g. "
|
|
44
|
+
/** The text to display in the UI (e.g. "Main Street"). */
|
|
28
45
|
label: string;
|
|
29
|
-
/** The actual address data.
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
/** The actual address data.
|
|
47
|
+
* Only present if this result completes an address or represents a specific entity.
|
|
48
|
+
*/
|
|
49
|
+
value?: AddressValue | string;
|
|
50
|
+
/** Secondary information (e.g., city name when suggesting a street). */
|
|
32
51
|
subtitle?: string | null;
|
|
33
|
-
/** Number of underlying results
|
|
52
|
+
/** Number of underlying results found for this suggestion. */
|
|
34
53
|
count?: number | string;
|
|
35
54
|
}
|
|
36
55
|
/**
|
|
37
|
-
* The complete state
|
|
56
|
+
* The complete UI state managed by InferCore.
|
|
38
57
|
*/
|
|
39
58
|
interface InferState {
|
|
40
|
-
/** The current value of the input
|
|
59
|
+
/** The current text value of the search input. */
|
|
41
60
|
query: string;
|
|
42
|
-
/** The current
|
|
61
|
+
/** The current logical stage of the address lookup. */
|
|
43
62
|
stage: Stage | null;
|
|
44
|
-
/** List of
|
|
63
|
+
/** List of city suggestions (used in `mixed` stage). */
|
|
45
64
|
cities: InferResult[];
|
|
46
|
-
/** List of
|
|
65
|
+
/** List of street suggestions (used in `mixed` stage). */
|
|
47
66
|
streets: InferResult[];
|
|
48
|
-
/** General suggestions
|
|
67
|
+
/** General list of suggestions for the current stage. */
|
|
49
68
|
suggestions: InferResult[];
|
|
50
|
-
/**
|
|
69
|
+
/** Flag indicating if the current selection is a complete, valid address. */
|
|
51
70
|
isValid: boolean;
|
|
52
|
-
/**
|
|
71
|
+
/** Flag indicating if the last API request failed. */
|
|
53
72
|
isError: boolean;
|
|
54
|
-
/**
|
|
73
|
+
/** Flag indicating if a network request is currently in progress. */
|
|
55
74
|
isLoading: boolean;
|
|
56
75
|
/**
|
|
57
|
-
* The index of the currently highlighted suggestion
|
|
58
|
-
* -
|
|
59
|
-
* -
|
|
76
|
+
* The index of the currently highlighted suggestion.
|
|
77
|
+
* - `0` to `n`: An item is highlighted via keyboard navigation.
|
|
78
|
+
* - `-1`: No item is highlighted.
|
|
60
79
|
*/
|
|
61
80
|
selectedSuggestionIndex: number;
|
|
62
81
|
}
|
|
63
82
|
/**
|
|
64
|
-
* Custom fetch implementation, compatible with
|
|
65
|
-
* Useful for
|
|
83
|
+
* Custom fetch implementation, compatible with the Web Fetch API.
|
|
84
|
+
* Useful for Node.js environments or proxying requests.
|
|
66
85
|
*/
|
|
67
86
|
type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
68
87
|
/**
|
|
69
|
-
* Configuration for Infer
|
|
88
|
+
* Configuration options for the Infer engine.
|
|
70
89
|
*/
|
|
71
90
|
interface InferConfig {
|
|
72
91
|
/**
|
|
73
|
-
* Pro6PP Authorization Key.
|
|
92
|
+
* Your Pro6PP Authorization Key.
|
|
74
93
|
*/
|
|
75
94
|
authKey: string;
|
|
76
95
|
/**
|
|
77
|
-
*
|
|
96
|
+
* The country to perform address lookups in.
|
|
78
97
|
*/
|
|
79
98
|
country: CountryCode;
|
|
80
99
|
/**
|
|
81
100
|
* Base URL for the Pro6PP API.
|
|
82
|
-
* Useful for proxying requests through your own backend.
|
|
83
101
|
* @default 'https://api.pro6pp.nl/v2'
|
|
84
102
|
*/
|
|
85
103
|
apiUrl?: string;
|
|
86
104
|
/**
|
|
87
|
-
* Custom fetch implementation.
|
|
88
|
-
*
|
|
105
|
+
* Custom fetch implementation for network requests.
|
|
106
|
+
* @default window.fetch
|
|
89
107
|
*/
|
|
90
108
|
fetcher?: Fetcher;
|
|
91
109
|
/**
|
|
92
|
-
* Maximum number of
|
|
110
|
+
* Maximum number of suggestions to request from the API.
|
|
93
111
|
* @default 1000
|
|
94
112
|
*/
|
|
95
113
|
limit?: number;
|
|
96
114
|
/**
|
|
97
|
-
*
|
|
115
|
+
* The delay in milliseconds before performing the API search.
|
|
116
|
+
* Note: A lower bound of 50ms is enforced to protect API stability.
|
|
117
|
+
* @default 150
|
|
118
|
+
*/
|
|
119
|
+
debounceMs?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Callback triggered whenever the internal state (suggestions, loading status, etc.) updates.
|
|
98
122
|
*/
|
|
99
123
|
onStateChange?: (state: InferState) => void;
|
|
100
124
|
/**
|
|
101
|
-
* Callback
|
|
125
|
+
* Callback triggered when a user selects an item.
|
|
126
|
+
* If the address is complete, returns an `AddressValue` object.
|
|
127
|
+
* If the selection is partial, returns a `string`.
|
|
102
128
|
*/
|
|
103
129
|
onSelect?: (selection: AddressValue | string | null) => void;
|
|
104
130
|
}
|
|
105
131
|
|
|
132
|
+
/**
|
|
133
|
+
* The initial state of the address inference engine.
|
|
134
|
+
*/
|
|
106
135
|
declare const INITIAL_STATE: InferState;
|
|
136
|
+
/**
|
|
137
|
+
* The core logic engine for Pro6PP Infer.
|
|
138
|
+
* Manages API communication, state transitions, and keyboard interaction logic.
|
|
139
|
+
*/
|
|
107
140
|
declare class InferCore {
|
|
108
141
|
private country;
|
|
109
142
|
private authKey;
|
|
@@ -112,17 +145,43 @@ declare class InferCore {
|
|
|
112
145
|
private fetcher;
|
|
113
146
|
private onStateChange;
|
|
114
147
|
private onSelect;
|
|
148
|
+
/**
|
|
149
|
+
* The current read-only state of the engine.
|
|
150
|
+
* Use `onStateChange` to react to updates.
|
|
151
|
+
*/
|
|
115
152
|
state: InferState;
|
|
116
153
|
private abortController;
|
|
117
154
|
private debouncedFetch;
|
|
118
155
|
private isSelecting;
|
|
156
|
+
/**
|
|
157
|
+
* Initializes a new instance of the Infer engine.
|
|
158
|
+
* @param config The configuration object including API keys and callbacks.
|
|
159
|
+
*/
|
|
119
160
|
constructor(config: InferConfig);
|
|
161
|
+
/**
|
|
162
|
+
* Processes new text input from the user.
|
|
163
|
+
* Triggers a debounced API request and updates the internal state.
|
|
164
|
+
* @param value The raw string from the input field.
|
|
165
|
+
*/
|
|
120
166
|
handleInput(value: string): void;
|
|
167
|
+
/**
|
|
168
|
+
* Handles keyboard events for the input field.
|
|
169
|
+
* Supports:
|
|
170
|
+
* - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
|
|
171
|
+
* - `Enter`: Select the currently highlighted suggestion.
|
|
172
|
+
* - `Space`: Automatically inserts a comma if a numeric house number is detected.
|
|
173
|
+
* @param event The keyboard event from the input element.
|
|
174
|
+
*/
|
|
121
175
|
handleKeyDown(event: KeyboardEvent | {
|
|
122
176
|
key: string;
|
|
123
177
|
target: EventTarget | null;
|
|
124
178
|
preventDefault: () => void;
|
|
125
179
|
}): void;
|
|
180
|
+
/**
|
|
181
|
+
* Manually selects a suggestion or a string value.
|
|
182
|
+
* This is typically called when a user clicks a suggestion in the UI.
|
|
183
|
+
* @param item The suggestion object or string to select.
|
|
184
|
+
*/
|
|
126
185
|
selectItem(item: InferResult | string): void;
|
|
127
186
|
private shouldAutoInsertComma;
|
|
128
187
|
private finishSelection;
|
|
@@ -138,6 +197,6 @@ declare class InferCore {
|
|
|
138
197
|
private debounce;
|
|
139
198
|
}
|
|
140
199
|
|
|
141
|
-
declare const DEFAULT_STYLES = "\n .pro6pp-wrapper {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n box-sizing: border-box;\n width: 100%;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1.5;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .pro6pp-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 9999;\n margin-top: 4px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n max-height: 300px;\n overflow-y: auto;\n list-style: none !important;\n padding: 0 !important;\n margin: 0 !important;\n
|
|
200
|
+
declare const DEFAULT_STYLES = "\n .pro6pp-wrapper {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n box-sizing: border-box;\n width: 100%;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1.5;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .pro6pp-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 9999;\n margin-top: 4px;\n background: white;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n max-height: 300px;\n overflow-y: auto;\n list-style: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n .pro6pp-item {\n padding: 10px 16px;\n cursor: pointer;\n display: flex;\n flex-direction: row;\n align-items: center;\n color: #000000;\n font-size: 14px;\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n }\n .pro6pp-item:hover, .pro6pp-item--active {\n background-color: #f5f5f5;\n }\n .pro6pp-item__label {\n font-weight: 500;\n flex-shrink: 0;\n }\n .pro6pp-item__subtitle {\n font-size: 14px;\n color: #404040;\n overflow: hidden;\n text-overflow: ellipsis;\n flex-shrink: 1;\n }\n .pro6pp-item__chevron {\n margin-left: auto;\n display: flex;\n align-items: center;\n color: #a3a3a3;\n padding-left: 8px;\n }\n .pro6pp-no-results {\n padding: 12px;\n color: #555555;\n font-size: 14px;\n text-align: center;\n user-select: none;\n pointer-events: none;\n }\n .pro6pp-loader {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n width: 16px;\n height: 16px;\n border: 2px solid #e0e0e0;\n border-top-color: #404040;\n border-radius: 50%;\n animation: pro6pp-spin 0.6s linear infinite;\n pointer-events: none;\n }\n @keyframes pro6pp-spin {\n to { transform: translateY(-50%) rotate(360deg); }\n }\n";
|
|
142
201
|
|
|
143
202
|
export { type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, INITIAL_STATE, type InferConfig, InferCore, type InferResult, type InferState, type Stage };
|
package/dist/index.global.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var Pro6PPCore=(()=>{var c=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var
|
|
1
|
+
"use strict";var Pro6PPCore=(()=>{var c=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var C=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var T=(n,e)=>{for(var t in e)c(n,t,{get:e[t],enumerable:!0})},w=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of v(e))!I.call(n,i)&&i!==t&&c(n,i,{get:()=>e[i],enumerable:!(s=y(e,i))||s.enumerable});return n};var _=n=>w(c({},"__esModule",{value:!0}),n);var o=(n,e,t)=>C(n,typeof e!="symbol"?e+"":e,t);var F={};T(F,{DEFAULT_STYLES:()=>b,INITIAL_STATE:()=>h,InferCore:()=>g});var d={API_URL:"https://api.pro6pp.nl/v2",LIMIT:1e3,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50},m={DIGITS_1_3:/^[0-9]{1,3}$/},h={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,isError:!1,isLoading:!1,selectedSuggestionIndex:-1},g=class{constructor(e){o(this,"country");o(this,"authKey");o(this,"apiUrl");o(this,"limit");o(this,"fetcher");o(this,"onStateChange");o(this,"onSelect");o(this,"state");o(this,"abortController",null);o(this,"debouncedFetch");o(this,"isSelecting",!1);this.country=e.country,this.authKey=e.authKey,this.apiUrl=e.apiUrl||d.API_URL,this.limit=e.limit||d.LIMIT,this.fetcher=e.fetcher||((i,r)=>fetch(i,r)),this.onStateChange=e.onStateChange||(()=>{}),this.onSelect=e.onSelect||(()=>{}),this.state={...h};let t=e.debounceMs!==void 0?e.debounceMs:d.DEBOUNCE_MS,s=Math.max(t,d.MIN_DEBOUNCE_MS);this.debouncedFetch=this.debounce(i=>this.executeFetch(i),s)}handleInput(e){if(this.isSelecting){this.isSelecting=!1;return}let t=this.state.stage==="final"&&e!==this.state.query;this.updateState({query:e,isValid:!1,isLoading:!!e.trim(),selectedSuggestionIndex:-1}),t&&this.onSelect(null),this.debouncedFetch(e)}handleKeyDown(e){let t=e.target;if(!t)return;let s=this.state.cities.length+this.state.streets.length+this.state.suggestions.length;if(s>0){if(e.key==="ArrowDown"){e.preventDefault();let r=this.state.selectedSuggestionIndex+1;r>=s&&(r=0),this.updateState({selectedSuggestionIndex:r});return}if(e.key==="ArrowUp"){e.preventDefault();let r=this.state.selectedSuggestionIndex-1;r<0&&(r=s-1),this.updateState({selectedSuggestionIndex:r});return}if(e.key==="Enter"&&this.state.selectedSuggestionIndex>=0){e.preventDefault();let a=[...this.state.cities,...this.state.streets,...this.state.suggestions][this.state.selectedSuggestionIndex];a&&(this.selectItem(a),this.updateState({selectedSuggestionIndex:-1}));return}}let i=t.value;if(e.key===" "&&this.shouldAutoInsertComma(i)){e.preventDefault();let r=`${i.trim()}, `;this.updateQueryAndFetch(r)}}selectItem(e){this.debouncedFetch.cancel(),this.abortController&&this.abortController.abort();let t=typeof e=="string"?e:e.label,s=t;typeof e!="string"&&typeof e.value=="string"&&(s=e.value);let i=typeof e!="string"&&typeof e.value=="object"?e.value:void 0,r=!!i&&Object.keys(i).length>0;if(this.isSelecting=!0,this.state.stage==="final"||r){let p=t;if(i&&Object.keys(i).length>0){let{street:l,street_number:u,house_number:x,city:f}=i,S=u||x;l&&S&&f&&(p=`${l} ${S}, ${f}`)}this.finishSelection(p,i);return}let a=typeof e!="string"?e.subtitle:null;this.processSelection(s,a)}shouldAutoInsertComma(e){if(!e.includes(",")&&m.DIGITS_1_3.test(e.trim()))return!0;if(this.state.stage==="house_number"){let s=this.getCurrentFragment(e);return m.DIGITS_1_3.test(s)}return!1}finishSelection(e,t){this.updateState({query:e,suggestions:[],cities:[],streets:[],isValid:!0,stage:"final"}),this.onSelect(t||e),setTimeout(()=>{this.isSelecting=!1},0)}processSelection(e,t){let{stage:s,query:i}=this.state,r=i;if(t&&(s==="city"||s==="street"||s==="mixed")){if(s==="city")r=`${t}, ${e}, `;else{let u=this.getQueryPrefix(i);r=u?`${u} ${e}, ${t}, `:`${e}, ${t}, `}this.updateQueryAndFetch(r);return}if(s==="direct"||s==="addition"){this.finishSelection(e);return}!i.includes(",")&&(s==="city"||s==="street"||s==="house_number_first")?r=`${e}, `:(r=this.replaceLastSegment(i,e),s!=="house_number"&&(r+=", ")),this.updateQueryAndFetch(r)}executeFetch(e){let t=(e||"").toString();if(!t.trim()){this.abortController?.abort(),this.resetState();return}this.updateState({isError:!1}),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;let s=new URL(`${this.apiUrl}/infer/${this.country.toLowerCase()}`),i={authKey:this.authKey,query:t,limit:this.limit.toString()};s.search=new URLSearchParams(i).toString(),this.fetcher(s.toString(),{signal:this.abortController.signal}).then(r=>{if(!r.ok)throw new Error("Network error");return r.json()}).then(r=>this.mapResponseToState(r)).catch(r=>{r.name!=="AbortError"&&this.updateState({isError:!0,isLoading:!1})})}mapResponseToState(e){let t={stage:e.stage,isLoading:!1},s=!1,i=null,r=e.suggestions||[],a=[],p=new Set;for(let l of r){let u=`${l.label}|${l.subtitle||""}|${JSON.stringify(l.value||{})}`;p.has(u)||(p.add(u),a.push(l))}if(e.stage==="mixed"?(t.cities=e.cities||[],t.streets=e.streets||[],t.suggestions=[]):(t.suggestions=a,t.cities=[],t.streets=[],e.stage==="final"&&a.length===1&&(s=!0,i=a[0])),t.isValid=e.stage==="final",s&&i){t.query=i.label,t.suggestions=[],t.cities=[],t.streets=[],t.isValid=!0,this.updateState(t);let l=typeof i.value=="object"?i.value:i.label;this.onSelect(l)}else this.updateState(t)}updateQueryAndFetch(e){this.updateState({query:e,suggestions:[],cities:[],streets:[]}),this.updateState({isLoading:!0,isValid:!1}),this.debouncedFetch(e),setTimeout(()=>{this.isSelecting=!1},0)}replaceLastSegment(e,t){let s=e.lastIndexOf(",");return s===-1?t:`${e.slice(0,s+1)} ${t}`.trim()}getQueryPrefix(e){let t=e.lastIndexOf(",");return t===-1?"":e.slice(0,t+1).trimEnd()}getCurrentFragment(e){return(e.split(",").slice(-1)[0]??"").trim()}resetState(){this.updateState({...h,query:this.state.query})}updateState(e){this.state={...this.state,...e},this.onStateChange(this.state)}debounce(e,t){let s,i=(...r)=>{s&&clearTimeout(s),s=setTimeout(()=>e.apply(this,r),t)};return i.cancel=()=>{s&&(clearTimeout(s),s=void 0)},i}};var b=`
|
|
2
2
|
.pro6pp-wrapper {
|
|
3
3
|
position: relative;
|
|
4
4
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -38,21 +38,18 @@
|
|
|
38
38
|
list-style: none !important;
|
|
39
39
|
padding: 0 !important;
|
|
40
40
|
margin: 0 !important;
|
|
41
|
-
overflow: hidden;
|
|
42
41
|
}
|
|
43
42
|
.pro6pp-item {
|
|
44
|
-
padding:
|
|
43
|
+
padding: 10px 16px;
|
|
45
44
|
cursor: pointer;
|
|
46
45
|
display: flex;
|
|
47
46
|
flex-direction: row;
|
|
48
47
|
align-items: center;
|
|
49
48
|
color: #000000;
|
|
50
49
|
font-size: 14px;
|
|
51
|
-
line-height: 1;
|
|
50
|
+
line-height: 1.2;
|
|
52
51
|
white-space: nowrap;
|
|
53
52
|
overflow: hidden;
|
|
54
|
-
border-radius: 0 !important;
|
|
55
|
-
margin: 0 !important;
|
|
56
53
|
}
|
|
57
54
|
.pro6pp-item:hover, .pro6pp-item--active {
|
|
58
55
|
background-color: #f5f5f5;
|
|
@@ -99,4 +96,4 @@
|
|
|
99
96
|
@keyframes pro6pp-spin {
|
|
100
97
|
to { transform: translateY(-50%) rotate(360deg); }
|
|
101
98
|
}
|
|
102
|
-
`;return F
|
|
99
|
+
`;return _(F);})();
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var b=Object.defineProperty;var x=(u,t
|
|
1
|
+
var b=Object.defineProperty;var x=(u,e,t)=>e in u?b(u,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):u[e]=t;var n=(u,e,t)=>x(u,typeof e!="symbol"?e+"":e,t);var c={API_URL:"https://api.pro6pp.nl/v2",LIMIT:1e3,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50},g={DIGITS_1_3:/^[0-9]{1,3}$/},f={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,isError:!1,isLoading:!1,selectedSuggestionIndex:-1},S=class{constructor(e){n(this,"country");n(this,"authKey");n(this,"apiUrl");n(this,"limit");n(this,"fetcher");n(this,"onStateChange");n(this,"onSelect");n(this,"state");n(this,"abortController",null);n(this,"debouncedFetch");n(this,"isSelecting",!1);this.country=e.country,this.authKey=e.authKey,this.apiUrl=e.apiUrl||c.API_URL,this.limit=e.limit||c.LIMIT,this.fetcher=e.fetcher||((r,i)=>fetch(r,i)),this.onStateChange=e.onStateChange||(()=>{}),this.onSelect=e.onSelect||(()=>{}),this.state={...f};let t=e.debounceMs!==void 0?e.debounceMs:c.DEBOUNCE_MS,s=Math.max(t,c.MIN_DEBOUNCE_MS);this.debouncedFetch=this.debounce(r=>this.executeFetch(r),s)}handleInput(e){if(this.isSelecting){this.isSelecting=!1;return}let t=this.state.stage==="final"&&e!==this.state.query;this.updateState({query:e,isValid:!1,isLoading:!!e.trim(),selectedSuggestionIndex:-1}),t&&this.onSelect(null),this.debouncedFetch(e)}handleKeyDown(e){let t=e.target;if(!t)return;let s=this.state.cities.length+this.state.streets.length+this.state.suggestions.length;if(s>0){if(e.key==="ArrowDown"){e.preventDefault();let i=this.state.selectedSuggestionIndex+1;i>=s&&(i=0),this.updateState({selectedSuggestionIndex:i});return}if(e.key==="ArrowUp"){e.preventDefault();let i=this.state.selectedSuggestionIndex-1;i<0&&(i=s-1),this.updateState({selectedSuggestionIndex:i});return}if(e.key==="Enter"&&this.state.selectedSuggestionIndex>=0){e.preventDefault();let o=[...this.state.cities,...this.state.streets,...this.state.suggestions][this.state.selectedSuggestionIndex];o&&(this.selectItem(o),this.updateState({selectedSuggestionIndex:-1}));return}}let r=t.value;if(e.key===" "&&this.shouldAutoInsertComma(r)){e.preventDefault();let i=`${r.trim()}, `;this.updateQueryAndFetch(i)}}selectItem(e){this.debouncedFetch.cancel(),this.abortController&&this.abortController.abort();let t=typeof e=="string"?e:e.label,s=t;typeof e!="string"&&typeof e.value=="string"&&(s=e.value);let r=typeof e!="string"&&typeof e.value=="object"?e.value:void 0,i=!!r&&Object.keys(r).length>0;if(this.isSelecting=!0,this.state.stage==="final"||i){let p=t;if(r&&Object.keys(r).length>0){let{street:a,street_number:l,house_number:m,city:d}=r,h=l||m;a&&h&&d&&(p=`${a} ${h}, ${d}`)}this.finishSelection(p,r);return}let o=typeof e!="string"?e.subtitle:null;this.processSelection(s,o)}shouldAutoInsertComma(e){if(!e.includes(",")&&g.DIGITS_1_3.test(e.trim()))return!0;if(this.state.stage==="house_number"){let s=this.getCurrentFragment(e);return g.DIGITS_1_3.test(s)}return!1}finishSelection(e,t){this.updateState({query:e,suggestions:[],cities:[],streets:[],isValid:!0,stage:"final"}),this.onSelect(t||e),setTimeout(()=>{this.isSelecting=!1},0)}processSelection(e,t){let{stage:s,query:r}=this.state,i=r;if(t&&(s==="city"||s==="street"||s==="mixed")){if(s==="city")i=`${t}, ${e}, `;else{let l=this.getQueryPrefix(r);i=l?`${l} ${e}, ${t}, `:`${e}, ${t}, `}this.updateQueryAndFetch(i);return}if(s==="direct"||s==="addition"){this.finishSelection(e);return}!r.includes(",")&&(s==="city"||s==="street"||s==="house_number_first")?i=`${e}, `:(i=this.replaceLastSegment(r,e),s!=="house_number"&&(i+=", ")),this.updateQueryAndFetch(i)}executeFetch(e){let t=(e||"").toString();if(!t.trim()){this.abortController?.abort(),this.resetState();return}this.updateState({isError:!1}),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;let s=new URL(`${this.apiUrl}/infer/${this.country.toLowerCase()}`),r={authKey:this.authKey,query:t,limit:this.limit.toString()};s.search=new URLSearchParams(r).toString(),this.fetcher(s.toString(),{signal:this.abortController.signal}).then(i=>{if(!i.ok)throw new Error("Network error");return i.json()}).then(i=>this.mapResponseToState(i)).catch(i=>{i.name!=="AbortError"&&this.updateState({isError:!0,isLoading:!1})})}mapResponseToState(e){let t={stage:e.stage,isLoading:!1},s=!1,r=null,i=e.suggestions||[],o=[],p=new Set;for(let a of i){let l=`${a.label}|${a.subtitle||""}|${JSON.stringify(a.value||{})}`;p.has(l)||(p.add(l),o.push(a))}if(e.stage==="mixed"?(t.cities=e.cities||[],t.streets=e.streets||[],t.suggestions=[]):(t.suggestions=o,t.cities=[],t.streets=[],e.stage==="final"&&o.length===1&&(s=!0,r=o[0])),t.isValid=e.stage==="final",s&&r){t.query=r.label,t.suggestions=[],t.cities=[],t.streets=[],t.isValid=!0,this.updateState(t);let a=typeof r.value=="object"?r.value:r.label;this.onSelect(a)}else this.updateState(t)}updateQueryAndFetch(e){this.updateState({query:e,suggestions:[],cities:[],streets:[]}),this.updateState({isLoading:!0,isValid:!1}),this.debouncedFetch(e),setTimeout(()=>{this.isSelecting=!1},0)}replaceLastSegment(e,t){let s=e.lastIndexOf(",");return s===-1?t:`${e.slice(0,s+1)} ${t}`.trim()}getQueryPrefix(e){let t=e.lastIndexOf(",");return t===-1?"":e.slice(0,t+1).trimEnd()}getCurrentFragment(e){return(e.split(",").slice(-1)[0]??"").trim()}resetState(){this.updateState({...f,query:this.state.query})}updateState(e){this.state={...this.state,...e},this.onStateChange(this.state)}debounce(e,t){let s,r=(...i)=>{s&&clearTimeout(s),s=setTimeout(()=>e.apply(this,i),t)};return r.cancel=()=>{s&&(clearTimeout(s),s=void 0)},r}};var y=`
|
|
2
2
|
.pro6pp-wrapper {
|
|
3
3
|
position: relative;
|
|
4
4
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -38,21 +38,18 @@ var b=Object.defineProperty;var x=(u,t,e)=>t in u?b(u,t,{enumerable:!0,configura
|
|
|
38
38
|
list-style: none !important;
|
|
39
39
|
padding: 0 !important;
|
|
40
40
|
margin: 0 !important;
|
|
41
|
-
overflow: hidden;
|
|
42
41
|
}
|
|
43
42
|
.pro6pp-item {
|
|
44
|
-
padding:
|
|
43
|
+
padding: 10px 16px;
|
|
45
44
|
cursor: pointer;
|
|
46
45
|
display: flex;
|
|
47
46
|
flex-direction: row;
|
|
48
47
|
align-items: center;
|
|
49
48
|
color: #000000;
|
|
50
49
|
font-size: 14px;
|
|
51
|
-
line-height: 1;
|
|
50
|
+
line-height: 1.2;
|
|
52
51
|
white-space: nowrap;
|
|
53
52
|
overflow: hidden;
|
|
54
|
-
border-radius: 0 !important;
|
|
55
|
-
margin: 0 !important;
|
|
56
53
|
}
|
|
57
54
|
.pro6pp-item:hover, .pro6pp-item--active {
|
|
58
55
|
background-color: #f5f5f5;
|
package/package.json
CHANGED