Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework maple bars and detail charts #31

Merged
merged 35 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
15272a0
maple and bar rework
Shane98c Dec 13, 2023
5180081
mobile, expander attempt, detail charts breakout
Shane98c Dec 13, 2023
3596f7b
handle error/missing
Shane98c Dec 13, 2023
57febb8
add vscode to gitignore
Shane98c Dec 13, 2023
bd62c03
Merge branch 'main' of https://github.com/carbonplan/offsets-db-web i…
Shane98c Dec 13, 2023
370757b
fix animation
Shane98c Dec 13, 2023
7a2d789
better math
Shane98c Dec 14, 2023
1c57e67
even better math
Shane98c Dec 14, 2023
b35e74b
no math
Shane98c Dec 14, 2023
a1635ae
improve maple spacing
Shane98c Dec 14, 2023
a6916b1
always mobile
Shane98c Dec 15, 2023
37a4bbb
rework badge/labels arrangement
Shane98c Dec 15, 2023
b04ca58
expander => triangle concept
Shane98c Dec 15, 2023
b94dcfb
rethinking detail?
Shane98c Dec 15, 2023
bd15a5f
details chart concept
Shane98c Dec 15, 2023
0ca9320
clean up messy columns
Shane98c Dec 15, 2023
0cdf94c
rm unused row
Shane98c Dec 15, 2023
bf5b34f
improve triangle
Shane98c Dec 15, 2023
0fac217
needed the rows
Shane98c Dec 15, 2023
f1f3f0c
wider, attempt to wrap on slash
Shane98c Dec 15, 2023
4ea3e6e
fix label typography
Shane98c Dec 15, 2023
6d066bd
revert to expander
Shane98c Dec 15, 2023
7b8bf04
conditional expander
Shane98c Dec 16, 2023
eff5a74
don't stack maple bars
Shane98c Jan 3, 2024
a2cf9bb
reorient and hide detail on mobile
Shane98c Jan 3, 2024
557b1d5
rework detail charts, add loading state placeholders
Shane98c Jan 4, 2024
b416de7
match colors/txt to maple badges
Shane98c Jan 4, 2024
3143338
handle error state
Shane98c Jan 4, 2024
a03adfc
spacing fixes for tablet/mobile
Shane98c Jan 4, 2024
a1ed843
rm unused import
Shane98c Jan 4, 2024
e022389
no pointer on detail charts
Shane98c Jan 4, 2024
4a4ce4b
don't show expander/expand if empty
Shane98c Jan 4, 2024
ff10316
cursor styling when no expansion possible
Shane98c Jan 4, 2024
978e004
prevent flash during expansion
Shane98c Jan 4, 2024
68558d6
fix mobile spacing
Shane98c Jan 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
.next/
node_modules/
.env.local
.vscode/
.env.local
162 changes: 72 additions & 90 deletions components/charts/category-bar.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Badge, Expander } from '@carbonplan/components'
import { Badge, Row, Column, Expander } from '@carbonplan/components'
import { useMemo } from 'react'
import { Box, Flex } from 'theme-ui'
import AnimateHeight from 'react-animate-height'

import { COLORS, LABELS } from '../constants'
import { formatValue } from '../utils'

const CategoryBar = ({ label, total, mapping, expanded, showExpander }) => {
const CategoryBar = ({
label,
total,
mapping,
issuedTotal,
expanded,
showExpander,
}) => {
const background = useMemo(() => {
if (Object.keys(mapping).length === 0) {
if (Object.keys(mapping).length === 0 || total === 0) {
return 'muted'
} else {
const colors = Object.keys(LABELS.category).map((key) => COLORS[key])
Expand All @@ -18,7 +24,7 @@ const CategoryBar = ({ label, total, mapping, expanded, showExpander }) => {
if (typeof mapValue !== 'number') {
mapValue = 0
}
let value = (mapValue / total) * 100
let value = (mapValue / (issuedTotal ?? total)) * 100
if (accum[i - 1]) {
value = value + accum[i - 1]
}
Expand All @@ -27,6 +33,10 @@ const CategoryBar = ({ label, total, mapping, expanded, showExpander }) => {
},
[]
)
if (issuedTotal) {
colors.push('muted')
percentages.push(100)
}

return (theme) =>
`linear-gradient(to right, ${percentages
Expand All @@ -36,101 +46,73 @@ const CategoryBar = ({ label, total, mapping, expanded, showExpander }) => {
})
.join(', ')})`
}
}, [total, mapping])
}, [total, issuedTotal, mapping])

const isEmpty = Object.keys(mapping).length === 0

return (
<Box sx={{ mt: [3, 3, '56px', '56px'], mb: 6 }}>
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
<Box
<>
<Row columns={[6, 8, 8, 8]}>
<Column
start={1}
width={8}
sx={{ mt: [!showExpander ? 5 : 0, 0, 0, 0] }} //add space when stacked
>
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
<Box
sx={{
fontSize: 1,
fontFamily: 'mono',
letterSpacing: 'mono',
color: 'secondary',
}}
>
{label}
</Box>
<Badge sx={{ fontSize: 4, height: ['34px'], px: 1, mb: '-2px' }}>
{isEmpty ? '-' : formatValue(total)}
</Badge>
</Flex>
</Column>
</Row>
<Row
columns={1}
sx={{
mt: 5,
}}
>
<Column
start={1}
width={8}
sx={{
fontSize: 1,
fontFamily: 'mono',
letterSpacing: 'mono',
color: 'secondary',
display: 'flex',
alignItems: 'center',
}}
>
{label}
</Box>
<Badge sx={{ fontSize: 4, height: ['34px'], px: 1, mb: '-2px' }}>
{isEmpty ? '-' : formatValue(total)}
</Badge>
</Flex>

<Box sx={{ mt: [4, '38px', '38px', '38px'], position: 'relative' }}>
{showExpander && (
<Expander
id='expander'
value={expanded}
{showExpander && (
<Expander
value={expanded}
id='expander'
sx={{
display: ['none', 'block', 'block', 'block'],
ml: [-3, -4, -5, -5],
width: [18, 22, 22, 22],
position: 'absolute',
mt: 1,
}}
/>
)}
<Box
sx={{
position: 'absolute',
left: '-22px',
top: '3px',
width: '18px',
height: '18px',
width: '100%',
height: '28px',
transition: 'background 0.2s',
background,
}}
/>
)}

<Box
sx={{
width: '100%',
height: '28px',
transition: 'background 0.2s',
background,
}}
/>
</Box>

<AnimateHeight
duration={100}
height={expanded ? 'auto' : 0}
easing={'linear'}
>
<Box sx={{ mt: 3 }}>
{Object.keys(LABELS.category).map((l) => (
<Box key={l} sx={{ mb: 2 }}>
<Flex
sx={{
justifyContent: 'space-between',
alignItems: 'flex-end',
fontSize: 1,
mb: 2,
}}
>
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<Box
sx={{
width: '8px',
height: '8px',
backgroundColor: COLORS[l],
}}
/>
{LABELS.category[l]}
</Flex>
<Badge>{isEmpty ? '-' : formatValue(mapping[l] ?? 0)}</Badge>
</Flex>
<Box
sx={{
height: '5px',
width: '100%',
transition: 'background 0.2s',
background: isEmpty
? 'muted'
: (theme) =>
`linear-gradient(to right, ${
theme.colors[COLORS[l]]
} 0% ${((mapping[l] ?? 0) / total) * 100}%, ${
theme.colors.muted
} ${((mapping[l] ?? 0) / total) * 100}% 100%)`,
}}
/>
</Box>
))}
</Box>
</AnimateHeight>
</Box>
</Column>
</Row>
</>
)
}

