Update FR colors, consolidate non-top answers into "Other" (#1031)
* Update FR colors, consolidate non-top answers into "Other" * Fix answer panel coloration to not be weird and work on Firefox
This commit is contained in:
		
							parent
							
								
									f587e0256d
								
							
						
					
					
						commit
						59cdc9f776
					
				|  | @ -23,7 +23,7 @@ import { Linkify } from 'web/components/linkify' | |||
| import { Button } from 'web/components/button' | ||||
| import { useAdmin } from 'web/hooks/use-admin' | ||||
| import { needsAdminToResolve } from 'web/pages/[username]/[contractSlug]' | ||||
| import { CATEGORY_COLORS } from '../charts/contract/choice' | ||||
| import { CHOICE_ANSWER_COLORS } from '../charts/contract/choice' | ||||
| import { useChartAnswers } from '../charts/contract/choice' | ||||
| 
 | ||||
| export function AnswersPanel(props: { | ||||
|  | @ -190,7 +190,10 @@ function OpenAnswer(props: { | |||
|   const probPercent = formatPercent(prob) | ||||
|   const [open, setOpen] = useState(false) | ||||
|   const color = | ||||
|     colorIndex != undefined ? CATEGORY_COLORS[colorIndex] : '#B1B1C7' | ||||
|     colorIndex != undefined && colorIndex < CHOICE_ANSWER_COLORS.length | ||||
|       ? CHOICE_ANSWER_COLORS[colorIndex] + '55' // semi-transparent
 | ||||
|       : '#B1B1C755' | ||||
|   const colorWidth = 100 * Math.max(prob, 0.01) | ||||
| 
 | ||||
|   return ( | ||||
|     <Col className="my-1 px-2"> | ||||
|  | @ -206,9 +209,12 @@ function OpenAnswer(props: { | |||
| 
 | ||||
|       <Col | ||||
|         className={clsx( | ||||
|           'bg-greyscale-1 relative w-full rounded-lg transition-all', | ||||
|           'relative w-full rounded-lg transition-all', | ||||
|           tradingAllowed(contract) ? 'text-greyscale-7' : 'text-greyscale-5' | ||||
|         )} | ||||
|         style={{ | ||||
|           background: `linear-gradient(to right, ${color} ${colorWidth}%, #FBFBFF ${colorWidth}%)`, | ||||
|         }} | ||||
|       > | ||||
|         <Row className="z-20 -mb-1 justify-between gap-2 py-2 px-3"> | ||||
|           <Row> | ||||
|  | @ -236,11 +242,6 @@ function OpenAnswer(props: { | |||
|             )} | ||||
|           </Row> | ||||
|         </Row> | ||||
|         <hr | ||||
|           color={color} | ||||
|           className="absolute z-0 h-full w-full rounded-l-lg border-none opacity-30" | ||||
|           style={{ width: `${100 * Math.max(prob, 0.01)}%` }} | ||||
|         /> | ||||
|       </Col> | ||||
|     </Col> | ||||
|   ) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import { useMemo } from 'react' | ||||
| import { last, sum, sortBy, groupBy } from 'lodash' | ||||
| import { last, range, sum, sortBy, groupBy } from 'lodash' | ||||
| import { scaleTime, scaleLinear } from 'd3-scale' | ||||
| import { curveStepAfter } from 'd3-shape' | ||||
| 
 | ||||
|  | @ -19,83 +19,36 @@ import { MultiPoint, MultiValueHistoryChart } from '../generic-charts' | |||
| import { Row } from 'web/components/layout/row' | ||||
| import { Avatar } from 'web/components/avatar' | ||||
| 
 | ||||
| export const CATEGORY_COLORS = [ | ||||
|   '#7eb0d5', | ||||
|   '#fd7f6f', | ||||
|   '#b2e061', | ||||
|   '#bd7ebe', | ||||
|   '#ffb55a', | ||||
|   '#ffee65', | ||||
|   '#beb9db', | ||||
|   '#fdcce5', | ||||
|   '#8bd3c7', | ||||
|   '#bddfb7', | ||||
|   '#e2e3f3', | ||||
|   '#fafafa', | ||||
|   '#9fcdeb', | ||||
|   '#d3d3d3', | ||||
|   '#b1a296', | ||||
|   '#e1bdb6', | ||||
|   '#f2dbc0', | ||||
|   '#fae5d3', | ||||
|   '#c5e0ec', | ||||
|   '#e0f0ff', | ||||
|   '#ffddcd', | ||||
|   '#fbd5e2', | ||||
|   '#f2e7e5', | ||||
|   '#ffe7ba', | ||||
|   '#eed9c4', | ||||
|   '#ea9999', | ||||
|   '#f9cb9c', | ||||
|   '#ffe599', | ||||
|   '#b6d7a8', | ||||
|   '#a2c4c9', | ||||
|   '#9fc5e8', | ||||
|   '#b4a7d6', | ||||
|   '#d5a6bd', | ||||
|   '#e06666', | ||||
|   '#f6b26b', | ||||
|   '#ffd966', | ||||
|   '#93c47d', | ||||
|   '#76a5af', | ||||
|   '#6fa8dc', | ||||
|   '#8e7cc3', | ||||
|   '#c27ba0', | ||||
|   '#cc0000', | ||||
|   '#e69138', | ||||
|   '#f1c232', | ||||
|   '#6aa84f', | ||||
|   '#45818e', | ||||
|   '#3d85c6', | ||||
|   '#674ea7', | ||||
|   '#a64d79', | ||||
|   '#990000', | ||||
|   '#b45f06', | ||||
|   '#bf9000', | ||||
| type ChoiceContract = FreeResponseContract | MultipleChoiceContract | ||||
| 
 | ||||
| export const CHOICE_ANSWER_COLORS = [ | ||||
|   '#97C1EB', | ||||
|   '#F39F83', | ||||
|   '#F9EBA5', | ||||
|   '#FFC7D2', | ||||
|   '#C7ECFF', | ||||
|   '#8CDEC7', | ||||
|   '#DBE96F', | ||||
| ] | ||||
| export const CHOICE_OTHER_COLOR = '#CCC' | ||||
| export const CHOICE_ALL_COLORS = [...CHOICE_ANSWER_COLORS, CHOICE_OTHER_COLOR] | ||||
| 
 | ||||
| const MARGIN = { top: 20, right: 10, bottom: 20, left: 40 } | ||||
| const MARGIN_X = MARGIN.left + MARGIN.right | ||||
| const MARGIN_Y = MARGIN.top + MARGIN.bottom | ||||
| 
 | ||||
| const getTrackedAnswers = ( | ||||
|   contract: FreeResponseContract | MultipleChoiceContract, | ||||
|   topN: number | ||||
| ) => { | ||||
|   const { answers, outcomeType, totalBets } = contract | ||||
|   const validAnswers = answers.filter((answer) => { | ||||
|     return ( | ||||
|       (answer.id !== '0' || outcomeType === 'MULTIPLE_CHOICE') && | ||||
|       totalBets[answer.id] > 0.000000001 | ||||
|     ) | ||||
|   }) | ||||
| const getAnswers = (contract: ChoiceContract) => { | ||||
|   const { answers, outcomeType } = contract | ||||
|   const validAnswers = answers.filter( | ||||
|     (answer) => answer.id !== '0' || outcomeType === 'MULTIPLE_CHOICE' | ||||
|   ) | ||||
|   return sortBy( | ||||
|     validAnswers, | ||||
|     (answer) => -1 * getOutcomeProbability(contract, answer.id) | ||||
|   ).slice(0, topN) | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| const getBetPoints = (answers: Answer[], bets: Bet[]) => { | ||||
| const getBetPoints = (answers: Answer[], bets: Bet[], topN?: number) => { | ||||
|   const sortedBets = sortBy(bets, (b) => b.createdTime) | ||||
|   const betsByOutcome = groupBy(sortedBets, (bet) => bet.outcome) | ||||
|   const sharesByOutcome = Object.fromEntries( | ||||
|  | @ -109,11 +62,14 @@ const getBetPoints = (answers: Answer[], bets: Bet[]) => { | |||
|     const sharesSquared = sum( | ||||
|       Object.values(sharesByOutcome).map((shares) => shares ** 2) | ||||
|     ) | ||||
|     points.push({ | ||||
|       x: new Date(bet.createdTime), | ||||
|       y: answers.map((a) => sharesByOutcome[a.id] ** 2 / sharesSquared), | ||||
|       obj: bet, | ||||
|     }) | ||||
|     const probs = answers.map((a) => sharesByOutcome[a.id] ** 2 / sharesSquared) | ||||
| 
 | ||||
|     if (topN != null && answers.length > topN) { | ||||
|       const y = [...probs.slice(0, topN), sum(probs.slice(topN))] | ||||
|       points.push({ x: new Date(bet.createdTime), y, obj: bet }) | ||||
|     } else { | ||||
|       points.push({ x: new Date(bet.createdTime), y: probs, obj: bet }) | ||||
|     } | ||||
|   } | ||||
|   return points | ||||
| } | ||||
|  | @ -141,17 +97,12 @@ const Legend = (props: { className?: string; items: LegendItem[] }) => { | |||
|   ) | ||||
| } | ||||
| 
 | ||||
| export function useChartAnswers( | ||||
|   contract: FreeResponseContract | MultipleChoiceContract | ||||
| ) { | ||||
|   return useMemo( | ||||
|     () => getTrackedAnswers(contract, CATEGORY_COLORS.length), | ||||
|     [contract] | ||||
|   ) | ||||
| export function useChartAnswers(contract: ChoiceContract) { | ||||
|   return useMemo(() => getAnswers(contract), [contract]) | ||||
| } | ||||
| 
 | ||||
| export const ChoiceContractChart = (props: { | ||||
|   contract: FreeResponseContract | MultipleChoiceContract | ||||
|   contract: ChoiceContract | ||||
|   bets: Bet[] | ||||
|   width: number | ||||
|   height: number | ||||
|  | @ -160,18 +111,33 @@ export const ChoiceContractChart = (props: { | |||
|   const { contract, bets, width, height, onMouseOver } = props | ||||
|   const [start, end] = getDateRange(contract) | ||||
|   const answers = useChartAnswers(contract) | ||||
|   const betPoints = useMemo(() => getBetPoints(answers, bets), [answers, bets]) | ||||
|   const data = useMemo( | ||||
|     () => [ | ||||
|       { x: new Date(start), y: answers.map((_) => 0) }, | ||||
|   const topN = Math.min(CHOICE_ANSWER_COLORS.length, answers.length) | ||||
|   const betPoints = useMemo( | ||||
|     () => getBetPoints(answers, bets, topN), | ||||
|     [answers, bets, topN] | ||||
|   ) | ||||
|   const endProbs = useMemo( | ||||
|     () => answers.map((a) => getOutcomeProbability(contract, a.id)), | ||||
|     [answers, contract] | ||||
|   ) | ||||
| 
 | ||||
|   const data = useMemo(() => { | ||||
|     const yCount = answers.length > topN ? topN + 1 : topN | ||||
|     const startY = range(0, yCount).map((_) => 0) | ||||
|     const endY = | ||||
|       answers.length > topN | ||||
|         ? [...endProbs.slice(0, topN), sum(endProbs.slice(topN))] | ||||
|         : endProbs | ||||
|     return [ | ||||
|       { x: new Date(start), y: startY }, | ||||
|       ...betPoints, | ||||
|       { | ||||
|         x: new Date(end ?? Date.now() + DAY_MS), | ||||
|         y: answers.map((a) => getOutcomeProbability(contract, a.id)), | ||||
|         y: endY, | ||||
|       }, | ||||
|     ], | ||||
|     [answers, contract, betPoints, start, end] | ||||
|   ) | ||||
|     ] | ||||
|   }, [answers.length, topN, betPoints, endProbs, start, end]) | ||||
| 
 | ||||
|   const rightmostDate = getRightmostVisibleDate( | ||||
|     end, | ||||
|     last(betPoints)?.x?.getTime(), | ||||
|  | @ -188,8 +154,8 @@ export const ChoiceContractChart = (props: { | |||
|       const d = xScale.invert(x) | ||||
|       const legendItems = sortBy( | ||||
|         data.y.map((p, i) => ({ | ||||
|           color: CATEGORY_COLORS[i], | ||||
|           label: answers[i].text, | ||||
|           color: CHOICE_ALL_COLORS[i], | ||||
|           label: i === CHOICE_ANSWER_COLORS.length ? 'Other' : answers[i].text, | ||||
|           value: formatPct(p), | ||||
|           p, | ||||
|         })), | ||||
|  | @ -221,7 +187,7 @@ export const ChoiceContractChart = (props: { | |||
|       yScale={yScale} | ||||
|       yKind="percent" | ||||
|       data={data} | ||||
|       colors={CATEGORY_COLORS} | ||||
|       colors={CHOICE_ALL_COLORS} | ||||
|       curve={curveStepAfter} | ||||
|       onMouseOver={onMouseOver} | ||||
|       Tooltip={ChoiceTooltip} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user