@hotelcard/ui 0.0.46 → 0.0.48

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
@@ -94,6 +94,7 @@ The package includes built-in translations for `de`, `en`, `fr`, and `it` locale
94
94
  | `Input` | Text input with label, helper text, and validation |
95
95
  | `Badge` | Status and discount badges |
96
96
  | `Card` | Hotel card component |
97
+ | `BaseCard` | Composable card primitives (container, image, content) for building custom cards |
97
98
  | `Chip` | Filter and tag chips |
98
99
  | `Checkbox` | Checkbox with label |
99
100
  | `RadioButton` | Radio button with label |
@@ -130,18 +131,21 @@ The package includes built-in translations for `de`, `en`, `fr`, and `it` locale
130
131
 
131
132
  | Component | Description |
132
133
  |-----------|-------------|
134
+ | `FilterPanel` | Complete filter sidebar with all filter sections (regions, price, category, etc.) |
135
+ | `FilterModal` | Modal wrapper for FilterPanel (web overlay style) |
133
136
  | `FilterCheckboxItem` | Base checkbox item for filters |
134
137
  | `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 |
138
+ | `PriceRangeFilter` | Price range slider with histogram |
139
+ | `HotelCategoryFilter` | Hotel star rating filter (1-5 stars, Swiss Lodge, No category) |
140
+ | `ReviewsFilter` | Guest review rating filter (Excellent, Very Good, Good, Fair, No rating) |
141
+ | `ExperienceFilter` | Experience/theme filter with counts |
142
+ | `RegionsFilter` | Accordion-style region filter with country/sub-region hierarchy |
143
+ | `MealsFilter` | Meal options filter (breakfast, restaurant) |
144
+ | `TransportFilter` | Transport options filter (parking, EV charging) |
145
+ | `WellnessFilter` | Wellness amenities filter (pool, sauna, massage) |
146
+ | `SelectedFiltersRow` | Display row showing active filters as removable chips |
147
+ | `FilterMinimap` | Map preview with "View in Map" button |
148
+ | `FilterSkeleton` | Loading skeleton for filter panel |
145
149
 
146
150
  ### Icons
147
151
 
@@ -222,6 +226,49 @@ import { Card } from '@hotelcard/ui';
222
226
  />
223
227
  ```
224
228
 
229
+ ### BaseCard (Composable Card Primitives)
230
+
231
+ Use `BaseCard`, `BaseCardImage`, and `BaseCardContent` to build custom cards with consistent styling:
232
+
233
+ ```tsx
234
+ import { BaseCard, BaseCardImage, BaseCardContent } from '@hotelcard/ui';
235
+
236
+ // Custom booking card using BaseCard primitives
237
+ <BaseCard hoverable onClick={() => navigateToBooking()}>
238
+ <BaseCardImage
239
+ src={hotel.imageUrl}
240
+ alt={hotel.name}
241
+ height={200}
242
+ topLeftSlot={<StatusBadge status="confirmed" />}
243
+ topRightSlot={<FavoriteButton />}
244
+ />
245
+ <BaseCardContent padding="large">
246
+ <h3>{hotel.name}</h3>
247
+ <p>{hotel.location}</p>
248
+ <div>Check-in: {checkInDate}</div>
249
+ </BaseCardContent>
250
+ </BaseCard>
251
+
252
+ // With aspect ratio instead of fixed height
253
+ <BaseCard borderRadius={16}>
254
+ <BaseCardImage
255
+ src={hotel.imageUrl}
256
+ alt={hotel.name}
257
+ aspectRatio="16/9"
258
+ showGradient={true}
259
+ bottomSlot={<ImageIndicatorDots count={3} active={0} />}
260
+ />
261
+ <BaseCardContent padding="medium">
262
+ {/* Your content */}
263
+ </BaseCardContent>
264
+ </BaseCard>
265
+ ```
266
+
267
+ BaseCard provides:
268
+ - **BaseCard**: Container with border, radius, shadow, hover effects
269
+ - **BaseCardImage**: Image with overlay slots (top-left, top-right, center, bottom)
270
+ - **BaseCardContent**: Content area with configurable padding ('none' | 'small' | 'medium' | 'large')
271
+
225
272
  ### Modal
226
273
 
227
274
  ```tsx
@@ -440,7 +487,119 @@ import { SearchControlsBar, type SearchParams, type ViewMode } from '@hotelcard/
440
487
  />
441
488
  ```
442
489
 
