@pro6pp/infer-core 0.0.2-beta.8 → 0.1.0-beta.17
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 +35 -1
- package/dist/index.cjs +117 -71
- package/dist/index.d.cts +53 -15
- package/dist/index.d.ts +53 -15
- package/dist/index.global.js +117 -71
- package/dist/index.js +117 -71
- package/dist/styles.css +203 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -7,14 +7,32 @@ Use this package if you are building a custom integration for a framework, or if
|
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
10
|
+
### Package Manager
|
|
11
|
+
|
|
10
12
|
```bash
|
|
11
13
|
npm install @pro6pp/infer-core
|
|
12
14
|
```
|
|
13
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
|
+
|
|
14
30
|
## Usage
|
|
15
31
|
|
|
16
32
|
The core logic is exposed via the `InferCore` class. It manages the API requests, state and parses input.
|
|
17
33
|
|
|
34
|
+
### Using ES Modules
|
|
35
|
+
|
|
18
36
|
```typescript
|
|
19
37
|
import { InferCore } from '@pro6pp/infer-core';
|
|
20
38
|
|
|
@@ -22,14 +40,30 @@ const core = new InferCore({
|
|
|
22
40
|
authKey: 'YOUR_AUTH_KEY',
|
|
23
41
|
country: 'NL',
|
|
24
42
|
onStateChange: (state) => {
|
|
25
|
-
// suggestions, isLoading, isValid, selectedSuggestionIndex, etc.
|
|
43
|
+
// suggestions, isLoading, isValid, value, selectedSuggestionIndex, etc.
|
|
26
44
|
console.log('Current State:', state);
|
|
27
45
|
},
|
|
28
46
|
onSelect: (result) => {
|
|
29
47
|
console.log('User selected:', result);
|
|
30
48
|
},
|
|
31
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
|
|
32
63
|
|
|
64
|
+
Once initialized, pass your input and keyboard events to the core instance to manage state.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
33
67
|
const input = document.querySelector('#my-input');
|
|
34
68
|
|
|
35
69
|
// pass input events to the core
|
package/dist/index.cjs
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var f=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var w=(n,t,e)=>t in n?f(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var A=(n,t)=>{for(var e in t)f(n,e,{get:t[e],enumerable:!0})},E=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of T(t))!L.call(n,r)&&r!==e&&f(n,r,{get:()=>t[r],enumerable:!(i=_(t,r))||i.enumerable});return n};var R=n=>E(f({},"__esModule",{value:!0}),n);var l=(n,t,e)=>w(n,typeof t!="symbol"?t+"":t,e);var U={};A(U,{DEFAULT_STYLES:()=>I,INITIAL_STATE:()=>y,InferCore:()=>S,formatLabelByInputOrder:()=>m,getHighlightSegments:()=>D});module.exports=R(U);function v(n){return n.toLowerCase().trim()}function $(n){return n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function F(n,t){let e=v(n),i=v(t);if(i.includes(" "))return e.indexOf(i);let s=new RegExp(`(?:^|[,\\s])${$(i)}(?:$|[,\\s])`,"g").exec(e);if(s){let o=s.index,p=e[o];return p===","||p===" "?o+1:o}return-1}function M(n,t){let e=[],i=[];t.street&&i.push({value:t.street,type:"street"}),t.city&&i.push({value:t.city,type:"city"}),t.postcode&&i.push({value:t.postcode,type:"postcode"}),t.street_number!==void 0&&t.street_number!==null&&i.push({value:String(t.street_number),type:"street_number"}),t.addition&&i.push({value:t.addition,type:"addition"});for(let r of i){let s=F(n,r.value);s!==-1&&e.push({type:r.type,value:r.value,position:s})}return e.sort((r,s)=>r.position-s.position),e}function m(n,t){if(!t||!n)return"";let e=M(n,t),i=new Set(e.map(o=>o.type)),r=[];for(let o of e)r.push(o.value);let s=["street","street_number","addition","postcode","city"];for(let o of s){if(i.has(o))continue;let p;switch(o){case"street":p=t.street;break;case"city":p=t.city;break;case"street_number":p=t.street_number!==void 0?String(t.street_number):void 0;break;case"postcode":p=t.postcode;break;case"addition":p=t.addition;break}p&&r.push(p)}return r.join(", ")}var g={API_URL:"https://api.pro6pp.nl/v2",LIMIT:20,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50,MAX_RETRIES:0},x={DIGITS_1_3:/^[0-9]{1,3}$/,STREET_NUMBER_PREFIX:/^(\d+)\s*,\s*$/},y={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,value:null,isError:!1,isLoading:!1,hasMore:!1,selectedSuggestionIndex:-1},S=class{constructor(t){l(this,"country");l(this,"authKey");l(this,"explicitApiUrl");l(this,"baseLimit");l(this,"currentLimit");l(this,"maxRetries");l(this,"language");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||g.LIMIT,this.currentLimit=this.baseLimit,this.language=t.language;let e=t.maxRetries!==void 0?t.maxRetries:g.MAX_RETRIES;this.maxRetries=Math.max(0,Math.min(e,10)),this.fetcher=t.fetcher||((s,o)=>fetch(s,o)),this.onStateChange=t.onStateChange||(()=>{}),this.onSelect=t.onSelect||(()=>{}),this.state={...y};let i=t.debounceMs!==void 0?t.debounceMs:g.DEBOUNCE_MS,r=Math.max(i,g.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,value:null,isLoading:!!t.trim(),selectedSuggestionIndex:-1,hasMore:!1,stage:e?null:this.state.stage}),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 o=[...this.state.cities,...this.state.streets,...this.state.suggestions][this.state.selectedSuggestionIndex];o&&(this.selectItem(o),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:c,street_number:a,postcode:d,city:u,addition:h}=r;if(c&&a&&u){let b=h?` ${h}`:"",C=d?`${d}, `:"";p=`${c}, ${a}${b}, ${C}${u}`}}return this.finishSelection(p,r),!0}let o=typeof t!="string"?t.subtitle:null;return this.processSelection(i,o),!1}shouldAutoInsertComma(t){if(!t.includes(",")&&x.DIGITS_1_3.test(t.trim()))return!0;if(this.state.stage==="street_number"){let i=this.getCurrentFragment(t);return x.DIGITS_1_3.test(i)}return!1}finishSelection(t,e){this.updateState({query:t,suggestions:[],cities:[],streets:[],isValid:!0,value:e||null,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 a=this.getQueryPrefix(r),d=!a||!a.includes(e),u=a;if(a&&e){let h=a.match(x.STREET_NUMBER_PREFIX);if(h){let b=h[1];e.startsWith(b)&&(u="")}}d?s=u?`${u} ${t}, ${e}, `:`${t}, ${e}, `:s=u?`${u} ${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:`${g.API_URL}/infer/${this.country.toLowerCase()}`,o=new URLSearchParams({query:i,limit:this.currentLimit.toString()});this.explicitApiUrl&&o.append("country",this.country.toLowerCase()),this.authKey&&o.set("authKey",this.authKey),this.language&&o.set("language",this.language);let p=s.includes("?")?"&":"?",c=`${s}${p}${o.toString()}`;this.fetcher(c,{signal:r}).then(a=>{if(!a.ok){if(e<this.maxRetries&&(a.status>=500||a.status===429))return this.retry(t,e,r);throw new Error("Network error")}return a.json()}).then(a=>{a&&this.mapResponseToState(a)}).catch(a=>{if(a.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 c=`${p.label}|${p.subtitle||""}|${JSON.stringify(p.value||{})}`;if(!s.has(c)){s.add(c);let a=this.reformatSuggestionLabel(p);r.push(a)}}let o=r.length+(t.cities?.length||0)+(t.streets?.length||0);e.hasMore=o>=this.currentLimit,t.stage==="mixed"?(e.cities=t.cities||[],e.streets=t.streets||[],e.cities?.length===0&&e.streets?.length===0?e.suggestions=r: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])}reformatSuggestionLabel(t){if(!t.value||typeof t.value=="string")return t;let e=t.value;if(!e.street||!e.city)return t;let i=m(this.state.query,e);return i?{...t,label:i}:t}updateQueryAndFetch(t){this.updateState({query:t,suggestions:[],cities:[],streets:[],isValid:!1,value:null,isLoading:!0,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({...y,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 k(n){if(n.length===0)return n;let t=[];for(let e of n){let i=t[t.length-1];i&&i.match===e.match?i.text+=e.text:t.push({text:e.text,match:e.match})}return t}function D(n,t){if(!t||!n)return[{text:n,match:!1}];let e=[],i=n.toLowerCase(),r=t.toLowerCase(),s=0,o=0;for(let a=0;a<n.length;a++){if(!(s<t.length&&i[a]===r[s]))continue;a>o&&e.push({text:n.slice(o,a),match:!1}),e.push({text:n[a],match:!0}),s++,o=a+1}return o<n.length&&e.push({text:n.slice(o),match:!1}),s===t.length?k(e):[{text:n,match:!1}]}var I=`
|
|
2
2
|
.pro6pp-wrapper {
|
|
3
3
|
position: relative;
|
|
4
4
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
5
5
|
box-sizing: border-box;
|
|
6
6
|
width: 100%;
|
|
7
|
+
-webkit-tap-highlight-color: transparent;
|
|
7
8
|
}
|
|
8
9
|
.pro6pp-wrapper * {
|
|
9
10
|
box-sizing: border-box;
|
|
10
11
|
}
|
|
11
12
|
.pro6pp-input {
|
|
12
13
|
width: 100%;
|
|
13
|
-
padding:
|
|
14
|
+
padding: 12px 14px;
|
|
14
15
|
padding-right: 48px;
|
|
15
16
|
border: 1px solid #e0e0e0;
|
|
16
|
-
border-radius:
|
|
17
|
+
border-radius: 8px;
|
|
17
18
|
font-size: 16px;
|
|
18
19
|
line-height: 1.5;
|
|
20
|
+
appearance: none;
|
|
19
21
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
20
22
|
}
|
|
23
|
+
|
|
24
|
+
.pro6pp-input::placeholder {
|
|
25
|
+
font-size: 16px;
|
|
26
|
+
color: #a3a3a3;
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
.pro6pp-input:focus {
|
|
22
30
|
outline: none;
|
|
23
31
|
border-color: #3b82f6;
|
|
@@ -26,12 +34,11 @@
|
|
|
26
34
|
|
|
27
35
|
.pro6pp-input-addons {
|
|
28
36
|
position: absolute;
|
|
29
|
-
right:
|
|
37
|
+
right: 4px;
|
|
30
38
|
top: 0;
|
|
31
39
|
bottom: 0;
|
|
32
40
|
display: flex;
|
|
33
41
|
align-items: center;
|
|
34
|
-
gap: 2px;
|
|
35
42
|
pointer-events: none;
|
|
36
43
|
}
|
|
37
44
|
.pro6pp-input-addons > * {
|
|
@@ -41,37 +48,27 @@
|
|
|
41
48
|
.pro6pp-clear-button {
|
|
42
49
|
background: none;
|
|
43
50
|
border: none;
|
|
44
|
-
width:
|
|
45
|
-
height:
|
|
51
|
+
width: 32px;
|
|
52
|
+
height: 32px;
|
|
46
53
|
cursor: pointer;
|
|
47
54
|
color: #a3a3a3;
|
|
48
55
|
display: flex;
|
|
49
56
|
align-items: center;
|
|
50
57
|
justify-content: center;
|
|
51
58
|
border-radius: 50%;
|
|
52
|
-
transition: color 0.2s, background-color 0.2s
|
|
53
|
-
|
|
54
|
-
.pro6pp-clear-button:hover {
|
|
55
|
-
color: #1f2937;
|
|
56
|
-
background-color: #f3f4f6;
|
|
57
|
-
}
|
|
58
|
-
.pro6pp-clear-button:active {
|
|
59
|
-
transform: scale(0.92);
|
|
59
|
+
transition: color 0.2s, background-color 0.2s;
|
|
60
|
+
touch-action: manipulation;
|
|
60
61
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
|
|
63
|
+
@media (hover: hover) {
|
|
64
|
+
.pro6pp-clear-button:hover {
|
|
65
|
+
color: #1f2937;
|
|
66
|
+
background-color: #f3f4f6;
|
|
67
|
+
}
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
.pro6pp-
|
|
67
|
-
|
|
68
|
-
height: 18px;
|
|
69
|
-
margin: 0 4px;
|
|
70
|
-
border: 2px solid #e0e0e0;
|
|
71
|
-
border-top-color: #6b7280;
|
|
72
|
-
border-radius: 50%;
|
|
73
|
-
animation: pro6pp-spin 0.6s linear infinite;
|
|
74
|
-
flex-shrink: 0;
|
|
70
|
+
.pro6pp-clear-button:active {
|
|
71
|
+
background-color: #f3f4f6;
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
.pro6pp-dropdown {
|
|
@@ -79,80 +76,129 @@
|
|
|
79
76
|
top: 100%;
|
|
80
77
|
left: 0;
|
|
81
78
|
right: 0;
|
|
82
|
-
z-index: 9999;
|
|
83
79
|
margin-top: 4px;
|
|
84
|
-
background:
|
|
85
|
-
border: 1px solid #
|
|
86
|
-
border-radius:
|
|
87
|
-
box-shadow: 0
|
|
88
|
-
|
|
80
|
+
background: #ffffff;
|
|
81
|
+
border: 1px solid #e5e7eb;
|
|
82
|
+
border-radius: 6px;
|
|
83
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
84
|
+
z-index: 9999;
|
|
85
|
+
padding: 0;
|
|
86
|
+
max-height: 280px;
|
|
89
87
|
overflow-y: auto;
|
|
90
88
|
display: flex;
|
|
91
89
|
flex-direction: column;
|
|
92
90
|
}
|
|
91
|
+
|
|
92
|
+
@media (max-height: 500px) {
|
|
93
|
+
.pro6pp-dropdown {
|
|
94
|
+
max-height: 180px;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
.pro6pp-list {
|
|
94
|
-
list-style: none
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
list-style: none;
|
|
100
|
+
margin: 0;
|
|
101
|
+
padding: 0;
|
|
102
|
+
width: 100%;
|
|
98
103
|
}
|
|
104
|
+
|
|
99
105
|
.pro6pp-item {
|
|
100
|
-
padding: 12px
|
|
106
|
+
padding: 12px 14px;
|
|
101
107
|
cursor: pointer;
|
|
102
108
|
display: flex;
|
|
103
|
-
flex-direction: row;
|
|
104
109
|
align-items: center;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
font-size: 15px;
|
|
111
|
+
line-height: 1.4;
|
|
112
|
+
color: #374151;
|
|
113
|
+
border-bottom: 1px solid #f3f4f6;
|
|
114
|
+
transition: background-color 0.1s;
|
|
115
|
+
flex-shrink: 0;
|
|
110
116
|
}
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
|
|
118
|
+
.pro6pp-item:last-child {
|
|
119
|
+
border-bottom: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@media (hover: hover) {
|
|
123
|
+
.pro6pp-item:hover, .pro6pp-item--active {
|
|
124
|
+
background-color: #f9fafb;
|
|
125
|
+
}
|
|
113
126
|
}
|
|
127
|
+
|
|
128
|
+
.pro6pp-item:active {
|
|
129
|
+
background-color: #f3f4f6;
|
|
130
|
+
}
|
|
131
|
+
|
|
114
132
|
.pro6pp-item__label {
|
|
115
|
-
font-weight:
|
|
116
|
-
flex-shrink:
|
|
133
|
+
font-weight: 400;
|
|
134
|
+
flex-shrink: 1;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.pro6pp-item__label--match {
|
|
141
|
+
font-weight: 520;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.pro6pp-item__label--unmatched {
|
|
145
|
+
font-weight: 400;
|
|
146
|
+
color: #4b5563;
|
|
117
147
|
}
|
|
148
|
+
|
|
118
149
|
.pro6pp-item__subtitle {
|
|
119
|
-
font-size: 14px;
|
|
120
150
|
color: #6b7280;
|
|
121
|
-
|
|
122
|
-
text-overflow: ellipsis;
|
|
123
|
-
flex-shrink: 1;
|
|
151
|
+
flex-shrink: 0;
|
|
124
152
|
}
|
|
153
|
+
|
|
125
154
|
.pro6pp-item__chevron {
|
|
126
|
-
|
|
155
|
+
color: #d1d5db;
|
|
127
156
|
display: flex;
|
|
128
157
|
align-items: center;
|
|
129
|
-
|
|
158
|
+
margin-left: auto;
|
|
130
159
|
padding-left: 8px;
|
|
131
160
|
}
|
|
161
|
+
|
|
132
162
|
.pro6pp-no-results {
|
|
133
|
-
padding: 16px;
|
|
163
|
+
padding: 24px 16px;
|
|
134
164
|
color: #6b7280;
|
|
135
|
-
font-size:
|
|
165
|
+
font-size: 15px;
|
|
136
166
|
text-align: center;
|
|
137
167
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
padding: 10px;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
flex-shrink: 0;
|
|
168
|
+
|
|
169
|
+
.pro6pp-loader-item {
|
|
170
|
+
padding: 10px 12px;
|
|
171
|
+
color: #6b7280;
|
|
172
|
+
font-size: 0.875rem;
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
gap: 8px;
|
|
177
|
+
background-color: #f9fafb;
|
|
178
|
+
border-top: 1px solid #f3f4f6;
|
|
150
179
|
}
|
|
151
|
-
|
|
152
|
-
|
|
180
|
+
|
|
181
|
+
.pro6pp-mini-spinner {
|
|
182
|
+
width: 14px;
|
|
183
|
+
height: 14px;
|
|
184
|
+
border: 2px solid #e5e7eb;
|
|
185
|
+
border-top-color: #6b7280;
|
|
186
|
+
border-radius: 50%;
|
|
187
|
+
animation: pro6pp-spin 0.6s linear infinite;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@media (max-width: 640px) {
|
|
191
|
+
.pro6pp-input {
|
|
192
|
+
font-size: 16px;
|
|
193
|
+
padding: 10px 12px;
|
|
194
|
+
}
|
|
195
|
+
.pro6pp-item {
|
|
196
|
+
padding: 10px 12px;
|
|
197
|
+
font-size: 14px;
|
|
198
|
+
}
|
|
153
199
|
}
|
|
154
200
|
|
|
155
201
|
@keyframes pro6pp-spin {
|
|
156
202
|
to { transform: rotate(360deg); }
|
|
157
203
|
}
|
|
158
|
-
`;0&&(module.exports={DEFAULT_STYLES,INITIAL_STATE,InferCore});
|
|
204
|
+
`;0&&(module.exports={DEFAULT_STYLES,INITIAL_STATE,InferCore,formatLabelByInputOrder,getHighlightSegments});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Supported ISO 3166-1 alpha-2 country codes.
|
|
3
3
|
*/
|
|
4
|
-
type CountryCode = 'NL' | 'DE';
|
|
4
|
+
type CountryCode = 'NL' | 'DE' | 'BE' | 'AT' | 'DK' | (string & {});
|
|
5
|
+
/**
|
|
6
|
+
* Supported language codes for address labels.
|
|
7
|
+
*/
|
|
8
|
+
type LanguageCode = 'nl' | 'fr' | 'de' | (string & {});
|
|
5
9
|
/**
|
|
6
10
|
* The current step in the address inference process.
|
|
7
11
|
* - `empty`: No input yet.
|
|
@@ -9,13 +13,13 @@ type CountryCode = 'NL' | 'DE';
|
|
|
9
13
|
* - `street`: User is selecting a street.
|
|
10
14
|
* - `city`: User is selecting a city.
|
|
11
15
|
* - `postcode`: User is entering a postcode.
|
|
12
|
-
* - `
|
|
13
|
-
* - `
|
|
14
|
-
* - `addition`: Selecting a
|
|
16
|
+
* - `street_number`: User is entering a street number.
|
|
17
|
+
* - `street_number_first`: Specialized mode where number is entered before street.
|
|
18
|
+
* - `addition`: Selecting a street number addition (e.g., 'A', 'III').
|
|
15
19
|
* - `direct`: Direct address hit (often via postcode).
|
|
16
20
|
* - `final`: A complete, valid address has been identified.
|
|
17
21
|
*/
|
|
18
|
-
type Stage = 'empty' | 'mixed' | 'street' | 'city' | 'postcode' | '
|
|
22
|
+
type Stage = 'empty' | 'mixed' | 'street' | 'city' | 'postcode' | 'street_number' | 'street_number_first' | 'addition' | 'direct' | 'final';
|
|
19
23
|
/**
|
|
20
24
|
* The standardized address object returned upon a successful final selection.
|
|
21
25
|
*/
|
|
@@ -24,15 +28,11 @@ interface AddressValue {
|
|
|
24
28
|
street: string;
|
|
25
29
|
/** The name of the city/locality. */
|
|
26
30
|
city: string;
|
|
27
|
-
/** The
|
|
31
|
+
/** The street number. */
|
|
28
32
|
street_number?: string | number;
|
|
29
|
-
/** The house number (numeric part). */
|
|
30
|
-
house_number?: string | number;
|
|
31
33
|
/** The postal code. */
|
|
32
34
|
postcode?: string;
|
|
33
|
-
/** The
|
|
34
|
-
postcode_full?: string;
|
|
35
|
-
/** The house number addition or suffix. */
|
|
35
|
+
/** The street number addition or suffix. */
|
|
36
36
|
addition?: string;
|
|
37
37
|
/** Allow for extra fields if API expands. */
|
|
38
38
|
[key: string]: unknown;
|
|
@@ -68,6 +68,8 @@ interface InferState {
|
|
|
68
68
|
suggestions: InferResult[];
|
|
69
69
|
/** Flag indicating if the current selection is a complete, valid address. */
|
|
70
70
|
isValid: boolean;
|
|
71
|
+
/** The complete address object if valid, otherwise null. */
|
|
72
|
+
value: AddressValue | null;
|
|
71
73
|
/** Flag indicating if the last API request failed. */
|
|
72
74
|
isError: boolean;
|
|
73
75
|
/** Flag indicating if a network request is currently in progress. */
|
|
@@ -126,6 +128,12 @@ interface InferConfig {
|
|
|
126
128
|
* @default 0
|
|
127
129
|
*/
|
|
128
130
|
maxRetries?: number;
|
|
131
|
+
/**
|
|
132
|
+
* Language code for response labels.
|
|
133
|
+
* Affects the language of returned address labels.
|
|
134
|
+
* Only applicable for BE country code.
|
|
135
|
+
*/
|
|
136
|
+
language?: LanguageCode;
|
|
129
137
|
/**
|
|
130
138
|
* Callback triggered whenever the internal state (suggestions, loading status, etc.) updates.
|
|
131
139
|
*/
|
|
@@ -137,6 +145,13 @@ interface InferConfig {
|
|
|
137
145
|
*/
|
|
138
146
|
onSelect?: (selection: AddressValue | string | null) => void;
|
|
139
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Represents a segment of text that should be highlighted or left plain.
|
|
150
|
+
*/
|
|
151
|
+
interface HighlightSegment {
|
|
152
|
+
text: string;
|
|
153
|
+
match: boolean;
|
|
154
|
+
}
|
|
140
155
|
|
|
141
156
|
/**
|
|
142
157
|
* The initial state of the address inference engine.
|
|
@@ -153,6 +168,7 @@ declare class InferCore {
|
|
|
153
168
|
private baseLimit;
|
|
154
169
|
private currentLimit;
|
|
155
170
|
private maxRetries;
|
|
171
|
+
private language?;
|
|
156
172
|
private fetcher;
|
|
157
173
|
private onStateChange;
|
|
158
174
|
private onSelect;
|
|
@@ -163,7 +179,6 @@ declare class InferCore {
|
|
|
163
179
|
state: InferState;
|
|
164
180
|
private abortController;
|
|
165
181
|
private debouncedFetch;
|
|
166
|
-
private isSelecting;
|
|
167
182
|
/**
|
|
168
183
|
* Initializes a new instance of the Infer engine.
|
|
169
184
|
* @param config The configuration object including API keys and callbacks.
|
|
@@ -184,7 +199,7 @@ declare class InferCore {
|
|
|
184
199
|
* Supports:
|
|
185
200
|
* - `ArrowUp`/`ArrowDown`: Navigate through the suggestion list.
|
|
186
201
|
* - `Enter`: Select the currently highlighted suggestion.
|
|
187
|
-
* - `Space`: Automatically inserts a comma if a numeric
|
|
202
|
+
* - `Space`: Automatically inserts a comma if a numeric street number is detected.
|
|
188
203
|
* @param event The keyboard event from the input element.
|
|
189
204
|
*/
|
|
190
205
|
handleKeyDown(event: KeyboardEvent | {
|
|
@@ -205,6 +220,12 @@ declare class InferCore {
|
|
|
205
220
|
private executeFetch;
|
|
206
221
|
private retry;
|
|
207
222
|
private mapResponseToState;
|
|
223
|
+
/**
|
|
224
|
+
* Reformats a suggestion's label based on the user's input order.
|
|
225
|
+
* If the suggestion has a structured value object, we reorder the label
|
|
226
|
+
* to match how the user typed the components.
|
|
227
|
+
*/
|
|
228
|
+
private reformatSuggestionLabel;
|
|
208
229
|
private updateQueryAndFetch;
|
|
209
230
|
private replaceLastSegment;
|
|
210
231
|
private getQueryPrefix;
|
|
@@ -214,6 +235,23 @@ declare class InferCore {
|
|
|
214
235
|
private debounce;
|
|
215
236
|
}
|
|
216
237
|
|
|
217
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Splits text into matched and unmatched segments based on a fuzzy query sequence.
|
|
240
|
+
* Consecutive matched characters are merged into single segments for cleaner rendering.
|
|
241
|
+
*/
|
|
242
|
+
declare function getHighlightSegments(text: string, query: string): HighlightSegment[];
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Formats a label for display based on the user's input order.
|
|
246
|
+
* Components the user typed appear first (in their order),
|
|
247
|
+
* followed by components they didn't type (new info from API).
|
|
248
|
+
*
|
|
249
|
+
* @param query The user's current query string
|
|
250
|
+
* @param value The structured address value from the API
|
|
251
|
+
* @returns A formatted label string
|
|
252
|
+
*/
|
|
253
|
+
declare function formatLabelByInputOrder(query: string, value: AddressValue): string;
|
|
254
|
+
|
|
255
|
+
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\n .pro6pp-input::placeholder {\n font-size: 16px;\n color: #a3a3a3;\n }\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: 32px;\n height: 32px;\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-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: 280px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n }\n\n @media (max-height: 500px) {\n .pro6pp-dropdown {\n max-height: 180px;\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: 15px;\n line-height: 1.4;\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: 400;\n flex-shrink: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .pro6pp-item__label--match {\n font-weight: 520;\n }\n\n .pro6pp-item__label--unmatched {\n font-weight: 400;\n color: #4b5563;\n }\n\n .pro6pp-item__subtitle {\n color: #6b7280;\n flex-shrink: 0;\n }\n\n .pro6pp-item__chevron {\n color: #d1d5db;\n display: flex;\n align-items: center;\n margin-left: auto;\n padding-left: 8px;\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-loader-item {\n padding: 10px 12px;\n color: #6b7280;\n font-size: 0.875rem;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background-color: #f9fafb;\n border-top: 1px solid #f3f4f6;\n }\n\n .pro6pp-mini-spinner {\n width: 14px;\n height: 14px;\n border: 2px solid #e5e7eb;\n border-top-color: #6b7280;\n border-radius: 50%;\n animation: pro6pp-spin 0.6s linear infinite;\n }\n\n @media (max-width: 640px) {\n .pro6pp-input {\n font-size: 16px;\n padding: 10px 12px;\n }\n .pro6pp-item {\n padding: 10px 12px;\n font-size: 14px;\n }\n }\n\n @keyframes pro6pp-spin {\n to { transform: rotate(360deg); }\n }\n";
|
|
218
256
|
|
|
219
|
-
export { type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, INITIAL_STATE, type InferConfig, InferCore, type InferResult, type InferState, type Stage };
|
|
257
|
+
export { type AddressValue, type CountryCode, DEFAULT_STYLES, type Fetcher, type HighlightSegment, INITIAL_STATE, type InferConfig, InferCore, type InferResult, type InferState, type LanguageCode, type Stage, formatLabelByInputOrder, getHighlightSegments };
|