@pro6pp/infer-core 0.0.2-beta.0 → 0.0.2-beta.10
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 +53 -9
- package/dist/index.cjs +180 -0
- package/dist/index.d.cts +226 -0
- package/dist/index.d.ts +136 -40
- package/dist/index.global.js +180 -0
- package/dist/index.js +176 -224
- package/package.json +37 -9
- package/dist/index.d.mts +0 -130
- package/dist/index.mjs +0 -202
package/README.md
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pro6PP Infer Core
|
|
2
2
|
|
|
3
3
|
The headless logic engine behind the Pro6PP Infer SDKs.
|
|
4
4
|
Use this package if you are building a custom integration for a framework, or if you need to run Infer in a non-standard environment.
|
|
5
5
|
|
|
6
|
-
> **Note:**
|
|
7
|
-
> If you are using plain HTML/JS, use [`@pro6pp/infer-js`](../js).
|
|
6
|
+
> **Note:** Use [`@pro6pp/infer-react`](https://www.npmjs.com/package/@pro6pp/infer-react) for React applications. For all other frameworks or Vanilla JS, use [`@pro6pp/infer-js`](https://www.npmjs.com/package/@pro6pp/infer-js).
|
|
8
7
|
|
|
9
8
|
## Installation
|
|
10
9
|
|
|
10
|
+
### Package Manager
|
|
11
|
+
|
|
11
12
|
```bash
|
|
12
13
|
npm install @pro6pp/infer-core
|
|
13
14
|
```
|
|
14
15
|
|
|
16
|
+
### CDN
|
|
17
|
+
|
|
18
|
+
You can also load the Core SDK directly in the browser via a CDN:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<script src="https://unpkg.com/@pro6pp/infer-core"></script>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<script src="https://cdn.jsdelivr.net/npm/@pro6pp/infer-core"></script>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
When loaded via a script tag, the library is available through the global `Pro6PPCore` object.
|
|
29
|
+
|
|
15
30
|
## Usage
|
|
16
31
|
|
|
17
32
|
The core logic is exposed via the `InferCore` class. It manages the API requests, state and parses input.
|
|
18
33
|
|
|
34
|
+
### Using ES Modules
|
|
35
|
+
|
|
19
36
|
```typescript
|
|
20
37
|
import { InferCore } from '@pro6pp/infer-core';
|
|
21
38
|
|
|
@@ -23,17 +40,44 @@ const core = new InferCore({
|
|
|
23
40
|
authKey: 'YOUR_AUTH_KEY',
|
|
24
41
|
country: 'NL',
|
|
25
42
|
onStateChange: (state) => {
|
|
26
|
-
|
|
27
|
-
console.log('
|
|
43
|
+
// suggestions, isLoading, isValid, selectedSuggestionIndex, etc.
|
|
44
|
+
console.log('Current State:', state);
|
|
28
45
|
},
|
|
29
46
|
onSelect: (result) => {
|
|
30
47
|
console.log('User selected:', result);
|
|
31
48
|
},
|
|
32
49
|
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Using via script tag (global)
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const core = new Pro6PPCore.InferCore({
|
|
56
|
+
authKey: 'YOUR_AUTH_KEY',
|
|
57
|
+
country: 'NL',
|
|
58
|
+
onSelect: (result) => console.log(result),
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Event handling
|
|
63
|
+
|
|
64
|
+
Once initialized, pass your input and keyboard events to the core instance to manage state.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const input = document.querySelector('#my-input');
|
|
68
|
+
|
|
69
|
+
// pass input events to the core
|
|
70
|
+
input.addEventListener('input', (e) => {
|
|
71
|
+
core.handleInput(e.target.value);
|
|
72
|
+
});
|
|
33
73
|
|
|
34
|
-
//
|
|
35
|
-
|
|
74
|
+
// pass keyboard events
|
|
75
|
+
input.addEventListener('keydown', (e) => {
|
|
76
|
+
core.handleKeyDown(e);
|
|
77
|
+
});
|
|
36
78
|
|
|
37
|
-
// handle
|
|
38
|
-
|
|
79
|
+
// handle clicks
|
|
80
|
+
function onSuggestionClick(item) {
|
|
81
|
+
core.selectItem(item);
|
|
82
|
+
}
|
|
39
83
|
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";var h=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var C=(o,t,e)=>t in o?h(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var T=(o,t)=>{for(var e in t)h(o,e,{get:t[e],enumerable:!0})},L=(o,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of v(t))!I.call(o,r)&&r!==e&&h(o,r,{get:()=>t[r],enumerable:!(i=y(t,r))||i.enumerable});return o};var A=o=>L(h({},"__esModule",{value:!0}),o);var l=(o,t,e)=>C(o,typeof t!="symbol"?t+"":t,e);var F={};T(F,{DEFAULT_STYLES:()=>x,INITIAL_STATE:()=>f,InferCore:()=>m,getHighlightSegments:()=>w});module.exports=A(F);var c={API_URL:"https://api.pro6pp.nl/v2",LIMIT:20,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50,MAX_RETRIES:0},b={DIGITS_1_3:/^[0-9]{1,3}$/},f={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,isError:!1,isLoading:!1,hasMore:!1,selectedSuggestionIndex:-1},m=class{constructor(t){l(this,"country");l(this,"authKey");l(this,"explicitApiUrl");l(this,"baseLimit");l(this,"currentLimit");l(this,"maxRetries");l(this,"fetcher");l(this,"onStateChange");l(this,"onSelect");l(this,"state");l(this,"abortController",null);l(this,"debouncedFetch");this.country=t.country,this.authKey=t.authKey,this.explicitApiUrl=t.apiUrl,this.baseLimit=t.limit||c.LIMIT,this.currentLimit=this.baseLimit;let e=t.maxRetries!==void 0?t.maxRetries:c.MAX_RETRIES;this.maxRetries=Math.max(0,Math.min(e,10)),this.fetcher=t.fetcher||((s,a)=>fetch(s,a)),this.onStateChange=t.onStateChange||(()=>{}),this.onSelect=t.onSelect||(()=>{}),this.state={...f};let i=t.debounceMs!==void 0?t.debounceMs:c.DEBOUNCE_MS,r=Math.max(i,c.MIN_DEBOUNCE_MS);this.debouncedFetch=this.debounce(s=>this.executeFetch(s),r)}handleInput(t){this.currentLimit=this.baseLimit;let e=this.state.stage==="final"&&t!==this.state.query;this.updateState({query:t,isValid:!1,isLoading:!!t.trim(),selectedSuggestionIndex:-1,hasMore:!1}),e&&this.onSelect(null),this.debouncedFetch(t)}loadMore(){this.state.isLoading||(this.currentLimit+=this.baseLimit,this.updateState({isLoading:!0}),this.executeFetch(this.state.query))}handleKeyDown(t){let e=t.target;if(!e)return;let i=this.state.cities.length+this.state.streets.length+this.state.suggestions.length;if(i>0){if(t.key==="ArrowDown"){t.preventDefault();let s=this.state.selectedSuggestionIndex+1;s>=i&&(s=0),this.updateState({selectedSuggestionIndex:s});return}if(t.key==="ArrowUp"){t.preventDefault();let s=this.state.selectedSuggestionIndex-1;s<0&&(s=i-1),this.updateState({selectedSuggestionIndex:s});return}if(t.key==="Enter"&&this.state.selectedSuggestionIndex>=0){t.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 r=e.value;if(t.key===" "&&this.shouldAutoInsertComma(r)){t.preventDefault();let s=`${r.trim()}, `;this.updateQueryAndFetch(s)}}selectItem(t){this.debouncedFetch.cancel(),this.abortController&&this.abortController.abort();let e=typeof t=="string"?t:t.label,i=e;typeof t!="string"&&typeof t.value=="string"&&(i=t.value);let r=typeof t!="string"&&typeof t.value=="object"?t.value:void 0,s=!!r&&Object.keys(r).length>0;if(this.state.stage==="final"||s){let p=e;if(r&&Object.keys(r).length>0){let{street:u,street_number:n,city:d,addition:g}=r;if(u&&n&&d){let S=g?` ${g}`:"";p=`${u} ${n}${S}, ${d}`}}return this.finishSelection(p,r),!0}let a=typeof t!="string"?t.subtitle:null;return this.processSelection(i,a),!1}shouldAutoInsertComma(t){if(!t.includes(",")&&b.DIGITS_1_3.test(t.trim()))return!0;if(this.state.stage==="street_number"){let i=this.getCurrentFragment(t);return b.DIGITS_1_3.test(i)}return!1}finishSelection(t,e){this.updateState({query:t,suggestions:[],cities:[],streets:[],isValid:!0,stage:"final",hasMore:!1}),this.onSelect(e||t)}processSelection(t,e){let{stage:i,query:r}=this.state,s=r;if(e&&(i==="city"||i==="street"||i==="mixed")){if(i==="city")s=`${e}, ${t}, `;else{let n=this.getQueryPrefix(r);!n||!n.includes(e)?s=n?`${n} ${t}, ${e}, `:`${t}, ${e}, `:s=n?`${n} ${t}, `:`${t}, `}this.updateQueryAndFetch(s);return}if(i==="direct"||i==="addition"){this.finishSelection(t);return}!r.includes(",")&&(i==="city"||i==="street"||i==="street_number_first")?s=`${t}, `:(s=this.replaceLastSegment(r,t),i!=="street_number"&&(s+=", ")),this.updateQueryAndFetch(s)}executeFetch(t,e=0){let i=(t||"").toString();if(!i.trim()){this.abortController?.abort(),this.resetState();return}e===0&&(this.updateState({isError:!1}),this.abortController&&this.abortController.abort(),this.abortController=new AbortController);let r=this.abortController?.signal,s=this.explicitApiUrl?this.explicitApiUrl:`${c.API_URL}/infer/${this.country.toLowerCase()}`,a=new URLSearchParams({country:this.country.toLowerCase(),query:i,limit:this.currentLimit.toString()});this.authKey&&a.set("authKey",this.authKey);let p=s.includes("?")?"&":"?",u=`${s}${p}${a.toString()}`;this.fetcher(u,{signal:r}).then(n=>{if(!n.ok){if(e<this.maxRetries&&(n.status>=500||n.status===429))return this.retry(t,e,r);throw new Error("Network error")}return n.json()}).then(n=>{n&&this.mapResponseToState(n)}).catch(n=>{if(n.name!=="AbortError"){if(e<this.maxRetries)return this.retry(t,e,r);this.updateState({isError:!0,isLoading:!1})}})}retry(t,e,i){if(i?.aborted)return;let r=Math.pow(2,e)*200;setTimeout(()=>{i?.aborted||this.executeFetch(t,e+1)},r)}mapResponseToState(t){let e={stage:t.stage,isLoading:!1},i=t.suggestions||[],r=[],s=new Set;for(let p of i){let u=`${p.label}|${p.subtitle||""}|${JSON.stringify(p.value||{})}`;s.has(u)||(s.add(u),r.push(p))}let a=r.length+(t.cities?.length||0)+(t.streets?.length||0);e.hasMore=a>=this.currentLimit,t.stage==="mixed"?(e.cities=t.cities||[],e.streets=t.streets||[],e.suggestions=[]):(e.suggestions=r,e.cities=[],e.streets=[]),e.isValid=t.stage==="final",this.updateState(e),e.isValid&&r.length===1&&this.selectItem(r[0])}updateQueryAndFetch(t){this.updateState({query:t,suggestions:[],cities:[],streets:[]}),this.updateState({isLoading:!0,isValid:!1,hasMore:!1}),this.debouncedFetch(t)}replaceLastSegment(t,e){let i=t.lastIndexOf(",");return i===-1?e:`${t.slice(0,i+1)} ${e}`.trim()}getQueryPrefix(t){let e=t.lastIndexOf(",");return e===-1?"":t.slice(0,e+1).trimEnd()}getCurrentFragment(t){return(t.split(",").slice(-1)[0]??"").trim()}resetState(){this.updateState({...f,query:this.state.query})}updateState(t){this.state={...this.state,...t},this.onStateChange(this.state)}debounce(t,e){let i,r=(...s)=>{i&&clearTimeout(i),i=setTimeout(()=>t.apply(this,s),e)};return r.cancel=()=>{i&&(clearTimeout(i),i=void 0)},r}};function w(o,t){if(!t||!o)return[{text:o,match:!1}];let e=[],i=o.toLowerCase(),r=t.toLowerCase(),s=0,a=0;for(let n=0;n<o.length;n++){if(!(s<t.length&&i[n]===r[s]))continue;n>a&&e.push({text:o.slice(a,n),match:!1}),e.push({text:o[n],match:!0}),s++,a=n+1}return a<o.length&&e.push({text:o.slice(a),match:!1}),s===t.length?e:[{text:o,match:!1}]}var x=`
|
|
2
|
+
.pro6pp-wrapper {
|
|
3
|
+
position: relative;
|
|
4
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
width: 100%;
|
|
7
|
+
-webkit-tap-highlight-color: transparent;
|
|
8
|
+
}
|
|
9
|
+
.pro6pp-wrapper * {
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
}
|
|
12
|
+
.pro6pp-input {
|
|
13
|
+
width: 100%;
|
|
14
|
+
padding: 12px 14px;
|
|
15
|
+
padding-right: 48px;
|
|
16
|
+
border: 1px solid #e0e0e0;
|
|
17
|
+
border-radius: 8px;
|
|
18
|
+
font-size: 16px;
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
appearance: none;
|
|
21
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
22
|
+
}
|
|
23
|
+
.pro6pp-input:focus {
|
|
24
|
+
outline: none;
|
|
25
|
+
border-color: #3b82f6;
|
|
26
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.pro6pp-input-addons {
|
|
30
|
+
position: absolute;
|
|
31
|
+
right: 4px;
|
|
32
|
+
top: 0;
|
|
33
|
+
bottom: 0;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
pointer-events: none;
|
|
37
|
+
}
|
|
38
|
+
.pro6pp-input-addons > * {
|
|
39
|
+
pointer-events: auto;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.pro6pp-clear-button {
|
|
43
|
+
background: none;
|
|
44
|
+
border: none;
|
|
45
|
+
width: 40px;
|
|
46
|
+
height: 40px;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
color: #a3a3a3;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
border-radius: 50%;
|
|
53
|
+
transition: color 0.2s, background-color 0.2s;
|
|
54
|
+
touch-action: manipulation;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (hover: hover) {
|
|
58
|
+
.pro6pp-clear-button:hover {
|
|
59
|
+
color: #1f2937;
|
|
60
|
+
background-color: #f3f4f6;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.pro6pp-clear-button:active {
|
|
65
|
+
background-color: #f3f4f6;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.pro6pp-loader {
|
|
69
|
+
width: 20px;
|
|
70
|
+
height: 20px;
|
|
71
|
+
margin: 0 8px;
|
|
72
|
+
border: 2px solid #e0e0e0;
|
|
73
|
+
border-top-color: #6b7280;
|
|
74
|
+
border-radius: 50%;
|
|
75
|
+
animation: pro6pp-spin 0.6s linear infinite;
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.pro6pp-dropdown {
|
|
80
|
+
position: absolute;
|
|
81
|
+
top: 100%;
|
|
82
|
+
left: 0;
|
|
83
|
+
right: 0;
|
|
84
|
+
margin-top: 4px;
|
|
85
|
+
background: #ffffff;
|
|
86
|
+
border: 1px solid #e5e7eb;
|
|
87
|
+
border-radius: 6px;
|
|
88
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
89
|
+
z-index: 9999;
|
|
90
|
+
padding: 0;
|
|
91
|
+
max-height: 260px;
|
|
92
|
+
overflow-y: auto;
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@media (max-height: 500px) {
|
|
98
|
+
.pro6pp-dropdown {
|
|
99
|
+
max-height: 140px;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.pro6pp-list {
|
|
104
|
+
list-style: none;
|
|
105
|
+
margin: 0;
|
|
106
|
+
padding: 0;
|
|
107
|
+
width: 100%;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.pro6pp-item {
|
|
111
|
+
padding: 12px 14px;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
font-size: 14px;
|
|
116
|
+
color: #374151;
|
|
117
|
+
border-bottom: 1px solid #f3f4f6;
|
|
118
|
+
transition: background-color 0.1s;
|
|
119
|
+
flex-shrink: 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.pro6pp-item:last-child {
|
|
123
|
+
border-bottom: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@media (hover: hover) {
|
|
127
|
+
.pro6pp-item:hover, .pro6pp-item--active {
|
|
128
|
+
background-color: #f9fafb;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.pro6pp-item:active {
|
|
133
|
+
background-color: #f3f4f6;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.pro6pp-item__label {
|
|
137
|
+
font-weight: 500;
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
}
|
|
140
|
+
.pro6pp-item__subtitle {
|
|
141
|
+
font-size: 14px;
|
|
142
|
+
color: #6b7280;
|
|
143
|
+
flex-grow: 1;
|
|
144
|
+
}
|
|
145
|
+
.pro6pp-item__chevron {
|
|
146
|
+
color: #d1d5db;
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
margin-left: auto;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.pro6pp-no-results {
|
|
153
|
+
padding: 24px 16px;
|
|
154
|
+
color: #6b7280;
|
|
155
|
+
font-size: 15px;
|
|
156
|
+
text-align: center;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.pro6pp-load-more {
|
|
160
|
+
width: 100%;
|
|
161
|
+
padding: 14px;
|
|
162
|
+
background: #f9fafb;
|
|
163
|
+
border: none;
|
|
164
|
+
border-top: 1px solid #e0e0e0;
|
|
165
|
+
color: #3b82f6;
|
|
166
|
+
font-size: 14px;
|
|
167
|
+
font-weight: 600;
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
flex-shrink: 0;
|
|
170
|
+
touch-action: manipulation;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.pro6pp-load-more:active {
|
|
174
|
+
background-color: #f3f4f6;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@keyframes pro6pp-spin {
|
|
178
|
+
to { transform: rotate(360deg); }
|
|
179
|
+
}
|
|
180
|
+
`;0&&(module.exports={DEFAULT_STYLES,INITIAL_STATE,InferCore,getHighlightSegments});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported ISO 3166-1 alpha-2 country codes.
|
|
3
|
+
*/
|
|
4
|
+
type CountryCode = 'NL' | 'DE';
|
|
5
|
+
/**
|
|
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
|
+
* - `street_number`: User is entering a street number.
|
|
13
|
+
* - `street_number_first`: Specialized mode where number is entered before street.
|
|
14
|
+
* - `addition`: Selecting a street number addition (e.g., 'A', 'III').
|
|
15
|
+
* - `direct`: Direct address hit (often via postcode).
|
|
16
|
+
* - `final`: A complete, valid address has been identified.
|
|
17
|
+
*/
|
|
18
|
+
type Stage = 'empty' | 'mixed' | 'street' | 'city' | 'postcode' | 'street_number' | 'street_number_first' | 'addition' | 'direct' | 'final';
|
|
19
|
+
/**
|
|
20
|
+
* The standardized address object returned upon a successful final selection.
|
|
21
|
+
*/
|
|
22
|
+
interface AddressValue {
|
|
23
|
+
/** The name of the street. */
|
|
24
|
+
street: string;
|
|
25
|
+
/** The name of the city/locality. */
|
|
26
|
+
city: string;
|
|
27
|
+
/** The street number. */
|
|
28
|
+
street_number?: string | number;
|
|
29
|
+
/** The postal code. */
|
|
30
|
+
postcode?: string;
|
|
31
|
+
/** The street number addition or suffix. */
|
|
32
|
+
addition?: string;
|
|
33
|
+
/** Allow for extra fields if API expands. */
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A single item in the suggestion list.
|
|
38
|
+
*/
|
|
39
|
+
interface InferResult {
|
|
40
|
+
/** The text to display in the UI (e.g. "Main Street"). */
|
|
41
|
+
label: string;
|
|
42
|
+
/** The actual address data.
|
|
43
|
+
* Only present if this result completes an address or represents a specific entity.
|
|
44
|
+
*/
|
|
45
|
+
value?: AddressValue | string;
|
|
46
|
+
/** Secondary information (e.g., city name when suggesting a street). */
|
|
47
|
+
subtitle?: string | null;
|
|
48
|
+
/** Number of underlying results found for this suggestion. */
|
|
49
|
+
count?: number | string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The complete UI state managed by InferCore.
|
|
53
|
+
*/
|
|
54
|
+
interface InferState {
|
|
55
|
+
/** The current text value of the search input. */
|
|
56
|
+
query: string;
|
|
57
|
+
/** The current logical stage of the address lookup. */
|
|
58
|
+
stage: Stage | null;
|
|
59
|
+
/** List of city suggestions (used in `mixed` stage). */
|
|
60
|
+
cities: InferResult[];
|
|
61
|
+
/** List of street suggestions (used in `mixed` stage). */
|
|
62
|
+
streets: InferResult[];
|
|
63
|
+
/** General list of suggestions for the current stage. */
|
|
64
|
+
suggestions: InferResult[];
|
|
65
|
+
/** Flag indicating if the current selection is a complete, valid address. */
|
|
66
|
+
isValid: boolean;
|
|
67
|
+
/** Flag indicating if the last API request failed. */
|
|
68
|
+
isError: boolean;
|
|
69
|
+
/** Flag indicating if a network request is currently in progress. */
|
|
70
|
+
isLoading: boolean;
|
|
71
|
+
/** Flag indicating if more results are available to load. */
|
|
72
|
+
hasMore: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* The index of the currently highlighted suggestion.
|
|
75
|
+
* - `0` to `n`: An item is highlighted via keyboard navigation.
|
|
76
|
+
* - `-1`: No item is highlighted.
|
|
77
|
+
*/
|
|
78
|
+
selectedSuggestionIndex: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Custom fetch implementation, compatible with the Web Fetch API.
|
|
82
|
+
* Useful for Node.js environments or proxying requests.
|
|
83
|
+
*/
|
|
84
|
+
type Fetcher = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
85
|
+
/**
|
|
86
|
+
* Configuration options for the Infer engine.
|
|
87
|
+
*/
|
|
88
|
+
interface InferConfig {
|
|
89
|
+
/**
|
|
90
|
+
* Your Pro6PP Authorization Key.
|
|
91
|
+
* Optional if using a proxy.
|
|
92
|
+
*/
|
|
93
|
+
authKey?: string;
|
|
94
|
+
/**
|
|
95
|
+
* The country to perform address lookups in.
|
|
96
|
+
*/
|
|
97
|
+
country: CountryCode;
|
|
98
|
+
/**
|
|
99
|
+
* * If provided, this URL is used as the API endpoint (query params will be appended).
|
|
100
|
+
* * If not provided, the SDK defaults to 'https://api.pro6pp.nl/v2/infer/{country}'.
|
|
101
|
+
*/
|
|
102
|
+
apiUrl?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Custom fetch implementation for network requests.
|
|
105
|
+
* @default window.fetch
|
|
106
|
+
*/
|
|
107
|
+
fetcher?: Fetcher;
|
|
108
|
+
/**
|
|
109
|
+
* Number of suggestions to request per batch.
|
|
110
|
+
* @default 20
|
|
111
|
+
*/
|
|
112
|
+
limit?: number;
|
|
113
|
+
/**
|
|
114
|
+
* The delay in milliseconds before performing the API search.
|
|
115
|
+
* Note: A lower bound of 50ms is enforced to protect API stability.
|
|
116
|
+
* @default 150
|
|
117
|
+
*/
|
|
118
|
+
debounceMs?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Maximum number of retry attempts for transient network errors.
|
|
121
|
+
* Valid range: 0 to 10.
|
|
122
|
+
* @default 0
|
|
123
|
+
*/
|
|
124
|
+
maxRetries?: number;
|
|
125
|
+
/**
|
|
126
|
+
* Callback triggered whenever the internal state (suggestions, loading status, etc.) updates.
|
|
127
|
+
*/
|
|
128
|
+
onStateChange?: (state: InferState) => void;
|
|
129
|
+
/**
|
|
130
|
+
* Callback triggered when a user selects an item.
|
|
131
|
+
* If the address is complete, returns an `AddressValue` object.
|
|
132
|
+
* If the selection is partial, returns a `string`.
|
|
133
|
+
*/
|
|
134
|
+
onSelect?: (selection: AddressValue | string | null) => void;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Represents a segment of text that should be highlighted or left plain.
|
|
138
|
+
*/
|
|
139
|
+
interface HighlightSegment {
|
|
140
|
+
text: string;
|
|
141
|
+
match: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The initial state of the address inference engine.
|
|
146
|
+
*/
|
|
147
|
+
declare const INITIAL_STATE: InferState;
|
|
148
|
+
/**
|
|
149
|
+
* The core logic engine for Pro6PP Infer.
|
|
150
|
+
* Manages API communication, state transitions, and keyboard interaction logic.
|
|
151
|
+
*/
|
|
152
|
+
declare class InferCore {
|
|
153
|
+
private country;
|
|
154
|
+
private authKey?;
|
|
155
|
+
private explicitApiUrl?;
|
|
156
|
+
private baseLimit;
|
|
157
|
+
private currentLimit;
|
|
158
|
+
private maxRetries;
|
|
159
|
+
private fetcher;
|
|
160
|
+
private onStateChange;
|
|
161
|
+
private onSelect;
|
|
162
|
+
/**
|
|
163
|
+
* The current read-only state of the engine.
|
|
164
|
+
* Use `onStateChange` to react to updates.
|
|
165
|
+
*/
|
|
166
|
+
state: InferState;
|
|
167
|
+
private abortController;
|
|
168
|
+
private debouncedFetch;
|
|
169
|
+
/**
|
|
170
|
+
* Initializes a new instance of the Infer engine.
|
|
171
|
+
* @param config The configuration object including API keys and callbacks.
|
|
172
|
+
*/
|
|
173
|
+
constructor(config: InferConfig);
|
|
174
|
+
/**
|
|
175
|
+
* Processes new text input from the user.
|
|
176
|
+
* Triggers a debounced API request and updates the internal state.
|
|
177
|
+
* @param value The raw string from the input field.
|
|
178
|
+
*/
|
|
179
|
+
handleInput(value: string): void;
|
|
180
|
+
/**
|
|
181
|
+
* Increases the current limit and re-fetches the query to show more results.
|
|
182
|
+
*/
|
|
183
|
+
loadMore(): void;
|
|
184
|
+
/**
|
|
185
|
+
* Handles keyboard events for the input field.
|
|
186
|
+
* Supports:
|
|
187
|
+
* - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
|
|
188
|
+
* - `Enter`: Select the currently highlighted suggestion.
|
|
189
|
+
* - `Space`: Automatically inserts a comma if a numeric street number is detected.
|
|
190
|
+
* @param event The keyboard event from the input element.
|
|
191
|
+
*/
|
|
192
|
+
handleKeyDown(event: KeyboardEvent | {
|
|
193
|
+
key: string;
|
|
194
|
+
target: EventTarget | null;
|
|
195
|
+
preventDefault: () => void;
|
|
196
|
+
}): void;
|
|
197
|
+
/**
|
|
198
|
+
* Manually selects a suggestion or a string value.
|
|
199
|
+
* This is typically called when a user clicks a suggestion in the UI.
|
|
200
|
+
* @param item The suggestion object or string to select.
|
|
201
|
+
* @returns boolean True if the selection is a final address.
|
|
202
|
+
*/
|
|
203
|
+
selectItem(item: InferResult | string): boolean;
|
|
204
|
+
private shouldAutoInsertComma;
|
|
205
|
+
private finishSelection;
|
|
206
|
+
private processSelection;
|
|
207
|
+
private executeFetch;
|
|
208
|
+
private retry;
|
|
209
|
+
private mapResponseToState;
|
|
210
|
+
private updateQueryAndFetch;
|
|
211
|
+
private replaceLastSegment;
|
|
212
|
+
private getQueryPrefix;
|
|
213
|
+
private getCurrentFragment;
|
|
214
|
+
private resetState;
|
|
215
|
+
private updateState;
|
|
216
|
+
private debounce;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Splits text into matched and unmatched segments based on a fuzzy query sequence.
|
|
221
|
+
*/
|
|
222
|
+
declare function getHighlightSegments(text: string, query: string): HighlightSegment[];
|
|
223
|
+
|
|
224
|
+
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 -webkit-tap-highlight-color: transparent;\n }\n .pro6pp-wrapper * {\n box-sizing: border-box;\n }\n .pro6pp-input {\n width: 100%;\n padding: 12px 14px;\n padding-right: 48px;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n font-size: 16px;\n line-height: 1.5;\n appearance: none;\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\n .pro6pp-input-addons {\n position: absolute;\n right: 4px;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n pointer-events: none;\n }\n .pro6pp-input-addons > * {\n pointer-events: auto;\n }\n\n .pro6pp-clear-button {\n background: none;\n border: none;\n width: 40px;\n height: 40px;\n cursor: pointer;\n color: #a3a3a3;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: color 0.2s, background-color 0.2s;\n touch-action: manipulation;\n }\n\n @media (hover: hover) {\n .pro6pp-clear-button:hover {\n color: #1f2937;\n background-color: #f3f4f6;\n }\n }\n\n .pro6pp-clear-button:active {\n background-color: #f3f4f6;\n }\n\n .pro6pp-loader {\n width: 20px;\n height: 20px;\n margin: 0 8px;\n border: 2px solid #e0e0e0;\n border-top-color: #6b7280;\n border-radius: 50%;\n animation: pro6pp-spin 0.6s linear infinite;\n flex-shrink: 0;\n }\n\n .pro6pp-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n margin-top: 4px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n z-index: 9999;\n padding: 0;\n max-height: 260px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n }\n\n @media (max-height: 500px) {\n .pro6pp-dropdown {\n max-height: 140px;\n }\n }\n\n .pro6pp-list {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n }\n\n .pro6pp-item {\n padding: 12px 14px;\n cursor: pointer;\n display: flex;\n align-items: center;\n font-size: 14px;\n color: #374151;\n border-bottom: 1px solid #f3f4f6;\n transition: background-color 0.1s;\n flex-shrink: 0;\n }\n\n .pro6pp-item:last-child {\n border-bottom: none;\n }\n\n @media (hover: hover) {\n .pro6pp-item:hover, .pro6pp-item--active {\n background-color: #f9fafb;\n }\n }\n\n .pro6pp-item:active {\n background-color: #f3f4f6;\n }\n\n .pro6pp-item__label {\n font-weight: 500;\n flex-shrink: 0;\n }\n .pro6pp-item__subtitle {\n font-size: 14px;\n color: #6b7280;\n flex-grow: 1;\n }\n .pro6pp-item__chevron {\n color: #d1d5db;\n display: flex;\n align-items: center;\n margin-left: auto;\n }\n\n .pro6pp-no-results {\n padding: 24px 16px;\n color: #6b7280;\n font-size: 15px;\n text-align: center;\n }\n\n .pro6pp-load-more {\n width: 100%;\n padding: 14px;\n background: #f9fafb;\n border: none;\n border-top: 1px solid #e0e0e0;\n color: #3b82f6;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n flex-shrink: 0;\n touch-action: manipulation;\n }\n\n .pro6pp-load-more:active {\n background-color: #f3f4f6;\n }\n\n @keyframes pro6pp-spin {\n to { transform: rotate(360deg); }\n }\n";
|
|
225
|
+
|
|
226
|
+
export { type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, type HighlightSegment, INITIAL_STATE, type InferConfig, InferCore, type InferResult, type InferState, type Stage, getHighlightSegments };
|