LGTM
This commit is contained in:
parent
701c17a703
commit
f114ada255
44 changed files with 5264 additions and 1674 deletions
|
|
@ -16,7 +16,12 @@ import {
|
|||
roundedPercentages,
|
||||
} from '../../lib/format';
|
||||
import { groupFeaturesByCategory } from '../../lib/features';
|
||||
import { PARTY_FEATURE_COLORS, STACKED_GROUPS, STACKED_ENUM_GROUPS } from '../../lib/consts';
|
||||
import {
|
||||
PARTY_FEATURE_COLORS,
|
||||
STACKED_GROUPS,
|
||||
STACKED_ENUM_GROUPS,
|
||||
STACKED_SEGMENT_COLORS,
|
||||
} from '../../lib/consts';
|
||||
import { DualHistogram, LoadingSkeleton } from './DualHistogram';
|
||||
import EnumBarChart from './EnumBarChart';
|
||||
import StackedBarChart from './StackedBarChart';
|
||||
|
|
@ -113,71 +118,75 @@ export default function AreaPane({
|
|||
return (
|
||||
<>
|
||||
<div className="h-full overflow-y-auto">
|
||||
<div className="p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold dark:text-warm-100">
|
||||
{isPostcode ? hexagonId : t('areaPane.areaStatistics')}
|
||||
</h2>
|
||||
{isPostcode && (
|
||||
<span className="text-xs text-warm-500 dark:text-warm-400">
|
||||
{t('common.postcode')}
|
||||
</span>
|
||||
)}
|
||||
<div className="border-b border-warm-200 bg-white dark:border-navy-700 dark:bg-navy-950">
|
||||
<div className="space-y-3 p-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<h2 className="truncate text-base font-semibold text-warm-900 dark:text-warm-100">
|
||||
{isPostcode ? hexagonId : t('areaPane.areaOverview')}
|
||||
</h2>
|
||||
{loading && (
|
||||
<span className="h-3 w-3 shrink-0 rounded-full border-2 border-teal-600 border-t-transparent dark:border-teal-400 dark:border-t-transparent animate-spin" />
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs leading-snug text-warm-500 dark:text-warm-400">
|
||||
{t('areaPane.statsFor', {
|
||||
type: isPostcode
|
||||
? t('common.postcode').toLowerCase()
|
||||
: t('common.area').toLowerCase(),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<div className="shrink-0 text-right">
|
||||
<div className="text-lg font-semibold tabular-nums leading-none text-navy-950 dark:text-warm-50">
|
||||
{propertyCount == null ? '...' : propertyCount.toLocaleString()}
|
||||
</div>
|
||||
<div className="mt-0.5 text-xs font-medium text-warm-500 dark:text-warm-400">
|
||||
{t('common.propertiesPlural')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{loading && stats && (
|
||||
<div className="w-3 h-3 border-2 border-teal-600 dark:border-teal-400 border-t-transparent rounded-full animate-spin" />
|
||||
|
||||
<div className="flex gap-2 border-l-2 border-teal-500 bg-warm-50 px-2.5 py-2 text-xs leading-snug text-warm-700 dark:bg-navy-900 dark:text-warm-300">
|
||||
<FilterIcon className="mt-0.5 h-3.5 w-3.5 shrink-0 text-teal-700 dark:text-teal-300" />
|
||||
<p>
|
||||
{activeFilterCount > 0
|
||||
? t('areaPane.filtersAffectStats', { count: activeFilterCount })
|
||||
: t('areaPane.noFiltersAffectStats')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{hasFilteredOutArea && (
|
||||
<div className="mt-2 rounded border border-amber-200 bg-amber-50 px-2.5 py-2 text-xs leading-snug text-amber-900 dark:border-amber-800/70 dark:bg-amber-950/40 dark:text-amber-100">
|
||||
<p className="font-semibold">{t('areaPane.noFilteredMatches')}</p>
|
||||
<p className="mt-1">
|
||||
{unfilteredCount != null && unfilteredCount > 0
|
||||
? t('areaPane.unfilteredAreaCount', { count: unfilteredCount })
|
||||
: unfilteredCount === 0
|
||||
? t('areaPane.noUnfilteredAreaProperties')
|
||||
: t('areaPane.relaxFiltersHint')}
|
||||
</p>
|
||||
{onClearFilters && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearFilters}
|
||||
className="mt-2 rounded bg-amber-600 px-2 py-1 text-xs font-medium text-white hover:bg-amber-700 dark:bg-amber-500 dark:text-amber-950 dark:hover:bg-amber-400"
|
||||
>
|
||||
{t('filters.clearAll')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{stats && stats.count > 0 && (
|
||||
<button
|
||||
onClick={onViewProperties}
|
||||
className="w-full text-sm py-1.5 rounded bg-teal-600 hover:bg-teal-700 text-white font-medium"
|
||||
>
|
||||
{t('areaPane.viewPropertiesShort')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{propertyCount != null && (
|
||||
<p className="text-sm text-warm-600 dark:text-warm-400 mt-1">
|
||||
{propertyCount.toLocaleString()} {t('common.propertiesPlural')}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-warm-500 dark:text-warm-400 mt-1">
|
||||
{t('areaPane.statsFor', {
|
||||
type: isPostcode
|
||||
? t('common.postcode').toLowerCase()
|
||||
: t('common.area').toLowerCase(),
|
||||
})}
|
||||
</p>
|
||||
<div className="mt-2 flex gap-2 rounded border border-teal-200 bg-teal-50 px-2.5 py-2 text-xs leading-snug text-teal-800 dark:border-teal-800/70 dark:bg-teal-950/40 dark:text-teal-200">
|
||||
<FilterIcon className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
||||
<p>
|
||||
{activeFilterCount > 0
|
||||
? t('areaPane.filtersAffectStats', { count: activeFilterCount })
|
||||
: t('areaPane.noFiltersAffectStats')}
|
||||
</p>
|
||||
</div>
|
||||
{hasFilteredOutArea && (
|
||||
<div className="mt-2 rounded border border-amber-200 bg-amber-50 px-2.5 py-2 text-xs leading-snug text-amber-900 dark:border-amber-800/70 dark:bg-amber-950/40 dark:text-amber-100">
|
||||
<p className="font-semibold">{t('areaPane.noFilteredMatches')}</p>
|
||||
<p className="mt-1">
|
||||
{unfilteredCount != null && unfilteredCount > 0
|
||||
? t('areaPane.unfilteredAreaCount', { count: unfilteredCount })
|
||||
: unfilteredCount === 0
|
||||
? t('areaPane.noUnfilteredAreaProperties')
|
||||
: t('areaPane.relaxFiltersHint')}
|
||||
</p>
|
||||
{onClearFilters && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClearFilters}
|
||||
className="mt-2 rounded bg-amber-600 px-2 py-1 text-xs font-medium text-white hover:bg-amber-700 dark:bg-amber-500 dark:text-amber-950 dark:hover:bg-amber-400"
|
||||
>
|
||||
{t('filters.clearAll')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{stats && stats.count > 0 && (
|
||||
<button
|
||||
onClick={onViewProperties}
|
||||
className="mt-2 w-full text-sm py-1.5 rounded bg-teal-600 hover:bg-teal-700 text-white font-medium"
|
||||
>
|
||||
{t('areaPane.viewProperties', { count: stats.count })}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hexagonLocation && stats && (
|
||||
|
|
@ -315,7 +324,7 @@ export default function AreaPane({
|
|||
colorMap={
|
||||
chart.label === 'Political vote share'
|
||||
? PARTY_FEATURE_COLORS
|
||||
: undefined
|
||||
: STACKED_SEGMENT_COLORS
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -369,7 +378,15 @@ export default function AreaPane({
|
|||
p1={numericStats.histogram.p1}
|
||||
p99={numericStats.histogram.p99}
|
||||
globalMean={globalMean}
|
||||
formatLabel={(v) => formatFilterValue(v, feature.raw)}
|
||||
meanLabel={t('areaPane.nationalAvg')}
|
||||
formatLabel={(v) =>
|
||||
formatFilterValue(
|
||||
v,
|
||||
feature.suffix === '%'
|
||||
? { raw: feature.raw, suffix: feature.suffix }
|
||||
: feature.raw
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<DualHistogram
|
||||
|
|
@ -377,7 +394,14 @@ export default function AreaPane({
|
|||
globalCounts={numericStats.histogram.counts}
|
||||
p1={numericStats.histogram.p1}
|
||||
p99={numericStats.histogram.p99}
|
||||
formatLabel={(v) => formatFilterValue(v, feature.raw)}
|
||||
formatLabel={(v) =>
|
||||
formatFilterValue(
|
||||
v,
|
||||
feature.suffix === '%'
|
||||
? { raw: feature.raw, suffix: feature.suffix }
|
||||
: feature.raw
|
||||
)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue