@tpzdsp/next-toolkit 2.0.0 → 2.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpzdsp/next-toolkit",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A reusable React component library for Next.js applications",
5
5
  "engines": {
6
6
  "node": "^24.12.0",
@@ -3,73 +3,13 @@
3
3
  import { useCallback, useEffect, useId, useRef, useState } from 'react';
4
4
 
5
5
  import type Map from 'ol/Map';
6
+ import { ImSpinner2 } from 'react-icons/im';
7
+ import { IoClose, IoSearch } from 'react-icons/io5';
6
8
 
7
9
  import { groupResults } from './groupResults';
8
10
  import type { GeocoderResult } from './types';
9
11
  import { useDebounce } from '../../hooks/useDebounce';
10
12
 
11
- // SVG Icons matching Mapbox GL Geocoder style
12
- const SearchIcon = () => (
13
- <svg
14
- className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
15
- width="20"
16
- height="20"
17
- viewBox="0 0 18 18"
18
- fill="none"
19
- xmlns="http://www.w3.org/2000/svg"
20
- >
21
- <path
22
- d="M7.5 13.5C10.8137 13.5 13.5 10.8137 13.5 7.5C13.5 4.18629 10.8137 1.5 7.5 1.5C4.18629 1.5 1.5 4.18629 1.5 7.5C1.5 10.8137 4.18629 13.5 7.5 13.5Z"
23
- stroke="#757575"
24
- strokeWidth="1.5"
25
- strokeLinecap="round"
26
- strokeLinejoin="round"
27
- />
28
-
29
- <path
30
- d="M16.5 16.5L11.625 11.625"
31
- stroke="#757575"
32
- strokeWidth="1.5"
33
- strokeLinecap="round"
34
- strokeLinejoin="round"
35
- />
36
- </svg>
37
- );
38
-
39
- const ClearIcon = () => (
40
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
41
- <path
42
- d="M15 5L5 15M5 5L15 15"
43
- stroke="#757575"
44
- strokeWidth="2"
45
- strokeLinecap="round"
46
- strokeLinejoin="round"
47
- />
48
- </svg>
49
- );
50
-
51
- const LoadingIcon = () => (
52
- <svg
53
- className="animate-spin"
54
- width="26"
55
- height="26"
56
- viewBox="0 0 26 26"
57
- fill="none"
58
- xmlns="http://www.w3.org/2000/svg"
59
- >
60
- <circle
61
- cx="13"
62
- cy="13"
63
- r="10"
64
- stroke="#757575"
65
- strokeWidth="3"
66
- strokeLinecap="round"
67
- strokeDasharray="60"
68
- strokeDashoffset="40"
69
- />
70
- </svg>
71
- );
72
-
73
13
  // Typeahead defaults
74
14
  const DEFAULT_TYPEAHEAD_DELAY_MS = 500;
75
15
 
@@ -217,19 +157,11 @@ export const Geocoder = ({
217
157
  if (shouldZoomOut) {
218
158
  // Step 1: Zoom out, then combined pan + zoom in (like ol-geocoder)
219
159
  view.animate({ zoom: flyZoom, duration: zoomOutDuration }, () => {
220
- view.animate({
221
- center: result.center,
222
- zoom: finalZoom,
223
- duration: flyToDuration,
224
- });
160
+ view.animate({ center: result.center, zoom: finalZoom, duration: flyToDuration });
225
161
  });
226
162
  } else {
227
163
  // Combined pan + zoom in one smooth animation
228
- view.animate({
229
- center: result.center,
230
- zoom: finalZoom,
231
- duration: flyToDuration,
232
- });
164
+ view.animate({ center: result.center, zoom: finalZoom, duration: flyToDuration });
233
165
  }
234
166
  }
235
167
 
@@ -278,12 +210,14 @@ export const Geocoder = ({
278
210
  Search for a place
279
211
  </label>
280
212
 
281
- {/* Mapbox-style geocoder container */}
282
213
  <div
283
214
  className="relative bg-white rounded shadow-lg transition-all duration-200
284
215
  focus-within:ring-2 focus-within:ring-focus"
285
216
  >
286
- <SearchIcon />
217
+ <IoSearch
218
+ size={20}
219
+ className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-gray-400"
220
+ />
287
221
 
288
222
  <input
289
223
  ref={inputRef}
@@ -336,13 +270,12 @@ export const Geocoder = ({
336
270
  placeholder:text-gray-500 focus:outline-none focus:ring-0"
337
271
  />
338
272
 
339
- {/* Clear or Loading button */}
340
273
  <div className="absolute right-2 top-1/2 -translate-y-1/2">
341
274
  {(() => {
342
275
  if (isSearching) {
343
276
  return (
344
277
  <div className="p-1">
345
- <LoadingIcon />
278
+ <ImSpinner2 size={22} className="animate-spin text-gray-500" />
346
279
  </div>
347
280
  );
348
281
  }
@@ -355,7 +288,7 @@ export const Geocoder = ({
355
288
  aria-label="Clear search"
356
289
  className="p-1 hover:bg-gray-100 rounded transition-colors"
357
290
  >
358
- <ClearIcon />
291
+ <IoClose size={20} className="text-gray-500" />
359
292
  </button>
360
293
  );
361
294
  }
@@ -369,7 +302,7 @@ export const Geocoder = ({
369
302
  {open ? (
370
303
  <div className="absolute z-1000 mt-1.5 w-full bg-white rounded shadow-lg overflow-hidden">
371
304
  {results.length > 0 ? (
372
- <div id={listboxId} role="listbox" className="max-h-60 overflow-auto">
305
+ <div id={listboxId} role="listbox" tabIndex={-1} className="max-h-60 overflow-auto">
373
306
  {Object.entries(grouped).map(([group, items]) => (
374
307
  <div key={group} role="presentation">
375
308
  <div
@@ -382,21 +315,22 @@ export const Geocoder = ({
382
315
  <div role="group">
383
316
  {items.map((item) => {
384
317
  flatIndex += 1;
385
- const isActive = flatIndex === activeIndex;
318
+ const itemIndex = flatIndex;
319
+ const isActive = itemIndex === activeIndex;
386
320
  const isSelected = item.id === selectedId;
387
321
 
388
322
  return (
389
323
  <div
390
- id={`${listboxId}-option-${flatIndex}`}
324
+ id={`${listboxId}-option-${itemIndex}`}
391
325
  key={item.id}
392
326
  role="option"
393
327
  aria-selected={isSelected}
394
328
  tabIndex={-1}
395
- className={`cursor-pointer px-3 py-2 text-sm text-gray-800
329
+ className={`scroll-mt-8 cursor-pointer px-3 py-2 text-sm text-gray-800
396
330
  transition-colors ${isSelected ? 'bg-blue-50 font-semibold' : ''}
397
331
  ${isActive && !isSelected ? 'bg-gray-100' : ''}
398
332
  ${!isSelected && !isActive ? 'hover:bg-gray-50' : ''}`}
399
- onMouseEnter={() => setActiveIndex(flatIndex)}
333
+ onMouseEnter={() => setActiveIndex(itemIndex)}
400
334
  onMouseDown={() => selectResult(item)}
401
335
  >
402
336
  <div className="truncate">{item.label}</div>