@ta-interaktiv/react-municipality-search 1.4.1 → 1.5.3
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/CHANGELOG.md +30 -0
- package/dist/index.js +92 -91
- package/dist/index.js.map +1 -1
- package/dist/municipalitySearch.d.ts.map +1 -1
- package/es/index.js +92 -91
- package/es/index.js.map +1 -1
- package/es/municipalitySearch.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/municipalitySearch.tsx +181 -92
- package/src/styles.module.css +0 -77
|
@@ -3,12 +3,9 @@ import React, { ChangeEvent, Component } from 'react'
|
|
|
3
3
|
import '@ta-interaktiv/semantic-ui/semantic/dist/components/icon.css'
|
|
4
4
|
import '@ta-interaktiv/semantic-ui/semantic/dist/components/input.css'
|
|
5
5
|
import '@ta-interaktiv/semantic-ui/semantic/dist/components/label.css'
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
config as springConfig,
|
|
10
|
-
Transition,
|
|
11
|
-
} from 'react-spring'
|
|
6
|
+
// import './styles.scss'
|
|
7
|
+
import styled from 'styled-components'
|
|
8
|
+
import { animated, config as springConfig, Transition } from 'react-spring'
|
|
12
9
|
import IntlMessageFormat from 'intl-messageformat'
|
|
13
10
|
import de from './locales/de.yml'
|
|
14
11
|
import fr from './locales/fr.yml'
|
|
@@ -133,7 +130,7 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
133
130
|
public static defaultProps = {
|
|
134
131
|
municipalityData: '2021v3',
|
|
135
132
|
locale: 'de',
|
|
136
|
-
numberOfLastSelectedMunicipalities: 1
|
|
133
|
+
numberOfLastSelectedMunicipalities: 1
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
public state = {
|
|
@@ -141,14 +138,14 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
141
138
|
results: [],
|
|
142
139
|
value: '',
|
|
143
140
|
error: null,
|
|
144
|
-
municipalities: []
|
|
141
|
+
municipalities: []
|
|
145
142
|
}
|
|
146
143
|
|
|
147
144
|
private resetComponent = () => {
|
|
148
145
|
this.setState({
|
|
149
146
|
isLoading: false,
|
|
150
147
|
results: [],
|
|
151
|
-
value: ''
|
|
148
|
+
value: ''
|
|
152
149
|
})
|
|
153
150
|
}
|
|
154
151
|
|
|
@@ -205,7 +202,7 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
205
202
|
window.dataLayer.push({
|
|
206
203
|
event: 'Interactions',
|
|
207
204
|
event_action: 'input',
|
|
208
|
-
event_label: `search:for_${searchTerm}:results_${results.length}
|
|
205
|
+
event_label: `search:for_${searchTerm}:results_${results.length}`
|
|
209
206
|
})
|
|
210
207
|
}
|
|
211
208
|
|
|
@@ -217,7 +214,7 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
217
214
|
searchTerm: string
|
|
218
215
|
): Municipality[] {
|
|
219
216
|
const map = new Map()
|
|
220
|
-
arr.forEach(
|
|
217
|
+
arr.forEach(v => {
|
|
221
218
|
if (this.props.dedupe) {
|
|
222
219
|
map.set(v.ORTNAME + v.GDENR, v)
|
|
223
220
|
} else {
|
|
@@ -276,48 +273,57 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
276
273
|
.fetch(url, {
|
|
277
274
|
method: 'GET',
|
|
278
275
|
mode: 'no-cors',
|
|
279
|
-
credentials: 'include'
|
|
276
|
+
credentials: 'include'
|
|
280
277
|
})
|
|
281
|
-
.then(
|
|
278
|
+
.then(response => {
|
|
282
279
|
console.log(response)
|
|
283
280
|
})
|
|
284
|
-
.catch(
|
|
281
|
+
.catch(error => {
|
|
285
282
|
console.log(error)
|
|
286
283
|
})
|
|
287
284
|
}
|
|
288
285
|
|
|
289
286
|
private pushToLocalStorageArray = (newMuni: Municipality) => {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
let prevSelectedMunicipalities
|
|
288
|
+
let newArray: Municipality[] = []
|
|
289
|
+
const localStorageItemName = 'selectedMunicipalities'
|
|
290
|
+
try {
|
|
291
|
+
prevSelectedMunicipalities =
|
|
292
|
+
localStorage.getItem(localStorageItemName) ?? JSON.stringify([])
|
|
293
|
+
newArray = JSON.parse(prevSelectedMunicipalities)
|
|
294
|
+
} catch {
|
|
295
|
+
console.warn('🗄 localStorage is not available')
|
|
296
|
+
}
|
|
295
297
|
// limit the number of stored municipalities
|
|
296
298
|
const slicedArray = newArray.slice(0, 15)
|
|
297
299
|
slicedArray.unshift(newMuni)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
muni
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
300
|
+
try {
|
|
301
|
+
// deduplicate the array and stringify it for localStorage
|
|
302
|
+
localStorage.setItem(
|
|
303
|
+
localStorageItemName,
|
|
304
|
+
JSON.stringify([
|
|
305
|
+
...new Map(
|
|
306
|
+
slicedArray.map((muni: Municipality) => [
|
|
307
|
+
muni.ORTNAME + muni.GDENR,
|
|
308
|
+
muni
|
|
309
|
+
])
|
|
310
|
+
).values()
|
|
311
|
+
])
|
|
312
|
+
)
|
|
313
|
+
} catch {
|
|
314
|
+
console.warn('🗄 localStorage is not writeable')
|
|
315
|
+
}
|
|
310
316
|
}
|
|
311
317
|
|
|
312
318
|
private getMunicipalitiesData = () => {
|
|
313
319
|
this.setState({
|
|
314
|
-
isLoading: true
|
|
320
|
+
isLoading: true
|
|
315
321
|
})
|
|
316
322
|
|
|
317
323
|
return fetch(
|
|
318
324
|
`https://interaktiv.tagesanzeiger.ch/static/gemeindesuche/${this.props.municipalityData}.json`
|
|
319
325
|
)
|
|
320
|
-
.then(
|
|
326
|
+
.then(res => {
|
|
321
327
|
if (!res.ok) {
|
|
322
328
|
throw new MunicipalityDownloadError(
|
|
323
329
|
`Download error: ${res.status}: ${res.statusText}.`
|
|
@@ -325,13 +331,14 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
325
331
|
}
|
|
326
332
|
return res.json()
|
|
327
333
|
})
|
|
328
|
-
.then(
|
|
334
|
+
.then(data => {
|
|
329
335
|
this.setState({
|
|
330
336
|
isLoading: false,
|
|
331
|
-
municipalities: data
|
|
337
|
+
municipalities: data
|
|
332
338
|
})
|
|
339
|
+
return Promise.resolve(data)
|
|
333
340
|
})
|
|
334
|
-
.catch(
|
|
341
|
+
.catch(error => {
|
|
335
342
|
if (error instanceof MunicipalityDownloadError) {
|
|
336
343
|
console.log(error)
|
|
337
344
|
console.info(
|
|
@@ -342,7 +349,7 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
342
349
|
)
|
|
343
350
|
this.setState({
|
|
344
351
|
isLoading: false,
|
|
345
|
-
error: 'error.municipalitiesNotDownloaded'
|
|
352
|
+
error: 'error.municipalitiesNotDownloaded'
|
|
346
353
|
})
|
|
347
354
|
} else {
|
|
348
355
|
throw error
|
|
@@ -361,24 +368,31 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
361
368
|
|
|
362
369
|
/** Component mounting */
|
|
363
370
|
public componentDidMount() {
|
|
364
|
-
// set the municipalities from localStorage as results
|
|
365
|
-
const locallyStoredMunicipalitiesArray = localStorage.getItem(
|
|
366
|
-
'selectedMunicipalities_' + this.props.municipalityData
|
|
367
|
-
)
|
|
368
|
-
if (
|
|
369
|
-
this.props.showLastSelectedMunicipalities &&
|
|
370
|
-
locallyStoredMunicipalitiesArray
|
|
371
|
-
) {
|
|
372
|
-
const parsed = JSON.parse(
|
|
373
|
-
locallyStoredMunicipalitiesArray
|
|
374
|
-
) as Municipality[]
|
|
375
|
-
const muniCount = this.props.numberOfLastSelectedMunicipalities ?? 1
|
|
376
|
-
this.setState({ results: parsed.slice(0, muniCount) })
|
|
377
|
-
}
|
|
378
|
-
|
|
379
371
|
// Download municipalities list
|
|
380
372
|
// This is a preliminary measure, before we get a proper API
|
|
381
|
-
this.getMunicipalitiesData()
|
|
373
|
+
this.getMunicipalitiesData().then(data => {
|
|
374
|
+
// set the municipalities from localStorage as results
|
|
375
|
+
let locallyStoredMunicipalitiesArray
|
|
376
|
+
try {
|
|
377
|
+
locallyStoredMunicipalitiesArray = localStorage.getItem(
|
|
378
|
+
'selectedMunicipalities'
|
|
379
|
+
)
|
|
380
|
+
} catch {
|
|
381
|
+
console.warn('🗄 localStorage is not available')
|
|
382
|
+
}
|
|
383
|
+
if (
|
|
384
|
+
this.props.showLastSelectedMunicipalities &&
|
|
385
|
+
locallyStoredMunicipalitiesArray
|
|
386
|
+
) {
|
|
387
|
+
const parsed = JSON.parse(
|
|
388
|
+
locallyStoredMunicipalitiesArray
|
|
389
|
+
) as Municipality[]
|
|
390
|
+
const allIDs = data.map(muni => muni.GDENR)
|
|
391
|
+
const filtered = parsed.filter(muni => allIDs.includes(muni.GDENR))
|
|
392
|
+
const muniCount = this.props.numberOfLastSelectedMunicipalities ?? 3
|
|
393
|
+
this.setState({ results: filtered.slice(0, muniCount) })
|
|
394
|
+
}
|
|
395
|
+
})
|
|
382
396
|
}
|
|
383
397
|
|
|
384
398
|
public render() {
|
|
@@ -414,13 +428,13 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
414
428
|
: 'left pointing'
|
|
415
429
|
|
|
416
430
|
return (
|
|
417
|
-
<
|
|
418
|
-
<
|
|
431
|
+
<MunicipalitySearchContainer className='municipality-search'>
|
|
432
|
+
<InputRow className='inputRow'>
|
|
419
433
|
<div className='ui left icon input'>
|
|
420
|
-
<
|
|
434
|
+
<FlexInput
|
|
421
435
|
id='search'
|
|
422
436
|
type='text'
|
|
423
|
-
className=
|
|
437
|
+
className='prompt flexInput'
|
|
424
438
|
placeholder={
|
|
425
439
|
this.props.placeholder || (t('placeholder') as string)
|
|
426
440
|
}
|
|
@@ -430,7 +444,20 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
430
444
|
}
|
|
431
445
|
onChange={this.handleSearchChange}
|
|
432
446
|
/>
|
|
433
|
-
<
|
|
447
|
+
<Icon>
|
|
448
|
+
<svg
|
|
449
|
+
width='18'
|
|
450
|
+
height='18'
|
|
451
|
+
viewBox='0 0 18 18'
|
|
452
|
+
fill='none'
|
|
453
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
454
|
+
stroke='black'
|
|
455
|
+
strokeWidth='3'
|
|
456
|
+
>
|
|
457
|
+
<circle cx='7.5' cy='7.5' r='6' />
|
|
458
|
+
<line x1='11' y1='11' x2='17' y2='17' />
|
|
459
|
+
</svg>
|
|
460
|
+
</Icon>
|
|
434
461
|
</div>
|
|
435
462
|
{!this.props.hideTooltip && (
|
|
436
463
|
<div>
|
|
@@ -444,9 +471,9 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
444
471
|
</label>
|
|
445
472
|
</div>
|
|
446
473
|
)}
|
|
447
|
-
</
|
|
474
|
+
</InputRow>
|
|
448
475
|
{showResults(results) && (
|
|
449
|
-
<
|
|
476
|
+
<Results className='results'>
|
|
450
477
|
{showResults(results) && (
|
|
451
478
|
<Transition
|
|
452
479
|
native
|
|
@@ -459,48 +486,110 @@ export class MunicipalitySearch extends Component<Props, State> {
|
|
|
459
486
|
leave={{ transform: 'translate(0, 60px)', opacity: 0 }}
|
|
460
487
|
config={springConfig.gentle}
|
|
461
488
|
>
|
|
462
|
-
{(values, result)
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
489
|
+
{(values, result) => (
|
|
490
|
+
<animated.div
|
|
491
|
+
onClick={() => {
|
|
492
|
+
this.props.onSelectionHandler(result)
|
|
493
|
+
// this.sendToDataPipeline(result.PLZ4)
|
|
494
|
+
if (this.props.showLastSelectedMunicipalities) {
|
|
495
|
+
this.pushToLocalStorageArray(result)
|
|
496
|
+
}
|
|
497
|
+
if (this.props.resetOnSelect) {
|
|
498
|
+
this.resetComponent()
|
|
499
|
+
}
|
|
500
|
+
}}
|
|
501
|
+
onKeyUp={e => {
|
|
502
|
+
if (e.key === 'Enter' || e.key === 'Space') {
|
|
466
503
|
this.props.onSelectionHandler(result)
|
|
467
504
|
// this.sendToDataPipeline(result.PLZ4)
|
|
468
|
-
if (this.props.showLastSelectedMunicipalities) {
|
|
469
|
-
this.pushToLocalStorageArray(result)
|
|
470
|
-
}
|
|
471
505
|
if (this.props.resetOnSelect) {
|
|
472
506
|
this.resetComponent()
|
|
473
507
|
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
<span className={styles.resultPlz}>{result.PLZ4}</span>{' '}
|
|
490
|
-
{result.ORTNAME}
|
|
491
|
-
</div>
|
|
492
|
-
<div className={styles.resultMeta}>
|
|
493
|
-
{t('list.municipalityPrefix')} {result.GDENAMK}
|
|
494
|
-
</div>
|
|
495
|
-
</animated.div>
|
|
496
|
-
)}
|
|
508
|
+
}
|
|
509
|
+
}}
|
|
510
|
+
className='result'
|
|
511
|
+
style={values}
|
|
512
|
+
tabIndex={0}
|
|
513
|
+
>
|
|
514
|
+
<ResultHeader className='resultHeader'>
|
|
515
|
+
<ResultPlz className='resultPlz'>{result.PLZ4}</ResultPlz>{' '}
|
|
516
|
+
{result.ORTNAME}
|
|
517
|
+
</ResultHeader>
|
|
518
|
+
<ResultMeta className='resultMeta'>
|
|
519
|
+
{t('list.municipalityPrefix')} {result.GDENAMK}
|
|
520
|
+
</ResultMeta>
|
|
521
|
+
</animated.div>
|
|
522
|
+
)}
|
|
497
523
|
</Transition>
|
|
498
524
|
)}
|
|
499
|
-
</
|
|
525
|
+
</Results>
|
|
500
526
|
)}
|
|
501
|
-
</
|
|
527
|
+
</MunicipalitySearchContainer>
|
|
502
528
|
)
|
|
503
529
|
}
|
|
504
530
|
|
|
505
531
|
// endregion
|
|
506
532
|
}
|
|
533
|
+
const Icon = styled.i`
|
|
534
|
+
position: absolute;
|
|
535
|
+
top: 50%;
|
|
536
|
+
transform: translateY(-50%);
|
|
537
|
+
display: flex;
|
|
538
|
+
padding-left: 0.8em;
|
|
539
|
+
opacity: 0.5;
|
|
540
|
+
transition: opacity 0.5s ease-in-out;
|
|
541
|
+
`
|
|
542
|
+
const MunicipalitySearchContainer = styled.div``
|
|
543
|
+
const InputRow = styled.div`
|
|
544
|
+
display: flex;
|
|
545
|
+
flex-direction: row;
|
|
546
|
+
align-items: center;
|
|
547
|
+
input:focus ~ i {
|
|
548
|
+
opacity: 1;
|
|
549
|
+
}
|
|
550
|
+
@media screen and (max-width: 599px) {
|
|
551
|
+
flex-direction: column;
|
|
552
|
+
.ui.input {
|
|
553
|
+
width: 100%;
|
|
554
|
+
max-width: 100%;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
`
|
|
558
|
+
const FlexInput = styled.input`
|
|
559
|
+
display: flex;
|
|
560
|
+
`
|
|
561
|
+
const Results = styled.div`
|
|
562
|
+
margin-top: 1ex;
|
|
563
|
+
display: grid;
|
|
564
|
+
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
|
|
565
|
+
grid-gap: 0;
|
|
566
|
+
font-family: var(--ui-font-stack);
|
|
567
|
+
.result {
|
|
568
|
+
padding: calc(11 / 16 * 1em) calc(14 / 16 * 1em);
|
|
569
|
+
border-radius: 0.2ex;
|
|
570
|
+
box-shadow: 0 0 0 rgba(0, 0, 0, 0.3);
|
|
571
|
+
//border: 1px solid #007abf;
|
|
572
|
+
color: rgba(0, 0, 0, 0.9);
|
|
573
|
+
line-height: 1.1em;
|
|
574
|
+
cursor: pointer;
|
|
575
|
+
transition: box-shadow 200ms ease-in-out;
|
|
576
|
+
background-color: transparent;
|
|
577
|
+
|
|
578
|
+
&:hover,
|
|
579
|
+
&:focus {
|
|
580
|
+
box-shadow: 0 0 0.5ex rgba(0, 0, 0, 0.2), 0 0 0 1px #007abf inset;
|
|
581
|
+
background-color: $backgroundColor;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
`
|
|
585
|
+
const ResultHeader = styled.div`
|
|
586
|
+
font-weight: 700;
|
|
587
|
+
`
|
|
588
|
+
const ResultPlz = styled.span`
|
|
589
|
+
opacity: 0.5;
|
|
590
|
+
font-weight: 400;
|
|
591
|
+
`
|
|
592
|
+
const ResultMeta = styled.div`
|
|
593
|
+
opacity: 0.66;
|
|
594
|
+
font-size: 0.9em;
|
|
595
|
+
`
|
package/src/styles.module.css
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/* IDENTIFICATOR STYLES */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
.flexInput {
|
|
5
|
-
display: flex;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
.municipalitySearch {
|
|
10
|
-
--backgroundColor: rgba(255, 255, 255, 0.95);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.inputRow {
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: row;
|
|
17
|
-
align-items: center;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@media screen and (max-width: 599px) {
|
|
21
|
-
flex-direction: column;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
:global .ui.input {
|
|
25
|
-
width: 100%;
|
|
26
|
-
max-width: 100%;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.results {
|
|
33
|
-
margin-top: 1ex;
|
|
34
|
-
display: grid;
|
|
35
|
-
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
|
|
36
|
-
grid-gap: 0;
|
|
37
|
-
font-family: var(--ui-font-stack);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.result {
|
|
42
|
-
padding: calc(11 / 16 * 1em) calc(14 / 16 * 1em);
|
|
43
|
-
border-radius: 0.2ex;
|
|
44
|
-
box-shadow: 0 0 0 rgba(0, 0, 0, 0.3);
|
|
45
|
-
/* //border: 1px solid #007abf; */
|
|
46
|
-
color: rgba(0, 0, 0, 0.9);
|
|
47
|
-
line-height: 1.1em;
|
|
48
|
-
cursor: pointer;
|
|
49
|
-
transition: box-shadow 200ms ease-in-out;
|
|
50
|
-
background-color: transparent;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
.result:hover,
|
|
56
|
-
.result:focus {
|
|
57
|
-
box-shadow: 0 0 0.5ex rgba(0, 0, 0, 0.2), 0 0 0 1px #007abf inset;
|
|
58
|
-
background-color: var(--backgroundColor);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.resultHeader {
|
|
63
|
-
font-weight: 700;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.resultPlz {
|
|
68
|
-
opacity: 0.5;
|
|
69
|
-
font-weight: 400;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.resultMeta {
|
|
74
|
-
opacity: 0.66;
|
|
75
|
-
font-size: 0.9em;
|
|
76
|
-
}
|
|
77
|
-
|