@mapvx/website-component 0.8.1 → 0.10.0

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
@@ -253,7 +253,7 @@ The web component accepts the following input properties to customize its behavi
253
253
  #### Required Properties Only
254
254
 
255
255
  ```html
256
- <mapvx-website api-key="your-api-key-here"></mapvx-website>
256
+ <mapvx-website api-key="your-api-key-here" institution-id="institution-123"></mapvx-website>
257
257
  ```
258
258
 
259
259
  #### With Optional Configuration
@@ -270,6 +270,462 @@ The web component accepts the following input properties to customize its behavi
270
270
  </mapvx-website>
271
271
  ```
272
272
 
273
+ ## 📤 Output Events
274
+
275
+ The web component emits custom events that you can listen to for user interactions:
276
+
277
+ | Event Name | Type | Description |
278
+ | -------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
279
+ | `cardSelected` | `string` | ⚠️ **Deprecated**: Use `userAction` with type `show-place` instead. Emitted when a location card is selected. Contains the card ID. |
280
+ | `userAction` | `UserAction` | Emitted when a user performs an action (filter selection, search, place navigation, etc.). Contains action type and associated data. |
281
+
282
+ ### Output Usage Examples
283
+
284
+ #### Vanilla JavaScript
285
+
286
+ ```javascript
287
+ // Wait for the web component to be registered
288
+ function setupUserActionListener() {
289
+ const element = document.querySelector('mapvx-website')
290
+ if (element) {
291
+ element.addEventListener('userAction', (event) => {
292
+ if (event instanceof CustomEvent && event.detail.type === 'show-place') {
293
+ const placeId = event.detail.data.placeId
294
+ console.log('Place shown:', placeId)
295
+ // Handle place display
296
+ }
297
+ })
298
+ } else {
299
+ // Retry if element is not yet available
300
+ setTimeout(setupUserActionListener, 100)
301
+ }
302
+ }
303
+
304
+ // Check if web component is already registered
305
+ if (customElements.get('mapvx-website')) {
306
+ setupUserActionListener()
307
+ } else {
308
+ // Wait for registration
309
+ const checkInterval = setInterval(() => {
310
+ if (customElements.get('mapvx-website')) {
311
+ setupUserActionListener()
312
+ clearInterval(checkInterval)
313
+ }
314
+ }, 100)
315
+ }
316
+ ```
317
+
318
+ #### Update Browser URL with `history.pushState`
319
+
320
+ You can react to user actions to change the browser URL without a full page reload, which is especially useful in SSR apps looking to keep client-side navigation in sync with the selected place.
321
+
322
+ ```html
323
+ <script>
324
+ // Wait for the web component to be registered
325
+ function setupUrlUpdater() {
326
+ const element = document.querySelector('mapvx-website')
327
+ if (!element) {
328
+ setTimeout(setupUrlUpdater, 100)
329
+ return
330
+ }
331
+
332
+ const handleUserAction = (event) => {
333
+ if (event instanceof CustomEvent) {
334
+ const action = event.detail
335
+ switch (action.type) {
336
+ case 'return-to-home':
337
+ case 'select-filter': {
338
+ const { filter } = action.data
339
+ history.pushState(
340
+ { page: 'home', filter },
341
+ '',
342
+ `${location.pathname}?tab=${encodeURIComponent(filter)}`,
343
+ )
344
+ break
345
+ }
346
+ case 'show-place': {
347
+ const { placeId } = action.data
348
+ history.pushState(
349
+ { page: 'profile', id: placeId },
350
+ '',
351
+ `${location.pathname}?tenant=${placeId}`,
352
+ )
353
+ break
354
+ }
355
+ case 'search': {
356
+ const { searchTerm } = action.data
357
+ history.pushState(
358
+ { page: 'search', term: searchTerm },
359
+ '',
360
+ `${location.pathname}?search=${encodeURIComponent(searchTerm)}`,
361
+ )
362
+ break
363
+ }
364
+ case 'select-destination': {
365
+ const { destinationId } = action.data
366
+ history.pushState(
367
+ { page: 'route', id: destinationId },
368
+ '',
369
+ `${location.pathname}?destination=${destinationId}`,
370
+ )
371
+ break
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ element.addEventListener('userAction', handleUserAction)
378
+ }
379
+
380
+ // Check if web component is already registered
381
+ if (customElements.get('mapvx-website')) {
382
+ setupUrlUpdater()
383
+ } else {
384
+ // Wait for registration
385
+ const waitForRegistration = setInterval(() => {
386
+ if (customElements.get('mapvx-website')) {
387
+ clearInterval(waitForRegistration)
388
+ setupUrlUpdater()
389
+ }
390
+ }, 100)
391
+ }
392
+ </script>
393
+ ```
394
+
395
+ #### React
396
+
397
+ ```jsx
398
+ import { useEffect, useRef } from 'react'
399
+
400
+ function App() {
401
+ const webComponentRef = useRef(null)
402
+
403
+ useEffect(() => {
404
+ const element = webComponentRef.current
405
+ if (!element) return
406
+
407
+ const handleUserAction = (event) => {
408
+ if (event instanceof CustomEvent && event.detail.type === 'show-place') {
409
+ const placeId = event.detail.data.placeId
410
+ console.log('Place shown:', placeId)
411
+ // Handle place display
412
+ }
413
+ }
414
+
415
+ element.addEventListener('userAction', handleUserAction)
416
+
417
+ return () => {
418
+ element.removeEventListener('userAction', handleUserAction)
419
+ }
420
+ }, [])
421
+
422
+ return <mapvx-website ref={webComponentRef} api-key="your-api-key-here" />
423
+ }
424
+ ```
425
+
426
+ #### Vue
427
+
428
+ ```vue
429
+ <template>
430
+ <mapvx-website ref="webComponent" api-key="your-api-key-here" />
431
+ </template>
432
+
433
+ <script setup>
434
+ import { ref, onMounted, onUnmounted } from 'vue'
435
+
436
+ const webComponent = ref(null)
437
+
438
+ const handleUserAction = (event) => {
439
+ if (event instanceof CustomEvent && event.detail.type === 'show-place') {
440
+ const placeId = event.detail.data.placeId
441
+ console.log('Place shown:', placeId)
442
+ // Handle place display
443
+ }
444
+ }
445
+
446
+ onMounted(() => {
447
+ if (webComponent.value) {
448
+ webComponent.value.addEventListener('userAction', handleUserAction)
449
+ }
450
+ })
451
+
452
+ onUnmounted(() => {
453
+ if (webComponent.value) {
454
+ webComponent.value.removeEventListener('userAction', handleUserAction)
455
+ }
456
+ })
457
+ </script>
458
+ ```
459
+
460
+ #### Angular
461
+
462
+ ```typescript
463
+ import { Component, ElementRef, OnDestroy, ViewChild, AfterViewInit } from '@angular/core'
464
+
465
+ @Component({
466
+ selector: 'app-mapvx',
467
+ template: ` <mapvx-website #webComponent [attr.api-key]="apiKey" /> `,
468
+ })
469
+ export class MapvxComponent implements AfterViewInit, OnDestroy {
470
+ @ViewChild('webComponent', { static: false }) webComponentRef!: ElementRef<HTMLElement>
471
+
472
+ apiKey = 'your-api-key-here'
473
+ private userActionListener?: (event: Event) => void
474
+
475
+ ngAfterViewInit() {
476
+ this.setupUserActionListener()
477
+ }
478
+
479
+ private setupUserActionListener() {
480
+ const element = this.webComponentRef?.nativeElement
481
+ if (!element) {
482
+ // Retry if element is not yet available
483
+ setTimeout(() => this.setupUserActionListener(), 100)
484
+ return
485
+ }
486
+
487
+ this.userActionListener = (event: Event) => {
488
+ if (event instanceof CustomEvent) {
489
+ const action = event.detail as { type: string; data: any }
490
+ if (action.type === 'show-place') {
491
+ const placeId = action.data.placeId
492
+ console.log('Place shown:', placeId)
493
+ // Handle place display
494
+ }
495
+ }
496
+ }
497
+
498
+ element.addEventListener('userAction', this.userActionListener)
499
+ }
500
+
501
+ ngOnDestroy() {
502
+ const element = this.webComponentRef?.nativeElement
503
+ if (element && this.userActionListener) {
504
+ element.removeEventListener('userAction', this.userActionListener)
505
+ }
506
+ }
507
+ }
508
+ ```
509
+
510
+ ### User Action Event
511
+
512
+ The `userAction` event provides detailed information about user interactions within the component. The event detail contains an object with `type` and `data` properties.
513
+
514
+ #### UserAction Types
515
+
516
+ | Type | Description | Data Structure |
517
+ | -------------------- | ------------------------------------------ | --------------------------- |
518
+ | `select-filter` | Emitted when a filter is selected | `{ filter: string }` |
519
+ | `show-place` | Emitted when a place is displayed | `{ placeId: string }` |
520
+ | `select-destination` | Emitted when a destination is selected | `{ destinationId: string }` |
521
+ | `search` | Emitted when a search is performed | `{ searchTerm: string }` |
522
+ | `return-to-home` | Emitted when user returns to the home view | `{ filter: string }` |
523
+
524
+ #### UserAction Usage Examples
525
+
526
+ #### Vanilla JavaScript
527
+
528
+ ```javascript
529
+ // Wait for the web component to be registered
530
+ function setupUserActionListener() {
531
+ const element = document.querySelector('mapvx-website')
532
+ if (element) {
533
+ element.addEventListener('userAction', (event) => {
534
+ if (event instanceof CustomEvent) {
535
+ const action = event.detail
536
+ console.log('User action:', action.type, action.data)
537
+
538
+ // Handle different action types
539
+ switch (action.type) {
540
+ case 'select-filter':
541
+ console.log('Filter selected:', action.data.filter)
542
+ break
543
+ case 'show-place':
544
+ console.log('Place shown:', action.data.placeId)
545
+ break
546
+ case 'select-destination':
547
+ console.log('Destination selected:', action.data.destinationId)
548
+ break
549
+ case 'search':
550
+ console.log('Search performed:', action.data.searchTerm)
551
+ break
552
+ case 'return-to-home':
553
+ console.log('Returned to home with filter:', action.data.filter)
554
+ break
555
+ }
556
+ }
557
+ })
558
+ } else {
559
+ // Retry if element is not yet available
560
+ setTimeout(setupUserActionListener, 100)
561
+ }
562
+ }
563
+
564
+ // Check if web component is already registered
565
+ if (customElements.get('mapvx-website')) {
566
+ setupUserActionListener()
567
+ } else {
568
+ // Wait for registration
569
+ const checkInterval = setInterval(() => {
570
+ if (customElements.get('mapvx-website')) {
571
+ setupUserActionListener()
572
+ clearInterval(checkInterval)
573
+ }
574
+ }, 100)
575
+ }
576
+ ```
577
+
578
+ #### React
579
+
580
+ ```jsx
581
+ import { useEffect, useRef } from 'react'
582
+
583
+ function App() {
584
+ const webComponentRef = useRef(null)
585
+
586
+ useEffect(() => {
587
+ const element = webComponentRef.current
588
+ if (!element) return
589
+
590
+ const handleUserAction = (event) => {
591
+ if (event instanceof CustomEvent) {
592
+ const action = event.detail
593
+ console.log('User action:', action.type, action.data)
594
+
595
+ // Handle different action types
596
+ switch (action.type) {
597
+ case 'select-filter':
598
+ // Handle filter selection
599
+ break
600
+ case 'show-place':
601
+ // Handle place display
602
+ break
603
+ case 'search':
604
+ // Handle search
605
+ break
606
+ // ... other cases
607
+ }
608
+ }
609
+ }
610
+
611
+ element.addEventListener('userAction', handleUserAction)
612
+
613
+ return () => {
614
+ element.removeEventListener('userAction', handleUserAction)
615
+ }
616
+ }, [])
617
+
618
+ return <mapvx-website ref={webComponentRef} api-key="your-api-key-here" />
619
+ }
620
+ ```
621
+
622
+ #### Vue
623
+
624
+ ```vue
625
+ <template>
626
+ <mapvx-website ref="webComponent" api-key="your-api-key-here" />
627
+ </template>
628
+
629
+ <script setup>
630
+ import { ref, onMounted, onUnmounted } from 'vue'
631
+
632
+ const webComponent = ref(null)
633
+
634
+ const handleUserAction = (event) => {
635
+ if (event instanceof CustomEvent) {
636
+ const action = event.detail
637
+ console.log('User action:', action.type, action.data)
638
+
639
+ // Handle different action types
640
+ switch (action.type) {
641
+ case 'select-filter':
642
+ // Handle filter selection
643
+ break
644
+ case 'show-place':
645
+ // Handle place display
646
+ break
647
+ case 'search':
648
+ // Handle search
649
+ break
650
+ // ... other cases
651
+ }
652
+ }
653
+ }
654
+
655
+ onMounted(() => {
656
+ if (webComponent.value) {
657
+ webComponent.value.addEventListener('userAction', handleUserAction)
658
+ }
659
+ })
660
+
661
+ onUnmounted(() => {
662
+ if (webComponent.value) {
663
+ webComponent.value.removeEventListener('userAction', handleUserAction)
664
+ }
665
+ })
666
+ </script>
667
+ ```
668
+
669
+ #### Angular
670
+
671
+ ```typescript
672
+ import { Component, ElementRef, OnDestroy, ViewChild, AfterViewInit } from '@angular/core'
673
+
674
+ @Component({
675
+ selector: 'app-mapvx',
676
+ template: ` <mapvx-website #webComponent [attr.api-key]="apiKey" /> `,
677
+ })
678
+ export class MapvxComponent implements AfterViewInit, OnDestroy {
679
+ @ViewChild('webComponent', { static: false }) webComponentRef!: ElementRef<HTMLElement>
680
+
681
+ apiKey = 'your-api-key-here'
682
+ private userActionListener?: (event: Event) => void
683
+
684
+ ngAfterViewInit() {
685
+ this.setupUserActionListener()
686
+ }
687
+
688
+ private setupUserActionListener() {
689
+ const element = this.webComponentRef?.nativeElement
690
+ if (!element) {
691
+ // Retry if element is not yet available
692
+ setTimeout(() => this.setupUserActionListener(), 100)
693
+ return
694
+ }
695
+
696
+ this.userActionListener = (event: Event) => {
697
+ if (event instanceof CustomEvent) {
698
+ const action = event.detail as { type: string; data: any }
699
+ console.log('User action:', action.type, action.data)
700
+
701
+ // Handle different action types
702
+ switch (action.type) {
703
+ case 'select-filter':
704
+ // Handle filter selection
705
+ break
706
+ case 'show-place':
707
+ // Handle place display
708
+ break
709
+ case 'search':
710
+ // Handle search
711
+ break
712
+ // ... other cases
713
+ }
714
+ }
715
+ }
716
+
717
+ element.addEventListener('userAction', this.userActionListener)
718
+ }
719
+
720
+ ngOnDestroy() {
721
+ const element = this.webComponentRef?.nativeElement
722
+ if (element && this.userActionListener) {
723
+ element.removeEventListener('userAction', this.userActionListener)
724
+ }
725
+ }
726
+ }
727
+ ```
728
+
273
729
  ## 🔧 Server-Side Rendering (SSR) Support
274
730
 
275
731
  For applications using Server-Side Rendering (SSR) where you need to preload initial data on the server before sending it to the browser: