@pro6pp/infer-js 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 CHANGED
@@ -1,104 +1,119 @@
1
- # @pro6pp/infer-js
1
+ # Pro6PP Infer JS SDK
2
2
 
3
3
  The official Vanilla JS SDK for the [Pro6PP Infer API](https://www.pro6pp.com/developer/infer/nl/parameters).
4
4
  A library that adds address autocompletion to any HTML input field.
5
5
 
6
6
  ## Installation
7
7
 
8
- ### Option 1: CDN
8
+ #### Option 1: CDN
9
9
 
10
- Simply add this script to your HTML file. It exposes a global `Pro6PP` variable.
10
+ Add this script to your HTML file. It exposes a global `Pro6PP` variable.
11
11
 
12
12
  ```html
13
- // TODO: add CDN src
14
- <script src=""></script>
13
+ <script src="https://unpkg.com/@pro6pp/infer-js"></script>
15
14
  ```
16
15
 
17
- ### Option 2: NPM
16
+ #### Option 2: NPM
18
17
 
19
- If you are using Webpack, Vite, or Rollup but not a framework like React.
18
+ If you are using a build tool like Webpack or Vite, but not a framework like React.
20
19
 
21
20
  ```bash
22
21
  npm install @pro6pp/infer-js
23
22
  ```
24
23
 
25
- > **Note:** If you are using React, use [`@pro6pp/infer-react`](../react) instead.
24
+ > **Note:** If you are using React, use [`@pro6pp/infer-react`](https://www.npmjs.com/package/@pro6pp/infer-react) instead.
25
+
26
+ #### Option 3: Direct download
27
+
28
+ You can also [download](https://unpkg.com/@pro6pp/infer-js@latest/dist/index.global.js) the latest `index.global.js` and place it in your project.
26
29
 
27
30
  ## Usage
28
31
 
29
- ### Option 1: CDN
32
+ #### Option 1: CDN
30
33
 
31
34
  1. Add the script to your page.
32
35
  2. Create an input field.
33
36
  3. Attach the core logic to that input using `Pro6PP.attach()`.
34
37
 
35
38
  ```html
36
- <!DOCTYPE html>
37
- <html lang="en">
38
- <head>
39
- <meta charset="UTF-8" />
40
- <title>Address Autocomplete</title>
41
- </head>
42
- <body>
43
- <label>Address:</label>
44
- // TODO: add CDN src
45
- <script src=""></script>
46
-
47
- <script>
48
- Pro6PP.attach('#my-address-input', {
49
- authKey: 'YOUR_AUTH_KEY',
50
- country: 'NL',
51
- onSelect: function (result) {
52
- console.log('Selected Address:', result);
53
- },
54
- });
55
- </script>
56
- </body>
57
- </html>
39
+ <label>Address:</label>
40
+ <input id="address-input" type="text" />
41
+
42
+ <!-- Inject the CDN -->
43
+ <script src="https://unpkg.com/@pro6pp/infer-js"></script>
44
+ <script>
45
+ Pro6PP.attach('#address-input', {
46
+ authKey: 'YOUR_AUTH_KEY',
47
+ country: 'NL',
48
+ onSelect: function (result) {
49
+ console.log('Selected Address:', result);
50
+ },
51
+ });
52
+ </script>
58
53
  ```
59
54
 
60
- ### Option 2: NPM
55
+ #### Option 2: NPM
56
+
57
+ 1. Create an input field.
58
+ 2. Import the `attach` function.
59
+ 3. Initialize the autocomplete on the input.
60
+
61
+ ```html
62
+ <input id="address-input" name="address" />
63
+ ```
61
64
 
62
65
  ```javascript
63
66
  import { attach } from '@pro6pp/infer-js';
64
67
 
65
- // attach to an input element directly
66
- const inputElement = document.querySelector('input[name="address"]');
68
+ const inputElement = document.getElementById('address-input');
67
69
 
68
70
  attach(inputElement, {
69
71
  authKey: 'YOUR_AUTH_KEY',
70
72
  country: 'NL',
71
73
  onSelect: (result) => {
72
- console.log(result);
74
+ console.log('Selected Address:', result);
73
75
  },
74
76
  });
75
77
  ```
76
78
 
77
- ## Styling
79
+ #### Option 3: Direct download
78
80
 
79
- By default, the SDK injects a small CSS block to make the dropdown look decent. If you want to control the styling with your own CSS, set `style: 'none'` in the config.
80
-
81
- HTML created by the SDK:
81
+ 1. [Download](https://unpkg.com/@pro6pp/infer-js@latest/dist/index.global.js) the latest `index.global.js`.
82
+ 2. Place it in your project and include it directly in your HTML.
82
83
 
83
84
  ```html
84
- <div class="pro6pp-autocomplete-wrapper">
85
- <input ... />
86
- <ul class="pro6pp-results">
87
- <li class="pro6pp-item">Suggestion 1</li>
88
- <li class="pro6pp-item pro6pp-selected">Suggestion 2</li>
89
- </ul>
90
- </div>
85
+ <script src="path/to/index.global.js"></script>
86
+ <script>
87
+ const instance = Pro6PP.attach('#my-input', {
88
+ authKey: 'YOUR_AUTH_KEY',
89
+ country: 'NL',
90
+ });
91
+ </script>
91
92
  ```
92
93
 
93
- You can target these classes in your CSS:
94
+ ## Styling
94
95
 
95
- ```css
96
- .pro6pp-results {
97
- background: white;
98
- border: 1px solid #ccc;
99
- }
96
+ By default, the SDK injects the necessary CSS for the dropdown. If you want to control the styling with your own styles, set `style: 'none'` in the config:
100
97
 
101
- .pro6pp-item:hover {
102
- background: #f0f0f0;
103
- }
98
+ ```js
99
+ attach(inputElement, {
100
+ authKey: '...',
101
+ country: 'NL',
102
+ style: 'none', // disables default styles
103
+ });
104
104
  ```
105
+
106
+ You can then target the following classes in your CSS:
107
+
108
+ | Class | Description |
109
+ | :----------------------- | :-------------------------------------------------------- |
110
+ | `.pro6pp-wrapper` | The container element wrapping the input and dropdown. |
111
+ | `.pro6pp-input` | The input element itself. |
112
+ | `.pro6pp-loader` | The loading spinner shown during API requests. |
113
+ | `.pro6pp-dropdown` | The `<ul>` list containing the suggestions. |
114
+ | `.pro6pp-item` | A single suggestion item (`<li>`). |
115
+ | `.pro6pp-item--active` | The currently highlighted item (for keyboard navigation). |
116
+ | `.pro6pp-item__label` | The main text/label of a suggestion. |
117
+ | `.pro6pp-item__subtitle` | The secondary text (e.g., city or result count). |
118
+ | `.pro6pp-item__chevron` | The icon indicating a folder/expandable result. |
119
+ | `.pro6pp-no-results` | The message shown when no suggestions are found. |
package/dist/index.cjs ADDED
@@ -0,0 +1,189 @@
1
+ "use strict";var g=Object.defineProperty,k=Object.defineProperties,A=Object.getOwnPropertyDescriptor,_=Object.getOwnPropertyDescriptors,B=Object.getOwnPropertyNames,w=Object.getOwnPropertySymbols;var I=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable;var E=(o,t,e)=>t in o?g(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e,c=(o,t)=>{for(var e in t||(t={}))I.call(t,e)&&E(o,e,t[e]);if(w)for(var e of w(t))D.call(t,e)&&E(o,e,t[e]);return o},m=(o,t)=>k(o,_(t));var F=(o,t)=>{for(var e in t)g(o,e,{get:t[e],enumerable:!0})},R=(o,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of B(t))!I.call(o,n)&&n!==e&&g(o,n,{get:()=>t[n],enumerable:!(i=A(t,n))||i.enumerable});return o};var N=o=>R(g({},"__esModule",{value:!0}),o);var U={};F(U,{InferJS:()=>x,attach:()=>$});module.exports=N(U);var f={API_URL:"https://api.pro6pp.nl/v2",LIMIT:20,DEBOUNCE_MS:150,MIN_DEBOUNCE_MS:50,MAX_RETRIES:0},L={DIGITS_1_3:/^[0-9]{1,3}$/},T={query:"",stage:null,cities:[],streets:[],suggestions:[],isValid:!1,isError:!1,isLoading:!1,hasMore:!1,selectedSuggestionIndex:-1},b=class{constructor(t){this.abortController=null;this.country=t.country,this.authKey=t.authKey,this.explicitApiUrl=t.apiUrl,this.baseLimit=t.limit||f.LIMIT,this.currentLimit=this.baseLimit;let e=t.maxRetries!==void 0?t.maxRetries:f.MAX_RETRIES;this.maxRetries=Math.max(0,Math.min(e,10)),this.fetcher=t.fetcher||((r,s)=>fetch(r,s)),this.onStateChange=t.onStateChange||(()=>{}),this.onSelect=t.onSelect||(()=>{}),this.state=c({},T);let i=t.debounceMs!==void 0?t.debounceMs:f.DEBOUNCE_MS,n=Math.max(i,f.MIN_DEBOUNCE_MS);this.debouncedFetch=this.debounce(r=>this.executeFetch(r),n)}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 r=this.state.selectedSuggestionIndex+1;r>=i&&(r=0),this.updateState({selectedSuggestionIndex:r});return}if(t.key==="ArrowUp"){t.preventDefault();let r=this.state.selectedSuggestionIndex-1;r<0&&(r=i-1),this.updateState({selectedSuggestionIndex:r});return}if(t.key==="Enter"&&this.state.selectedSuggestionIndex>=0){t.preventDefault();let s=[...this.state.cities,...this.state.streets,...this.state.suggestions][this.state.selectedSuggestionIndex];s&&(this.selectItem(s),this.updateState({selectedSuggestionIndex:-1}));return}}let n=e.value;if(t.key===" "&&this.shouldAutoInsertComma(n)){t.preventDefault();let r=`${n.trim()}, `;this.updateQueryAndFetch(r)}}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 n=typeof t!="string"&&typeof t.value=="object"?t.value:void 0,r=!!n&&Object.keys(n).length>0;if(this.state.stage==="final"||r){let u=e;if(n&&Object.keys(n).length>0){let{street:l,street_number:a,city:h}=n;l&&a&&h&&(u=`${l} ${a}, ${h}`)}return this.finishSelection(u,n),!0}let s=typeof t!="string"?t.subtitle:null;return this.processSelection(i,s),!1}shouldAutoInsertComma(t){if(!t.includes(",")&&L.DIGITS_1_3.test(t.trim()))return!0;if(this.state.stage==="street_number"){let i=this.getCurrentFragment(t);return L.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:n}=this.state,r=n;if(e&&(i==="city"||i==="street"||i==="mixed")){if(i==="city")r=`${e}, ${t}, `;else{let a=this.getQueryPrefix(n);r=a?`${a} ${t}, ${e}, `:`${t}, ${e}, `}this.updateQueryAndFetch(r);return}if(i==="direct"||i==="addition"){this.finishSelection(t);return}!n.includes(",")&&(i==="city"||i==="street"||i==="street_number_first")?r=`${t}, `:(r=this.replaceLastSegment(n,t),i!=="street_number"&&(r+=", ")),this.updateQueryAndFetch(r)}executeFetch(t,e=0){var a,h;let i=(t||"").toString();if(!i.trim()){(a=this.abortController)==null||a.abort(),this.resetState();return}e===0&&(this.updateState({isError:!1}),this.abortController&&this.abortController.abort(),this.abortController=new AbortController);let n=(h=this.abortController)==null?void 0:h.signal,r=this.explicitApiUrl?this.explicitApiUrl:`${f.API_URL}/infer/${this.country.toLowerCase()}`,s=new URLSearchParams({country:this.country.toLowerCase(),query:i,limit:this.currentLimit.toString()});this.authKey&&s.set("authKey",this.authKey);let u=r.includes("?")?"&":"?",l=`${r}${u}${s.toString()}`;this.fetcher(l,{signal:n}).then(d=>{if(!d.ok){if(e<this.maxRetries&&(d.status>=500||d.status===429))return this.retry(t,e,n);throw new Error("Network error")}return d.json()}).then(d=>{d&&this.mapResponseToState(d)}).catch(d=>{if(d.name!=="AbortError"){if(e<this.maxRetries)return this.retry(t,e,n);this.updateState({isError:!0,isLoading:!1})}})}retry(t,e,i){if(i!=null&&i.aborted)return;let n=Math.pow(2,e)*200;setTimeout(()=>{i!=null&&i.aborted||this.executeFetch(t,e+1)},n)}mapResponseToState(t){var u,l;let e={stage:t.stage,isLoading:!1},i=t.suggestions||[],n=[],r=new Set;for(let a of i){let h=`${a.label}|${a.subtitle||""}|${JSON.stringify(a.value||{})}`;r.has(h)||(r.add(h),n.push(a))}let s=n.length+(((u=t.cities)==null?void 0:u.length)||0)+(((l=t.streets)==null?void 0:l.length)||0);e.hasMore=s>=this.currentLimit,t.stage==="mixed"?(e.cities=t.cities||[],e.streets=t.streets||[],e.suggestions=[]):(e.suggestions=n,e.cities=[],e.streets=[]),e.isValid=t.stage==="final",this.updateState(e)}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){var e;return((e=t.split(",").slice(-1)[0])!=null?e:"").trim()}resetState(){this.updateState(m(c({},T),{query:this.state.query}))}updateState(t){this.state=c(c({},this.state),t),this.onStateChange(this.state)}debounce(t,e){let i,n=(...r)=>{i&&clearTimeout(i),i=setTimeout(()=>t.apply(this,r),e)};return n.cancel=()=>{i&&(clearTimeout(i),i=void 0)},n}};function M(o,t){if(!t||!o)return[{text:o,match:!1}];let e=[],i=o.toLowerCase(),n=t.toLowerCase(),r=0,s=0;for(let a=0;a<o.length;a++){if(!(r<t.length&&i[a]===n[r]))continue;a>s&&e.push({text:o.slice(s,a),match:!1}),e.push({text:o[a],match:!0}),r++,s=a+1}return s<o.length&&e.push({text:o.slice(s),match:!1}),r===t.length?e:[{text:o,match:!1}]}var v=`
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
+ `;var x=class{constructor(t,e){this.isOpen=!1;var r;let i=typeof t=="string"?document.querySelector(t):t;if(!i)throw new Error("InferJS: Target element not found.");if(this.noResultsText=e.noResultsText||"No results found",this.loadMoreText=e.loadMoreText||"Show more results...",this.showClearButton=e.showClearButton!==!1,this.useDefaultStyles=e.style!=="none",this.useDefaultStyles&&this.injectStyles(),this.wrapper=document.createElement("div"),this.wrapper.className="pro6pp-wrapper",i instanceof HTMLInputElement?(this.input=i,(r=this.input.parentNode)==null||r.insertBefore(this.wrapper,this.input),this.wrapper.appendChild(this.input)):(i.appendChild(this.wrapper),this.input=document.createElement("input"),this.input.type="text",e.placeholder&&(this.input.placeholder=e.placeholder),this.wrapper.appendChild(this.input)),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("autocorrect","off"),this.input.setAttribute("autocapitalize","none"),this.input.setAttribute("spellcheck","false"),this.input.setAttribute("inputmode","search"),this.input.setAttribute("enterkeyhint","search"),this.useDefaultStyles&&this.input.classList.add("pro6pp-input"),e.inputClass){let s=e.inputClass.split(" ");this.input.classList.add(...s)}let n=document.createElement("div");n.className="pro6pp-input-addons",this.wrapper.appendChild(n),this.loader=document.createElement("div"),this.loader.className="pro6pp-loader",this.loader.style.display="none",n.appendChild(this.loader),this.clearButton=document.createElement("button"),this.clearButton.type="button",this.clearButton.className="pro6pp-clear-button",this.clearButton.setAttribute("aria-label","Clear input"),this.clearButton.style.display="none",this.clearButton.innerHTML=`
181
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
182
+ <line x1="18" y1="6" x2="6" y2="18"></line>
183
+ <line x1="6" y1="6" x2="18" y2="18"></line>
184
+ </svg>
185
+ `,n.appendChild(this.clearButton),this.dropdown=document.createElement("div"),this.dropdown.className="pro6pp-dropdown",this.dropdown.style.display="none",this.wrapper.appendChild(this.dropdown),this.list=document.createElement("ul"),this.list.className="pro6pp-list",this.list.setAttribute("role","listbox"),this.dropdown.appendChild(this.list),this.loadMoreButton=document.createElement("button"),this.loadMoreButton.type="button",this.loadMoreButton.className="pro6pp-load-more",this.loadMoreButton.textContent=this.loadMoreText,this.loadMoreButton.style.display="none",this.dropdown.appendChild(this.loadMoreButton),this.core=new b(m(c({},e),{onStateChange:s=>{this.render(s),e.onStateChange&&e.onStateChange(s)},onSelect:s=>{typeof s=="string"?this.input.value=s:s&&typeof s=="object"&&(this.input.value=this.core.state.query),e.onSelect&&e.onSelect(s)}})),this.bindEvents()}injectStyles(){let t="pro6pp-styles";if(!document.getElementById(t)){let e=document.createElement("style");e.id=t,e.textContent=v,document.head.appendChild(e)}}bindEvents(){this.input.addEventListener("input",t=>{let e=t.target.value;this.isOpen=!0,this.core.handleInput(e)}),this.input.addEventListener("keydown",t=>{this.core.handleKeyDown(t)}),this.clearButton.addEventListener("click",()=>{this.core.handleInput(""),this.input.focus()}),this.loadMoreButton.addEventListener("click",t=>{t.preventDefault(),this.core.loadMore()}),document.addEventListener("mousedown",t=>{this.wrapper.contains(t.target)||(this.isOpen=!1,this.dropdown.style.display="none")}),this.input.addEventListener("focus",()=>{this.isOpen=!0,this.render(this.core.state)})}render(t){this.input.value!==t.query&&(this.input.value=t.query),this.loader.style.display=t.isLoading?"block":"none",this.showClearButton&&(this.clearButton.style.display=t.query.length>0?"flex":"none"),this.list.innerHTML="";let e=[...t.cities,...t.streets,...t.suggestions],i=e.length>0,n=!t.isLoading&&!t.isError&&t.query.length>0&&!i&&!t.isValid;if(!(this.isOpen&&(i||t.isLoading||n))){this.dropdown.style.display="none";return}if(this.dropdown.style.display="block",this.loadMoreButton.style.display=t.hasMore?"block":"none",t.isLoading&&!i){let s=document.createElement("li");s.className="pro6pp-no-results",s.textContent="Loading suggestions...",this.list.appendChild(s);return}if(n){let s=document.createElement("li");s.className="pro6pp-no-results",s.textContent=this.noResultsText,this.list.appendChild(s);return}e.forEach((s,u)=>{if(!s.label)return;let l=document.createElement("li");l.className="pro6pp-item",u===t.selectedSuggestionIndex&&l.classList.add("pro6pp-item--active"),l.setAttribute("role","option"),l.setAttribute("aria-selected",u===t.selectedSuggestionIndex?"true":"false");let a=document.createElement("span");a.className="pro6pp-item__label",M(s.label,t.query).forEach(({text:p,match:C})=>{if(C){let y=document.createElement("strong");y.className="pro6pp-item__label--match",y.textContent=p,a.appendChild(y)}else a.appendChild(document.createTextNode(p))}),l.appendChild(a);let d=s.count!==void 0&&s.count!==null?s.count:"",S=s.subtitle||d;if(S!==""){let p=document.createElement("span");p.className="pro6pp-item__subtitle",p.textContent=`, ${S}`,l.appendChild(p)}if(s.value===void 0||s.value===null){let p=document.createElement("div");p.className="pro6pp-item__chevron",p.innerHTML=`
186
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
187
+ <polyline points="9 18 15 12 9 6"></polyline>
188
+ </svg>
189
+ `,l.appendChild(p)}l.onmousedown=p=>p.preventDefault(),l.onclick=p=>{p.stopPropagation(),this.core.selectItem(s)?(this.isOpen=!1,this.dropdown.style.display="none"):setTimeout(()=>this.input.focus(),0)},this.list.appendChild(l)})}};function $(o,t){return new x(o,t)}0&&(module.exports={InferJS,attach});
package/dist/index.d.mts CHANGED
@@ -1,20 +1,74 @@
1
1
  import { InferConfig } from '@pro6pp/infer-core';
2
2
 
3
+ /**
4
+ * Configuration options for the JS Infer SDK.
5
+ */
3
6
  interface InferJSConfig extends InferConfig {
7
+ /**
8
+ * The styling theme to apply.
9
+ * - `default`: Injects the standard Pro6PP CSS theme.
10
+ * - `none`: No styles are applied, allowing for custom CSS.
11
+ * @default 'default'
12
+ */
4
13
  style?: 'default' | 'none';
14
+ /**
15
+ * Custom placeholder text for the input field.
16
+ */
17
+ placeholder?: string;
18
+ /**
19
+ * Additional CSS classes to add to the input element.
20
+ */
21
+ inputClass?: string;
22
+ /**
23
+ * The text to display when no suggestions are found.
24
+ * @default 'No results found'
25
+ */
26
+ noResultsText?: string;
27
+ /**
28
+ * The text to show on the load more button.
29
+ * @default 'Show more results...'
30
+ */
31
+ loadMoreText?: string;
32
+ /**
33
+ * If true, shows a clear button when the input is not empty.
34
+ * @default true
35
+ */
36
+ showClearButton?: boolean;
5
37
  }
38
+ /**
39
+ * The JS implementation of the Pro6PP Infer SDK.
40
+ * This class manages the DOM elements, event listeners, and rendering for the autocomplete UI.
41
+ */
6
42
  declare class InferJS {
7
43
  private core;
8
44
  private input;
9
45
  private list;
46
+ private dropdown;
10
47
  private wrapper;
48
+ private loader;
49
+ private clearButton;
50
+ private loadMoreButton;
11
51
  private useDefaultStyles;
12
- constructor(target: string | HTMLInputElement, config: InferJSConfig);
52
+ private noResultsText;
53
+ private loadMoreText;
54
+ private showClearButton;
55
+ private isOpen;
56
+ /**
57
+ * Initializes the Infer logic on a target element.
58
+ * @param target Either a CSS selector string or a direct HTMLElement.
59
+ * @param config Configuration options for the API and UI.
60
+ */
61
+ constructor(target: string | HTMLElement, config: InferJSConfig);
13
62
  private injectStyles;
14
- private setupDOM;
15
63
  private bindEvents;
16
64
  private render;
17
65
  }
18
- declare function attach(target: string | HTMLInputElement, config: InferJSConfig): InferJS;
66
+ /**
67
+ * A helper to initialize the Pro6PP Infer SDK on a target element.
68
+ * @param target A CSS selector string or HTMLElement.
69
+ * @param config Configuration for the SDK.
70
+ * @returns An instance of InferJS.
71
+ */
72
+ declare function attach(target: string | HTMLElement, config: InferJSConfig): InferJS;
19
73
 
20
74
  export { InferJS, type InferJSConfig, attach };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,74 @@
1
1
  import { InferConfig } from '@pro6pp/infer-core';
2
2
 
3
+ /**
4
+ * Configuration options for the JS Infer SDK.
5
+ */
3
6
  interface InferJSConfig extends InferConfig {
7
+ /**
8
+ * The styling theme to apply.
9
+ * - `default`: Injects the standard Pro6PP CSS theme.
10
+ * - `none`: No styles are applied, allowing for custom CSS.
11
+ * @default 'default'
12
+ */
4
13
  style?: 'default' | 'none';
14
+ /**
15
+ * Custom placeholder text for the input field.
16
+ */
17
+ placeholder?: string;
18
+ /**
19
+ * Additional CSS classes to add to the input element.
20
+ */
21
+ inputClass?: string;
22
+ /**
23
+ * The text to display when no suggestions are found.
24
+ * @default 'No results found'
25
+ */
26
+ noResultsText?: string;
27
+ /**
28
+ * The text to show on the load more button.
29
+ * @default 'Show more results...'
30
+ */
31
+ loadMoreText?: string;
32
+ /**
33
+ * If true, shows a clear button when the input is not empty.
34
+ * @default true
35
+ */
36
+ showClearButton?: boolean;
5
37
  }
38
+ /**
39
+ * The JS implementation of the Pro6PP Infer SDK.
40
+ * This class manages the DOM elements, event listeners, and rendering for the autocomplete UI.
41
+ */
6
42
  declare class InferJS {
7
43
  private core;
8
44
  private input;
9
45
  private list;
46
+ private dropdown;
10
47
  private wrapper;
48
+ private loader;
49
+ private clearButton;
50
+ private loadMoreButton;
11
51
  private useDefaultStyles;
12
- constructor(target: string | HTMLInputElement, config: InferJSConfig);
52
+ private noResultsText;
53
+ private loadMoreText;
54
+ private showClearButton;
55
+ private isOpen;
56
+ /**
57
+ * Initializes the Infer logic on a target element.
58
+ * @param target Either a CSS selector string or a direct HTMLElement.
59
+ * @param config Configuration options for the API and UI.
60
+ */
61
+ constructor(target: string | HTMLElement, config: InferJSConfig);
13
62
  private injectStyles;
14
- private setupDOM;
15
63
  private bindEvents;
16
64
  private render;
17
65
  }
18
- declare function attach(target: string | HTMLInputElement, config: InferJSConfig): InferJS;
66
+ /**
67
+ * A helper to initialize the Pro6PP Infer SDK on a target element.
68
+ * @param target A CSS selector string or HTMLElement.
69
+ * @param config Configuration for the SDK.
70
+ * @returns An instance of InferJS.
71
+ */
72
+ declare function attach(target: string | HTMLElement, config: InferJSConfig): InferJS;
19
73
 
20
74
  export { InferJS, type InferJSConfig, attach };