analytica-frontend-lib 1.3.24 → 1.3.25

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,"file":"ChoroplethMap.d.ts","sourceRoot":"","sources":["../../../src/components/ChoroplethMap/ChoroplethMap.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AAyN/B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,QAAA,MAAM,aAAa,GAAI,qEAQpB,kBAAkB,4CA4epB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ChoroplethMap.d.ts","sourceRoot":"","sources":["../../../src/components/ChoroplethMap/ChoroplethMap.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AA+N/B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,QAAA,MAAM,aAAa,GAAI,qEAQpB,kBAAkB,4CA4epB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -218,6 +218,7 @@ var Text_default = Text;
218
218
 
219
219
  // src/components/ChoroplethMap/ChoroplethMap.tsx
220
220
  var import_jsx_runtime2 = require("react/jsx-runtime");
221
+ var GOOGLE_MAPS_LOADER_ID = "google-maps-script";
221
222
  var FADE_DURATION = 400;
222
223
  var HOVER_DURATION = 200;
223
224
  var TARGET_OPACITY = 0.8;
@@ -352,7 +353,7 @@ var ChoroplethMap = ({
352
353
  onRegionClick,
353
354
  className
354
355
  }) => {
355
- const mapId = (0, import_react2.useId)();
356
+ const mapId = GOOGLE_MAPS_LOADER_ID;
356
357
  const [map, setMap] = (0, import_react2.useState)(null);
357
358
  const [hoveredRegion, setHoveredRegion] = (0, import_react2.useState)(null);
358
359
  const [infoPosition, setInfoPosition] = (0, import_react2.useState)(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ChoroplethMap/ChoroplethMap.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts","../../src/store/themeStore.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["/* global google */\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { GoogleMap, useJsApiLoader } from '@react-google-maps/api';\nimport union from '@turf/union';\nimport type { Feature, MultiPolygon, Polygon } from 'geojson';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\nimport Text from '../Text/Text';\nimport type {\n ChoroplethMapProps,\n ColorClass,\n RegionData,\n} from './ChoroplethMap.types';\n\n/**\n * Fade-in animation duration in milliseconds\n */\nconst FADE_DURATION = 400;\n\n/**\n * Hover animation duration in milliseconds\n */\nconst HOVER_DURATION = 200;\n\n/**\n * Target fill opacity for polygons\n */\nconst TARGET_OPACITY = 0.8;\n\n/**\n * Read a CSS custom property value from the document root\n * @param name - CSS variable name (e.g. '--color-map-highlight')\n * @param fallback - Fallback value when running outside browser\n * @returns Resolved CSS variable value\n */\nconst getCssVar = (name: string, fallback = ''): string => {\n if (typeof document === 'undefined') return fallback;\n return (\n getComputedStyle(document.documentElement).getPropertyValue(name).trim() ||\n fallback\n );\n};\n\n/**\n * Build color classes from CSS variables\n * @returns Color class array for the choropleth map\n */\nconst getColorClasses = (): ColorClass[] => [\n {\n min: 0.75,\n max: 1.01,\n fillColor: getCssVar('--color-map-highlight', '#1C61B2'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Destaque',\n },\n {\n min: 0.5,\n max: 0.75,\n fillColor: getCssVar('--color-map-above-avg', '#2883D7'),\n strokeColor: getCssVar('--color-map-above-avg', '#2883D7'),\n label: 'Acima da média',\n },\n {\n min: 0.25,\n max: 0.5,\n fillColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n strokeColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n label: 'Abaixo da média',\n },\n {\n min: 0,\n max: 0.25,\n fillColor: getCssVar('--color-map-attention', '#E3F1FB'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Ponto de atenção',\n },\n];\n\n/**\n * Get color class based on value\n * @param value - Normalized value between 0 and 1\n * @param colorClasses - Array of color class configurations\n * @returns Color class configuration\n */\nconst getColorClass = (\n value: number,\n colorClasses: ColorClass[]\n): ColorClass => {\n for (const colorClass of colorClasses) {\n if (value >= colorClass.min && value < colorClass.max) {\n return colorClass;\n }\n }\n return colorClasses[0];\n};\n\n/**\n * Compute NRE boundary polygons by merging city polygons that share the same regionName\n * @param data - Array of region data with individual city GeoJSON features\n * @returns Array of GeoJSON features representing NRE boundaries\n */\nconst computeNREBoundaries = (\n data: RegionData[]\n): Feature<Polygon | MultiPolygon>[] => {\n const groups = new Map<string, RegionData[]>();\n data.forEach((region) => {\n const existing = groups.get(region.name) ?? [];\n existing.push(region);\n groups.set(region.name, existing);\n });\n\n const boundaries: Feature<Polygon | MultiPolygon>[] = [];\n groups.forEach((regions) => {\n let merged: Feature<Polygon | MultiPolygon> | null = null;\n for (const region of regions) {\n if (region.geoJson.type !== 'Feature') continue;\n const feature: Feature<Polygon | MultiPolygon> = region.geoJson;\n if (merged) {\n const result: Feature<Polygon | MultiPolygon> | null = union({\n type: 'FeatureCollection' as const,\n features: [merged, feature],\n });\n if (result) merged = result;\n } else {\n merged = feature;\n }\n }\n if (merged) boundaries.push(merged);\n });\n\n return boundaries;\n};\n\n/**\n * Create style function for Data Layer features\n * @param opacity - Current fill opacity\n * @param colorClasses - Array of color class configurations\n * @param strokeCityColor - Stroke color for city borders\n * @returns Style function for map.data.setStyle\n */\nconst createStyleFunction = (\n opacity: number,\n colorClasses: ColorClass[],\n strokeCityColor: string\n) => {\n return (feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n return {\n fillColor: colorClass.fillColor,\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: 0.3,\n cursor: 'pointer',\n };\n };\n};\n\n/**\n * Map container style\n * Note: Google Maps requires explicit pixel dimensions to render properly\n */\nconst containerStyle = {\n width: '100%',\n height: '415px',\n borderRadius: '0.5rem',\n};\n\n/**\n * Default map center (Paraná, Brazil)\n */\nconst defaultCenter = {\n lat: -24.7,\n lng: -51.5,\n};\n\n/**\n * Legend Item component for the map\n * @param color - Fill color of the legend circle\n * @param label - Text label for the legend item\n * @param borderColor - Optional border color for the legend circle\n * @param active - Whether the legend class is active (visible on map)\n * @param onClick - Callback when the legend item is clicked\n */\nconst LegendItem = ({\n color,\n label,\n borderColor,\n active = true,\n onClick,\n}: {\n color: string;\n label: string;\n borderColor?: string;\n active?: boolean;\n onClick?: () => void;\n}) => (\n <Text\n as=\"button\"\n type=\"button\"\n aria-pressed={active}\n className=\"flex items-center gap-2 cursor-pointer transition-opacity duration-200\"\n style={{ opacity: active ? 1 : 0.4 }}\n onClick={onClick}\n >\n <div\n className=\"w-3 h-3 rounded-full\"\n style={{\n backgroundColor: color,\n border: borderColor ? `1px solid ${borderColor}` : 'none',\n }}\n />\n <Text as=\"span\" size=\"sm\" weight=\"medium\" color=\"text-text-600\">\n {label}\n </Text>\n </Text>\n);\n\n/**\n * Loading skeleton component\n */\nconst LoadingSkeleton = () => (\n <div className=\"w-full h-full flex items-center justify-center bg-background-50 rounded-lg animate-pulse\">\n <Text size=\"sm\" color=\"text-text-400\">\n Carregando mapa...\n </Text>\n </div>\n);\n\n/**\n * ChoroplethMap component for displaying regional performance data\n *\n * Displays an interactive Google Map with colored regions based on normalized values.\n * Uses 4 color classes to represent different performance levels.\n * Includes fade-in animation, smooth hover transitions, and zoom-to-region on click.\n * NRE boundaries are rendered as a separate overlay with thicker strokes.\n *\n * @param data - Array of region data with GeoJSON and values\n * @param apiKey - Google Maps API key\n * @param title - Optional title for the map section\n * @param loading - Loading state indicator\n * @param bounds - Map bounds for initial view\n * @param onRegionClick - Callback when a region is clicked\n * @param className - Additional CSS classes\n * @returns Choropleth map component\n *\n * @example\n * ```tsx\n * <ChoroplethMap\n * data={regionData}\n * apiKey=\"your-api-key\"\n * title=\"Performance por região\"\n * loading={false}\n * onRegionClick={(region) => console.log(region)}\n * />\n * ```\n */\nconst ChoroplethMap = ({\n data,\n apiKey,\n title = 'Performance por região',\n loading = false,\n bounds,\n onRegionClick,\n className,\n}: ChoroplethMapProps) => {\n const mapId = useId();\n const [map, setMap] = useState<google.maps.Map | null>(null);\n const [hoveredRegion, setHoveredRegion] = useState<RegionData | null>(null);\n const [infoPosition, setInfoPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const [activeClasses, setActiveClasses] = useState<Set<number>>(\n new Set([0, 1, 2, 3])\n );\n const fadeAnimationRef = useRef<number | null>(null);\n const hoverAnimationRef = useRef<number | null>(null);\n const nreBoundaryLayerRef = useRef<google.maps.Data | null>(null);\n const onRegionClickRef = useRef(onRegionClick);\n onRegionClickRef.current = onRegionClick;\n\n const { isDark } = useTheme();\n\n const dataSignature = useMemo(\n () =>\n data\n .map((d) => `${d.id}:${d.value}:${d.name}:${d.accessCount}`)\n .join('|'),\n [data]\n );\n const stableData = useMemo(() => data, [dataSignature]);\n\n const colorClasses = useMemo(() => getColorClasses(), [isDark]);\n\n const mapOptions: google.maps.MapOptions = useMemo(() => {\n const bgColor = getCssVar('--color-background-50', '#F6F6F6');\n return {\n disableDefaultUI: true,\n zoomControl: true,\n scrollwheel: true,\n draggable: true,\n backgroundColor: bgColor,\n styles: [\n {\n stylers: [{ color: bgColor }],\n },\n {\n elementType: 'labels',\n stylers: [{ visibility: 'off' }],\n },\n {\n elementType: 'geometry.stroke',\n stylers: [{ visibility: 'off' }],\n },\n ],\n };\n }, [isDark]);\n\n const { isLoaded, loadError } = useJsApiLoader({\n id: mapId,\n googleMapsApiKey: apiKey,\n });\n\n /**\n * Handle map load event\n */\n const onLoad = useCallback(\n (mapInstance: google.maps.Map) => {\n setMap(mapInstance);\n\n // Fit bounds if provided, then bump zoom to fill the container\n if (bounds) {\n const googleBounds = new google.maps.LatLngBounds(\n { lat: bounds.south, lng: bounds.west },\n { lat: bounds.north, lng: bounds.east }\n );\n mapInstance.fitBounds(googleBounds, 0);\n google.maps.event.addListenerOnce(mapInstance, 'idle', () => {\n const currentZoom = mapInstance.getZoom();\n if (currentZoom) {\n mapInstance.setZoom(currentZoom + 0.8);\n }\n });\n }\n },\n [bounds]\n );\n\n /**\n * Handle map unmount\n */\n const onUnmount = useCallback(() => {\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n }\n setMap(null);\n }, []);\n\n const nreBoundaries = useMemo(\n () => computeNREBoundaries(stableData),\n [stableData]\n );\n\n /**\n * Add GeoJSON data to map with animations\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const strokeCityColor = getCssVar('--color-map-stroke-city', '#64B5F6');\n const strokeNreColor = getCssVar('--color-map-stroke-nre', '#1565C0');\n\n // Clear existing data\n map.data.forEach((feature) => {\n map.data.remove(feature);\n });\n\n // Clear existing NRE boundary layer\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n\n // Add each region's GeoJSON\n stableData.forEach((region) => {\n try {\n const feature = map.data.addGeoJson(region.geoJson);\n // Store region data in the feature\n if (feature && feature.length > 0) {\n feature.forEach((f) => {\n f.setProperty('regionId', region.id);\n f.setProperty('regionName', region.name);\n f.setProperty('regionValue', region.value);\n f.setProperty('regionAccessCount', region.accessCount);\n });\n }\n } catch (error) {\n console.error(\n `Failed to add GeoJSON for region ${region.name}:`,\n error\n );\n }\n });\n\n // Build NRE feature index for O(1) lookup by region name\n const nreFeatureIndex = new Map<string, google.maps.Data.Feature[]>();\n map.data.forEach((f) => {\n const name = f.getProperty('regionName') as string;\n if (name) {\n const list = nreFeatureIndex.get(name) ?? [];\n list.push(f);\n nreFeatureIndex.set(name, list);\n }\n });\n\n // Start with opacity 0 for fade-in animation\n map.data.setStyle(createStyleFunction(0, colorClasses, strokeCityColor));\n\n // Add NRE boundary overlay\n const nreLayer = new google.maps.Data();\n nreBoundaries.forEach((boundary) => {\n nreLayer.addGeoJson(boundary);\n });\n nreLayer.setStyle({\n fillOpacity: 0,\n strokeColor: strokeNreColor,\n strokeWeight: 1.5,\n clickable: false,\n });\n nreLayer.setMap(map);\n nreBoundaryLayerRef.current = nreLayer;\n\n // Animate fade-in from 0 to TARGET_OPACITY\n const startTime = performance.now();\n const animateFadeIn = (currentTime: number) => {\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / FADE_DURATION, 1);\n const currentOpacity = progress * TARGET_OPACITY;\n\n map.data.setStyle(\n createStyleFunction(currentOpacity, colorClasses, strokeCityColor)\n );\n\n if (progress < 1) {\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n } else {\n fadeAnimationRef.current = null;\n }\n };\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n\n /**\n * Apply style overrides to a list of features\n * @param features - Features to style\n * @param opacity - Fill opacity\n * @param weight - Stroke weight\n */\n const applyHoverStyle = (\n features: google.maps.Data.Feature[],\n opacity: number,\n weight: number\n ) => {\n features.forEach((f) => {\n map.data.overrideStyle(f, {\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: weight,\n });\n });\n };\n\n /**\n * Animate hover transition for a group of features\n * @param features - Target features to animate\n * @param from - Starting opacity\n * @param to - Target opacity\n * @param fromWeight - Starting stroke weight\n * @param toWeight - Target stroke weight\n */\n const animateHover = (\n features: google.maps.Data.Feature[],\n from: number,\n to: number,\n fromWeight: number,\n toWeight: number\n ) => {\n if (hoverAnimationRef.current) {\n cancelAnimationFrame(hoverAnimationRef.current);\n }\n const start = performance.now();\n const animate = (now: number) => {\n const progress = Math.min((now - start) / HOVER_DURATION, 1);\n const opacity = from + (to - from) * progress;\n const weight = fromWeight + (toWeight - fromWeight) * progress;\n applyHoverStyle(features, opacity, weight);\n if (progress < 1) {\n hoverAnimationRef.current = requestAnimationFrame(animate);\n } else {\n hoverAnimationRef.current = null;\n }\n };\n hoverAnimationRef.current = requestAnimationFrame(animate);\n };\n\n // Handle hover events - highlight all cities in the same NRE\n let currentNRE: string | null = null;\n let revertTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Collect all features belonging to a given NRE name (O(1) index lookup)\n * @param nreName - NRE region name to match\n * @returns Array of matching features\n */\n const collectNREFeatures = (\n nreName: string\n ): google.maps.Data.Feature[] => {\n return nreFeatureIndex.get(nreName) ?? [];\n };\n\n const mouseoverListener = map.data.addListener(\n 'mouseover',\n (event: google.maps.Data.MouseEvent) => {\n // Cancel pending revert (when moving between cities in same NRE)\n if (revertTimeout) {\n clearTimeout(revertTimeout);\n revertTimeout = null;\n }\n\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n setHoveredRegion(region);\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n\n // Animate highlight for all features in the same NRE group\n if (currentNRE !== regionName) {\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n }\n currentNRE = regionName;\n const nreFeatures = collectNREFeatures(regionName);\n animateHover(nreFeatures, TARGET_OPACITY, 1, 0.5, 1.5);\n }\n }\n );\n\n const mouseoutListener = map.data.addListener('mouseout', () => {\n // Defer revert to avoid flicker when moving between cities in same NRE\n revertTimeout = setTimeout(() => {\n setHoveredRegion(null);\n setInfoPosition(null);\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n currentNRE = null;\n }\n }, 50);\n });\n\n const mousemoveListener = map.data.addListener(\n 'mousemove',\n (event: google.maps.Data.MouseEvent) => {\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n );\n\n /**\n * Compute bounds for all features matching a given NRE name\n * @param nreName - NRE region name to match\n * @returns LatLngBounds encompassing all matching features\n */\n const computeNREBounds = (nreName: string): google.maps.LatLngBounds => {\n const nreBounds = new google.maps.LatLngBounds();\n for (const f of collectNREFeatures(nreName)) {\n f.getGeometry()?.forEachLatLng((latLng) => {\n nreBounds.extend(latLng);\n });\n }\n return nreBounds;\n };\n\n // Handle click events - zoom to NRE region\n const clickListener = map.data.addListener(\n 'click',\n (event: google.maps.Data.MouseEvent) => {\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n onRegionClickRef.current?.(region);\n }\n\n const nreBounds = computeNREBounds(regionName);\n if (!nreBounds.isEmpty()) {\n map.fitBounds(nreBounds, 20);\n }\n }\n );\n\n return () => {\n if (fadeAnimationRef.current)\n cancelAnimationFrame(fadeAnimationRef.current);\n if (hoverAnimationRef.current)\n cancelAnimationFrame(hoverAnimationRef.current);\n if (revertTimeout) clearTimeout(revertTimeout);\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n google.maps.event.removeListener(mouseoverListener);\n google.maps.event.removeListener(mouseoutListener);\n google.maps.event.removeListener(mousemoveListener);\n google.maps.event.removeListener(clickListener);\n };\n }, [map, stableData, colorClasses, nreBoundaries]);\n\n /**\n * Apply visibility filter based on active legend classes\n * and adjust map bounds to fit visible features\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const visibleBounds = new google.maps.LatLngBounds();\n let hasVisibleFeatures = false;\n\n map.data.forEach((feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n const classIndex = colorClasses.indexOf(colorClass);\n\n if (activeClasses.has(classIndex)) {\n map.data.overrideStyle(feature, { visible: true });\n feature.getGeometry()?.forEachLatLng((latLng) => {\n visibleBounds.extend(latLng);\n });\n hasVisibleFeatures = true;\n } else {\n map.data.overrideStyle(feature, { visible: false });\n }\n });\n\n if (hasVisibleFeatures && !visibleBounds.isEmpty()) {\n map.fitBounds(visibleBounds, 20);\n }\n }, [map, stableData, activeClasses, colorClasses]);\n\n /**\n * Toggle a color class on/off in the legend filter\n * @param index - Index of the color class in colorClasses\n */\n const toggleClass = (index: number) => {\n setActiveClasses((prev) => {\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n };\n\n if (loadError) {\n return (\n <div className=\"p-5 bg-background border border-border-50 rounded-xl\">\n <Text color=\"text-error-700\">Erro ao carregar o mapa</Text>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'p-5 bg-background border border-border-50 rounded-xl flex flex-col gap-4',\n className\n )}\n >\n {/* Header */}\n <div className=\"flex flex-col gap-4\">\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n className=\"leading-[21px] tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Legend */}\n <div className=\"flex flex-wrap gap-8\">\n {colorClasses.map((colorClass, index) => (\n <LegendItem\n key={colorClass.label}\n color={colorClass.fillColor}\n borderColor={\n colorClass.fillColor === colorClass.strokeColor\n ? undefined\n : colorClass.strokeColor\n }\n label={colorClass.label}\n active={activeClasses.has(index)}\n onClick={() => toggleClass(index)}\n />\n ))}\n </div>\n </div>\n\n {/* Map Container */}\n <div className=\"bg-background-50 rounded-lg h-[415px] relative overflow-hidden\">\n {loading || !isLoaded ? (\n <LoadingSkeleton />\n ) : (\n <GoogleMap\n mapContainerStyle={containerStyle}\n center={defaultCenter}\n zoom={7}\n onLoad={onLoad}\n onUnmount={onUnmount}\n options={mapOptions}\n />\n )}\n\n {/* Tooltip */}\n {hoveredRegion && infoPosition && (\n <div\n className=\"fixed z-50 bg-background border border-border-50 shadow-lg rounded-lg p-3 pointer-events-none\"\n style={{\n left: Math.min(infoPosition.x + 10, window.innerWidth - 220),\n top: Math.min(infoPosition.y + 10, window.innerHeight - 80),\n }}\n >\n <Text size=\"sm\" weight=\"semibold\">\n {hoveredRegion.name}\n </Text>\n <Text size=\"xs\" color=\"text-text-700\">\n Acessos: {hoveredRegion.accessCount.toLocaleString('pt-BR')}\n </Text>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default ChoroplethMap;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { useEffect } from 'react';\nimport { useThemeStore, ThemeMode } from '../store/themeStore';\n\nexport type { ThemeMode };\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n * Utiliza Zustand para persistir o estado entre múltiplos arquivos e sessões\n */\nexport const useTheme = () => {\n const {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n initializeTheme,\n handleSystemThemeChange,\n } = useThemeStore();\n\n useEffect(() => {\n // Initialize theme on first render\n initializeTheme();\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [initializeTheme, handleSystemThemeChange]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Theme store state interface\n */\nexport interface ThemeState {\n /**\n * Current theme mode\n */\n themeMode: ThemeMode;\n /**\n * Whether the current theme is dark\n */\n isDark: boolean;\n}\n\n/**\n * Theme store actions interface\n */\nexport interface ThemeActions {\n /**\n * Apply theme based on the mode selected\n */\n applyTheme: (mode: ThemeMode) => void;\n /**\n * Toggle between themes\n */\n toggleTheme: () => void;\n /**\n * Set a specific theme mode\n */\n setTheme: (mode: ThemeMode) => void;\n /**\n * Initialize theme on app start\n */\n initializeTheme: () => void;\n /**\n * Handle system theme change\n */\n handleSystemThemeChange: () => void;\n}\n\nexport type ThemeStore = ThemeState & ThemeActions;\n\n/**\n * Apply theme to DOM based on mode\n */\nconst applyThemeToDOM = (mode: ThemeMode): boolean => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n return false;\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n return false;\n }\n }\n return false;\n};\n\n/**\n * Save original theme from white label\n */\nconst saveOriginalTheme = () => {\n const htmlElement = document.documentElement;\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n};\n\n/**\n * Theme store using Zustand with persistence\n */\nexport const useThemeStore = create<ThemeStore>()(\n devtools(\n persist(\n (set, get) => ({\n // Initial state\n themeMode: 'system',\n isDark: false,\n\n // Actions\n applyTheme: (mode: ThemeMode) => {\n const isDark = applyThemeToDOM(mode);\n set({ isDark });\n },\n\n toggleTheme: () => {\n const { themeMode, applyTheme } = get();\n let newMode: ThemeMode;\n\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n\n set({ themeMode: newMode });\n applyTheme(newMode);\n },\n\n setTheme: (mode: ThemeMode) => {\n const { applyTheme } = get();\n set({ themeMode: mode });\n applyTheme(mode);\n },\n\n initializeTheme: () => {\n const { themeMode, applyTheme } = get();\n\n // Save original theme from white label\n saveOriginalTheme();\n\n // Apply the current theme mode\n applyTheme(themeMode);\n },\n\n handleSystemThemeChange: () => {\n const { themeMode, applyTheme } = get();\n // Only respond to system changes when in system mode\n if (themeMode === 'system') {\n applyTheme('system');\n }\n },\n }),\n {\n name: 'theme-store', // Nome da chave no localStorage\n partialize: (state) => ({\n themeMode: state.themeMode,\n }), // Só persiste o themeMode, não o isDark\n }\n ),\n {\n name: 'theme-store',\n }\n )\n);\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["import_react","import_jsx_runtime","union"]}
1
+ {"version":3,"sources":["../../src/components/ChoroplethMap/ChoroplethMap.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts","../../src/store/themeStore.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["/* global google */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { GoogleMap, useJsApiLoader } from '@react-google-maps/api';\nimport union from '@turf/union';\nimport type { Feature, MultiPolygon, Polygon } from 'geojson';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\nimport Text from '../Text/Text';\nimport type {\n ChoroplethMapProps,\n ColorClass,\n RegionData,\n} from './ChoroplethMap.types';\n\n/**\n * Stable ID for the Google Maps script loader singleton.\n * Must NOT use useId() — the loader rejects re-initialization with different IDs.\n */\nconst GOOGLE_MAPS_LOADER_ID = 'google-maps-script';\n\n/**\n * Fade-in animation duration in milliseconds\n */\nconst FADE_DURATION = 400;\n\n/**\n * Hover animation duration in milliseconds\n */\nconst HOVER_DURATION = 200;\n\n/**\n * Target fill opacity for polygons\n */\nconst TARGET_OPACITY = 0.8;\n\n/**\n * Read a CSS custom property value from the document root\n * @param name - CSS variable name (e.g. '--color-map-highlight')\n * @param fallback - Fallback value when running outside browser\n * @returns Resolved CSS variable value\n */\nconst getCssVar = (name: string, fallback = ''): string => {\n if (typeof document === 'undefined') return fallback;\n return (\n getComputedStyle(document.documentElement).getPropertyValue(name).trim() ||\n fallback\n );\n};\n\n/**\n * Build color classes from CSS variables\n * @returns Color class array for the choropleth map\n */\nconst getColorClasses = (): ColorClass[] => [\n {\n min: 0.75,\n max: 1.01,\n fillColor: getCssVar('--color-map-highlight', '#1C61B2'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Destaque',\n },\n {\n min: 0.5,\n max: 0.75,\n fillColor: getCssVar('--color-map-above-avg', '#2883D7'),\n strokeColor: getCssVar('--color-map-above-avg', '#2883D7'),\n label: 'Acima da média',\n },\n {\n min: 0.25,\n max: 0.5,\n fillColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n strokeColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n label: 'Abaixo da média',\n },\n {\n min: 0,\n max: 0.25,\n fillColor: getCssVar('--color-map-attention', '#E3F1FB'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Ponto de atenção',\n },\n];\n\n/**\n * Get color class based on value\n * @param value - Normalized value between 0 and 1\n * @param colorClasses - Array of color class configurations\n * @returns Color class configuration\n */\nconst getColorClass = (\n value: number,\n colorClasses: ColorClass[]\n): ColorClass => {\n for (const colorClass of colorClasses) {\n if (value >= colorClass.min && value < colorClass.max) {\n return colorClass;\n }\n }\n return colorClasses[0];\n};\n\n/**\n * Compute NRE boundary polygons by merging city polygons that share the same regionName\n * @param data - Array of region data with individual city GeoJSON features\n * @returns Array of GeoJSON features representing NRE boundaries\n */\nconst computeNREBoundaries = (\n data: RegionData[]\n): Feature<Polygon | MultiPolygon>[] => {\n const groups = new Map<string, RegionData[]>();\n data.forEach((region) => {\n const existing = groups.get(region.name) ?? [];\n existing.push(region);\n groups.set(region.name, existing);\n });\n\n const boundaries: Feature<Polygon | MultiPolygon>[] = [];\n groups.forEach((regions) => {\n let merged: Feature<Polygon | MultiPolygon> | null = null;\n for (const region of regions) {\n if (region.geoJson.type !== 'Feature') continue;\n const feature: Feature<Polygon | MultiPolygon> = region.geoJson;\n if (merged) {\n const result: Feature<Polygon | MultiPolygon> | null = union({\n type: 'FeatureCollection' as const,\n features: [merged, feature],\n });\n if (result) merged = result;\n } else {\n merged = feature;\n }\n }\n if (merged) boundaries.push(merged);\n });\n\n return boundaries;\n};\n\n/**\n * Create style function for Data Layer features\n * @param opacity - Current fill opacity\n * @param colorClasses - Array of color class configurations\n * @param strokeCityColor - Stroke color for city borders\n * @returns Style function for map.data.setStyle\n */\nconst createStyleFunction = (\n opacity: number,\n colorClasses: ColorClass[],\n strokeCityColor: string\n) => {\n return (feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n return {\n fillColor: colorClass.fillColor,\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: 0.3,\n cursor: 'pointer',\n };\n };\n};\n\n/**\n * Map container style\n * Note: Google Maps requires explicit pixel dimensions to render properly\n */\nconst containerStyle = {\n width: '100%',\n height: '415px',\n borderRadius: '0.5rem',\n};\n\n/**\n * Default map center (Paraná, Brazil)\n */\nconst defaultCenter = {\n lat: -24.7,\n lng: -51.5,\n};\n\n/**\n * Legend Item component for the map\n * @param color - Fill color of the legend circle\n * @param label - Text label for the legend item\n * @param borderColor - Optional border color for the legend circle\n * @param active - Whether the legend class is active (visible on map)\n * @param onClick - Callback when the legend item is clicked\n */\nconst LegendItem = ({\n color,\n label,\n borderColor,\n active = true,\n onClick,\n}: {\n color: string;\n label: string;\n borderColor?: string;\n active?: boolean;\n onClick?: () => void;\n}) => (\n <Text\n as=\"button\"\n type=\"button\"\n aria-pressed={active}\n className=\"flex items-center gap-2 cursor-pointer transition-opacity duration-200\"\n style={{ opacity: active ? 1 : 0.4 }}\n onClick={onClick}\n >\n <div\n className=\"w-3 h-3 rounded-full\"\n style={{\n backgroundColor: color,\n border: borderColor ? `1px solid ${borderColor}` : 'none',\n }}\n />\n <Text as=\"span\" size=\"sm\" weight=\"medium\" color=\"text-text-600\">\n {label}\n </Text>\n </Text>\n);\n\n/**\n * Loading skeleton component\n */\nconst LoadingSkeleton = () => (\n <div className=\"w-full h-full flex items-center justify-center bg-background-50 rounded-lg animate-pulse\">\n <Text size=\"sm\" color=\"text-text-400\">\n Carregando mapa...\n </Text>\n </div>\n);\n\n/**\n * ChoroplethMap component for displaying regional performance data\n *\n * Displays an interactive Google Map with colored regions based on normalized values.\n * Uses 4 color classes to represent different performance levels.\n * Includes fade-in animation, smooth hover transitions, and zoom-to-region on click.\n * NRE boundaries are rendered as a separate overlay with thicker strokes.\n *\n * @param data - Array of region data with GeoJSON and values\n * @param apiKey - Google Maps API key\n * @param title - Optional title for the map section\n * @param loading - Loading state indicator\n * @param bounds - Map bounds for initial view\n * @param onRegionClick - Callback when a region is clicked\n * @param className - Additional CSS classes\n * @returns Choropleth map component\n *\n * @example\n * ```tsx\n * <ChoroplethMap\n * data={regionData}\n * apiKey=\"your-api-key\"\n * title=\"Performance por região\"\n * loading={false}\n * onRegionClick={(region) => console.log(region)}\n * />\n * ```\n */\nconst ChoroplethMap = ({\n data,\n apiKey,\n title = 'Performance por região',\n loading = false,\n bounds,\n onRegionClick,\n className,\n}: ChoroplethMapProps) => {\n const mapId = GOOGLE_MAPS_LOADER_ID;\n const [map, setMap] = useState<google.maps.Map | null>(null);\n const [hoveredRegion, setHoveredRegion] = useState<RegionData | null>(null);\n const [infoPosition, setInfoPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const [activeClasses, setActiveClasses] = useState<Set<number>>(\n new Set([0, 1, 2, 3])\n );\n const fadeAnimationRef = useRef<number | null>(null);\n const hoverAnimationRef = useRef<number | null>(null);\n const nreBoundaryLayerRef = useRef<google.maps.Data | null>(null);\n const onRegionClickRef = useRef(onRegionClick);\n onRegionClickRef.current = onRegionClick;\n\n const { isDark } = useTheme();\n\n const dataSignature = useMemo(\n () =>\n data\n .map((d) => `${d.id}:${d.value}:${d.name}:${d.accessCount}`)\n .join('|'),\n [data]\n );\n const stableData = useMemo(() => data, [dataSignature]);\n\n const colorClasses = useMemo(() => getColorClasses(), [isDark]);\n\n const mapOptions: google.maps.MapOptions = useMemo(() => {\n const bgColor = getCssVar('--color-background-50', '#F6F6F6');\n return {\n disableDefaultUI: true,\n zoomControl: true,\n scrollwheel: true,\n draggable: true,\n backgroundColor: bgColor,\n styles: [\n {\n stylers: [{ color: bgColor }],\n },\n {\n elementType: 'labels',\n stylers: [{ visibility: 'off' }],\n },\n {\n elementType: 'geometry.stroke',\n stylers: [{ visibility: 'off' }],\n },\n ],\n };\n }, [isDark]);\n\n const { isLoaded, loadError } = useJsApiLoader({\n id: mapId,\n googleMapsApiKey: apiKey,\n });\n\n /**\n * Handle map load event\n */\n const onLoad = useCallback(\n (mapInstance: google.maps.Map) => {\n setMap(mapInstance);\n\n // Fit bounds if provided, then bump zoom to fill the container\n if (bounds) {\n const googleBounds = new google.maps.LatLngBounds(\n { lat: bounds.south, lng: bounds.west },\n { lat: bounds.north, lng: bounds.east }\n );\n mapInstance.fitBounds(googleBounds, 0);\n google.maps.event.addListenerOnce(mapInstance, 'idle', () => {\n const currentZoom = mapInstance.getZoom();\n if (currentZoom) {\n mapInstance.setZoom(currentZoom + 0.8);\n }\n });\n }\n },\n [bounds]\n );\n\n /**\n * Handle map unmount\n */\n const onUnmount = useCallback(() => {\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n }\n setMap(null);\n }, []);\n\n const nreBoundaries = useMemo(\n () => computeNREBoundaries(stableData),\n [stableData]\n );\n\n /**\n * Add GeoJSON data to map with animations\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const strokeCityColor = getCssVar('--color-map-stroke-city', '#64B5F6');\n const strokeNreColor = getCssVar('--color-map-stroke-nre', '#1565C0');\n\n // Clear existing data\n map.data.forEach((feature) => {\n map.data.remove(feature);\n });\n\n // Clear existing NRE boundary layer\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n\n // Add each region's GeoJSON\n stableData.forEach((region) => {\n try {\n const feature = map.data.addGeoJson(region.geoJson);\n // Store region data in the feature\n if (feature && feature.length > 0) {\n feature.forEach((f) => {\n f.setProperty('regionId', region.id);\n f.setProperty('regionName', region.name);\n f.setProperty('regionValue', region.value);\n f.setProperty('regionAccessCount', region.accessCount);\n });\n }\n } catch (error) {\n console.error(\n `Failed to add GeoJSON for region ${region.name}:`,\n error\n );\n }\n });\n\n // Build NRE feature index for O(1) lookup by region name\n const nreFeatureIndex = new Map<string, google.maps.Data.Feature[]>();\n map.data.forEach((f) => {\n const name = f.getProperty('regionName') as string;\n if (name) {\n const list = nreFeatureIndex.get(name) ?? [];\n list.push(f);\n nreFeatureIndex.set(name, list);\n }\n });\n\n // Start with opacity 0 for fade-in animation\n map.data.setStyle(createStyleFunction(0, colorClasses, strokeCityColor));\n\n // Add NRE boundary overlay\n const nreLayer = new google.maps.Data();\n nreBoundaries.forEach((boundary) => {\n nreLayer.addGeoJson(boundary);\n });\n nreLayer.setStyle({\n fillOpacity: 0,\n strokeColor: strokeNreColor,\n strokeWeight: 1.5,\n clickable: false,\n });\n nreLayer.setMap(map);\n nreBoundaryLayerRef.current = nreLayer;\n\n // Animate fade-in from 0 to TARGET_OPACITY\n const startTime = performance.now();\n const animateFadeIn = (currentTime: number) => {\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / FADE_DURATION, 1);\n const currentOpacity = progress * TARGET_OPACITY;\n\n map.data.setStyle(\n createStyleFunction(currentOpacity, colorClasses, strokeCityColor)\n );\n\n if (progress < 1) {\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n } else {\n fadeAnimationRef.current = null;\n }\n };\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n\n /**\n * Apply style overrides to a list of features\n * @param features - Features to style\n * @param opacity - Fill opacity\n * @param weight - Stroke weight\n */\n const applyHoverStyle = (\n features: google.maps.Data.Feature[],\n opacity: number,\n weight: number\n ) => {\n features.forEach((f) => {\n map.data.overrideStyle(f, {\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: weight,\n });\n });\n };\n\n /**\n * Animate hover transition for a group of features\n * @param features - Target features to animate\n * @param from - Starting opacity\n * @param to - Target opacity\n * @param fromWeight - Starting stroke weight\n * @param toWeight - Target stroke weight\n */\n const animateHover = (\n features: google.maps.Data.Feature[],\n from: number,\n to: number,\n fromWeight: number,\n toWeight: number\n ) => {\n if (hoverAnimationRef.current) {\n cancelAnimationFrame(hoverAnimationRef.current);\n }\n const start = performance.now();\n const animate = (now: number) => {\n const progress = Math.min((now - start) / HOVER_DURATION, 1);\n const opacity = from + (to - from) * progress;\n const weight = fromWeight + (toWeight - fromWeight) * progress;\n applyHoverStyle(features, opacity, weight);\n if (progress < 1) {\n hoverAnimationRef.current = requestAnimationFrame(animate);\n } else {\n hoverAnimationRef.current = null;\n }\n };\n hoverAnimationRef.current = requestAnimationFrame(animate);\n };\n\n // Handle hover events - highlight all cities in the same NRE\n let currentNRE: string | null = null;\n let revertTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Collect all features belonging to a given NRE name (O(1) index lookup)\n * @param nreName - NRE region name to match\n * @returns Array of matching features\n */\n const collectNREFeatures = (\n nreName: string\n ): google.maps.Data.Feature[] => {\n return nreFeatureIndex.get(nreName) ?? [];\n };\n\n const mouseoverListener = map.data.addListener(\n 'mouseover',\n (event: google.maps.Data.MouseEvent) => {\n // Cancel pending revert (when moving between cities in same NRE)\n if (revertTimeout) {\n clearTimeout(revertTimeout);\n revertTimeout = null;\n }\n\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n setHoveredRegion(region);\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n\n // Animate highlight for all features in the same NRE group\n if (currentNRE !== regionName) {\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n }\n currentNRE = regionName;\n const nreFeatures = collectNREFeatures(regionName);\n animateHover(nreFeatures, TARGET_OPACITY, 1, 0.5, 1.5);\n }\n }\n );\n\n const mouseoutListener = map.data.addListener('mouseout', () => {\n // Defer revert to avoid flicker when moving between cities in same NRE\n revertTimeout = setTimeout(() => {\n setHoveredRegion(null);\n setInfoPosition(null);\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n currentNRE = null;\n }\n }, 50);\n });\n\n const mousemoveListener = map.data.addListener(\n 'mousemove',\n (event: google.maps.Data.MouseEvent) => {\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n );\n\n /**\n * Compute bounds for all features matching a given NRE name\n * @param nreName - NRE region name to match\n * @returns LatLngBounds encompassing all matching features\n */\n const computeNREBounds = (nreName: string): google.maps.LatLngBounds => {\n const nreBounds = new google.maps.LatLngBounds();\n for (const f of collectNREFeatures(nreName)) {\n f.getGeometry()?.forEachLatLng((latLng) => {\n nreBounds.extend(latLng);\n });\n }\n return nreBounds;\n };\n\n // Handle click events - zoom to NRE region\n const clickListener = map.data.addListener(\n 'click',\n (event: google.maps.Data.MouseEvent) => {\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n onRegionClickRef.current?.(region);\n }\n\n const nreBounds = computeNREBounds(regionName);\n if (!nreBounds.isEmpty()) {\n map.fitBounds(nreBounds, 20);\n }\n }\n );\n\n return () => {\n if (fadeAnimationRef.current)\n cancelAnimationFrame(fadeAnimationRef.current);\n if (hoverAnimationRef.current)\n cancelAnimationFrame(hoverAnimationRef.current);\n if (revertTimeout) clearTimeout(revertTimeout);\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n google.maps.event.removeListener(mouseoverListener);\n google.maps.event.removeListener(mouseoutListener);\n google.maps.event.removeListener(mousemoveListener);\n google.maps.event.removeListener(clickListener);\n };\n }, [map, stableData, colorClasses, nreBoundaries]);\n\n /**\n * Apply visibility filter based on active legend classes\n * and adjust map bounds to fit visible features\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const visibleBounds = new google.maps.LatLngBounds();\n let hasVisibleFeatures = false;\n\n map.data.forEach((feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n const classIndex = colorClasses.indexOf(colorClass);\n\n if (activeClasses.has(classIndex)) {\n map.data.overrideStyle(feature, { visible: true });\n feature.getGeometry()?.forEachLatLng((latLng) => {\n visibleBounds.extend(latLng);\n });\n hasVisibleFeatures = true;\n } else {\n map.data.overrideStyle(feature, { visible: false });\n }\n });\n\n if (hasVisibleFeatures && !visibleBounds.isEmpty()) {\n map.fitBounds(visibleBounds, 20);\n }\n }, [map, stableData, activeClasses, colorClasses]);\n\n /**\n * Toggle a color class on/off in the legend filter\n * @param index - Index of the color class in colorClasses\n */\n const toggleClass = (index: number) => {\n setActiveClasses((prev) => {\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n };\n\n if (loadError) {\n return (\n <div className=\"p-5 bg-background border border-border-50 rounded-xl\">\n <Text color=\"text-error-700\">Erro ao carregar o mapa</Text>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'p-5 bg-background border border-border-50 rounded-xl flex flex-col gap-4',\n className\n )}\n >\n {/* Header */}\n <div className=\"flex flex-col gap-4\">\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n className=\"leading-[21px] tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Legend */}\n <div className=\"flex flex-wrap gap-8\">\n {colorClasses.map((colorClass, index) => (\n <LegendItem\n key={colorClass.label}\n color={colorClass.fillColor}\n borderColor={\n colorClass.fillColor === colorClass.strokeColor\n ? undefined\n : colorClass.strokeColor\n }\n label={colorClass.label}\n active={activeClasses.has(index)}\n onClick={() => toggleClass(index)}\n />\n ))}\n </div>\n </div>\n\n {/* Map Container */}\n <div className=\"bg-background-50 rounded-lg h-[415px] relative overflow-hidden\">\n {loading || !isLoaded ? (\n <LoadingSkeleton />\n ) : (\n <GoogleMap\n mapContainerStyle={containerStyle}\n center={defaultCenter}\n zoom={7}\n onLoad={onLoad}\n onUnmount={onUnmount}\n options={mapOptions}\n />\n )}\n\n {/* Tooltip */}\n {hoveredRegion && infoPosition && (\n <div\n className=\"fixed z-50 bg-background border border-border-50 shadow-lg rounded-lg p-3 pointer-events-none\"\n style={{\n left: Math.min(infoPosition.x + 10, window.innerWidth - 220),\n top: Math.min(infoPosition.y + 10, window.innerHeight - 80),\n }}\n >\n <Text size=\"sm\" weight=\"semibold\">\n {hoveredRegion.name}\n </Text>\n <Text size=\"xs\" color=\"text-text-700\">\n Acessos: {hoveredRegion.accessCount.toLocaleString('pt-BR')}\n </Text>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default ChoroplethMap;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { useEffect } from 'react';\nimport { useThemeStore, ThemeMode } from '../store/themeStore';\n\nexport type { ThemeMode };\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n * Utiliza Zustand para persistir o estado entre múltiplos arquivos e sessões\n */\nexport const useTheme = () => {\n const {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n initializeTheme,\n handleSystemThemeChange,\n } = useThemeStore();\n\n useEffect(() => {\n // Initialize theme on first render\n initializeTheme();\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [initializeTheme, handleSystemThemeChange]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Theme store state interface\n */\nexport interface ThemeState {\n /**\n * Current theme mode\n */\n themeMode: ThemeMode;\n /**\n * Whether the current theme is dark\n */\n isDark: boolean;\n}\n\n/**\n * Theme store actions interface\n */\nexport interface ThemeActions {\n /**\n * Apply theme based on the mode selected\n */\n applyTheme: (mode: ThemeMode) => void;\n /**\n * Toggle between themes\n */\n toggleTheme: () => void;\n /**\n * Set a specific theme mode\n */\n setTheme: (mode: ThemeMode) => void;\n /**\n * Initialize theme on app start\n */\n initializeTheme: () => void;\n /**\n * Handle system theme change\n */\n handleSystemThemeChange: () => void;\n}\n\nexport type ThemeStore = ThemeState & ThemeActions;\n\n/**\n * Apply theme to DOM based on mode\n */\nconst applyThemeToDOM = (mode: ThemeMode): boolean => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n return false;\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n return false;\n }\n }\n return false;\n};\n\n/**\n * Save original theme from white label\n */\nconst saveOriginalTheme = () => {\n const htmlElement = document.documentElement;\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n};\n\n/**\n * Theme store using Zustand with persistence\n */\nexport const useThemeStore = create<ThemeStore>()(\n devtools(\n persist(\n (set, get) => ({\n // Initial state\n themeMode: 'system',\n isDark: false,\n\n // Actions\n applyTheme: (mode: ThemeMode) => {\n const isDark = applyThemeToDOM(mode);\n set({ isDark });\n },\n\n toggleTheme: () => {\n const { themeMode, applyTheme } = get();\n let newMode: ThemeMode;\n\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n\n set({ themeMode: newMode });\n applyTheme(newMode);\n },\n\n setTheme: (mode: ThemeMode) => {\n const { applyTheme } = get();\n set({ themeMode: mode });\n applyTheme(mode);\n },\n\n initializeTheme: () => {\n const { themeMode, applyTheme } = get();\n\n // Save original theme from white label\n saveOriginalTheme();\n\n // Apply the current theme mode\n applyTheme(themeMode);\n },\n\n handleSystemThemeChange: () => {\n const { themeMode, applyTheme } = get();\n // Only respond to system changes when in system mode\n if (themeMode === 'system') {\n applyTheme('system');\n }\n },\n }),\n {\n name: 'theme-store', // Nome da chave no localStorage\n partialize: (state) => ({\n themeMode: state.themeMode,\n }), // Só persiste o themeMode, não o isDark\n }\n ),\n {\n name: 'theme-store',\n }\n )\n);\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["import_react","import_jsx_runtime","union"]}
@@ -1,12 +1,5 @@
1
1
  // src/components/ChoroplethMap/ChoroplethMap.tsx
2
- import {
3
- useCallback,
4
- useEffect as useEffect2,
5
- useId,
6
- useMemo,
7
- useRef,
8
- useState
9
- } from "react";
2
+ import { useCallback, useEffect as useEffect2, useMemo, useRef, useState } from "react";
10
3
  import { GoogleMap, useJsApiLoader } from "@react-google-maps/api";
11
4
  import union from "@turf/union";
12
5
 
@@ -191,6 +184,7 @@ var Text_default = Text;
191
184
 
192
185
  // src/components/ChoroplethMap/ChoroplethMap.tsx
193
186
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
187
+ var GOOGLE_MAPS_LOADER_ID = "google-maps-script";
194
188
  var FADE_DURATION = 400;
195
189
  var HOVER_DURATION = 200;
196
190
  var TARGET_OPACITY = 0.8;
@@ -325,7 +319,7 @@ var ChoroplethMap = ({
325
319
  onRegionClick,
326
320
  className
327
321
  }) => {
328
- const mapId = useId();
322
+ const mapId = GOOGLE_MAPS_LOADER_ID;
329
323
  const [map, setMap] = useState(null);
330
324
  const [hoveredRegion, setHoveredRegion] = useState(null);
331
325
  const [infoPosition, setInfoPosition] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ChoroplethMap/ChoroplethMap.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts","../../src/store/themeStore.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["/* global google */\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { GoogleMap, useJsApiLoader } from '@react-google-maps/api';\nimport union from '@turf/union';\nimport type { Feature, MultiPolygon, Polygon } from 'geojson';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\nimport Text from '../Text/Text';\nimport type {\n ChoroplethMapProps,\n ColorClass,\n RegionData,\n} from './ChoroplethMap.types';\n\n/**\n * Fade-in animation duration in milliseconds\n */\nconst FADE_DURATION = 400;\n\n/**\n * Hover animation duration in milliseconds\n */\nconst HOVER_DURATION = 200;\n\n/**\n * Target fill opacity for polygons\n */\nconst TARGET_OPACITY = 0.8;\n\n/**\n * Read a CSS custom property value from the document root\n * @param name - CSS variable name (e.g. '--color-map-highlight')\n * @param fallback - Fallback value when running outside browser\n * @returns Resolved CSS variable value\n */\nconst getCssVar = (name: string, fallback = ''): string => {\n if (typeof document === 'undefined') return fallback;\n return (\n getComputedStyle(document.documentElement).getPropertyValue(name).trim() ||\n fallback\n );\n};\n\n/**\n * Build color classes from CSS variables\n * @returns Color class array for the choropleth map\n */\nconst getColorClasses = (): ColorClass[] => [\n {\n min: 0.75,\n max: 1.01,\n fillColor: getCssVar('--color-map-highlight', '#1C61B2'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Destaque',\n },\n {\n min: 0.5,\n max: 0.75,\n fillColor: getCssVar('--color-map-above-avg', '#2883D7'),\n strokeColor: getCssVar('--color-map-above-avg', '#2883D7'),\n label: 'Acima da média',\n },\n {\n min: 0.25,\n max: 0.5,\n fillColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n strokeColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n label: 'Abaixo da média',\n },\n {\n min: 0,\n max: 0.25,\n fillColor: getCssVar('--color-map-attention', '#E3F1FB'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Ponto de atenção',\n },\n];\n\n/**\n * Get color class based on value\n * @param value - Normalized value between 0 and 1\n * @param colorClasses - Array of color class configurations\n * @returns Color class configuration\n */\nconst getColorClass = (\n value: number,\n colorClasses: ColorClass[]\n): ColorClass => {\n for (const colorClass of colorClasses) {\n if (value >= colorClass.min && value < colorClass.max) {\n return colorClass;\n }\n }\n return colorClasses[0];\n};\n\n/**\n * Compute NRE boundary polygons by merging city polygons that share the same regionName\n * @param data - Array of region data with individual city GeoJSON features\n * @returns Array of GeoJSON features representing NRE boundaries\n */\nconst computeNREBoundaries = (\n data: RegionData[]\n): Feature<Polygon | MultiPolygon>[] => {\n const groups = new Map<string, RegionData[]>();\n data.forEach((region) => {\n const existing = groups.get(region.name) ?? [];\n existing.push(region);\n groups.set(region.name, existing);\n });\n\n const boundaries: Feature<Polygon | MultiPolygon>[] = [];\n groups.forEach((regions) => {\n let merged: Feature<Polygon | MultiPolygon> | null = null;\n for (const region of regions) {\n if (region.geoJson.type !== 'Feature') continue;\n const feature: Feature<Polygon | MultiPolygon> = region.geoJson;\n if (merged) {\n const result: Feature<Polygon | MultiPolygon> | null = union({\n type: 'FeatureCollection' as const,\n features: [merged, feature],\n });\n if (result) merged = result;\n } else {\n merged = feature;\n }\n }\n if (merged) boundaries.push(merged);\n });\n\n return boundaries;\n};\n\n/**\n * Create style function for Data Layer features\n * @param opacity - Current fill opacity\n * @param colorClasses - Array of color class configurations\n * @param strokeCityColor - Stroke color for city borders\n * @returns Style function for map.data.setStyle\n */\nconst createStyleFunction = (\n opacity: number,\n colorClasses: ColorClass[],\n strokeCityColor: string\n) => {\n return (feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n return {\n fillColor: colorClass.fillColor,\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: 0.3,\n cursor: 'pointer',\n };\n };\n};\n\n/**\n * Map container style\n * Note: Google Maps requires explicit pixel dimensions to render properly\n */\nconst containerStyle = {\n width: '100%',\n height: '415px',\n borderRadius: '0.5rem',\n};\n\n/**\n * Default map center (Paraná, Brazil)\n */\nconst defaultCenter = {\n lat: -24.7,\n lng: -51.5,\n};\n\n/**\n * Legend Item component for the map\n * @param color - Fill color of the legend circle\n * @param label - Text label for the legend item\n * @param borderColor - Optional border color for the legend circle\n * @param active - Whether the legend class is active (visible on map)\n * @param onClick - Callback when the legend item is clicked\n */\nconst LegendItem = ({\n color,\n label,\n borderColor,\n active = true,\n onClick,\n}: {\n color: string;\n label: string;\n borderColor?: string;\n active?: boolean;\n onClick?: () => void;\n}) => (\n <Text\n as=\"button\"\n type=\"button\"\n aria-pressed={active}\n className=\"flex items-center gap-2 cursor-pointer transition-opacity duration-200\"\n style={{ opacity: active ? 1 : 0.4 }}\n onClick={onClick}\n >\n <div\n className=\"w-3 h-3 rounded-full\"\n style={{\n backgroundColor: color,\n border: borderColor ? `1px solid ${borderColor}` : 'none',\n }}\n />\n <Text as=\"span\" size=\"sm\" weight=\"medium\" color=\"text-text-600\">\n {label}\n </Text>\n </Text>\n);\n\n/**\n * Loading skeleton component\n */\nconst LoadingSkeleton = () => (\n <div className=\"w-full h-full flex items-center justify-center bg-background-50 rounded-lg animate-pulse\">\n <Text size=\"sm\" color=\"text-text-400\">\n Carregando mapa...\n </Text>\n </div>\n);\n\n/**\n * ChoroplethMap component for displaying regional performance data\n *\n * Displays an interactive Google Map with colored regions based on normalized values.\n * Uses 4 color classes to represent different performance levels.\n * Includes fade-in animation, smooth hover transitions, and zoom-to-region on click.\n * NRE boundaries are rendered as a separate overlay with thicker strokes.\n *\n * @param data - Array of region data with GeoJSON and values\n * @param apiKey - Google Maps API key\n * @param title - Optional title for the map section\n * @param loading - Loading state indicator\n * @param bounds - Map bounds for initial view\n * @param onRegionClick - Callback when a region is clicked\n * @param className - Additional CSS classes\n * @returns Choropleth map component\n *\n * @example\n * ```tsx\n * <ChoroplethMap\n * data={regionData}\n * apiKey=\"your-api-key\"\n * title=\"Performance por região\"\n * loading={false}\n * onRegionClick={(region) => console.log(region)}\n * />\n * ```\n */\nconst ChoroplethMap = ({\n data,\n apiKey,\n title = 'Performance por região',\n loading = false,\n bounds,\n onRegionClick,\n className,\n}: ChoroplethMapProps) => {\n const mapId = useId();\n const [map, setMap] = useState<google.maps.Map | null>(null);\n const [hoveredRegion, setHoveredRegion] = useState<RegionData | null>(null);\n const [infoPosition, setInfoPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const [activeClasses, setActiveClasses] = useState<Set<number>>(\n new Set([0, 1, 2, 3])\n );\n const fadeAnimationRef = useRef<number | null>(null);\n const hoverAnimationRef = useRef<number | null>(null);\n const nreBoundaryLayerRef = useRef<google.maps.Data | null>(null);\n const onRegionClickRef = useRef(onRegionClick);\n onRegionClickRef.current = onRegionClick;\n\n const { isDark } = useTheme();\n\n const dataSignature = useMemo(\n () =>\n data\n .map((d) => `${d.id}:${d.value}:${d.name}:${d.accessCount}`)\n .join('|'),\n [data]\n );\n const stableData = useMemo(() => data, [dataSignature]);\n\n const colorClasses = useMemo(() => getColorClasses(), [isDark]);\n\n const mapOptions: google.maps.MapOptions = useMemo(() => {\n const bgColor = getCssVar('--color-background-50', '#F6F6F6');\n return {\n disableDefaultUI: true,\n zoomControl: true,\n scrollwheel: true,\n draggable: true,\n backgroundColor: bgColor,\n styles: [\n {\n stylers: [{ color: bgColor }],\n },\n {\n elementType: 'labels',\n stylers: [{ visibility: 'off' }],\n },\n {\n elementType: 'geometry.stroke',\n stylers: [{ visibility: 'off' }],\n },\n ],\n };\n }, [isDark]);\n\n const { isLoaded, loadError } = useJsApiLoader({\n id: mapId,\n googleMapsApiKey: apiKey,\n });\n\n /**\n * Handle map load event\n */\n const onLoad = useCallback(\n (mapInstance: google.maps.Map) => {\n setMap(mapInstance);\n\n // Fit bounds if provided, then bump zoom to fill the container\n if (bounds) {\n const googleBounds = new google.maps.LatLngBounds(\n { lat: bounds.south, lng: bounds.west },\n { lat: bounds.north, lng: bounds.east }\n );\n mapInstance.fitBounds(googleBounds, 0);\n google.maps.event.addListenerOnce(mapInstance, 'idle', () => {\n const currentZoom = mapInstance.getZoom();\n if (currentZoom) {\n mapInstance.setZoom(currentZoom + 0.8);\n }\n });\n }\n },\n [bounds]\n );\n\n /**\n * Handle map unmount\n */\n const onUnmount = useCallback(() => {\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n }\n setMap(null);\n }, []);\n\n const nreBoundaries = useMemo(\n () => computeNREBoundaries(stableData),\n [stableData]\n );\n\n /**\n * Add GeoJSON data to map with animations\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const strokeCityColor = getCssVar('--color-map-stroke-city', '#64B5F6');\n const strokeNreColor = getCssVar('--color-map-stroke-nre', '#1565C0');\n\n // Clear existing data\n map.data.forEach((feature) => {\n map.data.remove(feature);\n });\n\n // Clear existing NRE boundary layer\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n\n // Add each region's GeoJSON\n stableData.forEach((region) => {\n try {\n const feature = map.data.addGeoJson(region.geoJson);\n // Store region data in the feature\n if (feature && feature.length > 0) {\n feature.forEach((f) => {\n f.setProperty('regionId', region.id);\n f.setProperty('regionName', region.name);\n f.setProperty('regionValue', region.value);\n f.setProperty('regionAccessCount', region.accessCount);\n });\n }\n } catch (error) {\n console.error(\n `Failed to add GeoJSON for region ${region.name}:`,\n error\n );\n }\n });\n\n // Build NRE feature index for O(1) lookup by region name\n const nreFeatureIndex = new Map<string, google.maps.Data.Feature[]>();\n map.data.forEach((f) => {\n const name = f.getProperty('regionName') as string;\n if (name) {\n const list = nreFeatureIndex.get(name) ?? [];\n list.push(f);\n nreFeatureIndex.set(name, list);\n }\n });\n\n // Start with opacity 0 for fade-in animation\n map.data.setStyle(createStyleFunction(0, colorClasses, strokeCityColor));\n\n // Add NRE boundary overlay\n const nreLayer = new google.maps.Data();\n nreBoundaries.forEach((boundary) => {\n nreLayer.addGeoJson(boundary);\n });\n nreLayer.setStyle({\n fillOpacity: 0,\n strokeColor: strokeNreColor,\n strokeWeight: 1.5,\n clickable: false,\n });\n nreLayer.setMap(map);\n nreBoundaryLayerRef.current = nreLayer;\n\n // Animate fade-in from 0 to TARGET_OPACITY\n const startTime = performance.now();\n const animateFadeIn = (currentTime: number) => {\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / FADE_DURATION, 1);\n const currentOpacity = progress * TARGET_OPACITY;\n\n map.data.setStyle(\n createStyleFunction(currentOpacity, colorClasses, strokeCityColor)\n );\n\n if (progress < 1) {\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n } else {\n fadeAnimationRef.current = null;\n }\n };\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n\n /**\n * Apply style overrides to a list of features\n * @param features - Features to style\n * @param opacity - Fill opacity\n * @param weight - Stroke weight\n */\n const applyHoverStyle = (\n features: google.maps.Data.Feature[],\n opacity: number,\n weight: number\n ) => {\n features.forEach((f) => {\n map.data.overrideStyle(f, {\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: weight,\n });\n });\n };\n\n /**\n * Animate hover transition for a group of features\n * @param features - Target features to animate\n * @param from - Starting opacity\n * @param to - Target opacity\n * @param fromWeight - Starting stroke weight\n * @param toWeight - Target stroke weight\n */\n const animateHover = (\n features: google.maps.Data.Feature[],\n from: number,\n to: number,\n fromWeight: number,\n toWeight: number\n ) => {\n if (hoverAnimationRef.current) {\n cancelAnimationFrame(hoverAnimationRef.current);\n }\n const start = performance.now();\n const animate = (now: number) => {\n const progress = Math.min((now - start) / HOVER_DURATION, 1);\n const opacity = from + (to - from) * progress;\n const weight = fromWeight + (toWeight - fromWeight) * progress;\n applyHoverStyle(features, opacity, weight);\n if (progress < 1) {\n hoverAnimationRef.current = requestAnimationFrame(animate);\n } else {\n hoverAnimationRef.current = null;\n }\n };\n hoverAnimationRef.current = requestAnimationFrame(animate);\n };\n\n // Handle hover events - highlight all cities in the same NRE\n let currentNRE: string | null = null;\n let revertTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Collect all features belonging to a given NRE name (O(1) index lookup)\n * @param nreName - NRE region name to match\n * @returns Array of matching features\n */\n const collectNREFeatures = (\n nreName: string\n ): google.maps.Data.Feature[] => {\n return nreFeatureIndex.get(nreName) ?? [];\n };\n\n const mouseoverListener = map.data.addListener(\n 'mouseover',\n (event: google.maps.Data.MouseEvent) => {\n // Cancel pending revert (when moving between cities in same NRE)\n if (revertTimeout) {\n clearTimeout(revertTimeout);\n revertTimeout = null;\n }\n\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n setHoveredRegion(region);\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n\n // Animate highlight for all features in the same NRE group\n if (currentNRE !== regionName) {\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n }\n currentNRE = regionName;\n const nreFeatures = collectNREFeatures(regionName);\n animateHover(nreFeatures, TARGET_OPACITY, 1, 0.5, 1.5);\n }\n }\n );\n\n const mouseoutListener = map.data.addListener('mouseout', () => {\n // Defer revert to avoid flicker when moving between cities in same NRE\n revertTimeout = setTimeout(() => {\n setHoveredRegion(null);\n setInfoPosition(null);\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n currentNRE = null;\n }\n }, 50);\n });\n\n const mousemoveListener = map.data.addListener(\n 'mousemove',\n (event: google.maps.Data.MouseEvent) => {\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n );\n\n /**\n * Compute bounds for all features matching a given NRE name\n * @param nreName - NRE region name to match\n * @returns LatLngBounds encompassing all matching features\n */\n const computeNREBounds = (nreName: string): google.maps.LatLngBounds => {\n const nreBounds = new google.maps.LatLngBounds();\n for (const f of collectNREFeatures(nreName)) {\n f.getGeometry()?.forEachLatLng((latLng) => {\n nreBounds.extend(latLng);\n });\n }\n return nreBounds;\n };\n\n // Handle click events - zoom to NRE region\n const clickListener = map.data.addListener(\n 'click',\n (event: google.maps.Data.MouseEvent) => {\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n onRegionClickRef.current?.(region);\n }\n\n const nreBounds = computeNREBounds(regionName);\n if (!nreBounds.isEmpty()) {\n map.fitBounds(nreBounds, 20);\n }\n }\n );\n\n return () => {\n if (fadeAnimationRef.current)\n cancelAnimationFrame(fadeAnimationRef.current);\n if (hoverAnimationRef.current)\n cancelAnimationFrame(hoverAnimationRef.current);\n if (revertTimeout) clearTimeout(revertTimeout);\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n google.maps.event.removeListener(mouseoverListener);\n google.maps.event.removeListener(mouseoutListener);\n google.maps.event.removeListener(mousemoveListener);\n google.maps.event.removeListener(clickListener);\n };\n }, [map, stableData, colorClasses, nreBoundaries]);\n\n /**\n * Apply visibility filter based on active legend classes\n * and adjust map bounds to fit visible features\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const visibleBounds = new google.maps.LatLngBounds();\n let hasVisibleFeatures = false;\n\n map.data.forEach((feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n const classIndex = colorClasses.indexOf(colorClass);\n\n if (activeClasses.has(classIndex)) {\n map.data.overrideStyle(feature, { visible: true });\n feature.getGeometry()?.forEachLatLng((latLng) => {\n visibleBounds.extend(latLng);\n });\n hasVisibleFeatures = true;\n } else {\n map.data.overrideStyle(feature, { visible: false });\n }\n });\n\n if (hasVisibleFeatures && !visibleBounds.isEmpty()) {\n map.fitBounds(visibleBounds, 20);\n }\n }, [map, stableData, activeClasses, colorClasses]);\n\n /**\n * Toggle a color class on/off in the legend filter\n * @param index - Index of the color class in colorClasses\n */\n const toggleClass = (index: number) => {\n setActiveClasses((prev) => {\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n };\n\n if (loadError) {\n return (\n <div className=\"p-5 bg-background border border-border-50 rounded-xl\">\n <Text color=\"text-error-700\">Erro ao carregar o mapa</Text>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'p-5 bg-background border border-border-50 rounded-xl flex flex-col gap-4',\n className\n )}\n >\n {/* Header */}\n <div className=\"flex flex-col gap-4\">\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n className=\"leading-[21px] tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Legend */}\n <div className=\"flex flex-wrap gap-8\">\n {colorClasses.map((colorClass, index) => (\n <LegendItem\n key={colorClass.label}\n color={colorClass.fillColor}\n borderColor={\n colorClass.fillColor === colorClass.strokeColor\n ? undefined\n : colorClass.strokeColor\n }\n label={colorClass.label}\n active={activeClasses.has(index)}\n onClick={() => toggleClass(index)}\n />\n ))}\n </div>\n </div>\n\n {/* Map Container */}\n <div className=\"bg-background-50 rounded-lg h-[415px] relative overflow-hidden\">\n {loading || !isLoaded ? (\n <LoadingSkeleton />\n ) : (\n <GoogleMap\n mapContainerStyle={containerStyle}\n center={defaultCenter}\n zoom={7}\n onLoad={onLoad}\n onUnmount={onUnmount}\n options={mapOptions}\n />\n )}\n\n {/* Tooltip */}\n {hoveredRegion && infoPosition && (\n <div\n className=\"fixed z-50 bg-background border border-border-50 shadow-lg rounded-lg p-3 pointer-events-none\"\n style={{\n left: Math.min(infoPosition.x + 10, window.innerWidth - 220),\n top: Math.min(infoPosition.y + 10, window.innerHeight - 80),\n }}\n >\n <Text size=\"sm\" weight=\"semibold\">\n {hoveredRegion.name}\n </Text>\n <Text size=\"xs\" color=\"text-text-700\">\n Acessos: {hoveredRegion.accessCount.toLocaleString('pt-BR')}\n </Text>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default ChoroplethMap;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { useEffect } from 'react';\nimport { useThemeStore, ThemeMode } from '../store/themeStore';\n\nexport type { ThemeMode };\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n * Utiliza Zustand para persistir o estado entre múltiplos arquivos e sessões\n */\nexport const useTheme = () => {\n const {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n initializeTheme,\n handleSystemThemeChange,\n } = useThemeStore();\n\n useEffect(() => {\n // Initialize theme on first render\n initializeTheme();\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [initializeTheme, handleSystemThemeChange]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Theme store state interface\n */\nexport interface ThemeState {\n /**\n * Current theme mode\n */\n themeMode: ThemeMode;\n /**\n * Whether the current theme is dark\n */\n isDark: boolean;\n}\n\n/**\n * Theme store actions interface\n */\nexport interface ThemeActions {\n /**\n * Apply theme based on the mode selected\n */\n applyTheme: (mode: ThemeMode) => void;\n /**\n * Toggle between themes\n */\n toggleTheme: () => void;\n /**\n * Set a specific theme mode\n */\n setTheme: (mode: ThemeMode) => void;\n /**\n * Initialize theme on app start\n */\n initializeTheme: () => void;\n /**\n * Handle system theme change\n */\n handleSystemThemeChange: () => void;\n}\n\nexport type ThemeStore = ThemeState & ThemeActions;\n\n/**\n * Apply theme to DOM based on mode\n */\nconst applyThemeToDOM = (mode: ThemeMode): boolean => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n return false;\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n return false;\n }\n }\n return false;\n};\n\n/**\n * Save original theme from white label\n */\nconst saveOriginalTheme = () => {\n const htmlElement = document.documentElement;\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n};\n\n/**\n * Theme store using Zustand with persistence\n */\nexport const useThemeStore = create<ThemeStore>()(\n devtools(\n persist(\n (set, get) => ({\n // Initial state\n themeMode: 'system',\n isDark: false,\n\n // Actions\n applyTheme: (mode: ThemeMode) => {\n const isDark = applyThemeToDOM(mode);\n set({ isDark });\n },\n\n toggleTheme: () => {\n const { themeMode, applyTheme } = get();\n let newMode: ThemeMode;\n\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n\n set({ themeMode: newMode });\n applyTheme(newMode);\n },\n\n setTheme: (mode: ThemeMode) => {\n const { applyTheme } = get();\n set({ themeMode: mode });\n applyTheme(mode);\n },\n\n initializeTheme: () => {\n const { themeMode, applyTheme } = get();\n\n // Save original theme from white label\n saveOriginalTheme();\n\n // Apply the current theme mode\n applyTheme(themeMode);\n },\n\n handleSystemThemeChange: () => {\n const { themeMode, applyTheme } = get();\n // Only respond to system changes when in system mode\n if (themeMode === 'system') {\n applyTheme('system');\n }\n },\n }),\n {\n name: 'theme-store', // Nome da chave no localStorage\n partialize: (state) => ({\n themeMode: state.themeMode,\n }), // Só persiste o themeMode, não o isDark\n }\n ),\n {\n name: 'theme-store',\n }\n )\n);\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["useEffect","jsx","useEffect"]}
1
+ {"version":3,"sources":["../../src/components/ChoroplethMap/ChoroplethMap.tsx","../../src/utils/utils.ts","../../src/hooks/useTheme.ts","../../src/store/themeStore.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["/* global google */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { GoogleMap, useJsApiLoader } from '@react-google-maps/api';\nimport union from '@turf/union';\nimport type { Feature, MultiPolygon, Polygon } from 'geojson';\nimport { cn } from '../../utils/utils';\nimport { useTheme } from '../../hooks/useTheme';\nimport Text from '../Text/Text';\nimport type {\n ChoroplethMapProps,\n ColorClass,\n RegionData,\n} from './ChoroplethMap.types';\n\n/**\n * Stable ID for the Google Maps script loader singleton.\n * Must NOT use useId() — the loader rejects re-initialization with different IDs.\n */\nconst GOOGLE_MAPS_LOADER_ID = 'google-maps-script';\n\n/**\n * Fade-in animation duration in milliseconds\n */\nconst FADE_DURATION = 400;\n\n/**\n * Hover animation duration in milliseconds\n */\nconst HOVER_DURATION = 200;\n\n/**\n * Target fill opacity for polygons\n */\nconst TARGET_OPACITY = 0.8;\n\n/**\n * Read a CSS custom property value from the document root\n * @param name - CSS variable name (e.g. '--color-map-highlight')\n * @param fallback - Fallback value when running outside browser\n * @returns Resolved CSS variable value\n */\nconst getCssVar = (name: string, fallback = ''): string => {\n if (typeof document === 'undefined') return fallback;\n return (\n getComputedStyle(document.documentElement).getPropertyValue(name).trim() ||\n fallback\n );\n};\n\n/**\n * Build color classes from CSS variables\n * @returns Color class array for the choropleth map\n */\nconst getColorClasses = (): ColorClass[] => [\n {\n min: 0.75,\n max: 1.01,\n fillColor: getCssVar('--color-map-highlight', '#1C61B2'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Destaque',\n },\n {\n min: 0.5,\n max: 0.75,\n fillColor: getCssVar('--color-map-above-avg', '#2883D7'),\n strokeColor: getCssVar('--color-map-above-avg', '#2883D7'),\n label: 'Acima da média',\n },\n {\n min: 0.25,\n max: 0.5,\n fillColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n strokeColor: getCssVar('--color-map-below-avg', '#91C7F1'),\n label: 'Abaixo da média',\n },\n {\n min: 0,\n max: 0.25,\n fillColor: getCssVar('--color-map-attention', '#E3F1FB'),\n strokeColor: getCssVar('--color-map-highlight', '#1C61B2'),\n label: 'Ponto de atenção',\n },\n];\n\n/**\n * Get color class based on value\n * @param value - Normalized value between 0 and 1\n * @param colorClasses - Array of color class configurations\n * @returns Color class configuration\n */\nconst getColorClass = (\n value: number,\n colorClasses: ColorClass[]\n): ColorClass => {\n for (const colorClass of colorClasses) {\n if (value >= colorClass.min && value < colorClass.max) {\n return colorClass;\n }\n }\n return colorClasses[0];\n};\n\n/**\n * Compute NRE boundary polygons by merging city polygons that share the same regionName\n * @param data - Array of region data with individual city GeoJSON features\n * @returns Array of GeoJSON features representing NRE boundaries\n */\nconst computeNREBoundaries = (\n data: RegionData[]\n): Feature<Polygon | MultiPolygon>[] => {\n const groups = new Map<string, RegionData[]>();\n data.forEach((region) => {\n const existing = groups.get(region.name) ?? [];\n existing.push(region);\n groups.set(region.name, existing);\n });\n\n const boundaries: Feature<Polygon | MultiPolygon>[] = [];\n groups.forEach((regions) => {\n let merged: Feature<Polygon | MultiPolygon> | null = null;\n for (const region of regions) {\n if (region.geoJson.type !== 'Feature') continue;\n const feature: Feature<Polygon | MultiPolygon> = region.geoJson;\n if (merged) {\n const result: Feature<Polygon | MultiPolygon> | null = union({\n type: 'FeatureCollection' as const,\n features: [merged, feature],\n });\n if (result) merged = result;\n } else {\n merged = feature;\n }\n }\n if (merged) boundaries.push(merged);\n });\n\n return boundaries;\n};\n\n/**\n * Create style function for Data Layer features\n * @param opacity - Current fill opacity\n * @param colorClasses - Array of color class configurations\n * @param strokeCityColor - Stroke color for city borders\n * @returns Style function for map.data.setStyle\n */\nconst createStyleFunction = (\n opacity: number,\n colorClasses: ColorClass[],\n strokeCityColor: string\n) => {\n return (feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n return {\n fillColor: colorClass.fillColor,\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: 0.3,\n cursor: 'pointer',\n };\n };\n};\n\n/**\n * Map container style\n * Note: Google Maps requires explicit pixel dimensions to render properly\n */\nconst containerStyle = {\n width: '100%',\n height: '415px',\n borderRadius: '0.5rem',\n};\n\n/**\n * Default map center (Paraná, Brazil)\n */\nconst defaultCenter = {\n lat: -24.7,\n lng: -51.5,\n};\n\n/**\n * Legend Item component for the map\n * @param color - Fill color of the legend circle\n * @param label - Text label for the legend item\n * @param borderColor - Optional border color for the legend circle\n * @param active - Whether the legend class is active (visible on map)\n * @param onClick - Callback when the legend item is clicked\n */\nconst LegendItem = ({\n color,\n label,\n borderColor,\n active = true,\n onClick,\n}: {\n color: string;\n label: string;\n borderColor?: string;\n active?: boolean;\n onClick?: () => void;\n}) => (\n <Text\n as=\"button\"\n type=\"button\"\n aria-pressed={active}\n className=\"flex items-center gap-2 cursor-pointer transition-opacity duration-200\"\n style={{ opacity: active ? 1 : 0.4 }}\n onClick={onClick}\n >\n <div\n className=\"w-3 h-3 rounded-full\"\n style={{\n backgroundColor: color,\n border: borderColor ? `1px solid ${borderColor}` : 'none',\n }}\n />\n <Text as=\"span\" size=\"sm\" weight=\"medium\" color=\"text-text-600\">\n {label}\n </Text>\n </Text>\n);\n\n/**\n * Loading skeleton component\n */\nconst LoadingSkeleton = () => (\n <div className=\"w-full h-full flex items-center justify-center bg-background-50 rounded-lg animate-pulse\">\n <Text size=\"sm\" color=\"text-text-400\">\n Carregando mapa...\n </Text>\n </div>\n);\n\n/**\n * ChoroplethMap component for displaying regional performance data\n *\n * Displays an interactive Google Map with colored regions based on normalized values.\n * Uses 4 color classes to represent different performance levels.\n * Includes fade-in animation, smooth hover transitions, and zoom-to-region on click.\n * NRE boundaries are rendered as a separate overlay with thicker strokes.\n *\n * @param data - Array of region data with GeoJSON and values\n * @param apiKey - Google Maps API key\n * @param title - Optional title for the map section\n * @param loading - Loading state indicator\n * @param bounds - Map bounds for initial view\n * @param onRegionClick - Callback when a region is clicked\n * @param className - Additional CSS classes\n * @returns Choropleth map component\n *\n * @example\n * ```tsx\n * <ChoroplethMap\n * data={regionData}\n * apiKey=\"your-api-key\"\n * title=\"Performance por região\"\n * loading={false}\n * onRegionClick={(region) => console.log(region)}\n * />\n * ```\n */\nconst ChoroplethMap = ({\n data,\n apiKey,\n title = 'Performance por região',\n loading = false,\n bounds,\n onRegionClick,\n className,\n}: ChoroplethMapProps) => {\n const mapId = GOOGLE_MAPS_LOADER_ID;\n const [map, setMap] = useState<google.maps.Map | null>(null);\n const [hoveredRegion, setHoveredRegion] = useState<RegionData | null>(null);\n const [infoPosition, setInfoPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const [activeClasses, setActiveClasses] = useState<Set<number>>(\n new Set([0, 1, 2, 3])\n );\n const fadeAnimationRef = useRef<number | null>(null);\n const hoverAnimationRef = useRef<number | null>(null);\n const nreBoundaryLayerRef = useRef<google.maps.Data | null>(null);\n const onRegionClickRef = useRef(onRegionClick);\n onRegionClickRef.current = onRegionClick;\n\n const { isDark } = useTheme();\n\n const dataSignature = useMemo(\n () =>\n data\n .map((d) => `${d.id}:${d.value}:${d.name}:${d.accessCount}`)\n .join('|'),\n [data]\n );\n const stableData = useMemo(() => data, [dataSignature]);\n\n const colorClasses = useMemo(() => getColorClasses(), [isDark]);\n\n const mapOptions: google.maps.MapOptions = useMemo(() => {\n const bgColor = getCssVar('--color-background-50', '#F6F6F6');\n return {\n disableDefaultUI: true,\n zoomControl: true,\n scrollwheel: true,\n draggable: true,\n backgroundColor: bgColor,\n styles: [\n {\n stylers: [{ color: bgColor }],\n },\n {\n elementType: 'labels',\n stylers: [{ visibility: 'off' }],\n },\n {\n elementType: 'geometry.stroke',\n stylers: [{ visibility: 'off' }],\n },\n ],\n };\n }, [isDark]);\n\n const { isLoaded, loadError } = useJsApiLoader({\n id: mapId,\n googleMapsApiKey: apiKey,\n });\n\n /**\n * Handle map load event\n */\n const onLoad = useCallback(\n (mapInstance: google.maps.Map) => {\n setMap(mapInstance);\n\n // Fit bounds if provided, then bump zoom to fill the container\n if (bounds) {\n const googleBounds = new google.maps.LatLngBounds(\n { lat: bounds.south, lng: bounds.west },\n { lat: bounds.north, lng: bounds.east }\n );\n mapInstance.fitBounds(googleBounds, 0);\n google.maps.event.addListenerOnce(mapInstance, 'idle', () => {\n const currentZoom = mapInstance.getZoom();\n if (currentZoom) {\n mapInstance.setZoom(currentZoom + 0.8);\n }\n });\n }\n },\n [bounds]\n );\n\n /**\n * Handle map unmount\n */\n const onUnmount = useCallback(() => {\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n }\n setMap(null);\n }, []);\n\n const nreBoundaries = useMemo(\n () => computeNREBoundaries(stableData),\n [stableData]\n );\n\n /**\n * Add GeoJSON data to map with animations\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const strokeCityColor = getCssVar('--color-map-stroke-city', '#64B5F6');\n const strokeNreColor = getCssVar('--color-map-stroke-nre', '#1565C0');\n\n // Clear existing data\n map.data.forEach((feature) => {\n map.data.remove(feature);\n });\n\n // Clear existing NRE boundary layer\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n\n // Add each region's GeoJSON\n stableData.forEach((region) => {\n try {\n const feature = map.data.addGeoJson(region.geoJson);\n // Store region data in the feature\n if (feature && feature.length > 0) {\n feature.forEach((f) => {\n f.setProperty('regionId', region.id);\n f.setProperty('regionName', region.name);\n f.setProperty('regionValue', region.value);\n f.setProperty('regionAccessCount', region.accessCount);\n });\n }\n } catch (error) {\n console.error(\n `Failed to add GeoJSON for region ${region.name}:`,\n error\n );\n }\n });\n\n // Build NRE feature index for O(1) lookup by region name\n const nreFeatureIndex = new Map<string, google.maps.Data.Feature[]>();\n map.data.forEach((f) => {\n const name = f.getProperty('regionName') as string;\n if (name) {\n const list = nreFeatureIndex.get(name) ?? [];\n list.push(f);\n nreFeatureIndex.set(name, list);\n }\n });\n\n // Start with opacity 0 for fade-in animation\n map.data.setStyle(createStyleFunction(0, colorClasses, strokeCityColor));\n\n // Add NRE boundary overlay\n const nreLayer = new google.maps.Data();\n nreBoundaries.forEach((boundary) => {\n nreLayer.addGeoJson(boundary);\n });\n nreLayer.setStyle({\n fillOpacity: 0,\n strokeColor: strokeNreColor,\n strokeWeight: 1.5,\n clickable: false,\n });\n nreLayer.setMap(map);\n nreBoundaryLayerRef.current = nreLayer;\n\n // Animate fade-in from 0 to TARGET_OPACITY\n const startTime = performance.now();\n const animateFadeIn = (currentTime: number) => {\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / FADE_DURATION, 1);\n const currentOpacity = progress * TARGET_OPACITY;\n\n map.data.setStyle(\n createStyleFunction(currentOpacity, colorClasses, strokeCityColor)\n );\n\n if (progress < 1) {\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n } else {\n fadeAnimationRef.current = null;\n }\n };\n fadeAnimationRef.current = requestAnimationFrame(animateFadeIn);\n\n /**\n * Apply style overrides to a list of features\n * @param features - Features to style\n * @param opacity - Fill opacity\n * @param weight - Stroke weight\n */\n const applyHoverStyle = (\n features: google.maps.Data.Feature[],\n opacity: number,\n weight: number\n ) => {\n features.forEach((f) => {\n map.data.overrideStyle(f, {\n fillOpacity: opacity,\n strokeColor: strokeCityColor,\n strokeWeight: weight,\n });\n });\n };\n\n /**\n * Animate hover transition for a group of features\n * @param features - Target features to animate\n * @param from - Starting opacity\n * @param to - Target opacity\n * @param fromWeight - Starting stroke weight\n * @param toWeight - Target stroke weight\n */\n const animateHover = (\n features: google.maps.Data.Feature[],\n from: number,\n to: number,\n fromWeight: number,\n toWeight: number\n ) => {\n if (hoverAnimationRef.current) {\n cancelAnimationFrame(hoverAnimationRef.current);\n }\n const start = performance.now();\n const animate = (now: number) => {\n const progress = Math.min((now - start) / HOVER_DURATION, 1);\n const opacity = from + (to - from) * progress;\n const weight = fromWeight + (toWeight - fromWeight) * progress;\n applyHoverStyle(features, opacity, weight);\n if (progress < 1) {\n hoverAnimationRef.current = requestAnimationFrame(animate);\n } else {\n hoverAnimationRef.current = null;\n }\n };\n hoverAnimationRef.current = requestAnimationFrame(animate);\n };\n\n // Handle hover events - highlight all cities in the same NRE\n let currentNRE: string | null = null;\n let revertTimeout: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Collect all features belonging to a given NRE name (O(1) index lookup)\n * @param nreName - NRE region name to match\n * @returns Array of matching features\n */\n const collectNREFeatures = (\n nreName: string\n ): google.maps.Data.Feature[] => {\n return nreFeatureIndex.get(nreName) ?? [];\n };\n\n const mouseoverListener = map.data.addListener(\n 'mouseover',\n (event: google.maps.Data.MouseEvent) => {\n // Cancel pending revert (when moving between cities in same NRE)\n if (revertTimeout) {\n clearTimeout(revertTimeout);\n revertTimeout = null;\n }\n\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n setHoveredRegion(region);\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n\n // Animate highlight for all features in the same NRE group\n if (currentNRE !== regionName) {\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n }\n currentNRE = regionName;\n const nreFeatures = collectNREFeatures(regionName);\n animateHover(nreFeatures, TARGET_OPACITY, 1, 0.5, 1.5);\n }\n }\n );\n\n const mouseoutListener = map.data.addListener('mouseout', () => {\n // Defer revert to avoid flicker when moving between cities in same NRE\n revertTimeout = setTimeout(() => {\n setHoveredRegion(null);\n setInfoPosition(null);\n if (currentNRE) {\n const prevFeatures = collectNREFeatures(currentNRE);\n animateHover(prevFeatures, 1, TARGET_OPACITY, 1.5, 0.5);\n currentNRE = null;\n }\n }, 50);\n });\n\n const mousemoveListener = map.data.addListener(\n 'mousemove',\n (event: google.maps.Data.MouseEvent) => {\n if (event.domEvent) {\n setInfoPosition({\n x: (event.domEvent as MouseEvent).clientX,\n y: (event.domEvent as MouseEvent).clientY,\n });\n }\n }\n );\n\n /**\n * Compute bounds for all features matching a given NRE name\n * @param nreName - NRE region name to match\n * @returns LatLngBounds encompassing all matching features\n */\n const computeNREBounds = (nreName: string): google.maps.LatLngBounds => {\n const nreBounds = new google.maps.LatLngBounds();\n for (const f of collectNREFeatures(nreName)) {\n f.getGeometry()?.forEachLatLng((latLng) => {\n nreBounds.extend(latLng);\n });\n }\n return nreBounds;\n };\n\n // Handle click events - zoom to NRE region\n const clickListener = map.data.addListener(\n 'click',\n (event: google.maps.Data.MouseEvent) => {\n const regionId = event.feature.getProperty('regionId') as string;\n const regionName = event.feature.getProperty('regionName') as string;\n const region = stableData.find((r) => r.id === regionId);\n if (region) {\n onRegionClickRef.current?.(region);\n }\n\n const nreBounds = computeNREBounds(regionName);\n if (!nreBounds.isEmpty()) {\n map.fitBounds(nreBounds, 20);\n }\n }\n );\n\n return () => {\n if (fadeAnimationRef.current)\n cancelAnimationFrame(fadeAnimationRef.current);\n if (hoverAnimationRef.current)\n cancelAnimationFrame(hoverAnimationRef.current);\n if (revertTimeout) clearTimeout(revertTimeout);\n if (nreBoundaryLayerRef.current) {\n nreBoundaryLayerRef.current.setMap(null);\n nreBoundaryLayerRef.current = null;\n }\n google.maps.event.removeListener(mouseoverListener);\n google.maps.event.removeListener(mouseoutListener);\n google.maps.event.removeListener(mousemoveListener);\n google.maps.event.removeListener(clickListener);\n };\n }, [map, stableData, colorClasses, nreBoundaries]);\n\n /**\n * Apply visibility filter based on active legend classes\n * and adjust map bounds to fit visible features\n */\n useEffect(() => {\n if (!map || !stableData.length) return;\n\n const visibleBounds = new google.maps.LatLngBounds();\n let hasVisibleFeatures = false;\n\n map.data.forEach((feature: google.maps.Data.Feature) => {\n const value = feature.getProperty('regionValue') as number;\n const colorClass = getColorClass(value ?? 0, colorClasses);\n const classIndex = colorClasses.indexOf(colorClass);\n\n if (activeClasses.has(classIndex)) {\n map.data.overrideStyle(feature, { visible: true });\n feature.getGeometry()?.forEachLatLng((latLng) => {\n visibleBounds.extend(latLng);\n });\n hasVisibleFeatures = true;\n } else {\n map.data.overrideStyle(feature, { visible: false });\n }\n });\n\n if (hasVisibleFeatures && !visibleBounds.isEmpty()) {\n map.fitBounds(visibleBounds, 20);\n }\n }, [map, stableData, activeClasses, colorClasses]);\n\n /**\n * Toggle a color class on/off in the legend filter\n * @param index - Index of the color class in colorClasses\n */\n const toggleClass = (index: number) => {\n setActiveClasses((prev) => {\n const next = new Set(prev);\n if (next.has(index)) {\n next.delete(index);\n } else {\n next.add(index);\n }\n return next;\n });\n };\n\n if (loadError) {\n return (\n <div className=\"p-5 bg-background border border-border-50 rounded-xl\">\n <Text color=\"text-error-700\">Erro ao carregar o mapa</Text>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n 'p-5 bg-background border border-border-50 rounded-xl flex flex-col gap-4',\n className\n )}\n >\n {/* Header */}\n <div className=\"flex flex-col gap-4\">\n <Text\n as=\"h2\"\n size=\"lg\"\n weight=\"bold\"\n className=\"leading-[21px] tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Legend */}\n <div className=\"flex flex-wrap gap-8\">\n {colorClasses.map((colorClass, index) => (\n <LegendItem\n key={colorClass.label}\n color={colorClass.fillColor}\n borderColor={\n colorClass.fillColor === colorClass.strokeColor\n ? undefined\n : colorClass.strokeColor\n }\n label={colorClass.label}\n active={activeClasses.has(index)}\n onClick={() => toggleClass(index)}\n />\n ))}\n </div>\n </div>\n\n {/* Map Container */}\n <div className=\"bg-background-50 rounded-lg h-[415px] relative overflow-hidden\">\n {loading || !isLoaded ? (\n <LoadingSkeleton />\n ) : (\n <GoogleMap\n mapContainerStyle={containerStyle}\n center={defaultCenter}\n zoom={7}\n onLoad={onLoad}\n onUnmount={onUnmount}\n options={mapOptions}\n />\n )}\n\n {/* Tooltip */}\n {hoveredRegion && infoPosition && (\n <div\n className=\"fixed z-50 bg-background border border-border-50 shadow-lg rounded-lg p-3 pointer-events-none\"\n style={{\n left: Math.min(infoPosition.x + 10, window.innerWidth - 220),\n top: Math.min(infoPosition.y + 10, window.innerHeight - 80),\n }}\n >\n <Text size=\"sm\" weight=\"semibold\">\n {hoveredRegion.name}\n </Text>\n <Text size=\"xs\" color=\"text-text-700\">\n Acessos: {hoveredRegion.accessCount.toLocaleString('pt-BR')}\n </Text>\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default ChoroplethMap;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { useEffect } from 'react';\nimport { useThemeStore, ThemeMode } from '../store/themeStore';\n\nexport type { ThemeMode };\n\n/**\n * Hook para gerenciar temas com suporte a alternância manual e detecção automática do sistema\n * Este hook permite alternar entre temas light, dark e automático baseado nas preferências do sistema\n * Utiliza Zustand para persistir o estado entre múltiplos arquivos e sessões\n */\nexport const useTheme = () => {\n const {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n initializeTheme,\n handleSystemThemeChange,\n } = useThemeStore();\n\n useEffect(() => {\n // Initialize theme on first render\n initializeTheme();\n\n // Listener para mudanças nas preferências do sistema (apenas quando mode é 'system')\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n mediaQuery.addEventListener('change', handleSystemThemeChange);\n\n return () => {\n mediaQuery.removeEventListener('change', handleSystemThemeChange);\n };\n }, [initializeTheme, handleSystemThemeChange]);\n\n return {\n themeMode,\n isDark,\n toggleTheme,\n setTheme,\n };\n};\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\n/**\n * Theme store state interface\n */\nexport interface ThemeState {\n /**\n * Current theme mode\n */\n themeMode: ThemeMode;\n /**\n * Whether the current theme is dark\n */\n isDark: boolean;\n}\n\n/**\n * Theme store actions interface\n */\nexport interface ThemeActions {\n /**\n * Apply theme based on the mode selected\n */\n applyTheme: (mode: ThemeMode) => void;\n /**\n * Toggle between themes\n */\n toggleTheme: () => void;\n /**\n * Set a specific theme mode\n */\n setTheme: (mode: ThemeMode) => void;\n /**\n * Initialize theme on app start\n */\n initializeTheme: () => void;\n /**\n * Handle system theme change\n */\n handleSystemThemeChange: () => void;\n}\n\nexport type ThemeStore = ThemeState & ThemeActions;\n\n/**\n * Apply theme to DOM based on mode\n */\nconst applyThemeToDOM = (mode: ThemeMode): boolean => {\n const htmlElement = document.documentElement;\n const originalTheme = htmlElement.getAttribute('data-original-theme');\n\n if (mode === 'dark') {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (mode === 'light') {\n if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n }\n return false;\n } else if (mode === 'system') {\n const isSystemDark = window.matchMedia(\n '(prefers-color-scheme: dark)'\n ).matches;\n if (isSystemDark) {\n htmlElement.setAttribute('data-theme', 'dark');\n return true;\n } else if (originalTheme) {\n htmlElement.setAttribute('data-theme', originalTheme);\n return false;\n }\n }\n return false;\n};\n\n/**\n * Save original theme from white label\n */\nconst saveOriginalTheme = () => {\n const htmlElement = document.documentElement;\n const currentTheme = htmlElement.getAttribute('data-theme');\n if (currentTheme && !htmlElement.getAttribute('data-original-theme')) {\n htmlElement.setAttribute('data-original-theme', currentTheme);\n }\n};\n\n/**\n * Theme store using Zustand with persistence\n */\nexport const useThemeStore = create<ThemeStore>()(\n devtools(\n persist(\n (set, get) => ({\n // Initial state\n themeMode: 'system',\n isDark: false,\n\n // Actions\n applyTheme: (mode: ThemeMode) => {\n const isDark = applyThemeToDOM(mode);\n set({ isDark });\n },\n\n toggleTheme: () => {\n const { themeMode, applyTheme } = get();\n let newMode: ThemeMode;\n\n if (themeMode === 'light') {\n newMode = 'dark';\n } else if (themeMode === 'dark') {\n newMode = 'light';\n } else {\n // Se estiver em 'system', vai para 'dark'\n newMode = 'dark';\n }\n\n set({ themeMode: newMode });\n applyTheme(newMode);\n },\n\n setTheme: (mode: ThemeMode) => {\n const { applyTheme } = get();\n set({ themeMode: mode });\n applyTheme(mode);\n },\n\n initializeTheme: () => {\n const { themeMode, applyTheme } = get();\n\n // Save original theme from white label\n saveOriginalTheme();\n\n // Apply the current theme mode\n applyTheme(themeMode);\n },\n\n handleSystemThemeChange: () => {\n const { themeMode, applyTheme } = get();\n // Only respond to system changes when in system mode\n if (themeMode === 'system') {\n applyTheme('system');\n }\n },\n }),\n {\n name: 'theme-store', // Nome da chave no localStorage\n partialize: (state) => ({\n themeMode: state.themeMode,\n }), // Só persiste o themeMode, não o isDark\n }\n ),\n {\n name: 'theme-store',\n }\n )\n);\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":"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","names":["useEffect","jsx","useEffect"]}
package/dist/index.js CHANGED
@@ -15331,6 +15331,7 @@ var import_react45 = require("react");
15331
15331
  var import_api = require("@react-google-maps/api");
15332
15332
  var import_union = __toESM(require("@turf/union"));
15333
15333
  var import_jsx_runtime75 = require("react/jsx-runtime");
15334
+ var GOOGLE_MAPS_LOADER_ID = "google-maps-script";
15334
15335
  var FADE_DURATION = 400;
15335
15336
  var HOVER_DURATION = 200;
15336
15337
  var TARGET_OPACITY = 0.8;
@@ -15465,7 +15466,7 @@ var ChoroplethMap = ({
15465
15466
  onRegionClick,
15466
15467
  className
15467
15468
  }) => {
15468
- const mapId = (0, import_react45.useId)();
15469
+ const mapId = GOOGLE_MAPS_LOADER_ID;
15469
15470
  const [map, setMap] = (0, import_react45.useState)(null);
15470
15471
  const [hoveredRegion, setHoveredRegion] = (0, import_react45.useState)(null);
15471
15472
  const [infoPosition, setInfoPosition] = (0, import_react45.useState)(null);