443
- ### Filter Components
490
+ ### FilterPanel (Complete Filter Sidebar)
491
+
492
+ ```tsx
493
+ import {
494
+ FilterPanel,
495
+ type FilterState,
496
+ type FilterPanelProps,
497
+ type RegionOption,
498
+ type ThemeAggregation,
499
+ type FilterOption,
500
+ } from '@hotelcard/ui';
501
+
502
+ // FilterPanel displays filter data from search API aggregations
503
+ // It does NOT have default data - pass real aggregation data from your search response
504
+ <FilterPanel
505
+ // Filter change callback
506
+ onFilterChange={(filters: FilterState) => {
507
+ // filters: { regions, experiences, priceRange, discounts, options, meals, categories, ratings, transport, wellness }
508
+ applyFilters(filters);
509
+ }}
510
+
511
+ // Optional: Show map preview with "View in Map" button
512
+ onViewMap={() => setViewMode('map')}
513
+
514
+ // Price data from API aggregations (required for price filter to display)
515
+ // Round to nearest 10 for clean slider values
516
+ minPrice={Math.floor(aggregations.price_stats.min / 10) * 10}
517
+ maxPrice={Math.ceil(aggregations.price_stats.max / 10) * 10}
518
+ priceHistogram={aggregations.price_histogram.map(b => b.count)}
519
+
520
+ // Region data from API
521
+ regions={regionOptions}
522
+ themes={experienceThemes} // Pre-translated theme names from API
523
+ filterOptions={filterOptions} // Pre-translated filter options from API
524
+
525
+ // Counts for each filter option (from search aggregations)
526
+ // Use exact property names from API response:
527
+ discountCounts={aggregations.discount_counts}
528
+ optionsCounts={aggregations.options_counts}
529
+ categoryCounts={categoryCounts} // Transform from star_ratings array
530
+ mealsCounts={aggregations.meals_counts}
531
+ reviewsCounts={aggregations.reviews_counts}
532
+ transportCounts={aggregations.transport_counts}
533
+ wellnessCounts={aggregations.wellness_counts}
534
+ servicesCounts={aggregations.services_counts}
535
+
536
+ // Controlled selected values (sync with URL params)
537
+ selectedDiscounts={['50']}
538
+ selectedCategories={['4', '5']}
539
+ selectedRegions={['region_1']}
540
+ selectedPriceRange={{ min: 100, max: 300 }}
541
+
542
+ // Loading state - shows skeleton when true
543
+ isLoading={isLoading}
544
+ />
545
+ ```
546
+
547
+ ### FilterModal (Modal Wrapper)
548
+
549
+ For web apps, use `FilterModal` which provides the overlay/modal styling:
550
+
551
+ ```tsx
552
+ import { FilterModal, type FilterModalProps, type SelectedFilter } from '@hotelcard/ui';
553
+
554
+ <FilterModal
555
+ isOpen={isFilterModalOpen}
556
+ onClose={() => setIsFilterModalOpen(false)}
557
+ resultCount={totalResults}
558
+ selectedFilters={activeFilterChips}
559
+ onRemoveFilter={(filter: SelectedFilter) => removeFilter(filter)}
560
+ onClearAllFilters={() => clearAllFilters()}
561
+
562
+ // Pass through ALL FilterPanel props - data comes from search aggregations
563
+ onFilterChange={handleFilterChange}
564
+ minPrice={priceStats.minPrice}
565
+ maxPrice={priceStats.maxPrice}
566
+ priceHistogram={priceStats.histogram.length > 0 ? priceStats.histogram : undefined}
567
+ regions={regions}
568
+ themes={themes}
569
+ filterOptions={filterOptions}
570
+ discountCounts={aggregations.discount_counts}
571
+ optionsCounts={aggregations.options_counts}
572
+ categoryCounts={categoryCounts}
573
+ mealsCounts={aggregations.meals_counts}
574
+ reviewsCounts={aggregations.reviews_counts}
575
+ transportCounts={aggregations.transport_counts}
576
+ wellnessCounts={aggregations.wellness_counts}
577
+ servicesCounts={aggregations.services_counts}
578
+ selectedDiscounts={selectedDiscounts}
579
+ selectedCategories={selectedCategories}
580
+ selectedRegions={selectedRegions}
581
+ selectedPriceRange={selectedPriceRange}
582
+ // ... other selected* props
583
+ />
584
+ ```
585
+
586
+ **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.
587
+
588
+ For Ionic apps, create your own `IonModal` wrapper and pass props through to `FilterPanel`:
589
+
590
+ ```tsx
591
+ import { IonModal } from '@ionic/react';
592
+ import { FilterPanel, type FilterPanelProps } from '@hotelcard/ui';
593
+
594
+ // Simple pass-through wrapper
595
+ const FilterModal = ({ isOpen, onClose, ...filterPanelProps }) => (
596
+ <IonModal isOpen={isOpen} onDidDismiss={onClose}>
597
+ <FilterPanel {...filterPanelProps} />
598
+ </IonModal>
599
+ );
600
+ ```
601
+
602
+ ### Individual Filter Components
444
603
 
445
604
  ```tsx
446
605
  import {
@@ -501,6 +660,9 @@ import type {
501
660
  BadgeProps,
502
661
  InputProps,
503
662
  CardProps,
663
+ BaseCardProps,
664
+ BaseCardImageProps,
665
+ BaseCardContentProps,
504
666
  CheckboxProps,
505
667
  RadioButtonProps,
506
668
  DropdownProps,
@@ -541,21 +703,18 @@ import type {
541
703
  ViewMode,
542
704
 
543
705
  // Filter Component Props
706
+ FilterPanelProps,
707
+ FilterModalProps,
708
+ FilterState,
709
+ FilterOptions,
710
+ FilterOption,
711
+ ThemeAggregation,
712
+ SelectedFilter,
713
+ RegionOption,
544
714
  FilterCheckboxItemProps,
545
- CollapsibleFilterSectionProps,
546
- PriceRangeFilterProps,
547
- HotelCategoryFilterProps,
715
+ FilterMinimapProps,
548
716
  CategoryOption,
549
- ReviewsFilterProps,
550
717
  ReviewOption,
551
- ExperienceFilterProps,
552
- ThemeAggregation,
553
- RegionsFilterProps,
554
- RegionOption,
555
- CheckboxFilterProps,
556
- FilterOption,
557
- SelectedFiltersRowProps,
558
- SelectedFilter,
559
718
 
560
719
  // Context Types
561
720
  UIContextValue,