@hotelcard/ui 0.0.45 → 0.0.47

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
@@ -130,18 +130,21 @@ The package includes built-in translations for `de`, `en`, `fr`, and `it` locale
130
130
 
131
131
  | Component | Description |
132
132
  |-----------|-------------|
133
+ | `FilterPanel` | Complete filter sidebar with all filter sections (regions, price, category, etc.) |
134
+ | `FilterModal` | Modal wrapper for FilterPanel (web overlay style) |
133
135
  | `FilterCheckboxItem` | Base checkbox item for filters |
134
136
  | `CollapsibleFilterSection` | Expandable/collapsible filter section wrapper |
135
- | `PriceRangeFilter` | Price range slider filter |
136
- | `HotelCategoryFilter` | Hotel star rating filter (1-5 stars) |
137
- | `ReviewsFilter` | Guest review rating filter |
138
- | `ExperienceFilter` | Experience/theme filter |
139
- | `RegionsFilter` | Region/location filter with search |
140
- | `CheckboxFilter` | Generic checkbox filter component |
141
- | `MealsFilter` | Meal plan filter (breakfast, half-board, etc.) |
142
- | `TransportFilter` | Transport options filter |
143
- | `WellnessFilter` | Wellness amenities filter |
144
- | `SelectedFiltersRow` | Display row showing active filters with remove option |
137
+ | `PriceRangeFilter` | Price range slider with histogram |
138
+ | `HotelCategoryFilter` | Hotel star rating filter (1-5 stars, Swiss Lodge, No category) |
139
+ | `ReviewsFilter` | Guest review rating filter (Excellent, Very Good, Good, Fair, No rating) |
140
+ | `ExperienceFilter` | Experience/theme filter with counts |
141
+ | `RegionsFilter` | Accordion-style region filter with country/sub-region hierarchy |
142
+ | `MealsFilter` | Meal options filter (breakfast, restaurant) |
143
+ | `TransportFilter` | Transport options filter (parking, EV charging) |
144
+ | `WellnessFilter` | Wellness amenities filter (pool, sauna, massage) |
145
+ | `SelectedFiltersRow` | Display row showing active filters as removable chips |
146
+ | `FilterMinimap` | Map preview with "View in Map" button |
147
+ | `FilterSkeleton` | Loading skeleton for filter panel |
145
148
 
146
149
  ### Icons
147
150
 
@@ -440,7 +443,119 @@ import { SearchControlsBar, type SearchParams, type ViewMode } from '@hotelcard/
440
443
  />
441
444
  ```
442
445
 
443
- ### Filter Components
446
+ ### FilterPanel (Complete Filter Sidebar)
447
+
448
+ ```tsx
449
+ import {
450
+ FilterPanel,
451
+ type FilterState,
452
+ type FilterPanelProps,
453
+ type RegionOption,
454
+ type ThemeAggregation,
455
+ type FilterOption,
456
+ } from '@hotelcard/ui';
457
+
458
+ // FilterPanel displays filter data from search API aggregations
459
+ // It does NOT have default data - pass real aggregation data from your search response
460
+ <FilterPanel
461
+ // Filter change callback
462
+ onFilterChange={(filters: FilterState) => {
463
+ // filters: { regions, experiences, priceRange, discounts, options, meals, categories, ratings, transport, wellness }
464
+ applyFilters(filters);
465
+ }}
466
+
467
+ // Optional: Show map preview with "View in Map" button
468
+ onViewMap={() => setViewMode('map')}
469
+
470
+ // Price data from API aggregations (required for price filter to display)
471
+ // Round to nearest 10 for clean slider values
472
+ minPrice={Math.floor(aggregations.price_stats.min / 10) * 10}
473
+ maxPrice={Math.ceil(aggregations.price_stats.max / 10) * 10}
474
+ priceHistogram={aggregations.price_histogram.map(b => b.count)}
475
+
476
+ // Region data from API
477
+ regions={regionOptions}
478
+ themes={experienceThemes} // Pre-translated theme names from API
479
+ filterOptions={filterOptions} // Pre-translated filter options from API
480
+
481
+ // Counts for each filter option (from search aggregations)
482
+ // Use exact property names from API response:
483
+ discountCounts={aggregations.discount_counts}
484
+ optionsCounts={aggregations.options_counts}
485
+ categoryCounts={categoryCounts} // Transform from star_ratings array
486
+ mealsCounts={aggregations.meals_counts}
487
+ reviewsCounts={aggregations.reviews_counts}
488
+ transportCounts={aggregations.transport_counts}
489
+ wellnessCounts={aggregations.wellness_counts}
490
+ servicesCounts={aggregations.services_counts}
491
+
492
+ // Controlled selected values (sync with URL params)
493
+ selectedDiscounts={['50']}
494
+ selectedCategories={['4', '5']}
495
+ selectedRegions={['region_1']}
496
+ selectedPriceRange={{ min: 100, max: 300 }}
497
+
498
+ // Loading state - shows skeleton when true
499
+ isLoading={isLoading}
500
+ />
501
+ ```
502
+
503
+ ### FilterModal (Modal Wrapper)
504
+
505
+ For web apps, use `FilterModal` which provides the overlay/modal styling:
506
+
507
+ ```tsx
508
+ import { FilterModal, type FilterModalProps, type SelectedFilter } from '@hotelcard/ui';
509
+
510
+ <FilterModal
511
+ isOpen={isFilterModalOpen}
512
+ onClose={() => setIsFilterModalOpen(false)}
513
+ resultCount={totalResults}
514
+ selectedFilters={activeFilterChips}
515
+ onRemoveFilter={(filter: SelectedFilter) => removeFilter(filter)}
516
+ onClearAllFilters={() => clearAllFilters()}
517
+
518
+ // Pass through ALL FilterPanel props - data comes from search aggregations
519
+ onFilterChange={handleFilterChange}
520
+ minPrice={priceStats.minPrice}
521
+ maxPrice={priceStats.maxPrice}
522
+ priceHistogram={priceStats.histogram.length > 0 ? priceStats.histogram : undefined}
523
+ regions={regions}
524
+ themes={themes}
525
+ filterOptions={filterOptions}
526
+ discountCounts={aggregations.discount_counts}
527
+ optionsCounts={aggregations.options_counts}
528
+ categoryCounts={categoryCounts}
529
+ mealsCounts={aggregations.meals_counts}
530
+ reviewsCounts={aggregations.reviews_counts}
531
+ transportCounts={aggregations.transport_counts}
532
+ wellnessCounts={aggregations.wellness_counts}
533
+ servicesCounts={aggregations.services_counts}
534
+ selectedDiscounts={selectedDiscounts}
535
+ selectedCategories={selectedCategories}
536
+ selectedRegions={selectedRegions}
537
+ selectedPriceRange={selectedPriceRange}
538
+ // ... other selected* props
539
+ />
540
+ ```
541
+
542
+ **Important:** FilterPanel and FilterModal do NOT have hardcoded default data. All filter data (price range, counts, etc.) must come from your search API aggregations. Pass `undefined` for `priceHistogram` when data is loading to show skeleton state.
543
+
544
+ For Ionic apps, create your own `IonModal` wrapper and pass props through to `FilterPanel`:
545
+
546
+ ```tsx
547
+ import { IonModal } from '@ionic/react';
548
+ import { FilterPanel, type FilterPanelProps } from '@hotelcard/ui';
549
+
550
+ // Simple pass-through wrapper
551
+ const FilterModal = ({ isOpen, onClose, ...filterPanelProps }) => (
552
+ <IonModal isOpen={isOpen} onDidDismiss={onClose}>
553
+ <FilterPanel {...filterPanelProps} />
554
+ </IonModal>
555
+ );
556
+ ```
557
+
558
+ ### Individual Filter Components
444
559
 
445
560
  ```tsx
446
561
  import {
@@ -541,21 +656,18 @@ import type {
541
656
  ViewMode,
542
657
 
543
658
  // Filter Component Props
659
+ FilterPanelProps,
660
+ FilterModalProps,
661
+ FilterState,
662
+ FilterOptions,
663
+ FilterOption,
664
+ ThemeAggregation,
665
+ SelectedFilter,
666
+ RegionOption,
544
667
  FilterCheckboxItemProps,
545
- CollapsibleFilterSectionProps,
546
- PriceRangeFilterProps,
547
- HotelCategoryFilterProps,
668
+ FilterMinimapProps,
548
669
  CategoryOption,
549
- ReviewsFilterProps,
550
670
  ReviewOption,
551
- ExperienceFilterProps,
552
- ThemeAggregation,
553
- RegionsFilterProps,
554
- RegionOption,
555
- CheckboxFilterProps,
556
- FilterOption,
557
- SelectedFiltersRowProps,
558
- SelectedFilter,
559
671
 
560
672
  // Context Types
561
673
  UIContextValue,