@mapfirst.ai/react 0.0.8 → 0.0.10

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/components/SmartFilter.tsx","../src/components/Icons.tsx","../src/components/smart-filter/FilterChips.tsx","../src/components/smart-filter/Chip.tsx","../src/components/smart-filter/MinRatingFilterChip.tsx","../src/hooks/useTranslation.ts","../src/components/smart-filter/utils.tsx","../src/components/smart-filter/PriceRangeFilterChip.tsx","../src/components/smart-filter/RestaurantPriceLevelChip.tsx","../src/components/smart-filter/TransformedQueryChip.tsx","../src/hooks/useFilterScroll.ts","../src/hooks/useIsPortrait.ts"],"sourcesContent":["import React from \"react\";\nimport {\n MapFirstCore,\n type MapFirstOptions,\n type BaseMapFirstOptions,\n type Property,\n type MapLibreNamespace,\n type GoogleMapsNamespace,\n type MapboxNamespace,\n type MapState,\n type PropertyType,\n} from \"@mapfirst.ai/core\";\n\n// Export all components\nexport * from \"./components\";\n\n// Export all hooks\nexport * from \"./hooks\";\n\n// Import additional types for search functionality\ntype InitialRequestBody = {\n initial?: boolean;\n query?: string;\n bounds?: {\n sw: { lat: number; lng: number };\n ne: { lat: number; lng: number };\n };\n filters?: any;\n city?: string;\n country?: string;\n location_id?: number;\n longitude?: number;\n latitude?: number;\n radius?: number;\n};\n\ntype SmartFilter = {\n id: string;\n label: string;\n type:\n | \"amenity\"\n | \"hotelStyle\"\n | \"priceRange\"\n | \"minRating\"\n | \"starRating\"\n | \"primary_type\"\n | \"transformed_query\"\n | \"selected_restaurant_price_levels\";\n value: string;\n numericValue?: number;\n priceRange?: {\n min: number;\n max?: number;\n };\n propertyType?: PropertyType;\n priceLevels?: any[];\n};\n\n/**\n * Hook that creates a MapFirstCore instance that can be initialized before maps are ready.\n * Supports two-phase initialization: create SDK first, attach map later.\n * Returns the instance and reactive state that updates when SDK state changes.\n *\n * @example\n * ```tsx\n * // Phase 1: Create SDK instance with location data\n * const { mapFirst, state } = useMapFirstCore({\n * initialLocationData: {\n * city: \"New York\",\n * country: \"United States\",\n * currency: \"USD\"\n * }\n * });\n *\n * // Access reactive state\n * console.log(state.properties); // Updates when properties change\n * console.log(state.isSearching); // Updates when search state changes\n *\n * // Phase 2: Attach map when ready\n * useEffect(() => {\n * if (mapLibreInstance && mapFirst) {\n * mapFirst.attachMap(mapLibreInstance, {\n * platform: \"maplibre\",\n * maplibregl: maplibregl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n * }\n * }, [mapLibreInstance, mapFirst]);\n * ```\n */\nexport function useMapFirstCore(options: BaseMapFirstOptions) {\n const instanceRef = React.useRef<MapFirstCore | null>(null);\n const [state, setState] = React.useState<MapState | null>(null);\n\n // Memoize the options to prevent recreation on every render\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n });\n\n React.useEffect(() => {\n const opts = optionsRef.current;\n\n // Create MapFirstCore instance without map using adapter-driven options\n const coreOptions: MapFirstOptions = {\n adapter: null as any, // Will be set when attachMap is called\n ...opts,\n callbacks: {\n ...opts.callbacks,\n // Add internal callbacks to trigger React re-renders\n onPropertiesChange: (properties) => {\n setState((prev) => (prev ? { ...prev, properties } : null));\n optionsRef.current.callbacks?.onPropertiesChange?.(properties);\n },\n onSelectedPropertyChange: (id) => {\n setState((prev) =>\n prev ? { ...prev, selectedPropertyId: id } : null\n );\n optionsRef.current.callbacks?.onSelectedPropertyChange?.(id);\n },\n onPrimaryTypeChange: (type) => {\n setState((prev) => (prev ? { ...prev, primary: type } : null));\n optionsRef.current.callbacks?.onPrimaryTypeChange?.(type);\n },\n onFiltersChange: (filters) => {\n setState((prev) => (prev ? { ...prev, filters } : null));\n optionsRef.current.callbacks?.onFiltersChange?.(filters);\n },\n onBoundsChange: (bounds) => {\n setState((prev) => (prev ? { ...prev, bounds } : null));\n optionsRef.current.callbacks?.onBoundsChange?.(bounds);\n },\n onCenterChange: (center, zoom) => {\n setState((prev) => (prev ? { ...prev, center, zoom } : null));\n optionsRef.current.callbacks?.onCenterChange?.(center, zoom);\n },\n onZoomChange: (zoom) => {\n setState((prev) => (prev ? { ...prev, zoom } : null));\n optionsRef.current.callbacks?.onZoomChange?.(zoom);\n },\n onActiveLocationChange: (location) => {\n setState((prev) =>\n prev ? { ...prev, activeLocation: location } : null\n );\n optionsRef.current.callbacks?.onActiveLocationChange?.(location);\n },\n onLoadingStateChange: (loading) => {\n setState((prev) =>\n prev ? { ...prev, initialLoading: loading } : null\n );\n optionsRef.current.callbacks?.onLoadingStateChange?.(loading);\n },\n onSearchingStateChange: (searching) => {\n setState((prev) =>\n prev ? { ...prev, isSearching: searching } : null\n );\n optionsRef.current.callbacks?.onSearchingStateChange?.(searching);\n },\n },\n };\n\n const instance = new MapFirstCore(coreOptions);\n instanceRef.current = instance;\n\n // Initialize state from SDK\n setState(instance.getState());\n\n return () => {\n instance.destroy();\n instanceRef.current = null;\n setState(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return { mapFirst: instanceRef.current, state };\n}\n\n/**\n * Hook to access reactive properties from MapFirst SDK.\n * Returns the current properties array that updates when properties change.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const properties = useMapFirstProperties(mapFirst);\n *\n * return <div>Found {properties.length} properties</div>;\n * ```\n */\nexport function useMapFirstProperties(\n mapFirst: MapFirstCore | null\n): Property[] {\n const [properties, setProperties] = React.useState<Property[]>([]);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setProperties([]);\n return;\n }\n\n // Initialize with current state\n setProperties(mapFirst.getState().properties);\n }, [mapFirst]);\n\n return properties;\n}\n\n/**\n * Hook to access the selected property ID from MapFirst SDK.\n * Returns the currently selected property ID that updates when selection changes.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const selectedId = useMapFirstSelectedProperty(mapFirst);\n *\n * return <div>Selected: {selectedId || 'None'}</div>;\n * ```\n */\nexport function useMapFirstSelectedProperty(\n mapFirst: MapFirstCore | null\n): number | null {\n const [selectedId, setSelectedId] = React.useState<number | null>(null);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setSelectedId(null);\n return;\n }\n\n // Initialize with current state\n setSelectedId(mapFirst.getState().selectedPropertyId);\n }, [mapFirst]);\n\n return selectedId;\n}\n\n/**\n * Hook to access and control the primary property type.\n * Returns the current primary type and a setter function.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const [primaryType, setPrimaryType] = usePrimaryType(mapFirst);\n *\n * return (\n * <select value={primaryType} onChange={(e) => setPrimaryType(e.target.value as PropertyType)}>\n * <option value=\"Accommodation\">Hotels</option>\n * <option value=\"Restaurant\">Restaurants</option>\n * <option value=\"Attraction\">Attractions</option>\n * </select>\n * );\n * ```\n */\nexport function usePrimaryType(\n mapFirst: MapFirstCore | null\n): [PropertyType, (type: PropertyType) => void] {\n const [primaryType, setPrimaryTypeState] =\n React.useState<PropertyType>(\"Accommodation\");\n\n React.useEffect(() => {\n if (!mapFirst) {\n setPrimaryTypeState(\"Accommodation\");\n return;\n }\n\n // Initialize with current state\n setPrimaryTypeState(mapFirst.getState().primary);\n }, [mapFirst]);\n\n const setPrimaryType = React.useCallback(\n (type: PropertyType) => {\n if (mapFirst) {\n mapFirst.setPrimaryType(type);\n setPrimaryTypeState(type);\n }\n },\n [mapFirst]\n );\n\n return [primaryType, setPrimaryType];\n}\n\n/**\n * Hook to access and control the selected marker.\n * Returns the current selected marker ID and a setter function.\n * Note: This hook requires the MapFirstCore instance. For simpler usage with reactive updates,\n * use state.selectedPropertyId from useMapFirstCore instead.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const [selectedMarker, setSelectedMarker] = useSelectedMarker(mapFirst);\n *\n * return (\n * <div>\n * <p>Selected: {selectedMarker || 'None'}</p>\n * <button onClick={() => setSelectedMarker(null)}>Clear Selection</button>\n * </div>\n * );\n * ```\n */\nexport function useSelectedMarker(\n mapFirst: MapFirstCore | null\n): [number | null, (id: number | null) => void] {\n const [selectedMarker, setSelectedMarkerState] = React.useState<\n number | null\n >(null);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setSelectedMarkerState(null);\n return;\n }\n\n // Initialize with current state\n setSelectedMarkerState(mapFirst.getState().selectedPropertyId);\n }, [mapFirst]);\n\n const setSelectedMarker = React.useCallback(\n (id: number | null) => {\n if (mapFirst) {\n mapFirst.setSelectedMarker(id);\n }\n },\n [mapFirst]\n );\n\n return [selectedMarker, setSelectedMarker];\n}\n\n/**\n * Hook for MapLibre GL JS integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"Paris\", country: \"France\" } });\n * const mapRef = useRef<maplibregl.Map | null>(null);\n *\n * useMapLibreAttachment({\n * mapFirst,\n * map: mapRef.current,\n * maplibregl: maplibregl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.properties);\n * ```\n */\nexport function useMapLibreAttachment({\n mapFirst,\n map,\n maplibregl,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n maplibregl: MapLibreNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"maplibre\",\n maplibregl,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, maplibregl, onMarkerClick]);\n}\n\n/**\n * Hook for Google Maps integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"Tokyo\", country: \"Japan\" } });\n * const mapRef = useRef<google.maps.Map | null>(null);\n *\n * useGoogleMapsAttachment({\n * mapFirst,\n * map: mapRef.current,\n * google: window.google,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.isSearching);\n * ```\n */\nexport function useGoogleMapsAttachment({\n mapFirst,\n map,\n google,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n google: GoogleMapsNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"google\",\n google,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, google, onMarkerClick]);\n}\n\n/**\n * Hook for Mapbox GL JS integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"London\", country: \"United Kingdom\" } });\n * const mapRef = useRef<mapboxgl.Map | null>(null);\n *\n * useMapboxAttachment({\n * mapFirst,\n * map: mapRef.current,\n * mapboxgl: mapboxgl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.filters);\n * ```\n */\nexport function useMapboxAttachment({\n mapFirst,\n map,\n mapboxgl,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n mapboxgl: MapboxNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"mapbox\",\n mapboxgl,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, mapboxgl, onMarkerClick]);\n}\n\n/**\n * Legacy hook that creates the MapFirstCore instance with a map immediately.\n * Use useMapFirstCore + useMap*Attachment hooks for better control.\n *\n * @deprecated Use useMapFirstCore and platform-specific attachment hooks instead\n */\nexport function useMapFirst(options: MapFirstOptions | null) {\n const instanceRef = React.useRef<MapFirstCore | null>(null);\n\n React.useEffect(() => {\n if (!options) {\n return undefined;\n }\n const instance = new MapFirstCore(options);\n instanceRef.current = instance;\n\n return () => {\n instance.destroy();\n instanceRef.current = null;\n };\n }, [options]);\n\n return instanceRef;\n}\n\n/**\n * Hook to run properties search with the MapFirst SDK.\n * Returns a function to trigger the search and loading state.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const { search, isLoading, error } = usePropertiesSearch(mapFirst);\n *\n * const handleSearch = async () => {\n * await search({\n * body: {\n * city: \"Paris\",\n * country: \"France\",\n * filters: {\n * checkIn: new Date(),\n * checkOut: new Date(Date.now() + 86400000),\n * numAdults: 2,\n * numRooms: 1\n * }\n * }\n * });\n * };\n * ```\n */\nexport function usePropertiesSearch(mapFirst: MapFirstCore | null) {\n const [isLoading, setIsLoading] = React.useState(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n const search = React.useCallback(\n async (options: {\n body: InitialRequestBody;\n beforeApplyProperties?: (data: any) => {\n price?: any;\n limit?: number;\n };\n smartFiltersClearable?: boolean;\n }) => {\n if (!mapFirst) {\n const err = new Error(\"MapFirst instance not available\");\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mapFirst.runPropertiesSearch({\n ...options,\n onError: (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n },\n });\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [mapFirst]\n );\n\n return { search, isLoading, error };\n}\n\n/**\n * Hook to run smart filter search with the MapFirst SDK.\n * Returns a function to trigger the search and loading state.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const { search, isLoading, error } = useSmartFilterSearch(mapFirst);\n *\n * const handleSearch = async () => {\n * await search({\n * query: \"hotels near beach with pool\"\n * });\n * };\n *\n * // Or with filters\n * const handleFilterSearch = async () => {\n * await search({\n * filters: [\n * { id: \"pool\", label: \"Pool\", type: \"amenity\", value: \"pool\" },\n * { id: \"4star\", label: \"4 Star\", type: \"starRating\", value: \"4\", numericValue: 4 }\n * ]\n * });\n * };\n * ```\n */\nexport function useSmartFilterSearch(mapFirst: MapFirstCore | null) {\n const [isLoading, setIsLoading] = React.useState(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n const search = React.useCallback(\n async (options: {\n query?: string;\n filters?: SmartFilter[];\n onProcessFilters?: (\n filters: any,\n location_id?: number\n ) => {\n smartFilters?: SmartFilter[];\n price?: any;\n limit?: number;\n language?: string;\n };\n }) => {\n if (!mapFirst) {\n const err = new Error(\"MapFirst instance not available\");\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mapFirst.runSmartFilterSearch({\n ...options,\n onError: (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n },\n });\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [mapFirst]\n );\n\n return { search, isLoading, error };\n}\n\n/**\n * Helper component that simply renders the markers it receives so non-React environments\n * can verify data flows before wiring the SDK into a map.\n */\nexport function MarkerDebugList({ markers }: { markers: Property[] }) {\n return (\n <div style={{ fontFamily: \"sans-serif\", fontSize: 14 }}>\n <strong>Markers</strong>\n <ul>\n {markers.map((marker) => (\n <li key={String(marker.tripadvisor_id)}>\n {marker.name} — {marker.location?.lat?.toFixed(3) ?? \"n/a\"},{\" \"}\n {marker.location?.lon?.toFixed(3) ?? \"n/a\"}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n","import React, {\r\n FormEventHandler,\r\n FunctionComponent,\r\n useCallback,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { AiIcon } from \"./Icons\";\r\nimport { FilterChips } from \"./smart-filter/FilterChips\";\r\nimport { useIsPortrait } from \"../hooks/useIsPortrait\";\r\nimport { useTranslation } from \"../hooks/useTranslation\";\r\nimport type { Filter } from \"./smart-filter/types\";\r\nimport type {\r\n MapFirstCore,\r\n FilterSchema,\r\n PropertyType,\r\n PriceLevel,\r\n} from \"@mapfirst.ai/core\";\r\n\r\nexport interface SmartFilterProps {\r\n mapFirst: MapFirstCore | null;\r\n filters: Filter[];\r\n value?: string;\r\n isSearching?: boolean;\r\n placeholder?: string;\r\n onSearch: (query: string, filters?: Filter[]) => Promise<void> | void;\r\n onFilterChange: (filters: Filter[]) => Promise<void> | void;\r\n onValueChange?: (value: string) => void;\r\n showTypingPrompt?: boolean;\r\n customTranslations?: Record<string, string>;\r\n currency?: string;\r\n style?: CSSProperties;\r\n inputStyle?: CSSProperties;\r\n containerStyle?: CSSProperties;\r\n}\r\n\r\nconst containerStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n width: \"100%\",\r\n};\r\n\r\nconst formStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n width: \"100%\",\r\n};\r\n\r\nconst inputContainerStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n flex: 1,\r\n backgroundColor: \"white\",\r\n borderRadius: \"24px\",\r\n border: \"1px solid #e5e5e5\",\r\n padding: \"12px 16px\",\r\n boxShadow: \"0 1px 3px rgba(0,0,0,0.1)\",\r\n};\r\n\r\nconst iconStyles: CSSProperties = {\r\n width: \"20px\",\r\n height: \"20px\",\r\n color: \"#03852e\",\r\n flexShrink: 0,\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n flex: 1,\r\n border: \"none\",\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n backgroundColor: \"transparent\",\r\n color: \"#000\",\r\n};\r\n\r\nconst loaderContainerStyles: CSSProperties = {\r\n position: \"absolute\",\r\n right: \"16px\",\r\n top: \"50%\",\r\n transform: \"translateY(-50%)\",\r\n};\r\n\r\nconst loaderStyles: CSSProperties = {\r\n width: \"20px\",\r\n height: \"20px\",\r\n border: \"2px solid #e5e5e5\",\r\n borderTop: \"2px solid #03852e\",\r\n borderRadius: \"50%\",\r\n animation: \"spin 1s linear infinite\",\r\n};\r\n\r\nconst typingPromptStyles: CSSProperties = {\r\n position: \"absolute\",\r\n left: \"48px\",\r\n top: \"50%\",\r\n transform: \"translateY(-50%)\",\r\n color: \"#737373\",\r\n pointerEvents: \"none\",\r\n fontSize: \"16px\",\r\n};\r\n\r\n/**\r\n * SmartFilter component for AI-powered search with filter chips.\r\n * Provides a search input with smart filtering capabilities.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { mapFirst, state } = useMapFirstCore({ ... });\r\n * const [filters, setFilters] = useState<Filter[]>([]);\r\n * const [searchValue, setSearchValue] = useState(\"\");\r\n *\r\n * const handleSearch = async (query: string, currentFilters?: Filter[]) => {\r\n * // Perform search using mapFirst.runSmartFilterSearch\r\n * const result = await mapFirst.runSmartFilterSearch({\r\n * query,\r\n * filters: currentFilters\r\n * });\r\n * // Update filters based on response\r\n * };\r\n *\r\n * return (\r\n * <SmartFilter\r\n * mapFirst={mapFirst}\r\n * filters={filters}\r\n * value={searchValue}\r\n * isSearching={state?.isSearching}\r\n * onSearch={handleSearch}\r\n * onFilterChange={setFilters}\r\n * onValueChange={setSearchValue}\r\n * />\r\n * );\r\n * ```\r\n */\r\nexport const SmartFilter: FunctionComponent<SmartFilterProps> = ({\r\n mapFirst,\r\n filters,\r\n value: controlledValue,\r\n isSearching = false,\r\n placeholder,\r\n onSearch,\r\n onFilterChange,\r\n onValueChange,\r\n showTypingPrompt = true,\r\n customTranslations,\r\n currency = \"USD\",\r\n style,\r\n inputStyle,\r\n containerStyle,\r\n}) => {\r\n const [internalValue, setInternalValue] = useState(\"\");\r\n const value = controlledValue !== undefined ? controlledValue : internalValue;\r\n const setValue = onValueChange || setInternalValue;\r\n\r\n const isPortrait = useIsPortrait();\r\n const { t, formatCurrency } = useTranslation(customTranslations);\r\n\r\n const minRatingSuffix = t(\"smartFilter.minRating.suffix\");\r\n const placeholderText = placeholder || t(\"smartFilter.placeholder\");\r\n const typingPrompt = t(\"smartFilter.typingPrompt\");\r\n const previousFiltersLabel = t(\"smartFilter.nav.previous\");\r\n const nextFiltersLabel = t(\"smartFilter.nav.next\");\r\n const clearAllLabel = t(\"smartFilter.clearAll\");\r\n\r\n const formSubmit: FormEventHandler<HTMLFormElement> = async (e) => {\r\n e.preventDefault();\r\n const query = value.trim();\r\n if (!query || isSearching) {\r\n return;\r\n }\r\n try {\r\n await onSearch(query);\r\n } catch (error) {\r\n console.error(\"Search error:\", error);\r\n }\r\n };\r\n\r\n const handleFilterChange = useCallback(\r\n async (nextFilters: Filter[], clearAll?: boolean) => {\r\n if (isSearching) {\r\n return;\r\n }\r\n try {\r\n await onFilterChange(nextFilters);\r\n } catch (error) {\r\n console.error(\"Filter change error:\", error);\r\n }\r\n },\r\n [isSearching, onFilterChange]\r\n );\r\n\r\n const resetFilters = useCallback(() => {\r\n void handleFilterChange([]);\r\n }, [handleFilterChange]);\r\n\r\n const clearAllFilters = useCallback(() => {\r\n void handleFilterChange([], true);\r\n setValue(\"\");\r\n }, [handleFilterChange, setValue]);\r\n\r\n return (\r\n <div style={{ ...containerStyles, ...containerStyle }}>\r\n <style>\r\n {`\r\n @keyframes spin {\r\n 0% { transform: rotate(0deg); }\r\n 100% { transform: rotate(360deg); }\r\n }\r\n `}\r\n </style>\r\n <form onSubmit={formSubmit} style={{ ...formStyles, ...style }}>\r\n <div style={inputContainerStyles}>\r\n <AiIcon style={iconStyles} />\r\n <input\r\n type=\"text\"\r\n value={value}\r\n onChange={(e) => setValue(e.target.value)}\r\n placeholder={placeholderText}\r\n disabled={isSearching}\r\n style={{ ...inputStyles, ...inputStyle }}\r\n autoComplete=\"off\"\r\n aria-label=\"Smart search\"\r\n />\r\n {showTypingPrompt && value.length === 0 && !isSearching && (\r\n <span style={typingPromptStyles}>{typingPrompt}</span>\r\n )}\r\n {isSearching && (\r\n <div style={loaderContainerStyles}>\r\n <div style={loaderStyles} />\r\n </div>\r\n )}\r\n </div>\r\n </form>\r\n\r\n {filters.length > 0 && (\r\n <FilterChips\r\n filters={filters}\r\n isPortrait={isPortrait}\r\n currency={currency}\r\n minRatingSuffix={minRatingSuffix}\r\n clearAllLabel={clearAllLabel}\r\n previousFiltersLabel={previousFiltersLabel}\r\n nextFiltersLabel={nextFiltersLabel}\r\n formatCurrency={formatCurrency}\r\n onFilterChange={handleFilterChange}\r\n onResetFilters={resetFilters}\r\n onClearAll={clearAllFilters}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React, { CSSProperties } from \"react\";\r\n\r\nexport interface IconProps {\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\nexport const SearchIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\r\n <path d=\"m21 21-4.35-4.35\" />\r\n </svg>\r\n);\r\n\r\nexport const CloseIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\r\n </svg>\r\n);\r\n\r\nexport const EditIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\r\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" />\r\n </svg>\r\n);\r\n\r\nexport const NextIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n);\r\n\r\nexport const AiIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" />\r\n <path d=\"M2 17l10 5 10-5\" />\r\n <path d=\"M2 12l10 5 10-5\" />\r\n </svg>\r\n);\r\n\r\nexport const StarIcon: React.FC<IconProps & { fill?: string }> = ({\r\n className,\r\n style,\r\n fill = \"none\",\r\n}) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill={fill}\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\r\n </svg>\r\n);\r\n","import React, { FunctionComponent, CSSProperties } from \"react\";\r\nimport { Chip } from \"./Chip\";\r\nimport { MinRatingFilterChip } from \"./MinRatingFilterChip\";\r\nimport { PriceRangeFilterChip } from \"./PriceRangeFilterChip\";\r\nimport { RestaurantPriceLevelChip } from \"./RestaurantPriceLevelChip\";\r\nimport { TransformedQueryChip } from \"./TransformedQueryChip\";\r\nimport { SearchIcon, NextIcon } from \"../Icons\";\r\nimport { useFilterScroll } from \"../../hooks/useFilterScroll\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { Filter } from \"./types\";\r\n\r\nexport interface FilterChipsProps {\r\n filters: Filter[];\r\n isPortrait: boolean;\r\n currency: string;\r\n minRatingSuffix: string;\r\n clearAllLabel: string;\r\n previousFiltersLabel: string;\r\n nextFiltersLabel: string;\r\n formatCurrency: (value: number, currency?: string) => string;\r\n onFilterChange: (\r\n filters: Filter[],\r\n clearAll?: boolean\r\n ) => void | Promise<void>;\r\n onResetFilters: () => void;\r\n onClearAll: () => void;\r\n}\r\n\r\nconst containerStyles: CSSProperties = {\r\n position: \"relative\",\r\n width: \"100%\",\r\n};\r\n\r\nconst scrollContainerBase: CSSProperties = {\r\n display: \"flex\",\r\n gap: \"8px\",\r\n overflowX: \"auto\",\r\n alignItems: \"center\",\r\n width: \"100%\",\r\n scrollbarWidth: \"none\",\r\n msOverflowStyle: \"none\",\r\n};\r\n\r\nconst gradientStyles: CSSProperties = {\r\n pointerEvents: \"none\",\r\n position: \"absolute\",\r\n top: 0,\r\n bottom: 0,\r\n width: \"40px\",\r\n};\r\n\r\nconst navButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"50%\",\r\n transform: \"translateY(-50%)\",\r\n backgroundColor: \"white\",\r\n color: \"#003c30\",\r\n border: \"1px solid #003c30\",\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 1px 3px rgba(0,0,0,0.1)\",\r\n cursor: \"pointer\",\r\n};\r\n\r\nexport const FilterChips: FunctionComponent<FilterChipsProps> = ({\r\n filters,\r\n isPortrait,\r\n currency,\r\n minRatingSuffix,\r\n clearAllLabel,\r\n previousFiltersLabel,\r\n nextFiltersLabel,\r\n formatCurrency,\r\n onFilterChange,\r\n onResetFilters,\r\n onClearAll,\r\n}) => {\r\n const { scrollerRef, atStart, atEnd, scrollByDir } = useFilterScroll(\r\n filters.length\r\n );\r\n const { t } = useTranslation();\r\n const [navHover, setNavHover] = React.useState<\"prev\" | \"next\" | null>(null);\r\n const [resetHover, setResetHover] = React.useState(false);\r\n const [clearHover, setClearHover] = React.useState(false);\r\n\r\n const scrollContainerStyles: CSSProperties = {\r\n ...scrollContainerBase,\r\n padding: isPortrait ? \"8px 16px\" : \"8px\",\r\n };\r\n\r\n return (\r\n <div style={containerStyles}>\r\n <div\r\n ref={scrollerRef}\r\n style={{\r\n ...scrollContainerStyles,\r\n // Hide scrollbar for webkit browsers\r\n WebkitOverflowScrolling: \"touch\",\r\n }}\r\n >\r\n <style>\r\n {`\r\n div::-webkit-scrollbar {\r\n display: none;\r\n }\r\n `}\r\n </style>\r\n <button\r\n style={{\r\n flexShrink: 0,\r\n backgroundColor: resetHover ? \"#03a03e\" : \"#03852e\",\r\n borderRadius: \"50%\",\r\n padding: \"8px\",\r\n cursor: \"pointer\",\r\n border: \"none\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n onClick={onResetFilters}\r\n onMouseEnter={() => setResetHover(true)}\r\n onMouseLeave={() => setResetHover(false)}\r\n >\r\n <SearchIcon\r\n style={{ width: \"20px\", height: \"20px\", color: \"white\" }}\r\n />\r\n </button>\r\n {filters.map((filter) => {\r\n const renderStandardChip = () => (\r\n <Chip\r\n key={filter.id}\r\n label={filter.label}\r\n icon={filter.icon}\r\n remove={() => {\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id));\r\n }}\r\n />\r\n );\r\n\r\n if (filter.type === \"minRating\") {\r\n const currentRating = filter.numericValue ?? Number(filter.value);\r\n if (!Number.isFinite(currentRating)) {\r\n return renderStandardChip();\r\n }\r\n\r\n return (\r\n <MinRatingFilterChip\r\n key={filter.id}\r\n rating={currentRating}\r\n onChange={(nextRating) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n numericValue: nextRating,\r\n value: String(nextRating),\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"starRating\") {\r\n const currentRating = filter.numericValue ?? Number(filter.value);\r\n if (!Number.isFinite(currentRating)) {\r\n return renderStandardChip();\r\n }\r\n\r\n return (\r\n <MinRatingFilterChip\r\n star\r\n key={filter.id}\r\n rating={currentRating}\r\n onChange={(nextRating) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n numericValue: nextRating,\r\n value: String(nextRating),\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"priceRange\" && filter.priceRange) {\r\n return (\r\n <PriceRangeFilterChip\r\n key={filter.id}\r\n priceRange={filter.priceRange}\r\n currency={currency}\r\n onChange={(nextRange) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n priceRange: nextRange,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"transformed_query\") {\r\n return (\r\n <TransformedQueryChip\r\n key={filter.id}\r\n value={filter.value}\r\n onChange={(nextValue) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n value: nextValue,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"selected_restaurant_price_levels\") {\r\n return (\r\n <RestaurantPriceLevelChip\r\n key={filter.id}\r\n values={filter.priceLevels ?? []}\r\n onChange={(nextLevels) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n priceLevels: nextLevels,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n return renderStandardChip();\r\n })}\r\n <button\r\n style={{\r\n flexShrink: 0,\r\n padding: \"4px 16px\",\r\n borderRadius: \"9999px\",\r\n cursor: \"pointer\",\r\n fontSize: \"14px\",\r\n userSelect: \"none\",\r\n border: \"none\",\r\n backgroundColor: clearHover ? \"#e5e5e5\" : \"transparent\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n onClick={onClearAll}\r\n onMouseEnter={() => setClearHover(true)}\r\n onMouseLeave={() => setClearHover(false)}\r\n >\r\n {clearAllLabel}\r\n </button>\r\n </div>\r\n\r\n {!atStart && (\r\n <div\r\n aria-hidden=\"true\"\r\n style={{\r\n ...gradientStyles,\r\n left: 0,\r\n background: \"linear-gradient(to right, white, transparent)\",\r\n }}\r\n />\r\n )}\r\n\r\n {!atEnd && (\r\n <div\r\n aria-hidden=\"true\"\r\n style={{\r\n ...gradientStyles,\r\n right: 0,\r\n background: \"linear-gradient(to left, white, transparent)\",\r\n }}\r\n />\r\n )}\r\n\r\n {!atStart && (\r\n <button\r\n type=\"button\"\r\n aria-label={previousFiltersLabel}\r\n style={{\r\n ...navButtonStyles,\r\n left: \"4px\",\r\n transform: \"translateY(-50%) rotate(180deg)\",\r\n backgroundColor: navHover === \"prev\" ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={() => scrollByDir(\"prev\")}\r\n onMouseEnter={() => setNavHover(\"prev\")}\r\n onMouseLeave={() => setNavHover(null)}\r\n >\r\n <NextIcon style={{ width: \"20px\", height: \"20px\" }} />\r\n </button>\r\n )}\r\n\r\n {!atEnd && (\r\n <button\r\n type=\"button\"\r\n aria-label={nextFiltersLabel}\r\n style={{\r\n ...navButtonStyles,\r\n right: \"4px\",\r\n backgroundColor: navHover === \"next\" ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={() => scrollByDir(\"next\")}\r\n onMouseEnter={() => setNavHover(\"next\")}\r\n onMouseLeave={() => setNavHover(null)}\r\n >\r\n <NextIcon style={{ width: \"20px\", height: \"20px\" }} />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React, { CSSProperties, ReactNode } from \"react\";\r\nimport { CloseIcon } from \"../Icons\";\r\n\r\nexport interface ChipProps {\r\n label: string | ReactNode;\r\n icon?: ReactNode;\r\n remove: () => void;\r\n style?: CSSProperties;\r\n}\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst removeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst iconStyles: CSSProperties = {\r\n width: \"17px\",\r\n height: \"17px\",\r\n};\r\n\r\nexport const Chip: React.FC<ChipProps> = ({ label, icon, remove, style }) => {\r\n const [isHovering, setIsHovering] = React.useState(false);\r\n\r\n return (\r\n <div style={{ ...chipStyles, ...style }}>\r\n {icon && (\r\n <span style={{ display: \"flex\", alignItems: \"center\" }}>{icon}</span>\r\n )}\r\n <span style={{ whiteSpace: \"nowrap\" }}>{label}</span>\r\n <button\r\n style={{\r\n ...removeButtonStyles,\r\n backgroundColor: isHovering ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={remove}\r\n onMouseEnter={() => setIsHovering(true)}\r\n onMouseLeave={() => setIsHovering(false)}\r\n aria-label=\"Remove filter\"\r\n >\r\n <CloseIcon style={iconStyles} />\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import React, {\r\n FunctionComponent,\r\n useState,\r\n FocusEvent,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { CloseIcon, StarIcon } from \"../Icons\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport { formatRatingValue } from \"./utils\";\r\n\r\nconst chipContainerStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst removeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst starContainerStyles: CSSProperties = {\r\n display: \"flex\",\r\n gap: \"1px\",\r\n userSelect: \"none\",\r\n};\r\n\r\nconst circleBaseStyles: CSSProperties = {\r\n display: \"block\",\r\n width: \"12px\",\r\n height: \"12px\",\r\n borderRadius: \"50%\",\r\n border: \"1px solid #03852e\",\r\n pointerEvents: \"none\",\r\n};\r\n\r\nconst buttonBaseStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: 0,\r\n height: \"100%\",\r\n cursor: \"pointer\",\r\n backgroundColor: \"transparent\",\r\n border: \"none\",\r\n padding: 0,\r\n};\r\n\r\nexport const MinRatingFilterChip: FunctionComponent<{\r\n star?: boolean;\r\n rating: number;\r\n onChange: (rating: number) => void;\r\n onRemove: () => void;\r\n}> = ({ rating, onChange, onRemove, star = false }) => {\r\n const [hoverRating, setHoverRating] = useState<number | null>(null);\r\n const [removeHover, setRemoveHover] = useState(false);\r\n const { t } = useTranslation();\r\n\r\n const displayRating = hoverRating ?? rating;\r\n const formatLabel = (value: number) =>\r\n star && value\r\n ? value.toString()\r\n : t(\"smartFilter.minRating.label\", { value: formatRatingValue(value) });\r\n const removeLabel = t(\"smartFilter.minRating.remove\");\r\n const setLabel = (value: number) =>\r\n t(\"smartFilter.minRating.setTo\", { rating: formatRatingValue(value) });\r\n\r\n const getFillForStar = (index: number) => {\r\n const starNumber = index + 1;\r\n if (displayRating >= starNumber) {\r\n return \"full\" as const;\r\n }\r\n if (displayRating >= starNumber - 0.5) {\r\n return \"half\" as const;\r\n }\r\n return \"empty\" as const;\r\n };\r\n\r\n const handleSelect = (nextRating: number) => {\r\n setHoverRating(null);\r\n if (nextRating === rating) {\r\n return;\r\n }\r\n onChange(nextRating);\r\n };\r\n\r\n const handleBlur = (event: FocusEvent<HTMLButtonElement>) => {\r\n const related = event.relatedTarget as HTMLElement | null;\r\n if (\r\n !related ||\r\n !(event.currentTarget as HTMLElement)\r\n .closest(\"[data-min-rating-chip]\")\r\n ?.contains(related)\r\n ) {\r\n setHoverRating(null);\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipContainerStyles} data-min-rating-chip>\r\n <div\r\n style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\r\n onMouseLeave={() => setHoverRating(null)}\r\n >\r\n <div style={starContainerStyles}>\r\n {Array.from({ length: 5 }).map((_, index) => {\r\n const fillState = getFillForStar(index);\r\n const starNumber = index + 1;\r\n const halfValue = starNumber - 0.5;\r\n\r\n if (star) {\r\n return (\r\n <div\r\n key={index}\r\n style={{\r\n position: \"relative\",\r\n width: \"16px\",\r\n height: \"16px\",\r\n }}\r\n >\r\n <StarIcon\r\n fill={displayRating >= starNumber ? \"#03852e\" : \"none\"}\r\n style={{\r\n width: \"16px\",\r\n height: \"16px\",\r\n pointerEvents: \"none\",\r\n }}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: 0,\r\n width: \"50%\",\r\n borderRadius: \"50% 0 0 50%\",\r\n }}\r\n onMouseEnter={() => setHoverRating(halfValue)}\r\n onFocus={() => setHoverRating(halfValue)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(halfValue)}\r\n aria-label={setLabel(halfValue)}\r\n title={formatLabel(halfValue)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: \"50%\",\r\n width: \"50%\",\r\n borderRadius: \"0 50% 50% 0\",\r\n }}\r\n onMouseEnter={() => setHoverRating(starNumber)}\r\n onFocus={() => setHoverRating(starNumber)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(starNumber)}\r\n aria-label={setLabel(starNumber)}\r\n title={formatLabel(starNumber)}\r\n />\r\n </div>\r\n );\r\n }\r\n\r\n const circleStyles: CSSProperties =\r\n fillState === \"full\"\r\n ? { ...circleBaseStyles, backgroundColor: \"#03852e\" }\r\n : circleBaseStyles;\r\n\r\n const halfCircleStyles: CSSProperties = {\r\n ...circleBaseStyles,\r\n background:\r\n \"linear-gradient(90deg, #03852e 50%, transparent 50%)\",\r\n };\r\n\r\n return (\r\n <div\r\n key={index}\r\n style={{ position: \"relative\", width: \"12px\", height: \"12px\" }}\r\n >\r\n <span\r\n style={fillState === \"half\" ? halfCircleStyles : circleStyles}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: 0,\r\n width: \"50%\",\r\n borderRadius: \"50% 0 0 50%\",\r\n outline: \"2px solid transparent\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={() => setHoverRating(halfValue)}\r\n onFocus={() => setHoverRating(halfValue)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(halfValue)}\r\n aria-label={setLabel(halfValue)}\r\n title={formatLabel(halfValue)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: \"50%\",\r\n width: \"50%\",\r\n borderRadius: \"0 50% 50% 0\",\r\n outline: \"2px solid transparent\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={() => setHoverRating(starNumber)}\r\n onFocus={() => setHoverRating(starNumber)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(starNumber)}\r\n aria-label={setLabel(starNumber)}\r\n title={formatLabel(starNumber)}\r\n />\r\n </div>\r\n );\r\n })}\r\n </div>\r\n <span style={{ whiteSpace: \"nowrap\" }}>\r\n {formatLabel(displayRating)}\r\n </span>\r\n </div>\r\n <button\r\n style={{\r\n ...removeButtonStyles,\r\n backgroundColor: removeHover ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={onRemove}\r\n onMouseEnter={() => setRemoveHover(true)}\r\n onMouseLeave={() => setRemoveHover(false)}\r\n aria-label={removeLabel}\r\n title={removeLabel}\r\n >\r\n <CloseIcon style={{ width: \"17px\", height: \"17px\" }} />\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import { useCallback, useState } from \"react\";\r\n\r\nexport type Locale = \"en\" | \"es\" | \"de\" | \"fr\" | \"it\" | \"pt\";\r\n\r\ntype TranslationFunction = (\r\n key: string,\r\n params?: Record<string, any>\r\n) => string;\r\ntype FormatCurrencyFunction = (value: number, currency?: string) => string;\r\n\r\nconst defaultTranslations: Record<string, string> = {\r\n \"smartFilter.placeholder\":\r\n \"Search for hotels, restaurants, or attractions...\",\r\n \"smartFilter.typingPrompt\": \"Type to search...\",\r\n \"smartFilter.nav.previous\": \"Previous filters\",\r\n \"smartFilter.nav.next\": \"Next filters\",\r\n \"smartFilter.toast.locationRequired\": \"Please select a location first\",\r\n \"smartFilter.clearAll\": \"Clear all\",\r\n \"smartFilter.minRating.suffix\": \"+\",\r\n \"smartFilter.minRating.label\": \"{{value}}+\",\r\n \"smartFilter.minRating.remove\": \"Remove rating filter\",\r\n \"smartFilter.minRating.setTo\": \"Set rating to {{rating}}\",\r\n \"smartFilter.priceRange.label\": \"Price Range\",\r\n \"smartFilter.priceRange.remove\": \"Remove price filter\",\r\n \"smartFilter.priceRange.edit\": \"Edit price\",\r\n \"smartFilter.transformedQuery.remove\": \"Remove search query\",\r\n \"smartFilter.transformedQuery.edit\": \"Edit search query\",\r\n \"smartFilter.restaurantPriceLevel.label\": \"Price Level\",\r\n \"smartFilter.restaurantPriceLevel.remove\": \"Remove price level filter\",\r\n \"smartFilter.restaurantPriceLevel.none\": \"Any\",\r\n \"smartFilter.restaurantPriceLevel.options.cheapEats\": \"Cheap Eats\",\r\n \"smartFilter.restaurantPriceLevel.options.midRange\": \"Mid Range\",\r\n \"smartFilter.restaurantPriceLevel.options.fineDining\": \"Fine Dining\",\r\n};\r\n\r\nconst formatCurrencyDefault: FormatCurrencyFunction = (\r\n value,\r\n currency = \"USD\"\r\n) => {\r\n return new Intl.NumberFormat(\"en-US\", {\r\n style: \"currency\",\r\n currency: currency,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 0,\r\n }).format(value);\r\n};\r\n\r\n/**\r\n * Simple translation hook with default English translations.\r\n * Can be extended with custom translations and locales.\r\n */\r\nexport const useTranslation = (\r\n customTranslations?: Record<string, string>,\r\n customFormatCurrency?: FormatCurrencyFunction\r\n) => {\r\n const [locale, setLocale] = useState<Locale>(\"en\");\r\n\r\n const t: TranslationFunction = useCallback(\r\n (key: string, params?: Record<string, any>) => {\r\n const translations = { ...defaultTranslations, ...customTranslations };\r\n let translation = translations[key] || key;\r\n\r\n if (params) {\r\n Object.keys(params).forEach((paramKey) => {\r\n translation = translation.replace(\r\n new RegExp(`{{${paramKey}}}`, \"g\"),\r\n String(params[paramKey])\r\n );\r\n });\r\n }\r\n\r\n return translation;\r\n },\r\n [customTranslations]\r\n );\r\n\r\n const formatCurrency = useCallback(\r\n (value: number, currency?: string) => {\r\n if (customFormatCurrency) {\r\n return customFormatCurrency(value, currency);\r\n }\r\n return formatCurrencyDefault(value, currency);\r\n },\r\n [customFormatCurrency]\r\n );\r\n\r\n return {\r\n t,\r\n locale,\r\n setLocale,\r\n formatCurrency,\r\n };\r\n};\r\n","import React, { ReactNode, CSSProperties } from \"react\";\r\n\r\nexport const renderStars = (rating: number): ReactNode[] => {\r\n const stars: ReactNode[] = [];\r\n const fullStars = Math.floor(rating);\r\n const hasHalfStar = rating % 1 !== 0;\r\n\r\n const baseStyles: CSSProperties = {\r\n display: \"block\",\r\n width: \"12px\",\r\n height: \"12px\",\r\n borderRadius: \"50%\",\r\n border: \"1px solid #03852e\",\r\n pointerEvents: \"none\",\r\n };\r\n\r\n const fullStarStyles: CSSProperties = {\r\n ...baseStyles,\r\n backgroundColor: \"#03852e\",\r\n };\r\n\r\n const halfStarStyles: CSSProperties = {\r\n ...baseStyles,\r\n background: \"linear-gradient(90deg, #03852e 50%, transparent 50%)\",\r\n };\r\n\r\n for (let i = 0; i < fullStars; i += 1) {\r\n stars.push(<span key={`full-${i}`} style={fullStarStyles} />);\r\n }\r\n\r\n if (hasHalfStar) {\r\n stars.push(<span key=\"half\" style={halfStarStyles} />);\r\n }\r\n\r\n const remainingStars = Math.max(0, 5 - Math.ceil(rating));\r\n for (let i = 0; i < remainingStars; i += 1) {\r\n stars.push(<span key={`empty-${i}`} style={baseStyles} />);\r\n }\r\n\r\n return stars;\r\n};\r\n\r\nexport const createMinRatingFilterLabel = (\r\n rating: number,\r\n suffix?: string\r\n): ReactNode => (\r\n <span style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}>\r\n <span\r\n style={{\r\n display: \"flex\",\r\n gap: \"1px\",\r\n userSelect: \"none\",\r\n }}\r\n >\r\n {renderStars(rating)}\r\n </span>{\" \"}\r\n {suffix}\r\n </span>\r\n);\r\n\r\nexport const formatRatingValue = (rating: number): string => rating.toFixed(1);\r\n\r\nexport const createPriceRangeFilterLabel = (\r\n min: number,\r\n max: number | undefined,\r\n currency: string | undefined,\r\n formatCurrencyFn: (value: number, currency?: string) => string\r\n): string =>\r\n `${formatCurrencyFn(min, currency)} - ${formatCurrencyFn(\r\n max ?? 0,\r\n currency\r\n )}`;\r\n","import React, {\r\n ChangeEvent,\r\n FunctionComponent,\r\n KeyboardEvent,\r\n useEffect,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { CloseIcon, EditIcon } from \"../Icons\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { PriceRangeValue } from \"./types\";\r\n\r\ntype Boundary = \"min\" | \"max\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst removeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n backgroundColor: \"transparent\",\r\n borderRadius: \"2px\",\r\n padding: \"2px 8px\",\r\n width: \"64px\",\r\n textAlign: \"center\",\r\n border: \"none\",\r\n};\r\n\r\nconst editButtonStyles: CSSProperties = {\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n color: \"#737373\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n};\r\n\r\ninterface PriceBoundaryChipProps {\r\n boundary: Boundary;\r\n label: string;\r\n value?: number;\r\n placeholder?: string;\r\n currency: string;\r\n isOptional?: boolean;\r\n showRemoveButton?: boolean;\r\n removeLabel?: string;\r\n editLabel?: string;\r\n showAddWhenEmpty?: boolean;\r\n onCommit: (value?: number) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nconst PriceBoundaryChip: FunctionComponent<PriceBoundaryChipProps> = ({\r\n boundary,\r\n label,\r\n value,\r\n placeholder,\r\n currency,\r\n isOptional = false,\r\n showRemoveButton = false,\r\n removeLabel,\r\n editLabel,\r\n showAddWhenEmpty = false,\r\n onCommit,\r\n onRemove,\r\n}) => {\r\n const [draft, setDraft] = useState<string>(\r\n value !== undefined ? String(value) : \"\"\r\n );\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editHover, setEditHover] = useState(false);\r\n const [removeHover, setRemoveHover] = useState(false);\r\n const hasValue = value !== undefined;\r\n\r\n useEffect(() => {\r\n setDraft(value !== undefined ? String(value) : \"\");\r\n setIsEditing(false);\r\n }, [value]);\r\n\r\n const resetDraft = () => {\r\n setDraft(value !== undefined ? String(value) : \"\");\r\n };\r\n\r\n const commitValue = () => {\r\n if (draft.trim() === \"\") {\r\n if (isOptional) {\r\n onCommit(undefined);\r\n setDraft(\"\");\r\n return;\r\n }\r\n resetDraft();\r\n return;\r\n }\r\n\r\n const parsed = Number(draft);\r\n if (!Number.isFinite(parsed)) {\r\n resetDraft();\r\n return;\r\n }\r\n\r\n const normalized = Math.max(0, parsed);\r\n if (normalized === value) {\r\n resetDraft();\r\n return;\r\n }\r\n onCommit(normalized);\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n const next = event.target.value.replace(/[^\\d]/g, \"\");\r\n setDraft(next);\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n setIsEditing(false);\r\n return;\r\n }\r\n\r\n if (event.key === \"Escape\") {\r\n event.preventDefault();\r\n resetDraft();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n setIsEditing(false);\r\n return;\r\n }\r\n\r\n const allowed =\r\n (event.key.length === 1 && /[0-9]/.test(event.key)) ||\r\n event.key === \"Backspace\" ||\r\n event.key === \"Delete\" ||\r\n event.key === \"Tab\" ||\r\n event.key === \"ArrowLeft\" ||\r\n event.key === \"ArrowRight\" ||\r\n event.key === \"Home\" ||\r\n event.key === \"End\";\r\n\r\n if (!allowed) {\r\n event.preventDefault();\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <span\r\n style={{\r\n fontSize: \"10px\",\r\n textTransform: \"uppercase\",\r\n fontWeight: 600,\r\n letterSpacing: \"0.05em\",\r\n }}\r\n >\r\n {label}\r\n </span>\r\n {isEditing ? (\r\n <input\r\n value={draft}\r\n onChange={handleChange}\r\n onBlur={() => {\r\n commitValue();\r\n setIsEditing(false);\r\n }}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n inputMode=\"numeric\"\r\n pattern=\"[0-9]*\"\r\n aria-label={label}\r\n style={inputStyles}\r\n autoFocus\r\n />\r\n ) : hasValue ? (\r\n <span style={{ fontSize: \"16px\" }}>\r\n {currency}\r\n {value}\r\n </span>\r\n ) : showAddWhenEmpty ? (\r\n <button\r\n type=\"button\"\r\n style={{\r\n fontSize: \"16px\",\r\n color: \"#737373\",\r\n cursor: \"pointer\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n padding: 0,\r\n }}\r\n onClick={() => setIsEditing(true)}\r\n aria-label={editLabel}\r\n >\r\n +\r\n </button>\r\n ) : (\r\n <span style={{ fontSize: \"16px\", color: \"#737373\" }}>-</span>\r\n )}\r\n {(!showAddWhenEmpty || (showAddWhenEmpty && isEditing)) && (\r\n <span style={{ color: \"#737373\", fontSize: \"12px\" }}>{currency}</span>\r\n )}\r\n {!isEditing && (!showAddWhenEmpty || hasValue) && (\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...editButtonStyles,\r\n backgroundColor: editHover ? \"#e5e5e5\" : \"transparent\",\r\n }}\r\n aria-label={editLabel}\r\n title={editLabel}\r\n onClick={() => setIsEditing(true)}\r\n onMouseEnter={() => setEditHover(true)}\r\n onMouseLeave={() => setEditHover(false)}\r\n >\r\n <EditIcon />\r\n </button>\r\n )}\r\n {showRemoveButton && (\r\n <button\r\n style={{\r\n ...removeButtonStyles,\r\n backgroundColor: removeHover ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={onRemove}\r\n onMouseEnter={() => setRemoveHover(true)}\r\n onMouseLeave={() => setRemoveHover(false)}\r\n aria-label={removeLabel}\r\n title={removeLabel}\r\n >\r\n <CloseIcon style={{ width: \"17px\", height: \"17px\" }} />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nexport const PriceRangeFilterChip: FunctionComponent<{\r\n priceRange: PriceRangeValue;\r\n currency: string;\r\n onChange: (range: PriceRangeValue) => void;\r\n onRemove: () => void;\r\n}> = ({ priceRange, currency, onChange, onRemove }) => {\r\n const { t } = useTranslation();\r\n\r\n const minLabel = \"Min\";\r\n const maxChipLabel = \"Max\";\r\n const removeLabel = t(\"smartFilter.priceRange.remove\");\r\n const editLabel = t(\"smartFilter.priceRange.edit\");\r\n\r\n const handleBoundaryCommit = (boundary: Boundary, nextValue?: number) => {\r\n const nextRange: PriceRangeValue = {\r\n min: priceRange.min,\r\n max: priceRange.max,\r\n };\r\n\r\n if (boundary === \"min\") {\r\n nextRange.min = nextValue;\r\n if (\r\n nextValue !== undefined &&\r\n priceRange.max !== undefined &&\r\n nextValue > priceRange.max\r\n ) {\r\n nextRange.max = nextValue;\r\n }\r\n } else {\r\n nextRange.max = nextValue;\r\n if (\r\n nextValue !== undefined &&\r\n priceRange.min !== undefined &&\r\n nextValue < priceRange.min\r\n ) {\r\n nextRange.min = nextValue;\r\n }\r\n }\r\n\r\n if (nextRange.min !== priceRange.min || nextRange.max !== priceRange.max) {\r\n onChange(nextRange);\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <PriceBoundaryChip\r\n boundary=\"min\"\r\n label={minLabel}\r\n value={priceRange.min}\r\n currency={currency}\r\n editLabel={editLabel}\r\n showRemoveButton={priceRange.min !== undefined && priceRange.min !== 0}\r\n onCommit={(value) => handleBoundaryCommit(\"min\", value)}\r\n onRemove={onRemove}\r\n />\r\n <PriceBoundaryChip\r\n boundary=\"max\"\r\n label={maxChipLabel}\r\n value={priceRange.max}\r\n currency={currency}\r\n isOptional\r\n showRemoveButton={priceRange.max !== undefined}\r\n removeLabel={removeLabel}\r\n editLabel={editLabel}\r\n showAddWhenEmpty\r\n onCommit={(value) => handleBoundaryCommit(\"max\", value)}\r\n onRemove={onRemove}\r\n />\r\n </>\r\n );\r\n};\r\n","import React, { ChangeEvent, FunctionComponent, CSSProperties } from \"react\";\r\nimport { CloseIcon } from \"../Icons\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { PriceLevel } from \"@mapfirst.ai/core\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"16px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst removeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst PRICE_LEVEL_OPTIONS = [\r\n { value: \"Cheap Eats\" as PriceLevel, key: \"cheapEats\" },\r\n { value: \"Mid Range\" as PriceLevel, key: \"midRange\" },\r\n { value: \"Fine Dining\" as PriceLevel, key: \"fineDining\" },\r\n] as const;\r\n\r\nexport interface RestaurantPriceLevelChipProps {\r\n values: PriceLevel[];\r\n onChange: (values: PriceLevel[]) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nexport const RestaurantPriceLevelChip: FunctionComponent<\r\n RestaurantPriceLevelChipProps\r\n> = ({ values, onChange, onRemove }) => {\r\n const { t } = useTranslation();\r\n const [removeHover, setRemoveHover] = React.useState(false);\r\n\r\n const label = t(\"smartFilter.restaurantPriceLevel.label\");\r\n const removeLabel = t(\"smartFilter.restaurantPriceLevel.remove\");\r\n const noneSelectedLabel = t(\"smartFilter.restaurantPriceLevel.none\");\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n const { value, checked } = event.target;\r\n const valueAsPriceLevel = value as PriceLevel;\r\n const selection = new Set(values);\r\n if (checked) {\r\n selection.add(valueAsPriceLevel);\r\n } else {\r\n selection.delete(valueAsPriceLevel);\r\n }\r\n const orderedSelection = PRICE_LEVEL_OPTIONS.filter((option) =>\r\n selection.has(option.value)\r\n ).map((option) => option.value);\r\n onChange(orderedSelection);\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexWrap: \"wrap\",\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontSize: \"10px\",\r\n textTransform: \"uppercase\",\r\n fontWeight: 600,\r\n letterSpacing: \"0.05em\",\r\n }}\r\n >\r\n {label}\r\n </span>\r\n <div style={{ display: \"flex\", gap: \"12px\" }}>\r\n {PRICE_LEVEL_OPTIONS.map((option) => {\r\n const optionLabel = t(\r\n `smartFilter.restaurantPriceLevel.options.${option.key}`\r\n );\r\n const checkboxId = `price-level-${option.key}`;\r\n return (\r\n <label\r\n key={option.value}\r\n htmlFor={checkboxId}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"4px\",\r\n fontSize: \"12px\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n <input\r\n id={checkboxId}\r\n type=\"checkbox\"\r\n value={option.value}\r\n checked={values.includes(option.value)}\r\n onChange={handleChange}\r\n style={{ accentColor: \"#03852e\", cursor: \"pointer\" }}\r\n />\r\n <span>{optionLabel}</span>\r\n </label>\r\n );\r\n })}\r\n {values.length === 0 && (\r\n <span style={{ fontSize: \"12px\", color: \"#737373\" }}>\r\n {noneSelectedLabel}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <button\r\n style={{\r\n ...removeButtonStyles,\r\n backgroundColor: removeHover ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={onRemove}\r\n onMouseEnter={() => setRemoveHover(true)}\r\n onMouseLeave={() => setRemoveHover(false)}\r\n aria-label={removeLabel ?? label}\r\n title={removeLabel ?? label}\r\n >\r\n <CloseIcon style={{ width: \"17px\", height: \"17px\" }} />\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import React, {\r\n ChangeEvent,\r\n FunctionComponent,\r\n KeyboardEvent,\r\n useEffect,\r\n useRef,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { CloseIcon, EditIcon, SearchIcon } from \"../Icons\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n userSelect: \"none\",\r\n height: \"34px\",\r\n};\r\n\r\nconst removeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n backgroundColor: \"#ececec\",\r\n borderRadius: \"2px\",\r\n padding: \"2px 8px\",\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n minWidth: \"8ch\",\r\n border: \"none\",\r\n};\r\n\r\nconst editButtonStyles: CSSProperties = {\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n color: \"#737373\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n};\r\n\r\nexport interface TransformedQueryChipProps {\r\n value: string;\r\n onChange: (nextValue: string) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nexport const TransformedQueryChip: FunctionComponent<\r\n TransformedQueryChipProps\r\n> = ({ value, onChange, onRemove }) => {\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const [draft, setDraft] = useState(value);\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editHover, setEditHover] = useState(false);\r\n const [removeHover, setRemoveHover] = useState(false);\r\n const { t } = useTranslation();\r\n\r\n const removeLabel = t(\"smartFilter.transformedQuery.remove\");\r\n const editLabel = t(\"smartFilter.transformedQuery.edit\");\r\n\r\n useEffect(() => {\r\n setDraft(value);\r\n setIsEditing(false);\r\n }, [value]);\r\n\r\n const applyChanges = () => {\r\n const nextValue = draft.trim();\r\n if (!nextValue.length) {\r\n setDraft(value);\r\n return;\r\n }\r\n if (nextValue === value) {\r\n return;\r\n }\r\n onChange(nextValue);\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n setDraft(event.target.value);\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n return;\r\n }\r\n\r\n if (event.key === \"Escape\") {\r\n event.preventDefault();\r\n setDraft(value);\r\n (event.currentTarget as HTMLInputElement).blur();\r\n return;\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <SearchIcon style={{ width: \"16px\", height: \"16px\", color: \"#03852e\" }} />\r\n {isEditing ? (\r\n <input\r\n ref={inputRef}\r\n value={draft}\r\n onChange={handleChange}\r\n onBlur={() => {\r\n applyChanges();\r\n setIsEditing(false);\r\n }}\r\n onKeyDown={handleKeyDown}\r\n aria-label={editLabel}\r\n style={inputStyles}\r\n autoFocus\r\n />\r\n ) : (\r\n <span style={{ fontSize: \"16px\" }}>{value}</span>\r\n )}\r\n {!isEditing && (\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...editButtonStyles,\r\n backgroundColor: editHover ? \"#e5e5e5\" : \"transparent\",\r\n }}\r\n aria-label={editLabel}\r\n title={editLabel}\r\n onClick={() => setIsEditing(true)}\r\n onMouseEnter={() => setEditHover(true)}\r\n onMouseLeave={() => setEditHover(false)}\r\n >\r\n <EditIcon />\r\n </button>\r\n )}\r\n <button\r\n style={{\r\n ...removeButtonStyles,\r\n backgroundColor: removeHover ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={onRemove}\r\n onMouseEnter={() => setRemoveHover(true)}\r\n onMouseLeave={() => setRemoveHover(false)}\r\n aria-label={removeLabel}\r\n title={removeLabel}\r\n >\r\n <CloseIcon style={{ width: \"17px\", height: \"17px\" }} />\r\n </button>\r\n </div>\r\n );\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\nexport const useFilterScroll = (dependency: number) => {\r\n const scrollerRef = useRef<HTMLDivElement | null>(null);\r\n const [atStart, setAtStart] = useState(true);\r\n const [atEnd, setAtEnd] = useState(true);\r\n\r\n const updateScrollButtons = useCallback(() => {\r\n const el = scrollerRef.current;\r\n if (!el) {\r\n setAtStart(true);\r\n setAtEnd(true);\r\n return;\r\n }\r\n\r\n const { scrollLeft, scrollWidth, clientWidth } = el;\r\n setAtStart(scrollLeft <= 0);\r\n setAtEnd(scrollLeft + clientWidth >= scrollWidth - 1);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const el = scrollerRef.current;\r\n updateScrollButtons();\r\n if (!el) {\r\n return;\r\n }\r\n\r\n const handleScroll = () => updateScrollButtons();\r\n el.addEventListener(\"scroll\", handleScroll, { passive: true });\r\n window.addEventListener(\"resize\", updateScrollButtons);\r\n\r\n return () => {\r\n el.removeEventListener(\"scroll\", handleScroll);\r\n window.removeEventListener(\"resize\", updateScrollButtons);\r\n };\r\n }, [dependency, updateScrollButtons]);\r\n\r\n const scrollByDir = useCallback((dir: \"prev\" | \"next\") => {\r\n const el = scrollerRef.current;\r\n if (!el) {\r\n return;\r\n }\r\n\r\n const delta = el.clientWidth * 0.7;\r\n el.scrollBy({\r\n left: dir === \"next\" ? delta : -delta,\r\n behavior: \"smooth\",\r\n });\r\n }, []);\r\n\r\n return {\r\n scrollerRef,\r\n atStart,\r\n atEnd,\r\n scrollByDir,\r\n };\r\n};\r\n","import { useEffect, useState } from \"react\";\r\n\r\n/**\r\n * Hook to detect if the viewport is in portrait orientation.\r\n * Updates on window resize.\r\n */\r\nexport const useIsPortrait = (): boolean => {\r\n const [isPortrait, setIsPortrait] = useState(\r\n typeof window !== \"undefined\"\r\n ? window.innerHeight > window.innerWidth\r\n : false\r\n );\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") {\r\n return;\r\n }\r\n\r\n const handleResize = () => {\r\n setIsPortrait(window.innerHeight > window.innerWidth);\r\n };\r\n\r\n window.addEventListener(\"resize\", handleResize);\r\n return () => window.removeEventListener(\"resize\", handleResize);\r\n }, []);\r\n\r\n return isPortrait;\r\n};\r\n"],"mappings":"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","names":["React","MapFirstCore","useCallback","useState","jsx","jsxs","SearchIcon","className","style","CloseIcon","EditIcon","NextIcon","AiIcon","StarIcon","fill","React","React","jsx","jsxs","chipStyles","removeButtonStyles","iconStyles","Chip","label","icon","remove","style","isHovering","setIsHovering","React","CloseIcon","useState","useCallback","useState","defaultTranslations","formatCurrencyDefault","value","currency","useTranslation","customTranslations","customFormatCurrency","locale","setLocale","t","key","params","translation","paramKey","formatCurrency","jsx","jsxs","renderStars","rating","stars","fullStars","hasHalfStar","baseStyles","fullStarStyles","halfStarStyles","i","remainingStars","createMinRatingFilterLabel","suffix","formatRatingValue","createPriceRangeFilterLabel","min","max","currency","formatCurrencyFn","jsx","jsxs","chipContainerStyles","removeButtonStyles","starContainerStyles","circleBaseStyles","buttonBaseStyles","MinRatingFilterChip","rating","onChange","onRemove","star","hoverRating","setHoverRating","useState","removeHover","setRemoveHover","t","useTranslation","displayRating","formatLabel","value","formatRatingValue","removeLabel","setLabel","getFillForStar","index","starNumber","handleSelect","nextRating","handleBlur","event","_a","related","_","fillState","halfValue","StarIcon","circleStyles","halfCircleStyles","CloseIcon","useEffect","useState","Fragment","jsx","jsxs","chipStyles","removeButtonStyles","inputStyles","editButtonStyles","PriceBoundaryChip","boundary","label","value","placeholder","currency","isOptional","showRemoveButton","removeLabel","editLabel","showAddWhenEmpty","onCommit","onRemove","draft","setDraft","useState","isEditing","setIsEditing","editHover","setEditHover","removeHover","setRemoveHover","hasValue","useEffect","resetDraft","commitValue","parsed","normalized","event","next","EditIcon","CloseIcon","PriceRangeFilterChip","priceRange","onChange","t","useTranslation","minLabel","maxChipLabel","handleBoundaryCommit","nextValue","nextRange","React","jsx","jsxs","chipStyles","removeButtonStyles","PRICE_LEVEL_OPTIONS","RestaurantPriceLevelChip","values","onChange","onRemove","t","useTranslation","removeHover","setRemoveHover","React","label","removeLabel","noneSelectedLabel","handleChange","event","value","checked","valueAsPriceLevel","selection","orderedSelection","option","optionLabel","checkboxId","CloseIcon","useEffect","useRef","useState","jsx","jsxs","chipStyles","removeButtonStyles","inputStyles","editButtonStyles","TransformedQueryChip","value","onChange","onRemove","inputRef","useRef","draft","setDraft","useState","isEditing","setIsEditing","editHover","setEditHover","removeHover","setRemoveHover","t","useTranslation","removeLabel","editLabel","useEffect","applyChanges","nextValue","SearchIcon","event","EditIcon","CloseIcon","useCallback","useEffect","useRef","useState","useFilterScroll","dependency","scrollerRef","atStart","setAtStart","atEnd","setAtEnd","updateScrollButtons","el","scrollLeft","scrollWidth","clientWidth","handleScroll","scrollByDir","dir","delta","jsx","jsxs","containerStyles","scrollContainerBase","gradientStyles","navButtonStyles","FilterChips","filters","isPortrait","currency","minRatingSuffix","clearAllLabel","previousFiltersLabel","nextFiltersLabel","formatCurrency","onFilterChange","onResetFilters","onClearAll","scrollerRef","atStart","atEnd","scrollByDir","useFilterScroll","t","useTranslation","navHover","setNavHover","React","resetHover","setResetHover","clearHover","setClearHover","scrollContainerStyles","SearchIcon","filter","_a","_b","_c","renderStandardChip","Chip","f","currentRating","MinRatingFilterChip","nextRating","nextFilters","PriceRangeFilterChip","nextRange","TransformedQueryChip","nextValue","RestaurantPriceLevelChip","nextLevels","NextIcon","useEffect","useState","useIsPortrait","isPortrait","setIsPortrait","handleResize","jsx","jsxs","containerStyles","formStyles","inputContainerStyles","iconStyles","inputStyles","loaderContainerStyles","loaderStyles","typingPromptStyles","SmartFilter","mapFirst","filters","controlledValue","isSearching","placeholder","onSearch","onFilterChange","onValueChange","showTypingPrompt","customTranslations","currency","style","inputStyle","containerStyle","internalValue","setInternalValue","useState","value","setValue","isPortrait","useIsPortrait","t","formatCurrency","useTranslation","minRatingSuffix","placeholderText","typingPrompt","previousFiltersLabel","nextFiltersLabel","clearAllLabel","formSubmit","e","query","error","handleFilterChange","useCallback","nextFilters","clearAll","resetFilters","clearAllFilters","AiIcon","FilterChips","jsx","jsxs","useMapFirstCore","options","instanceRef","React","state","setState","optionsRef","opts","coreOptions","properties","_a","_b","prev","id","type","filters","bounds","center","zoom","location","loading","searching","instance","MapFirstCore","useMapFirstProperties","mapFirst","setProperties","useMapFirstSelectedProperty","selectedId","setSelectedId","usePrimaryType","primaryType","setPrimaryTypeState","setPrimaryType","useSelectedMarker","selectedMarker","setSelectedMarkerState","setSelectedMarker","useMapLibreAttachment","map","maplibregl","onMarkerClick","attachedRef","useGoogleMapsAttachment","google","useMapboxAttachment","mapboxgl","useMapFirst","usePropertiesSearch","isLoading","setIsLoading","error","setError","err","useSmartFilterSearch","MarkerDebugList","markers","marker","_c","_d","_e","_f"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/components/SmartFilter.tsx","../src/components/smart-filter/FilterChips.tsx","../src/components/smart-filter/CloseButton.tsx","../src/components/Icons.tsx","../src/components/smart-filter/Chip.tsx","../src/components/smart-filter/MinRatingFilterChip.tsx","../src/hooks/useTranslation.ts","../src/components/smart-filter/utils.tsx","../src/components/smart-filter/PriceRangeFilterChip.tsx","../src/components/smart-filter/RestaurantPriceLevelChip.tsx","../src/components/smart-filter/TransformedQueryChip.tsx","../src/hooks/useFilterScroll.ts","../src/hooks/useIsPortrait.ts"],"sourcesContent":["import React from \"react\";\nimport {\n MapFirstCore,\n type MapFirstOptions,\n type BaseMapFirstOptions,\n type Property,\n type MapLibreNamespace,\n type GoogleMapsNamespace,\n type MapboxNamespace,\n type MapState,\n type PropertyType,\n} from \"@mapfirst.ai/core\";\n\n// Export all components\nexport * from \"./components\";\n\n// Export all hooks\nexport * from \"./hooks\";\n\n// Import additional types for search functionality\ntype InitialRequestBody = {\n initial?: boolean;\n query?: string;\n bounds?: {\n sw: { lat: number; lng: number };\n ne: { lat: number; lng: number };\n };\n filters?: any;\n city?: string;\n country?: string;\n location_id?: number;\n longitude?: number;\n latitude?: number;\n radius?: number;\n};\n\ntype SmartFilter = {\n id: string;\n label: string;\n type:\n | \"amenity\"\n | \"hotelStyle\"\n | \"priceRange\"\n | \"minRating\"\n | \"starRating\"\n | \"primary_type\"\n | \"transformed_query\"\n | \"selected_restaurant_price_levels\";\n value: string;\n numericValue?: number;\n priceRange?: {\n min: number;\n max?: number;\n };\n propertyType?: PropertyType;\n priceLevels?: any[];\n};\n\n/**\n * Hook that creates a MapFirstCore instance that can be initialized before maps are ready.\n * Supports two-phase initialization: create SDK first, attach map later.\n * Returns the instance and reactive state that updates when SDK state changes.\n *\n * @example\n * ```tsx\n * // Phase 1: Create SDK instance with location data\n * const { mapFirst, state } = useMapFirstCore({\n * initialLocationData: {\n * city: \"New York\",\n * country: \"United States\",\n * currency: \"USD\"\n * }\n * });\n *\n * // Access reactive state\n * console.log(state.properties); // Updates when properties change\n * console.log(state.isSearching); // Updates when search state changes\n *\n * // Phase 2: Attach map when ready\n * useEffect(() => {\n * if (mapLibreInstance && mapFirst) {\n * mapFirst.attachMap(mapLibreInstance, {\n * platform: \"maplibre\",\n * maplibregl: maplibregl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n * }\n * }, [mapLibreInstance, mapFirst]);\n * ```\n */\nexport function useMapFirstCore(options: BaseMapFirstOptions) {\n const instanceRef = React.useRef<MapFirstCore | null>(null);\n const [state, setState] = React.useState<MapState | null>(null);\n\n // Memoize the options to prevent recreation on every render\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n });\n\n React.useEffect(() => {\n const opts = optionsRef.current;\n\n // Create MapFirstCore instance without map using adapter-driven options\n const coreOptions: MapFirstOptions = {\n adapter: null as any, // Will be set when attachMap is called\n ...opts,\n callbacks: {\n ...opts.callbacks,\n // Add internal callbacks to trigger React re-renders\n onPropertiesChange: (properties) => {\n setState((prev) => (prev ? { ...prev, properties } : null));\n optionsRef.current.callbacks?.onPropertiesChange?.(properties);\n },\n onSelectedPropertyChange: (id) => {\n setState((prev) =>\n prev ? { ...prev, selectedPropertyId: id } : null\n );\n optionsRef.current.callbacks?.onSelectedPropertyChange?.(id);\n },\n onPrimaryTypeChange: (type) => {\n setState((prev) => (prev ? { ...prev, primary: type } : null));\n optionsRef.current.callbacks?.onPrimaryTypeChange?.(type);\n },\n onFiltersChange: (filters) => {\n setState((prev) => (prev ? { ...prev, filters } : null));\n optionsRef.current.callbacks?.onFiltersChange?.(filters);\n },\n onBoundsChange: (bounds) => {\n setState((prev) => (prev ? { ...prev, bounds } : null));\n optionsRef.current.callbacks?.onBoundsChange?.(bounds);\n },\n onPendingBoundsChange: (pendingBounds) => {\n setState((prev) => (prev ? { ...prev, pendingBounds } : null));\n optionsRef.current.callbacks?.onPendingBoundsChange?.(pendingBounds);\n },\n onCenterChange: (center, zoom) => {\n setState((prev) => (prev ? { ...prev, center, zoom } : null));\n optionsRef.current.callbacks?.onCenterChange?.(center, zoom);\n },\n onZoomChange: (zoom) => {\n setState((prev) => (prev ? { ...prev, zoom } : null));\n optionsRef.current.callbacks?.onZoomChange?.(zoom);\n },\n onActiveLocationChange: (location) => {\n setState((prev) =>\n prev ? { ...prev, activeLocation: location } : null\n );\n optionsRef.current.callbacks?.onActiveLocationChange?.(location);\n },\n onLoadingStateChange: (loading) => {\n setState((prev) =>\n prev ? { ...prev, initialLoading: loading } : null\n );\n optionsRef.current.callbacks?.onLoadingStateChange?.(loading);\n },\n onSearchingStateChange: (searching) => {\n setState((prev) =>\n prev ? { ...prev, isSearching: searching } : null\n );\n optionsRef.current.callbacks?.onSearchingStateChange?.(searching);\n },\n },\n };\n\n const instance = new MapFirstCore(coreOptions);\n instanceRef.current = instance;\n\n // Initialize state from SDK\n setState(instance.getState());\n\n return () => {\n instance.destroy();\n instanceRef.current = null;\n setState(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return { mapFirst: instanceRef.current, state };\n}\n\n/**\n * Hook to access reactive properties from MapFirst SDK.\n * Returns the current properties array that updates when properties change.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const properties = useMapFirstProperties(mapFirst);\n *\n * return <div>Found {properties.length} properties</div>;\n * ```\n */\nexport function useMapFirstProperties(\n mapFirst: MapFirstCore | null\n): Property[] {\n const [properties, setProperties] = React.useState<Property[]>([]);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setProperties([]);\n return;\n }\n\n // Initialize with current state\n setProperties(mapFirst.getState().properties);\n }, [mapFirst]);\n\n return properties;\n}\n\n/**\n * Hook to access the selected property ID from MapFirst SDK.\n * Returns the currently selected property ID that updates when selection changes.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const selectedId = useMapFirstSelectedProperty(mapFirst);\n *\n * return <div>Selected: {selectedId || 'None'}</div>;\n * ```\n */\nexport function useMapFirstSelectedProperty(\n mapFirst: MapFirstCore | null\n): number | null {\n const [selectedId, setSelectedId] = React.useState<number | null>(null);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setSelectedId(null);\n return;\n }\n\n // Initialize with current state\n setSelectedId(mapFirst.getState().selectedPropertyId);\n }, [mapFirst]);\n\n return selectedId;\n}\n\n/**\n * Hook to access and control the primary property type.\n * Returns the current primary type and a setter function.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const [primaryType, setPrimaryType] = usePrimaryType(mapFirst);\n *\n * return (\n * <select value={primaryType} onChange={(e) => setPrimaryType(e.target.value as PropertyType)}>\n * <option value=\"Accommodation\">Hotels</option>\n * <option value=\"Restaurant\">Restaurants</option>\n * <option value=\"Attraction\">Attractions</option>\n * </select>\n * );\n * ```\n */\nexport function usePrimaryType(\n mapFirst: MapFirstCore | null\n): [PropertyType, (type: PropertyType) => void] {\n const [primaryType, setPrimaryTypeState] =\n React.useState<PropertyType>(\"Accommodation\");\n\n React.useEffect(() => {\n if (!mapFirst) {\n setPrimaryTypeState(\"Accommodation\");\n return;\n }\n\n // Initialize with current state\n setPrimaryTypeState(mapFirst.getState().primary);\n }, [mapFirst]);\n\n const setPrimaryType = React.useCallback(\n (type: PropertyType) => {\n if (mapFirst) {\n mapFirst.setPrimaryType(type);\n setPrimaryTypeState(type);\n }\n },\n [mapFirst]\n );\n\n return [primaryType, setPrimaryType];\n}\n\n/**\n * Hook to access and control the selected marker.\n * Returns the current selected marker ID and a setter function.\n * Note: This hook requires the MapFirstCore instance. For simpler usage with reactive updates,\n * use state.selectedPropertyId from useMapFirstCore instead.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const [selectedMarker, setSelectedMarker] = useSelectedMarker(mapFirst);\n *\n * return (\n * <div>\n * <p>Selected: {selectedMarker || 'None'}</p>\n * <button onClick={() => setSelectedMarker(null)}>Clear Selection</button>\n * </div>\n * );\n * ```\n */\nexport function useSelectedMarker(\n mapFirst: MapFirstCore | null\n): [number | null, (id: number | null) => void] {\n const [selectedMarker, setSelectedMarkerState] = React.useState<\n number | null\n >(null);\n\n React.useEffect(() => {\n if (!mapFirst) {\n setSelectedMarkerState(null);\n return;\n }\n\n // Initialize with current state\n setSelectedMarkerState(mapFirst.getState().selectedPropertyId);\n }, [mapFirst]);\n\n const setSelectedMarker = React.useCallback(\n (id: number | null) => {\n if (mapFirst) {\n mapFirst.setSelectedMarker(id);\n }\n },\n [mapFirst]\n );\n\n return [selectedMarker, setSelectedMarker];\n}\n\n/**\n * Hook for MapLibre GL JS integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"Paris\", country: \"France\" } });\n * const mapRef = useRef<maplibregl.Map | null>(null);\n *\n * useMapLibreAttachment({\n * mapFirst,\n * map: mapRef.current,\n * maplibregl: maplibregl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.properties);\n * ```\n */\nexport function useMapLibreAttachment({\n mapFirst,\n map,\n maplibregl,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n maplibregl: MapLibreNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"maplibre\",\n maplibregl,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, maplibregl, onMarkerClick]);\n}\n\n/**\n * Hook for Google Maps integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"Tokyo\", country: \"Japan\" } });\n * const mapRef = useRef<google.maps.Map | null>(null);\n *\n * useGoogleMapsAttachment({\n * mapFirst,\n * map: mapRef.current,\n * google: window.google,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.isSearching);\n * ```\n */\nexport function useGoogleMapsAttachment({\n mapFirst,\n map,\n google,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n google: GoogleMapsNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"google\",\n google,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, google, onMarkerClick]);\n}\n\n/**\n * Hook for Mapbox GL JS integration.\n * Automatically attaches the map when both the SDK instance and map are available.\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: \"London\", country: \"United Kingdom\" } });\n * const mapRef = useRef<mapboxgl.Map | null>(null);\n *\n * useMapboxAttachment({\n * mapFirst,\n * map: mapRef.current,\n * mapboxgl: mapboxgl,\n * onMarkerClick: (marker) => console.log(marker)\n * });\n *\n * // Access reactive state\n * console.log(state?.filters);\n * ```\n */\nexport function useMapboxAttachment({\n mapFirst,\n map,\n mapboxgl,\n onMarkerClick,\n}: {\n mapFirst: MapFirstCore | null;\n map: any | null;\n mapboxgl: MapboxNamespace;\n onMarkerClick?: (marker: Property) => void;\n}) {\n const attachedRef = React.useRef(false);\n\n React.useEffect(() => {\n if (!mapFirst || !map || attachedRef.current) {\n return;\n }\n\n mapFirst.attachMap(map, {\n platform: \"mapbox\",\n mapboxgl,\n onMarkerClick,\n });\n\n attachedRef.current = true;\n }, [mapFirst, map, mapboxgl, onMarkerClick]);\n}\n\n/**\n * Legacy hook that creates the MapFirstCore instance with a map immediately.\n * Use useMapFirstCore + useMap*Attachment hooks for better control.\n *\n * @deprecated Use useMapFirstCore and platform-specific attachment hooks instead\n */\nexport function useMapFirst(options: MapFirstOptions | null) {\n const instanceRef = React.useRef<MapFirstCore | null>(null);\n\n React.useEffect(() => {\n if (!options) {\n return undefined;\n }\n const instance = new MapFirstCore(options);\n instanceRef.current = instance;\n\n return () => {\n instance.destroy();\n instanceRef.current = null;\n };\n }, [options]);\n\n return instanceRef;\n}\n\n/**\n * Hook to run properties search with the MapFirst SDK.\n * Returns a function to trigger the search and loading state.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const { search, isLoading, error } = usePropertiesSearch(mapFirst);\n *\n * const handleSearch = async () => {\n * await search({\n * body: {\n * city: \"Paris\",\n * country: \"France\",\n * filters: {\n * checkIn: new Date(),\n * checkOut: new Date(Date.now() + 86400000),\n * numAdults: 2,\n * numRooms: 1\n * }\n * }\n * });\n * };\n * ```\n */\nexport function usePropertiesSearch(mapFirst: MapFirstCore | null) {\n const [isLoading, setIsLoading] = React.useState(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n const search = React.useCallback(\n async (options: {\n body: InitialRequestBody;\n beforeApplyProperties?: (data: any) => {\n price?: any;\n limit?: number;\n };\n smartFiltersClearable?: boolean;\n }) => {\n if (!mapFirst) {\n const err = new Error(\"MapFirst instance not available\");\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mapFirst.runPropertiesSearch({\n ...options,\n onError: (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n },\n });\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [mapFirst]\n );\n\n return { search, isLoading, error };\n}\n\n/**\n * Hook to run smart filter search with the MapFirst SDK.\n * Returns a function to trigger the search and loading state.\n *\n * @example\n * ```tsx\n * const { mapFirst } = useMapFirstCore({ ... });\n * const { search, isLoading, error } = useSmartFilterSearch(mapFirst);\n *\n * const handleSearch = async () => {\n * await search({\n * query: \"hotels near beach with pool\"\n * });\n * };\n *\n * // Or with filters\n * const handleFilterSearch = async () => {\n * await search({\n * filters: [\n * { id: \"pool\", label: \"Pool\", type: \"amenity\", value: \"pool\" },\n * { id: \"4star\", label: \"4 Star\", type: \"starRating\", value: \"4\", numericValue: 4 }\n * ]\n * });\n * };\n * ```\n */\nexport function useSmartFilterSearch(mapFirst: MapFirstCore | null) {\n const [isLoading, setIsLoading] = React.useState(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n const search = React.useCallback(\n async (options: {\n query?: string;\n filters?: SmartFilter[];\n onProcessFilters?: (\n filters: any,\n location_id?: number\n ) => {\n smartFilters?: SmartFilter[];\n price?: any;\n limit?: number;\n language?: string;\n };\n }) => {\n if (!mapFirst) {\n const err = new Error(\"MapFirst instance not available\");\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await mapFirst.runSmartFilterSearch({\n ...options,\n onError: (err) => {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n },\n });\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsLoading(false);\n }\n },\n [mapFirst]\n );\n\n return { search, isLoading, error };\n}\n\n/**\n * Hook to perform a bounds search when the user moves the map\n *\n * @example\n * ```tsx\n * const { mapFirst, state } = useMapFirstCore({ ... });\n * const { performBoundsSearch, isSearching } = useMapFirstBoundsSearch(mapFirst);\n *\n * // When user clicks \"Search this area\" button\n * <button onClick={performBoundsSearch} disabled={!state.pendingBounds || isSearching}>\n * Search this area\n * </button>\n * ```\n */\nexport function useMapFirstBoundsSearch(mapFirst: MapFirstCore | null) {\n const [isSearching, setIsSearching] = React.useState(false);\n const [error, setError] = React.useState<Error | null>(null);\n\n const performBoundsSearch = React.useCallback(async () => {\n if (!mapFirst) {\n return null;\n }\n\n setIsSearching(true);\n setError(null);\n\n try {\n const result = await mapFirst.performBoundsSearch();\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n throw error;\n } finally {\n setIsSearching(false);\n }\n }, [mapFirst]);\n\n return { performBoundsSearch, isSearching, error };\n}\n\n/**\n * Helper component that simply renders the markers it receives so non-React environments\n * can verify data flows before wiring the SDK into a map.\n */\nexport function MarkerDebugList({ markers }: { markers: Property[] }) {\n return (\n <div style={{ fontFamily: \"sans-serif\", fontSize: 14 }}>\n <strong>Markers</strong>\n <ul>\n {markers.map((marker) => (\n <li key={String(marker.tripadvisor_id)}>\n {marker.name} — {marker.location?.lat?.toFixed(3) ?? \"n/a\"},{\" \"}\n {marker.location?.lon?.toFixed(3) ?? \"n/a\"}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n","import React, {\r\n FormEventHandler,\r\n FunctionComponent,\r\n useCallback,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { FilterChips } from \"./smart-filter/FilterChips\";\r\nimport { useIsPortrait } from \"../hooks/useIsPortrait\";\r\nimport { useTranslation } from \"../hooks/useTranslation\";\r\nimport type { Filter } from \"./smart-filter/types\";\r\nimport type { MapFirstCore } from \"@mapfirst.ai/core\";\r\n\r\nexport interface SmartFilterProps {\r\n mapFirst: MapFirstCore | null;\r\n filters: Filter[];\r\n value?: string;\r\n isSearching?: boolean;\r\n placeholder?: string;\r\n onSearch: (query: string, filters?: Filter[]) => Promise<void> | void;\r\n onFilterChange: (filters: Filter[]) => Promise<void> | void;\r\n onValueChange?: (value: string) => void;\r\n showTypingPrompt?: boolean;\r\n customTranslations?: Record<string, string>;\r\n currency?: string;\r\n style?: CSSProperties;\r\n inputStyle?: CSSProperties;\r\n containerStyle?: CSSProperties;\r\n}\r\n\r\nconst containerStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n gap: \"8px\",\r\n width: \"100%\",\r\n};\r\n\r\nconst formStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n width: \"100%\",\r\n};\r\n\r\nconst inputContainerStyles: CSSProperties = {\r\n position: \"relative\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n flex: 1,\r\n backgroundColor: \"white\",\r\n borderRadius: \"24px\",\r\n border: \"1px solid #e5e5e5\",\r\n padding: \"0 16px\",\r\n boxShadow: \"0 1px 3px rgba(0,0,0,0.1)\",\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n flex: 1,\r\n border: \"none\",\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n backgroundColor: \"transparent\",\r\n color: \"#000\",\r\n padding: \"10px\",\r\n};\r\n\r\nconst loaderContainerStyles: CSSProperties = {\r\n position: \"absolute\",\r\n right: \"16px\",\r\n top: \"50%\",\r\n transform: \"translateY(-50%)\",\r\n};\r\n\r\nconst loaderStyles: CSSProperties = {\r\n width: \"20px\",\r\n height: \"20px\",\r\n border: \"2px solid #e5e5e5\",\r\n borderTop: \"2px solid #03852e\",\r\n borderRadius: \"50%\",\r\n animation: \"spin 1s linear infinite\",\r\n};\r\n\r\nconst typingPromptStyles: CSSProperties = {\r\n position: \"absolute\",\r\n padding: \"10px\",\r\n color: \"#737373\",\r\n pointerEvents: \"none\",\r\n fontSize: \"16px\",\r\n};\r\n\r\n/**\r\n * SmartFilter component for AI-powered search with filter chips.\r\n * Provides a search input with smart filtering capabilities.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { mapFirst, state } = useMapFirstCore({ ... });\r\n * const [filters, setFilters] = useState<Filter[]>([]);\r\n * const [searchValue, setSearchValue] = useState(\"\");\r\n *\r\n * const handleSearch = async (query: string, currentFilters?: Filter[]) => {\r\n * // Perform search using mapFirst.runSmartFilterSearch\r\n * const result = await mapFirst.runSmartFilterSearch({\r\n * query,\r\n * filters: currentFilters\r\n * });\r\n * // Update filters based on response\r\n * };\r\n *\r\n * return (\r\n * <SmartFilter\r\n * mapFirst={mapFirst}\r\n * filters={filters}\r\n * value={searchValue}\r\n * isSearching={state?.isSearching}\r\n * onSearch={handleSearch}\r\n * onFilterChange={setFilters}\r\n * onValueChange={setSearchValue}\r\n * />\r\n * );\r\n * ```\r\n */\r\nexport const SmartFilter: FunctionComponent<SmartFilterProps> = ({\r\n mapFirst,\r\n filters,\r\n value: controlledValue,\r\n isSearching = false,\r\n placeholder,\r\n onSearch,\r\n onFilterChange,\r\n onValueChange,\r\n showTypingPrompt = true,\r\n customTranslations,\r\n currency = \"USD\",\r\n style,\r\n inputStyle,\r\n containerStyle,\r\n}) => {\r\n const [internalValue, setInternalValue] = useState(\"\");\r\n const value = controlledValue !== undefined ? controlledValue : internalValue;\r\n const setValue = onValueChange || setInternalValue;\r\n\r\n const isPortrait = useIsPortrait();\r\n const { t, formatCurrency } = useTranslation(customTranslations);\r\n\r\n const minRatingSuffix = t(\"smartFilter.minRating.suffix\");\r\n const typingPrompt = placeholder || t(\"smartFilter.typingPrompt\");\r\n const previousFiltersLabel = t(\"smartFilter.nav.previous\");\r\n const nextFiltersLabel = t(\"smartFilter.nav.next\");\r\n const clearAllLabel = t(\"smartFilter.clearAll\");\r\n\r\n const formSubmit: FormEventHandler<HTMLFormElement> = async (e) => {\r\n e.preventDefault();\r\n const query = value.trim();\r\n if (!query || isSearching) {\r\n return;\r\n }\r\n try {\r\n await onSearch(query);\r\n } catch (error) {\r\n console.error(\"Search error:\", error);\r\n }\r\n };\r\n\r\n const handleFilterChange = useCallback(\r\n async (nextFilters: Filter[], clearAll?: boolean) => {\r\n if (isSearching) {\r\n return;\r\n }\r\n try {\r\n await onFilterChange(nextFilters);\r\n } catch (error) {\r\n console.error(\"Filter change error:\", error);\r\n }\r\n },\r\n [isSearching, onFilterChange]\r\n );\r\n\r\n const resetFilters = useCallback(() => {\r\n void handleFilterChange([]);\r\n }, [handleFilterChange]);\r\n\r\n const clearAllFilters = useCallback(() => {\r\n void handleFilterChange([], true);\r\n setValue(\"\");\r\n }, [handleFilterChange, setValue]);\r\n\r\n return (\r\n <div style={{ ...containerStyles, ...containerStyle }}>\r\n <style>\r\n {`\r\n @keyframes spin {\r\n 0% { transform: rotate(0deg); }\r\n 100% { transform: rotate(360deg); }\r\n }\r\n `}\r\n </style>\r\n <form onSubmit={formSubmit} style={{ ...formStyles, ...style }}>\r\n <div style={inputContainerStyles}>\r\n <input\r\n type=\"text\"\r\n value={value}\r\n onChange={(e) => setValue(e.target.value)}\r\n disabled={isSearching}\r\n style={{ ...inputStyles, ...inputStyle }}\r\n autoComplete=\"off\"\r\n aria-label=\"Smart search\"\r\n />\r\n {showTypingPrompt && value.length === 0 && !isSearching && (\r\n <span style={typingPromptStyles}>{typingPrompt}</span>\r\n )}\r\n {isSearching && (\r\n <div style={loaderContainerStyles}>\r\n <div style={loaderStyles} />\r\n </div>\r\n )}\r\n </div>\r\n </form>\r\n\r\n {filters.length > 0 && (\r\n <FilterChips\r\n filters={filters}\r\n isPortrait={isPortrait}\r\n currency={currency}\r\n minRatingSuffix={minRatingSuffix}\r\n clearAllLabel={clearAllLabel}\r\n previousFiltersLabel={previousFiltersLabel}\r\n nextFiltersLabel={nextFiltersLabel}\r\n formatCurrency={formatCurrency}\r\n onFilterChange={handleFilterChange}\r\n onResetFilters={resetFilters}\r\n onClearAll={clearAllFilters}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React, { FunctionComponent, CSSProperties } from \"react\";\r\nimport { Chip } from \"./Chip\";\r\nimport { MinRatingFilterChip } from \"./MinRatingFilterChip\";\r\nimport { PriceRangeFilterChip } from \"./PriceRangeFilterChip\";\r\nimport { RestaurantPriceLevelChip } from \"./RestaurantPriceLevelChip\";\r\nimport { TransformedQueryChip } from \"./TransformedQueryChip\";\r\nimport { SearchIcon, NextIcon } from \"../Icons\";\r\nimport { useFilterScroll } from \"../../hooks/useFilterScroll\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { Filter } from \"./types\";\r\n\r\nexport interface FilterChipsProps {\r\n filters: Filter[];\r\n isPortrait: boolean;\r\n currency: string;\r\n minRatingSuffix: string;\r\n clearAllLabel: string;\r\n previousFiltersLabel: string;\r\n nextFiltersLabel: string;\r\n formatCurrency: (value: number, currency?: string) => string;\r\n onFilterChange: (\r\n filters: Filter[],\r\n clearAll?: boolean\r\n ) => void | Promise<void>;\r\n onResetFilters: () => void;\r\n onClearAll: () => void;\r\n}\r\n\r\nconst containerStyles: CSSProperties = {\r\n position: \"relative\",\r\n width: \"100%\",\r\n};\r\n\r\nconst scrollContainerBase: CSSProperties = {\r\n display: \"flex\",\r\n gap: \"8px\",\r\n overflowX: \"auto\",\r\n alignItems: \"center\",\r\n width: \"100%\",\r\n scrollbarWidth: \"none\",\r\n msOverflowStyle: \"none\",\r\n};\r\n\r\nconst gradientStyles: CSSProperties = {\r\n pointerEvents: \"none\",\r\n position: \"absolute\",\r\n top: 0,\r\n bottom: 0,\r\n width: \"40px\",\r\n};\r\n\r\nconst navButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"50%\",\r\n transform: \"translateY(-50%)\",\r\n backgroundColor: \"white\",\r\n color: \"#003c30\",\r\n border: \"1px solid #003c30\",\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n boxShadow: \"0 1px 3px rgba(0,0,0,0.1)\",\r\n cursor: \"pointer\",\r\n};\r\n\r\nexport const FilterChips: FunctionComponent<FilterChipsProps> = ({\r\n filters,\r\n isPortrait,\r\n currency,\r\n minRatingSuffix,\r\n clearAllLabel,\r\n previousFiltersLabel,\r\n nextFiltersLabel,\r\n formatCurrency,\r\n onFilterChange,\r\n onResetFilters,\r\n onClearAll,\r\n}) => {\r\n const { scrollerRef, atStart, atEnd, scrollByDir } = useFilterScroll(\r\n filters.length\r\n );\r\n const { t } = useTranslation();\r\n const [navHover, setNavHover] = React.useState<\"prev\" | \"next\" | null>(null);\r\n const [resetHover, setResetHover] = React.useState(false);\r\n const [clearHover, setClearHover] = React.useState(false);\r\n\r\n const scrollContainerStyles: CSSProperties = {\r\n ...scrollContainerBase,\r\n padding: isPortrait ? \"8px 16px\" : \"8px\",\r\n };\r\n\r\n return (\r\n <div style={containerStyles}>\r\n <div\r\n ref={scrollerRef}\r\n style={{\r\n ...scrollContainerStyles,\r\n // Hide scrollbar for webkit browsers\r\n WebkitOverflowScrolling: \"touch\",\r\n }}\r\n >\r\n <style>\r\n {`\r\n div::-webkit-scrollbar {\r\n display: none;\r\n }\r\n `}\r\n </style>\r\n <button\r\n style={{\r\n flexShrink: 0,\r\n backgroundColor: resetHover ? \"#03a03e\" : \"#03852e\",\r\n borderRadius: \"50%\",\r\n padding: \"8px\",\r\n cursor: \"pointer\",\r\n border: \"none\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n onClick={onResetFilters}\r\n onMouseEnter={() => setResetHover(true)}\r\n onMouseLeave={() => setResetHover(false)}\r\n >\r\n <SearchIcon\r\n style={{ width: \"20px\", height: \"20px\", color: \"white\" }}\r\n />\r\n </button>\r\n {filters.map((filter) => {\r\n const renderStandardChip = () => (\r\n <Chip\r\n key={filter.id}\r\n label={filter.label}\r\n icon={filter.icon}\r\n remove={() => {\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id));\r\n }}\r\n />\r\n );\r\n\r\n if (filter.type === \"minRating\") {\r\n const currentRating = filter.numericValue ?? Number(filter.value);\r\n if (!Number.isFinite(currentRating)) {\r\n return renderStandardChip();\r\n }\r\n\r\n return (\r\n <MinRatingFilterChip\r\n key={filter.id}\r\n rating={currentRating}\r\n onChange={(nextRating) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n numericValue: nextRating,\r\n value: String(nextRating),\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"starRating\") {\r\n const currentRating = filter.numericValue ?? Number(filter.value);\r\n if (!Number.isFinite(currentRating)) {\r\n return renderStandardChip();\r\n }\r\n\r\n return (\r\n <MinRatingFilterChip\r\n star\r\n key={filter.id}\r\n rating={currentRating}\r\n onChange={(nextRating) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n numericValue: nextRating,\r\n value: String(nextRating),\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"priceRange\" && filter.priceRange) {\r\n return (\r\n <PriceRangeFilterChip\r\n key={filter.id}\r\n priceRange={filter.priceRange}\r\n currency={currency}\r\n onChange={(nextRange) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n priceRange: nextRange,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"transformed_query\") {\r\n return (\r\n <TransformedQueryChip\r\n key={filter.id}\r\n value={filter.value}\r\n onChange={(nextValue) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n value: nextValue,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n if (filter.type === \"selected_restaurant_price_levels\") {\r\n return (\r\n <RestaurantPriceLevelChip\r\n key={filter.id}\r\n values={filter.priceLevels ?? []}\r\n onChange={(nextLevels) => {\r\n const nextFilters = filters.map((f) =>\r\n f.id === filter.id\r\n ? {\r\n ...f,\r\n priceLevels: nextLevels,\r\n }\r\n : f\r\n );\r\n void onFilterChange(nextFilters);\r\n }}\r\n onRemove={() =>\r\n void onFilterChange(filters.filter((f) => f.id !== filter.id))\r\n }\r\n />\r\n );\r\n }\r\n\r\n return renderStandardChip();\r\n })}\r\n <button\r\n style={{\r\n flexShrink: 0,\r\n padding: \"4px 16px\",\r\n borderRadius: \"9999px\",\r\n cursor: \"pointer\",\r\n fontSize: \"14px\",\r\n userSelect: \"none\",\r\n border: \"none\",\r\n backgroundColor: clearHover ? \"#e5e5e5\" : \"transparent\",\r\n transition: \"background-color 0.2s\",\r\n }}\r\n onClick={onClearAll}\r\n onMouseEnter={() => setClearHover(true)}\r\n onMouseLeave={() => setClearHover(false)}\r\n >\r\n {clearAllLabel}\r\n </button>\r\n </div>\r\n\r\n {!atStart && (\r\n <div\r\n aria-hidden=\"true\"\r\n style={{\r\n ...gradientStyles,\r\n left: 0,\r\n background: \"linear-gradient(to right, white, transparent)\",\r\n }}\r\n />\r\n )}\r\n\r\n {!atEnd && (\r\n <div\r\n aria-hidden=\"true\"\r\n style={{\r\n ...gradientStyles,\r\n right: 0,\r\n background: \"linear-gradient(to left, white, transparent)\",\r\n }}\r\n />\r\n )}\r\n\r\n {!atStart && (\r\n <button\r\n type=\"button\"\r\n aria-label={previousFiltersLabel}\r\n style={{\r\n ...navButtonStyles,\r\n left: \"4px\",\r\n transform: \"translateY(-50%) rotate(180deg)\",\r\n backgroundColor: navHover === \"prev\" ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={() => scrollByDir(\"prev\")}\r\n onMouseEnter={() => setNavHover(\"prev\")}\r\n onMouseLeave={() => setNavHover(null)}\r\n >\r\n <NextIcon style={{ width: \"20px\", height: \"20px\" }} />\r\n </button>\r\n )}\r\n\r\n {!atEnd && (\r\n <button\r\n type=\"button\"\r\n aria-label={nextFiltersLabel}\r\n style={{\r\n ...navButtonStyles,\r\n right: \"4px\",\r\n backgroundColor: navHover === \"next\" ? \"#e5e5e5\" : \"white\",\r\n }}\r\n onClick={() => scrollByDir(\"next\")}\r\n onMouseEnter={() => setNavHover(\"next\")}\r\n onMouseLeave={() => setNavHover(null)}\r\n >\r\n <NextIcon style={{ width: \"20px\", height: \"20px\" }} />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React, { CSSProperties } from \"react\";\r\nimport { CloseIcon } from \"../Icons\";\r\n\r\nexport interface CloseButtonProps {\r\n onClick: () => void;\r\n style?: CSSProperties;\r\n}\r\n\r\nconst closeButtonStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: \"-8px\",\r\n right: \"-8px\",\r\n padding: \"2px\",\r\n borderRadius: \"50%\",\r\n backgroundColor: \"white\",\r\n border: \"1px solid #03852e\",\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n transition: \"background-color 0.2s\",\r\n};\r\n\r\nconst iconStyles: CSSProperties = {\r\n width: \"17px\",\r\n height: \"17px\",\r\n};\r\n\r\nexport const CloseButton: React.FC<CloseButtonProps> = ({ onClick, style }) => {\r\n const [isHovering, setIsHovering] = React.useState(false);\r\n\r\n return (\r\n <button\r\n style={{\r\n ...closeButtonStyles,\r\n backgroundColor: isHovering ? \"#e5e5e5\" : \"white\",\r\n ...style,\r\n }}\r\n onClick={onClick}\r\n onMouseEnter={() => setIsHovering(true)}\r\n onMouseLeave={() => setIsHovering(false)}\r\n aria-label=\"Remove filter\"\r\n >\r\n <CloseIcon style={iconStyles} />\r\n </button>\r\n );\r\n};\r\n","import React, { CSSProperties } from \"react\";\r\n\r\nexport interface IconProps {\r\n className?: string;\r\n style?: CSSProperties;\r\n}\r\n\r\nexport const SearchIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\r\n <path d=\"m21 21-4.35-4.35\" />\r\n </svg>\r\n);\r\n\r\nexport const CloseIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\r\n </svg>\r\n);\r\n\r\nexport const EditIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\r\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" />\r\n </svg>\r\n);\r\n\r\nexport const NextIcon: React.FC<IconProps> = ({ className, style }) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n);\r\n\r\nexport const StarIcon: React.FC<IconProps & { fill?: string }> = ({\r\n className,\r\n style,\r\n fill = \"none\",\r\n}) => (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill={fill}\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n style={{ width: \"1em\", height: \"1em\", ...style }}\r\n >\r\n <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\" />\r\n </svg>\r\n);\r\n","import React, { CSSProperties, ReactNode } from \"react\";\r\nimport { CloseButton } from \"./CloseButton\";\r\n\r\nexport interface ChipProps {\r\n label: string | ReactNode;\r\n icon?: ReactNode;\r\n remove: () => void;\r\n style?: CSSProperties;\r\n}\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nexport const Chip: React.FC<ChipProps> = ({ label, icon, remove, style }) => {\r\n return (\r\n <div style={{ ...chipStyles, ...style }}>\r\n {icon && (\r\n <span style={{ display: \"flex\", alignItems: \"center\" }}>{icon}</span>\r\n )}\r\n <span style={{ whiteSpace: \"nowrap\" }}>{label}</span>\r\n <CloseButton onClick={remove} />\r\n </div>\r\n );\r\n};\r\n","import React, {\r\n FunctionComponent,\r\n useState,\r\n FocusEvent,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { StarIcon } from \"../Icons\";\r\nimport { CloseButton } from \"./CloseButton\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport { formatRatingValue } from \"./utils\";\r\n\r\nconst chipContainerStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n gap: \"8px\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst starContainerStyles: CSSProperties = {\r\n display: \"flex\",\r\n gap: \"1px\",\r\n userSelect: \"none\",\r\n};\r\n\r\nconst circleBaseStyles: CSSProperties = {\r\n display: \"block\",\r\n width: \"12px\",\r\n height: \"12px\",\r\n borderRadius: \"50%\",\r\n border: \"1px solid #03852e\",\r\n pointerEvents: \"none\",\r\n};\r\n\r\nconst buttonBaseStyles: CSSProperties = {\r\n position: \"absolute\",\r\n top: 0,\r\n height: \"100%\",\r\n cursor: \"pointer\",\r\n backgroundColor: \"transparent\",\r\n border: \"none\",\r\n padding: 0,\r\n};\r\n\r\nexport const MinRatingFilterChip: FunctionComponent<{\r\n star?: boolean;\r\n rating: number;\r\n onChange: (rating: number) => void;\r\n onRemove: () => void;\r\n}> = ({ rating, onChange, onRemove, star = false }) => {\r\n const [hoverRating, setHoverRating] = useState<number | null>(null);\r\n const { t } = useTranslation();\r\n\r\n const displayRating = hoverRating ?? rating;\r\n const formatLabel = (value: number) =>\r\n star && value\r\n ? value.toString()\r\n : t(\"smartFilter.minRating.label\", { value: formatRatingValue(value) });\r\n const removeLabel = t(\"smartFilter.minRating.remove\");\r\n const setLabel = (value: number) =>\r\n t(\"smartFilter.minRating.setTo\", { rating: formatRatingValue(value) });\r\n\r\n const getFillForStar = (index: number) => {\r\n const starNumber = index + 1;\r\n if (displayRating >= starNumber) {\r\n return \"full\" as const;\r\n }\r\n if (displayRating >= starNumber - 0.5) {\r\n return \"half\" as const;\r\n }\r\n return \"empty\" as const;\r\n };\r\n\r\n const handleSelect = (nextRating: number) => {\r\n setHoverRating(null);\r\n if (nextRating === rating) {\r\n return;\r\n }\r\n onChange(nextRating);\r\n };\r\n\r\n const handleBlur = (event: FocusEvent<HTMLButtonElement>) => {\r\n const related = event.relatedTarget as HTMLElement | null;\r\n if (\r\n !related ||\r\n !(event.currentTarget as HTMLElement)\r\n .closest(\"[data-min-rating-chip]\")\r\n ?.contains(related)\r\n ) {\r\n setHoverRating(null);\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipContainerStyles} data-min-rating-chip>\r\n <div\r\n style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\r\n onMouseLeave={() => setHoverRating(null)}\r\n >\r\n <div style={starContainerStyles}>\r\n {Array.from({ length: 5 }).map((_, index) => {\r\n const fillState = getFillForStar(index);\r\n const starNumber = index + 1;\r\n const halfValue = starNumber - 0.5;\r\n\r\n if (star) {\r\n return (\r\n <div\r\n key={index}\r\n style={{\r\n position: \"relative\",\r\n width: \"16px\",\r\n height: \"16px\",\r\n }}\r\n >\r\n <StarIcon\r\n fill={displayRating >= starNumber ? \"#03852e\" : \"none\"}\r\n style={{\r\n width: \"16px\",\r\n height: \"16px\",\r\n pointerEvents: \"none\",\r\n }}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: 0,\r\n width: \"50%\",\r\n borderRadius: \"50% 0 0 50%\",\r\n }}\r\n onMouseEnter={() => setHoverRating(halfValue)}\r\n onFocus={() => setHoverRating(halfValue)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(halfValue)}\r\n aria-label={setLabel(halfValue)}\r\n title={formatLabel(halfValue)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: \"50%\",\r\n width: \"50%\",\r\n borderRadius: \"0 50% 50% 0\",\r\n }}\r\n onMouseEnter={() => setHoverRating(starNumber)}\r\n onFocus={() => setHoverRating(starNumber)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(starNumber)}\r\n aria-label={setLabel(starNumber)}\r\n title={formatLabel(starNumber)}\r\n />\r\n </div>\r\n );\r\n }\r\n\r\n const circleStyles: CSSProperties =\r\n fillState === \"full\"\r\n ? { ...circleBaseStyles, backgroundColor: \"#03852e\" }\r\n : circleBaseStyles;\r\n\r\n const halfCircleStyles: CSSProperties = {\r\n ...circleBaseStyles,\r\n background:\r\n \"linear-gradient(90deg, #03852e 50%, transparent 50%)\",\r\n };\r\n\r\n return (\r\n <div\r\n key={index}\r\n style={{ position: \"relative\", width: \"12px\", height: \"12px\" }}\r\n >\r\n <span\r\n style={fillState === \"half\" ? halfCircleStyles : circleStyles}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: 0,\r\n width: \"50%\",\r\n borderRadius: \"50% 0 0 50%\",\r\n outline: \"2px solid transparent\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={() => setHoverRating(halfValue)}\r\n onFocus={() => setHoverRating(halfValue)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(halfValue)}\r\n aria-label={setLabel(halfValue)}\r\n title={formatLabel(halfValue)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...buttonBaseStyles,\r\n left: \"50%\",\r\n width: \"50%\",\r\n borderRadius: \"0 50% 50% 0\",\r\n outline: \"2px solid transparent\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={() => setHoverRating(starNumber)}\r\n onFocus={() => setHoverRating(starNumber)}\r\n onBlur={handleBlur}\r\n onClick={() => handleSelect(starNumber)}\r\n aria-label={setLabel(starNumber)}\r\n title={formatLabel(starNumber)}\r\n />\r\n </div>\r\n );\r\n })}\r\n </div>\r\n <span style={{ whiteSpace: \"nowrap\" }}>\r\n {formatLabel(displayRating)}\r\n </span>\r\n </div>\r\n <CloseButton onClick={onRemove} />\r\n </div>\r\n );\r\n};\r\n","import { useCallback, useState } from \"react\";\r\n\r\nexport type Locale = \"en\" | \"es\" | \"de\" | \"fr\" | \"it\" | \"pt\";\r\n\r\ntype TranslationFunction = (\r\n key: string,\r\n params?: Record<string, any>\r\n) => string;\r\ntype FormatCurrencyFunction = (value: number, currency?: string) => string;\r\n\r\nconst defaultTranslations: Record<string, string> = {\r\n \"smartFilter.typingPrompt\":\r\n \"Search for hotels, restaurants, or attractions...\",\r\n \"smartFilter.nav.previous\": \"Previous filters\",\r\n \"smartFilter.nav.next\": \"Next filters\",\r\n \"smartFilter.toast.locationRequired\": \"Please select a location first\",\r\n \"smartFilter.clearAll\": \"Clear all\",\r\n \"smartFilter.minRating.suffix\": \"+\",\r\n \"smartFilter.minRating.label\": \"{{value}}+\",\r\n \"smartFilter.minRating.remove\": \"Remove rating filter\",\r\n \"smartFilter.minRating.setTo\": \"Set rating to {{rating}}\",\r\n \"smartFilter.priceRange.label\": \"Price Range\",\r\n \"smartFilter.priceRange.remove\": \"Remove price filter\",\r\n \"smartFilter.priceRange.edit\": \"Edit price\",\r\n \"smartFilter.transformedQuery.remove\": \"Remove search query\",\r\n \"smartFilter.transformedQuery.edit\": \"Edit search query\",\r\n \"smartFilter.restaurantPriceLevel.label\": \"Price Level\",\r\n \"smartFilter.restaurantPriceLevel.remove\": \"Remove price level filter\",\r\n \"smartFilter.restaurantPriceLevel.none\": \"Any\",\r\n \"smartFilter.restaurantPriceLevel.options.cheapEats\": \"Cheap Eats\",\r\n \"smartFilter.restaurantPriceLevel.options.midRange\": \"Mid Range\",\r\n \"smartFilter.restaurantPriceLevel.options.fineDining\": \"Fine Dining\",\r\n};\r\n\r\nconst formatCurrencyDefault: FormatCurrencyFunction = (\r\n value,\r\n currency = \"USD\"\r\n) => {\r\n return new Intl.NumberFormat(\"en-US\", {\r\n style: \"currency\",\r\n currency: currency,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 0,\r\n }).format(value);\r\n};\r\n\r\n/**\r\n * Simple translation hook with default English translations.\r\n * Can be extended with custom translations and locales.\r\n */\r\nexport const useTranslation = (\r\n customTranslations?: Record<string, string>,\r\n customFormatCurrency?: FormatCurrencyFunction\r\n) => {\r\n const [locale, setLocale] = useState<Locale>(\"en\");\r\n\r\n const t: TranslationFunction = useCallback(\r\n (key: string, params?: Record<string, any>) => {\r\n const translations = { ...defaultTranslations, ...customTranslations };\r\n let translation = translations[key] || key;\r\n\r\n if (params) {\r\n Object.keys(params).forEach((paramKey) => {\r\n translation = translation.replace(\r\n new RegExp(`{{${paramKey}}}`, \"g\"),\r\n String(params[paramKey])\r\n );\r\n });\r\n }\r\n\r\n return translation;\r\n },\r\n [customTranslations]\r\n );\r\n\r\n const formatCurrency = useCallback(\r\n (value: number, currency?: string) => {\r\n if (customFormatCurrency) {\r\n return customFormatCurrency(value, currency);\r\n }\r\n return formatCurrencyDefault(value, currency);\r\n },\r\n [customFormatCurrency]\r\n );\r\n\r\n return {\r\n t,\r\n locale,\r\n setLocale,\r\n formatCurrency,\r\n };\r\n};\r\n","import React, { ReactNode, CSSProperties } from \"react\";\r\n\r\nexport const renderStars = (rating: number): ReactNode[] => {\r\n const stars: ReactNode[] = [];\r\n const fullStars = Math.floor(rating);\r\n const hasHalfStar = rating % 1 !== 0;\r\n\r\n const baseStyles: CSSProperties = {\r\n display: \"block\",\r\n width: \"12px\",\r\n height: \"12px\",\r\n borderRadius: \"50%\",\r\n border: \"1px solid #03852e\",\r\n pointerEvents: \"none\",\r\n };\r\n\r\n const fullStarStyles: CSSProperties = {\r\n ...baseStyles,\r\n backgroundColor: \"#03852e\",\r\n };\r\n\r\n const halfStarStyles: CSSProperties = {\r\n ...baseStyles,\r\n background: \"linear-gradient(90deg, #03852e 50%, transparent 50%)\",\r\n };\r\n\r\n for (let i = 0; i < fullStars; i += 1) {\r\n stars.push(<span key={`full-${i}`} style={fullStarStyles} />);\r\n }\r\n\r\n if (hasHalfStar) {\r\n stars.push(<span key=\"half\" style={halfStarStyles} />);\r\n }\r\n\r\n const remainingStars = Math.max(0, 5 - Math.ceil(rating));\r\n for (let i = 0; i < remainingStars; i += 1) {\r\n stars.push(<span key={`empty-${i}`} style={baseStyles} />);\r\n }\r\n\r\n return stars;\r\n};\r\n\r\nexport const createMinRatingFilterLabel = (\r\n rating: number,\r\n suffix?: string\r\n): ReactNode => (\r\n <span style={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}>\r\n <span\r\n style={{\r\n display: \"flex\",\r\n gap: \"1px\",\r\n userSelect: \"none\",\r\n }}\r\n >\r\n {renderStars(rating)}\r\n </span>{\" \"}\r\n {suffix}\r\n </span>\r\n);\r\n\r\nexport const formatRatingValue = (rating: number): string => rating.toFixed(1);\r\n\r\nexport const createPriceRangeFilterLabel = (\r\n min: number,\r\n max: number | undefined,\r\n currency: string | undefined,\r\n formatCurrencyFn: (value: number, currency?: string) => string\r\n): string =>\r\n `${formatCurrencyFn(min, currency)} - ${formatCurrencyFn(\r\n max ?? 0,\r\n currency\r\n )}`;\r\n","import React, {\r\n ChangeEvent,\r\n FunctionComponent,\r\n KeyboardEvent,\r\n useEffect,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { EditIcon } from \"../Icons\";\r\nimport { CloseButton } from \"./CloseButton\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { PriceRangeValue } from \"./types\";\r\n\r\ntype Boundary = \"min\" | \"max\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n backgroundColor: \"transparent\",\r\n borderRadius: \"2px\",\r\n padding: \"2px 8px\",\r\n width: \"64px\",\r\n textAlign: \"center\",\r\n border: \"none\",\r\n};\r\n\r\nconst editButtonStyles: CSSProperties = {\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n color: \"#737373\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n};\r\n\r\ninterface PriceBoundaryChipProps {\r\n boundary: Boundary;\r\n label: string;\r\n value?: number;\r\n placeholder?: string;\r\n currency: string;\r\n isOptional?: boolean;\r\n showRemoveButton?: boolean;\r\n removeLabel?: string;\r\n editLabel?: string;\r\n showAddWhenEmpty?: boolean;\r\n onCommit: (value?: number) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nconst PriceBoundaryChip: FunctionComponent<PriceBoundaryChipProps> = ({\r\n boundary,\r\n label,\r\n value,\r\n placeholder,\r\n currency,\r\n isOptional = false,\r\n showRemoveButton = false,\r\n removeLabel,\r\n editLabel,\r\n showAddWhenEmpty = false,\r\n onCommit,\r\n onRemove,\r\n}) => {\r\n const [draft, setDraft] = useState<string>(\r\n value !== undefined ? String(value) : \"\"\r\n );\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editHover, setEditHover] = useState(false);\r\n const hasValue = value !== undefined;\r\n\r\n useEffect(() => {\r\n setDraft(value !== undefined ? String(value) : \"\");\r\n setIsEditing(false);\r\n }, [value]);\r\n\r\n const resetDraft = () => {\r\n setDraft(value !== undefined ? String(value) : \"\");\r\n };\r\n\r\n const commitValue = () => {\r\n if (draft.trim() === \"\") {\r\n if (isOptional) {\r\n onCommit(undefined);\r\n setDraft(\"\");\r\n return;\r\n }\r\n resetDraft();\r\n return;\r\n }\r\n\r\n const parsed = Number(draft);\r\n if (!Number.isFinite(parsed)) {\r\n resetDraft();\r\n return;\r\n }\r\n\r\n const normalized = Math.max(0, parsed);\r\n if (normalized === value) {\r\n resetDraft();\r\n return;\r\n }\r\n onCommit(normalized);\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n const next = event.target.value.replace(/[^\\d]/g, \"\");\r\n setDraft(next);\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n setIsEditing(false);\r\n return;\r\n }\r\n\r\n if (event.key === \"Escape\") {\r\n event.preventDefault();\r\n resetDraft();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n setIsEditing(false);\r\n return;\r\n }\r\n\r\n const allowed =\r\n (event.key.length === 1 && /[0-9]/.test(event.key)) ||\r\n event.key === \"Backspace\" ||\r\n event.key === \"Delete\" ||\r\n event.key === \"Tab\" ||\r\n event.key === \"ArrowLeft\" ||\r\n event.key === \"ArrowRight\" ||\r\n event.key === \"Home\" ||\r\n event.key === \"End\";\r\n\r\n if (!allowed) {\r\n event.preventDefault();\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <span\r\n style={{\r\n fontSize: \"10px\",\r\n textTransform: \"uppercase\",\r\n fontWeight: 600,\r\n letterSpacing: \"0.05em\",\r\n }}\r\n >\r\n {label}\r\n </span>\r\n {isEditing ? (\r\n <input\r\n value={draft}\r\n onChange={handleChange}\r\n onBlur={() => {\r\n commitValue();\r\n setIsEditing(false);\r\n }}\r\n onKeyDown={handleKeyDown}\r\n placeholder={placeholder}\r\n inputMode=\"numeric\"\r\n pattern=\"[0-9]*\"\r\n aria-label={label}\r\n style={inputStyles}\r\n autoFocus\r\n />\r\n ) : hasValue ? (\r\n <span style={{ fontSize: \"16px\" }}>\r\n {currency}\r\n {value}\r\n </span>\r\n ) : showAddWhenEmpty ? (\r\n <button\r\n type=\"button\"\r\n style={{\r\n fontSize: \"16px\",\r\n color: \"#737373\",\r\n cursor: \"pointer\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n padding: 0,\r\n }}\r\n onClick={() => setIsEditing(true)}\r\n aria-label={editLabel}\r\n >\r\n +\r\n </button>\r\n ) : (\r\n <span style={{ fontSize: \"16px\", color: \"#737373\" }}>-</span>\r\n )}\r\n {(!showAddWhenEmpty || (showAddWhenEmpty && isEditing)) && (\r\n <span style={{ color: \"#737373\", fontSize: \"12px\" }}>{currency}</span>\r\n )}\r\n {!isEditing && (!showAddWhenEmpty || hasValue) && (\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...editButtonStyles,\r\n backgroundColor: editHover ? \"#e5e5e5\" : \"transparent\",\r\n }}\r\n aria-label={editLabel}\r\n title={editLabel}\r\n onClick={() => setIsEditing(true)}\r\n onMouseEnter={() => setEditHover(true)}\r\n onMouseLeave={() => setEditHover(false)}\r\n >\r\n <EditIcon />\r\n </button>\r\n )}\r\n {showRemoveButton && <CloseButton onClick={onRemove} />}\r\n </div>\r\n );\r\n};\r\n\r\nexport const PriceRangeFilterChip: FunctionComponent<{\r\n priceRange: PriceRangeValue;\r\n currency: string;\r\n onChange: (range: PriceRangeValue) => void;\r\n onRemove: () => void;\r\n}> = ({ priceRange, currency, onChange, onRemove }) => {\r\n const { t } = useTranslation();\r\n\r\n const minLabel = \"Min\";\r\n const maxChipLabel = \"Max\";\r\n const removeLabel = t(\"smartFilter.priceRange.remove\");\r\n const editLabel = t(\"smartFilter.priceRange.edit\");\r\n\r\n const handleBoundaryCommit = (boundary: Boundary, nextValue?: number) => {\r\n const nextRange: PriceRangeValue = {\r\n min: priceRange.min,\r\n max: priceRange.max,\r\n };\r\n\r\n if (boundary === \"min\") {\r\n nextRange.min = nextValue;\r\n if (\r\n nextValue !== undefined &&\r\n priceRange.max !== undefined &&\r\n nextValue > priceRange.max\r\n ) {\r\n nextRange.max = nextValue;\r\n }\r\n } else {\r\n nextRange.max = nextValue;\r\n if (\r\n nextValue !== undefined &&\r\n priceRange.min !== undefined &&\r\n nextValue < priceRange.min\r\n ) {\r\n nextRange.min = nextValue;\r\n }\r\n }\r\n\r\n if (nextRange.min !== priceRange.min || nextRange.max !== priceRange.max) {\r\n onChange(nextRange);\r\n }\r\n };\r\n\r\n return (\r\n <>\r\n <PriceBoundaryChip\r\n boundary=\"min\"\r\n label={minLabel}\r\n value={priceRange.min}\r\n currency={currency}\r\n editLabel={editLabel}\r\n showRemoveButton={priceRange.min !== undefined && priceRange.min !== 0}\r\n onCommit={(value) => handleBoundaryCommit(\"min\", value)}\r\n onRemove={onRemove}\r\n />\r\n <PriceBoundaryChip\r\n boundary=\"max\"\r\n label={maxChipLabel}\r\n value={priceRange.max}\r\n currency={currency}\r\n isOptional\r\n showRemoveButton={priceRange.max !== undefined}\r\n removeLabel={removeLabel}\r\n editLabel={editLabel}\r\n showAddWhenEmpty\r\n onCommit={(value) => handleBoundaryCommit(\"max\", value)}\r\n onRemove={onRemove}\r\n />\r\n </>\r\n );\r\n};\r\n","import React, { ChangeEvent, FunctionComponent, CSSProperties } from \"react\";\r\nimport { CloseButton } from \"./CloseButton\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\nimport type { PriceLevel } from \"@mapfirst.ai/core\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"16px\",\r\n flexShrink: 0,\r\n height: \"34px\",\r\n};\r\n\r\nconst PRICE_LEVEL_OPTIONS = [\r\n { value: \"Cheap Eats\" as PriceLevel, key: \"cheapEats\" },\r\n { value: \"Mid Range\" as PriceLevel, key: \"midRange\" },\r\n { value: \"Fine Dining\" as PriceLevel, key: \"fineDining\" },\r\n] as const;\r\n\r\nexport interface RestaurantPriceLevelChipProps {\r\n values: PriceLevel[];\r\n onChange: (values: PriceLevel[]) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nexport const RestaurantPriceLevelChip: FunctionComponent<\r\n RestaurantPriceLevelChipProps\r\n> = ({ values, onChange, onRemove }) => {\r\n const { t } = useTranslation();\r\n\r\n const label = t(\"smartFilter.restaurantPriceLevel.label\");\r\n const removeLabel = t(\"smartFilter.restaurantPriceLevel.remove\");\r\n const noneSelectedLabel = t(\"smartFilter.restaurantPriceLevel.none\");\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n const { value, checked } = event.target;\r\n const valueAsPriceLevel = value as PriceLevel;\r\n const selection = new Set(values);\r\n if (checked) {\r\n selection.add(valueAsPriceLevel);\r\n } else {\r\n selection.delete(valueAsPriceLevel);\r\n }\r\n const orderedSelection = PRICE_LEVEL_OPTIONS.filter((option) =>\r\n selection.has(option.value)\r\n ).map((option) => option.value);\r\n onChange(orderedSelection);\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexWrap: \"wrap\",\r\n }}\r\n >\r\n <span\r\n style={{\r\n fontSize: \"10px\",\r\n textTransform: \"uppercase\",\r\n fontWeight: 600,\r\n letterSpacing: \"0.05em\",\r\n }}\r\n >\r\n {label}\r\n </span>\r\n <div style={{ display: \"flex\", gap: \"12px\" }}>\r\n {PRICE_LEVEL_OPTIONS.map((option) => {\r\n const optionLabel = t(\r\n `smartFilter.restaurantPriceLevel.options.${option.key}`\r\n );\r\n const checkboxId = `price-level-${option.key}`;\r\n return (\r\n <label\r\n key={option.value}\r\n htmlFor={checkboxId}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"4px\",\r\n fontSize: \"12px\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n <input\r\n id={checkboxId}\r\n type=\"checkbox\"\r\n value={option.value}\r\n checked={values.includes(option.value)}\r\n onChange={handleChange}\r\n style={{ accentColor: \"#03852e\", cursor: \"pointer\" }}\r\n />\r\n <span>{optionLabel}</span>\r\n </label>\r\n );\r\n })}\r\n {values.length === 0 && (\r\n <span style={{ fontSize: \"12px\", color: \"#737373\" }}>\r\n {noneSelectedLabel}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <CloseButton onClick={onRemove} />\r\n </div>\r\n );\r\n};\r\n","import React, {\r\n ChangeEvent,\r\n FunctionComponent,\r\n KeyboardEvent,\r\n useEffect,\r\n useRef,\r\n useState,\r\n CSSProperties,\r\n} from \"react\";\r\nimport { EditIcon, SearchIcon } from \"../Icons\";\r\nimport { CloseButton } from \"./CloseButton\";\r\nimport { useTranslation } from \"../../hooks/useTranslation\";\r\n\r\nconst chipStyles: CSSProperties = {\r\n position: \"relative\",\r\n backgroundColor: \"white\",\r\n color: \"black\",\r\n fontSize: \"14px\",\r\n borderRadius: \"9999px\",\r\n padding: \"0 16px\",\r\n paddingRight: \"20px\",\r\n border: \"1px solid #03852e\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n flexShrink: 0,\r\n userSelect: \"none\",\r\n height: \"34px\",\r\n};\r\n\r\nconst inputStyles: CSSProperties = {\r\n backgroundColor: \"#ececec\",\r\n borderRadius: \"2px\",\r\n padding: \"2px 8px\",\r\n outline: \"none\",\r\n fontSize: \"16px\",\r\n minWidth: \"8ch\",\r\n border: \"none\",\r\n};\r\n\r\nconst editButtonStyles: CSSProperties = {\r\n padding: \"4px\",\r\n borderRadius: \"50%\",\r\n cursor: \"pointer\",\r\n transition: \"background-color 0.2s\",\r\n color: \"#737373\",\r\n border: \"none\",\r\n backgroundColor: \"transparent\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n};\r\n\r\nexport interface TransformedQueryChipProps {\r\n value: string;\r\n onChange: (nextValue: string) => void;\r\n onRemove: () => void;\r\n}\r\n\r\nexport const TransformedQueryChip: FunctionComponent<\r\n TransformedQueryChipProps\r\n> = ({ value, onChange, onRemove }) => {\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const [draft, setDraft] = useState(value);\r\n const [isEditing, setIsEditing] = useState(false);\r\n const [editHover, setEditHover] = useState(false);\r\n const { t } = useTranslation();\r\n\r\n const removeLabel = t(\"smartFilter.transformedQuery.remove\");\r\n const editLabel = t(\"smartFilter.transformedQuery.edit\");\r\n\r\n useEffect(() => {\r\n setDraft(value);\r\n setIsEditing(false);\r\n }, [value]);\r\n\r\n const applyChanges = () => {\r\n const nextValue = draft.trim();\r\n if (!nextValue.length) {\r\n setDraft(value);\r\n return;\r\n }\r\n if (nextValue === value) {\r\n return;\r\n }\r\n onChange(nextValue);\r\n };\r\n\r\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\r\n setDraft(event.target.value);\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\r\n if (event.key === \"Enter\") {\r\n event.preventDefault();\r\n (event.currentTarget as HTMLInputElement).blur();\r\n return;\r\n }\r\n\r\n if (event.key === \"Escape\") {\r\n event.preventDefault();\r\n setDraft(value);\r\n (event.currentTarget as HTMLInputElement).blur();\r\n return;\r\n }\r\n };\r\n\r\n return (\r\n <div style={chipStyles}>\r\n <SearchIcon style={{ width: \"16px\", height: \"16px\", color: \"#03852e\" }} />\r\n {isEditing ? (\r\n <input\r\n ref={inputRef}\r\n value={draft}\r\n onChange={handleChange}\r\n onBlur={() => {\r\n applyChanges();\r\n setIsEditing(false);\r\n }}\r\n onKeyDown={handleKeyDown}\r\n aria-label={editLabel}\r\n style={inputStyles}\r\n autoFocus\r\n />\r\n ) : (\r\n <span style={{ fontSize: \"16px\" }}>{value}</span>\r\n )}\r\n {!isEditing && (\r\n <button\r\n type=\"button\"\r\n style={{\r\n ...editButtonStyles,\r\n backgroundColor: editHover ? \"#e5e5e5\" : \"transparent\",\r\n }}\r\n aria-label={editLabel}\r\n title={editLabel}\r\n onClick={() => setIsEditing(true)}\r\n onMouseEnter={() => setEditHover(true)}\r\n onMouseLeave={() => setEditHover(false)}\r\n >\r\n <EditIcon />\r\n </button>\r\n )}\r\n <CloseButton onClick={onRemove} />\r\n </div>\r\n );\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\n\r\nexport const useFilterScroll = (dependency: number) => {\r\n const scrollerRef = useRef<HTMLDivElement | null>(null);\r\n const [atStart, setAtStart] = useState(true);\r\n const [atEnd, setAtEnd] = useState(true);\r\n\r\n const updateScrollButtons = useCallback(() => {\r\n const el = scrollerRef.current;\r\n if (!el) {\r\n setAtStart(true);\r\n setAtEnd(true);\r\n return;\r\n }\r\n\r\n const { scrollLeft, scrollWidth, clientWidth } = el;\r\n setAtStart(scrollLeft <= 0);\r\n setAtEnd(scrollLeft + clientWidth >= scrollWidth - 1);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const el = scrollerRef.current;\r\n updateScrollButtons();\r\n if (!el) {\r\n return;\r\n }\r\n\r\n const handleScroll = () => updateScrollButtons();\r\n el.addEventListener(\"scroll\", handleScroll, { passive: true });\r\n window.addEventListener(\"resize\", updateScrollButtons);\r\n\r\n return () => {\r\n el.removeEventListener(\"scroll\", handleScroll);\r\n window.removeEventListener(\"resize\", updateScrollButtons);\r\n };\r\n }, [dependency, updateScrollButtons]);\r\n\r\n const scrollByDir = useCallback((dir: \"prev\" | \"next\") => {\r\n const el = scrollerRef.current;\r\n if (!el) {\r\n return;\r\n }\r\n\r\n const delta = el.clientWidth * 0.7;\r\n el.scrollBy({\r\n left: dir === \"next\" ? delta : -delta,\r\n behavior: \"smooth\",\r\n });\r\n }, []);\r\n\r\n return {\r\n scrollerRef,\r\n atStart,\r\n atEnd,\r\n scrollByDir,\r\n };\r\n};\r\n","import { useEffect, useState } from \"react\";\r\n\r\n/**\r\n * Hook to detect if the viewport is in portrait orientation.\r\n * Updates on window resize.\r\n */\r\nexport const useIsPortrait = (): boolean => {\r\n const [isPortrait, setIsPortrait] = useState(\r\n typeof window !== \"undefined\"\r\n ? window.innerHeight > window.innerWidth\r\n : false\r\n );\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") {\r\n return;\r\n }\r\n\r\n const handleResize = () => {\r\n setIsPortrait(window.innerHeight > window.innerWidth);\r\n };\r\n\r\n window.addEventListener(\"resize\", handleResize);\r\n return () => window.removeEventListener(\"resize\", handleResize);\r\n }, []);\r\n\r\n return isPortrait;\r\n};\r\n"],"mappings":"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","names":["React","MapFirstCore","useCallback","useState","React","React","jsx","jsxs","SearchIcon","className","style","CloseIcon","EditIcon","NextIcon","StarIcon","fill","jsx","closeButtonStyles","iconStyles","CloseButton","onClick","style","isHovering","setIsHovering","React","CloseIcon","jsx","jsxs","chipStyles","Chip","label","icon","remove","style","CloseButton","useState","useCallback","useState","defaultTranslations","formatCurrencyDefault","value","currency","useTranslation","customTranslations","customFormatCurrency","locale","setLocale","t","key","params","translation","paramKey","formatCurrency","jsx","jsxs","renderStars","rating","stars","fullStars","hasHalfStar","baseStyles","fullStarStyles","halfStarStyles","i","remainingStars","createMinRatingFilterLabel","suffix","formatRatingValue","createPriceRangeFilterLabel","min","max","currency","formatCurrencyFn","jsx","jsxs","chipContainerStyles","starContainerStyles","circleBaseStyles","buttonBaseStyles","MinRatingFilterChip","rating","onChange","onRemove","star","hoverRating","setHoverRating","useState","t","useTranslation","displayRating","formatLabel","value","formatRatingValue","removeLabel","setLabel","getFillForStar","index","starNumber","handleSelect","nextRating","handleBlur","event","_a","related","_","fillState","halfValue","StarIcon","circleStyles","halfCircleStyles","CloseButton","useEffect","useState","Fragment","jsx","jsxs","chipStyles","inputStyles","editButtonStyles","PriceBoundaryChip","boundary","label","value","placeholder","currency","isOptional","showRemoveButton","removeLabel","editLabel","showAddWhenEmpty","onCommit","onRemove","draft","setDraft","useState","isEditing","setIsEditing","editHover","setEditHover","hasValue","useEffect","resetDraft","commitValue","parsed","normalized","event","next","EditIcon","CloseButton","PriceRangeFilterChip","priceRange","onChange","t","useTranslation","minLabel","maxChipLabel","handleBoundaryCommit","nextValue","nextRange","jsx","jsxs","chipStyles","PRICE_LEVEL_OPTIONS","RestaurantPriceLevelChip","values","onChange","onRemove","t","useTranslation","label","removeLabel","noneSelectedLabel","handleChange","event","value","checked","valueAsPriceLevel","selection","orderedSelection","option","optionLabel","checkboxId","CloseButton","useEffect","useRef","useState","jsx","jsxs","chipStyles","inputStyles","editButtonStyles","TransformedQueryChip","value","onChange","onRemove","inputRef","useRef","draft","setDraft","useState","isEditing","setIsEditing","editHover","setEditHover","t","useTranslation","removeLabel","editLabel","useEffect","applyChanges","nextValue","SearchIcon","event","EditIcon","CloseButton","useCallback","useEffect","useRef","useState","useFilterScroll","dependency","scrollerRef","atStart","setAtStart","atEnd","setAtEnd","updateScrollButtons","el","scrollLeft","scrollWidth","clientWidth","handleScroll","scrollByDir","dir","delta","jsx","jsxs","containerStyles","scrollContainerBase","gradientStyles","navButtonStyles","FilterChips","filters","isPortrait","currency","minRatingSuffix","clearAllLabel","previousFiltersLabel","nextFiltersLabel","formatCurrency","onFilterChange","onResetFilters","onClearAll","scrollerRef","atStart","atEnd","scrollByDir","useFilterScroll","t","useTranslation","navHover","setNavHover","React","resetHover","setResetHover","clearHover","setClearHover","scrollContainerStyles","SearchIcon","filter","_a","_b","_c","renderStandardChip","Chip","f","currentRating","MinRatingFilterChip","nextRating","nextFilters","PriceRangeFilterChip","nextRange","TransformedQueryChip","nextValue","RestaurantPriceLevelChip","nextLevels","NextIcon","useEffect","useState","useIsPortrait","isPortrait","setIsPortrait","handleResize","jsx","jsxs","containerStyles","formStyles","inputContainerStyles","inputStyles","loaderContainerStyles","loaderStyles","typingPromptStyles","SmartFilter","mapFirst","filters","controlledValue","isSearching","placeholder","onSearch","onFilterChange","onValueChange","showTypingPrompt","customTranslations","currency","style","inputStyle","containerStyle","internalValue","setInternalValue","useState","value","setValue","isPortrait","useIsPortrait","t","formatCurrency","useTranslation","minRatingSuffix","typingPrompt","previousFiltersLabel","nextFiltersLabel","clearAllLabel","formSubmit","e","query","error","handleFilterChange","useCallback","nextFilters","clearAll","resetFilters","clearAllFilters","FilterChips","jsx","jsxs","useMapFirstCore","options","instanceRef","React","state","setState","optionsRef","opts","coreOptions","properties","_a","_b","prev","id","type","filters","bounds","pendingBounds","center","zoom","location","loading","searching","instance","MapFirstCore","useMapFirstProperties","mapFirst","setProperties","useMapFirstSelectedProperty","selectedId","setSelectedId","usePrimaryType","primaryType","setPrimaryTypeState","setPrimaryType","useSelectedMarker","selectedMarker","setSelectedMarkerState","setSelectedMarker","useMapLibreAttachment","map","maplibregl","onMarkerClick","attachedRef","useGoogleMapsAttachment","google","useMapboxAttachment","mapboxgl","useMapFirst","usePropertiesSearch","isLoading","setIsLoading","error","setError","err","useSmartFilterSearch","useMapFirstBoundsSearch","isSearching","setIsSearching","MarkerDebugList","markers","marker","_c","_d","_e","_f"]}