Expand Down
132 changes: 132 additions & 0 deletions components/charts/detail-charts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useState, useEffect } from 'react'
import { Box, Flex, Badge } from 'theme-ui'
import { Row, Column } from '@carbonplan/components'
import { alpha } from '@theme-ui/color'
import { COLORS, LABELS } from '../constants'
import { formatValue } from '../utils'

const DetailCharts = ({ issued, retired, isLoading, error }) => {
const [previousCategories, setPreviousCategories] = useState([])

useEffect(() => {
if (!isLoading && !error) {
setPreviousCategories(Object.keys(issued.mapping))
}
}, [isLoading, error])

const createGradient = (theme, l, isRetired) => {
const percent = ((issued.mapping[l] ?? 0) / issued.total) * 100
const retiredPercent = isRetired
? ((retired.mapping[l] ?? 0) / issued.total) * 100
: percent

if (isRetired) {
return `linear-gradient(to right,
${theme.rawColors[COLORS[l]]} 0%,
${theme.rawColors[COLORS[l]]} ${retiredPercent}%,
${alpha(
theme.rawColors[COLORS[l]],
0.3
)(theme)} ${retiredPercent}%,
${alpha(theme.rawColors[COLORS[l]], 0.3)(theme)} ${percent}%,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is looking really really nice 🤌

${theme.colors.muted} ${percent}%,
${theme.colors.muted} 100%)`
} else {
return `linear-gradient(to right, ${
theme.colors[COLORS[l]]
} 0% ${percent}%, ${theme.colors.muted} ${percent}% 100%)`
}
}
const chartColumn = (isRetired) => {
const categoryKeys =
isLoading || error
? previousCategories
Comment on lines +42 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super clean, very nice!

: Object.keys(LABELS.category).filter((l) => Boolean(issued.mapping[l]))

return (
<Column
start={[
isRetired ? 4 : 1,
isRetired ? 5 : 1,
isRetired ? 5 : 1,
isRetired ? 5 : 1,
]}
width={[3, 4, 4, 4]}
>
{categoryKeys.map((l) => (
<Box key={l} sx={{ mb: 2 }}>
<Flex
sx={{
justifyContent: 'space-between',
fontSize: 2,
mb: 1,
}}
>
{isLoading || error ? (
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<Box
sx={{
width: '8px',
height: '8px',
backgroundColor: 'muted',
}}
/>
<Box sx={{ fontSize: 1, color: 'muted' }}>----</Box>
</Flex>
) : (
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<Box
sx={{
width: '8px',
height: '8px',
backgroundColor: COLORS[l],
}}
/>
<Box sx={{ fontSize: 1 }}>{LABELS.category[l]}</Box>
</Flex>
)}

<Badge
sx={{
color: isLoading || error ? 'primary' : COLORS[l],
backgroundColor:
isLoading || error ? 'muted' : alpha(COLORS[l], 0.3),
fontSize: 2,
mb: '3px',
}}
>
{isLoading || error
? '-'
: formatValue(
isRetired ? retired.mapping[l] : issued.mapping[l]
) ?? 0}
</Badge>
</Flex>
<Box
sx={{
height: '5px',
width: '100%',
transition: 'background 0.2s',
background:
isLoading || error
? 'muted'
: (theme) => createGradient(theme, l, isRetired),
}}
/>
</Box>
))}
</Column>
)
}

return (
<>
<Row columns={[6, 8, 8, 8]} sx={{ mb: 5 }}>
{chartColumn(false)}
{chartColumn(true)}
</Row>
</>
)
}

export default DetailCharts
Loading
Loading