From 164e5b2e41c7808ae965376551c1052f89c1a0c4 Mon Sep 17 00:00:00 2001 From: Hanbyul Jo Date: Thu, 26 Sep 2024 12:40:10 -0400 Subject: [PATCH] Deprecate date sliders --- .../components/common/dateslider/constants.ts | 14 -- .../common/dateslider/data-points.tsx | 184 ---------------- .../common/dateslider/date-axis.tsx | 156 -------------- .../components/common/dateslider/faders.tsx | 82 ------- .../components/common/dateslider/index.tsx | 203 ------------------ .../common/dateslider/trigger-rect.tsx | 115 ---------- .../components/common/dateslider/utils.tsx | 143 ------------ .../components/sandbox/dateslider/index.js | 139 ------------ app/scripts/components/sandbox/index.js | 6 - 9 files changed, 1042 deletions(-) delete mode 100644 app/scripts/components/common/dateslider/constants.ts delete mode 100644 app/scripts/components/common/dateslider/data-points.tsx delete mode 100644 app/scripts/components/common/dateslider/date-axis.tsx delete mode 100644 app/scripts/components/common/dateslider/faders.tsx delete mode 100644 app/scripts/components/common/dateslider/index.tsx delete mode 100644 app/scripts/components/common/dateslider/trigger-rect.tsx delete mode 100644 app/scripts/components/common/dateslider/utils.tsx delete mode 100644 app/scripts/components/sandbox/dateslider/index.js diff --git a/app/scripts/components/common/dateslider/constants.ts b/app/scripts/components/common/dateslider/constants.ts deleted file mode 100644 index 73481931e..000000000 --- a/app/scripts/components/common/dateslider/constants.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface DateSliderDataItem { - index: number; - date: Date; - hasData: boolean; - breakLength?: number; -} - -export type DateSliderTimeDensity = 'day' | 'month' | 'year'; - -export const DATA_POINT_WIDTH = 32; - -export const RANGE_PADDING = 24; - -export const CHART_HEIGHT = 48; diff --git a/app/scripts/components/common/dateslider/data-points.tsx b/app/scripts/components/common/dateslider/data-points.tsx deleted file mode 100644 index 8f22151e4..000000000 --- a/app/scripts/components/common/dateslider/data-points.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import styled from 'styled-components'; -import { ScaleLinear, select, Selection } from 'd3'; -import { themeVal } from '@devseed-ui/theme-provider'; - -import { findDate } from './utils'; -import { DateSliderDataItem, DateSliderTimeDensity } from './constants'; - -import useReducedMotion from '$utils/use-prefers-reduced-motion'; - -const StyledG = styled.g` - .data-point { - fill: #fff; - stroke-width: 1px; - stroke: ${themeVal('color.base-100')}; - transition: fill 160ms ease-in; - - &.over { - fill: ${themeVal('color.base')}; - } - } - - .data-point-valid { - fill: ${themeVal('color.base')}; - } - - .select-highlight { - fill: none; - stroke-width: 2px; - stroke: ${themeVal('color.base')}; - } - - .data-point-break { - fill: none; - stroke: ${themeVal('color.base-200')}; - } -`; - -const DataLineSelf = styled.line` - stroke: ${themeVal('color.base-100')}; -`; - -type HighlightCircle = Selection< - SVGCircleElement, - DateSliderDataItem, - SVGGElement | null, - unknown ->; - -function animateHighlight(circle: HighlightCircle) { - return circle - .attr('r', 1) - .style('opacity', 1) - .transition() - .duration(1500) - .style('opacity', 0) - .attr('r', 10) - .on('end', () => animateHighlight(circle)); -} - -interface DataPointsProps { - data: DateSliderDataItem[]; - hoveringDataPoint: Date | null; - value?: Date; - x: ScaleLinear; - zoomXTranslation: number; - timeDensity: DateSliderTimeDensity; -} - -export function DataPoints(props: DataPointsProps) { - const { data, value, timeDensity, x, zoomXTranslation, hoveringDataPoint } = - props; - const container = useRef(null); - - const reduceMotion = useReducedMotion(); - - useEffect(() => { - const rootG = select(container.current); - - const classAttr = (d, c) => { - return hoveringDataPoint === d.index ? `${c} over` : c; - }; - - rootG - .selectAll('circle.data-point') - .data(data.filter((d) => d.hasData)) - .join('circle') - .attr('class', (d) => classAttr(d, 'data-point')) - .attr('cx', (d) => x(d.index)) - .attr('cy', 12) - .attr('r', 4); - - rootG - .selectAll('circle.data-point-valid') - .data(data.filter((d) => d.hasData)) - .join('circle') - .attr('class', (d) => classAttr(d, 'data-point-valid')) - .attr('cx', (d) => x(d.index)) - .attr('cy', 12) - .attr('r', 2); - - // Add a squiggly line to indicate there is a data break. - rootG - .selectAll('path.data-point-break') - .data(data.filter((d) => d.breakLength)) - .join('path') - .attr('class', (d) => classAttr(d, 'data-point-break')) - .attr('d', (d) => { - // Center point on the horizontal line. We draw a bit left and right. - const h = x(d.index); - return `M${h - 12} 12 - L${h - 9} 8 - L${h - 3} 16 - L${h + 3} 8 - L${h + 9} 16 - L${h + 12} 12`; - }); - }, [data, x, hoveringDataPoint]); - - useEffect(() => { - const rootG = select(container.current); - - const item = findDate(data, value, timeDensity); - - if (item) { - let circle = rootG.select('.select-highlight') as HighlightCircle; - - if (circle.empty()) { - circle = rootG - .append('circle') - .lower() - .datum(item) - .attr('class', 'select-highlight') - .attr('cy', 12); - } - - circle.attr('cx', (d) => x(d.index)); - - // Animate or not. - if (reduceMotion) { - circle.attr('r', 6).style('opacity', 1); - } else { - circle.call(animateHighlight); - } - } - - return () => { - if (item) { - rootG.select('.select-highlight').remove(); - } - }; - }, [data, value, timeDensity, x, reduceMotion]); - - return ( - - ); -} - -interface DataLineProps { - x: ScaleLinear; - zoomXTranslation: number; -} - -export function DataLine(props: DataLineProps) { - const { x, zoomXTranslation } = props; - - // The line should occupy the full scale. - const [x1, x2] = x.range(); - - return ( - - ); -} diff --git a/app/scripts/components/common/dateslider/date-axis.tsx b/app/scripts/components/common/dateslider/date-axis.tsx deleted file mode 100644 index ea6f31cbe..000000000 --- a/app/scripts/components/common/dateslider/date-axis.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import styled from 'styled-components'; -import { ScaleLinear, select } from 'd3'; -import { format } from 'date-fns'; -import { themeVal } from '@devseed-ui/theme-provider'; -import { createSubtitleStyles } from '@devseed-ui/typography'; - -import { DateSliderDataItem, DateSliderTimeDensity } from './constants'; - -const timeFormat = { - day: 'dd', - month: 'MMM', - year: 'yyyy' -}; - -const parentTimeFormat = { - day: 'MMM yyyy', - month: 'yyyy' -}; - -const parentSearchFormat = { - day: 'yyyy-MM', - month: 'yyyy' -}; - -const StyledG = styled.g` - .date-value, - .date-parent-value { - ${createSubtitleStyles()} - fill: ${themeVal('color.base-400')}; - font-size: 0.75rem; - line-height: 1rem; - } -`; - -interface DateAxisProps { - data: DateSliderDataItem[]; - x: ScaleLinear; - zoomXTranslation: number; - timeDensity: DateSliderTimeDensity; -} - -export function DateAxis(props: DateAxisProps) { - const { data, x, zoomXTranslation, timeDensity } = props; - const dateGRef = useRef(null); - - useEffect(() => { - const dateG = select(dateGRef.current); - - dateG - .selectAll('text.date-value') - .data(data.filter((d) => !d.breakLength)) - .join('text') - .attr('class', 'date-value') - .attr('x', (d) => x(d.index)) - .attr('y', 16) - .attr('dy', '1em') - .attr('text-anchor', 'middle') - .text((d) => format(d.date, timeFormat[timeDensity])); - }, [data, x, timeDensity]); - - return ( - - ); -} - -interface DateAxisParentProps { - data: DateSliderDataItem[]; - x: ScaleLinear; - zoomXTranslation: number; - timeDensity: DateSliderTimeDensity; -} - -export function DateAxisParent(props: DateAxisParentProps) { - const { data, x, zoomXTranslation, timeDensity } = props; - const parentGref = useRef(null); - - useEffect(() => { - const parentG = select(parentGref.current); - // There's no parent for the year time unit. - if (timeDensity === 'year') { - parentG.selectAll('text.date-parent-value').remove(); - } else { - const uniqueParent = data.reduce((acc, item) => { - const { date } = item; - const formatStr = parentSearchFormat[timeDensity]; - - const exists = acc.find( - (d) => format(d.date, formatStr) === format(date, formatStr) - ); - - return exists ? acc : acc.concat(item); - }, []); - - parentG - .selectAll('text.date-parent-value') - .data(uniqueParent) - .join('text') - .attr('class', 'date-parent-value') - .attr('y', 30) - .attr('dy', '1em') - .attr('text-anchor', d => { - const isLastElement = d.index === x.domain()[1]; - return isLastElement ? 'end' : 'middle'; - }) - .attr('dx', d => { - const isLastElement = d.index === x.domain()[1]; - return isLastElement ? '1em' : ''; - }) - .text((d) => format(d.date, parentTimeFormat[timeDensity])); - } - }, [data, x, timeDensity]); - - useEffect(() => { - select(parentGref.current) - .selectAll('text.date-parent-value') - .each((d, i, n) => { - // Expected position of this node. - const expectedPosition = x(d.index); - const expectedAfterTrans = expectedPosition + zoomXTranslation; - - const nextNode = n[i + 1] as SVGTextElement | undefined; - - let maxPos = Infinity; - // If there's a node after this one, that node will push on this one, so - // the max "x" position for this node will be start of the next. - if (nextNode) { - // Width of current node. - const { width: nextNodeW } = nextNode.getBBox(); - // Position of the next item. - const nextItemData = select( - nextNode - ).datum(); - const nextItemPos = x(nextItemData.index) + zoomXTranslation; - - maxPos = nextItemPos - nextNodeW; - } - - // The node should stay close to the left, so we get the width / 2 - // because of text anchor middle. Add 4px for spacing. - const leftPadding = n[i].getBBox().width / 2 + 4; - - const xTrans = Math.min( - Math.max(expectedAfterTrans, leftPadding), - maxPos - ); - select(n[i]).attr('x', xTrans); - }); - }, [zoomXTranslation, x]); - - return ; -} diff --git a/app/scripts/components/common/dateslider/faders.tsx b/app/scripts/components/common/dateslider/faders.tsx deleted file mode 100644 index 8caafb056..000000000 --- a/app/scripts/components/common/dateslider/faders.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { Fragment } from 'react'; -import { ScaleLinear } from 'd3'; - -import { DateSliderDataItem } from './constants'; -import { getZoomTranslateExtent } from './utils'; - -export const MASK_ID = 'gradient-mask'; -const FADE_ID = 'fade-gradient'; - -interface FaderDefinitionProps { - data: DateSliderDataItem[]; - x: ScaleLinear; - zoomXTranslation: number; - width: number; - height: number; - getUID: (base: string) => string; -} - -export function FaderDefinition(props: FaderDefinitionProps) { - const { zoomXTranslation, width, height, data, x, getUID } = props; - - const [[xMinExtent], [xMaxExtent]] = getZoomTranslateExtent(data, x); - - // Invert the value. - const xTranslation = zoomXTranslation * -1; - - // Fade the masks. - // Fade in 5px (1/5) - const fadePx = 1 / 5; - const maxX = xMaxExtent - width; - const minX = xMinExtent; - - // Decreasing straight line equation. - // y = -mx + b - const b = 1 + fadePx * minX; - const leftOpc = Math.max(-fadePx * xTranslation + b, 0); - - // Increasing straight line equation. - // y = mx + b - const b2 = 1 - fadePx * maxX; - const rightOpc = Math.max(fadePx * xTranslation + b2, 0); - - return ( - - - - - - - - - - - - ); -} diff --git a/app/scripts/components/common/dateslider/index.tsx b/app/scripts/components/common/dateslider/index.tsx deleted file mode 100644 index 76f151a24..000000000 --- a/app/scripts/components/common/dateslider/index.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import styled from 'styled-components'; -import { debounce } from 'lodash'; -import { interpolate, scaleLinear, select, zoom } from 'd3'; -import { themeVal } from '@devseed-ui/theme-provider'; - -import { findDate, getZoomTranslateExtent, useChartDimensions } from './utils'; -import { DataPoints, DataLine } from './data-points'; -import TriggerRect from './trigger-rect'; -import { FaderDefinition, MASK_ID } from './faders'; -import { DateAxis, DateAxisParent } from './date-axis'; -import { - DATA_POINT_WIDTH, - DateSliderDataItem, - DateSliderTimeDensity, - RANGE_PADDING -} from './constants'; - -import { useEffectPrevious } from '$utils/use-effect-previous'; -import useReducedMotion from '$utils/use-prefers-reduced-motion'; - -const StyledSvg = styled.svg` - display: block; - width: 100%; - font-family: ${themeVal('type.base.family')}; -`; - -interface DateSliderControlProps { - id?: string; - data: DateSliderDataItem[]; - value?: Date; - timeDensity: DateSliderTimeDensity; - onChange: (value: { date: Date }) => void; -} - -export default function DateSliderControl(props: DateSliderControlProps) { - const { id, data, value, timeDensity, onChange } = props; - const { observe, width, height, outerWidth, outerHeight, margin } = - useChartDimensions(); - const svgRef = useRef(null); - const [hoveringDataPoint, setHoveringDataPoint] = useState(null); - - // Unique id creator - const getUID = useMemo(() => { - const rand = `ts-${Math.random().toString(36).substring(2, 8)}`; - return (base) => `${id ?? rand}-${base}`; - }, [id]); - - const x = useMemo(() => { - const dataWidth = data.length * DATA_POINT_WIDTH; - return scaleLinear() - .domain([data[0].index, data.last.index]) - .range([RANGE_PADDING, Math.max(dataWidth, width) - RANGE_PADDING]); - }, [data, width]); - - const [zoomXTranslation, setZoomXTranslation] = useState(0); - const zoomBehavior = useMemo( - () => - zoom() - .translateExtent(getZoomTranslateExtent(data, x)) - // Remove the zoom interpolation so it doesn't zoom back and forth. - .interpolate(interpolate) - .on('zoom', (event) => { - setZoomXTranslation(event.transform.x); - }), - [data, x] - ); - - const onDataOverOut = useCallback(({ hover, date }) => { - setHoveringDataPoint(date || null); - - if (svgRef.current) { - svgRef.current.style.cursor = hover ? 'pointer' : 'ew-resize'; - } - }, []); - - // Limit the data that is rendered so that performance is not hindered by very - // large datasets. - // Lower and Upper pixel bounds of the data that should be rendered taking the - // horizontal drag window into account. - const minX = zoomXTranslation * -1; - const maxX = width - zoomXTranslation; - // Using the scale, get the indices of the data that would be rendered. - const indices = [ - Math.max(Math.floor(x.invert(minX)), 0), - // Add one to account for when there's a single data point. - Math.ceil(x.invert(maxX)) + 1 - ]; - - const dataToRender = useMemo( - () => data.slice(...indices), - [data, ...indices] - ); - - // Recenter the slider to the selected date when data changes or when the - // chart gets resized. - const item = findDate(data, value, timeDensity); - useRecenterSlider({ value: item?.index, width, x, zoomBehavior, svgRef }); - - return ( -
- - - - - - - - - - - - - - -
- ); -} - -function useRecenterSlider({ value, width, x, zoomBehavior, svgRef }) { - const reduceMotion = useReducedMotion(); - - // The recenter function must be debounced because if it is triggered while - // another animation is already running, the X translation extent gets messed - // up. Debouncing a function with React is tricky. Since the function must - // access "fresh" parameters it is defined through a ref and then invoked in - // the debounced function created through useMemo. - - const recenterFnRef = useRef<() => void>(); - recenterFnRef.current = () => { - if (isNaN(value)) return; - - select(svgRef.current) - .select('.trigger-rect') - .transition() - .duration(500) - .call(zoomBehavior.translateTo, x(value), 0); - }; - - const debouncedRecenter = useMemo( - () => - debounce(() => { - recenterFnRef.current?.(); - }, 400), - [] - ); - - useEffectPrevious( - (deps, mounted) => { - if (isNaN(value) || !mounted) return; - - // No animation if reduce motion. - if (reduceMotion) { - zoomBehavior.translateTo( - select(svgRef.current).select('.trigger-rect'), - x(value), - 0 - ); - return; - } - - debouncedRecenter(); - - return () => { - debouncedRecenter.cancel(); - }; - }, - [value, width, x, zoomBehavior] - ); -} diff --git a/app/scripts/components/common/dateslider/trigger-rect.tsx b/app/scripts/components/common/dateslider/trigger-rect.tsx deleted file mode 100644 index d82af253d..000000000 --- a/app/scripts/components/common/dateslider/trigger-rect.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import React, { useEffect, useMemo, useRef } from 'react'; -import { ScaleLinear, select, ZoomBehavior } from 'd3'; - -import { DateSliderDataItem } from './constants'; - -interface TriggerRectProps { - onDataOverOut: (result: { hover: boolean; date: Date | null }) => void; - onDataClick: (result: { date: Date }) => void; - data: DateSliderDataItem[]; - x: ScaleLinear; - zoomXTranslation: number; - width: number; - height: number; - zoomBehavior: ZoomBehavior; -} - -interface Point { - x: number; - y: number; -} - -interface HotZone extends Point { - date: Date; - radius: number; -} - -function dist(p1: Point, p2: Point) { - return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); -} - -function getHotZone(zones: HotZone[], mouse) { - return zones.find( - (z) => dist(z, { x: mouse.layerX, y: mouse.layerY }) <= z.radius - ); -} - -export default function TriggerRect(props: TriggerRectProps) { - const { - onDataOverOut, - onDataClick, - width, - height, - data, - x, - zoomXTranslation, - zoomBehavior - } = props; - - const elRef = useRef(null); - const hoverDataRef = useRef(false); - - // Since we're applying the zoom behavior on an invisible trigger rect, it - // swallows all mouse events. To be able to handle a hover state or even a - // click on a data point we create a list of coordinates for where the data - // is, and then check if a mouse event is within a given radius of them. - const dataHotZones = useMemo(() => { - const dataList = data.filter((d) => d.hasData); - return dataList.map((d) => ({ - date: d.date, - x: x(d.index) + zoomXTranslation, - y: 12, - radius: 8 - })); - }, [data, x, zoomXTranslation]); - - useEffect(() => { - select(elRef.current) - .call(zoomBehavior) - .on('dblclick.zoom', null) - .on('wheel.zoom', (event) => { - event.preventDefault(); - zoomBehavior.translateBy(select(event.target), event.wheelDelta, 0); - }); - }, [zoomBehavior, x]); - - useEffect(() => { - select(elRef.current) - .on('click', (event) => { - const zone = getHotZone(dataHotZones, event); - if (zone) { - onDataClick({ date: zone.date }); - } - }) - .on('mousemove', (event) => { - const zone = getHotZone(dataHotZones, event); - const currHover = !!zone; - const prevHover = hoverDataRef.current; - - if (currHover !== prevHover) { - hoverDataRef.current = currHover; - onDataOverOut({ - hover: currHover, - date: currHover ? zone.date : null - }); - } - }) - .on('mouseout', () => { - onDataOverOut({ - hover: false, - date: null - }); - }); - }, [dataHotZones, onDataClick, onDataOverOut]); - - return ( - - ); -} diff --git a/app/scripts/components/common/dateslider/utils.tsx b/app/scripts/components/common/dateslider/utils.tsx deleted file mode 100644 index 21b650545..000000000 --- a/app/scripts/components/common/dateslider/utils.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import useDimensions from 'react-cool-dimensions'; -import { extent } from 'd3'; -import { - eachDayOfInterval, - eachMonthOfInterval, - eachYearOfInterval, - format -} from 'date-fns'; - -import { - CHART_HEIGHT, - DateSliderDataItem, - DateSliderTimeDensity -} from './constants'; - -const margin = { top: 0, right: 0, bottom: 0, left: 0 }; - -export function useChartDimensions() { - const { observe, width: elementWidth } = useDimensions(); - - const outerWidth = elementWidth > 0 ? elementWidth : 100; - const outerHeight = CHART_HEIGHT - margin.top - margin.bottom; - - return { - observe, - // Dimensions of the wrapper. - outerWidth, - outerHeight, - // Dimensions of the visualization. - width: outerWidth - margin.left - margin.right, - height: outerHeight - margin.top - margin.bottom, - margin - }; -} - -const dateFormatByTimeDensity = { - day: 'yyyy-MM-dd', - month: 'yyyy-MM', - year: 'yyyy' -}; - -/** - * Creates and array of date entries based on the extent of the provided list of - * dates, and the time density. - * For example, if the time density is month it will return a list of monthly - * dates between the min and max of the given list. - * For each date in the extent interval we check if there is data for that date. - * If there is we set the `hasData` property to true and continue. If there is - * no data, we start grouping the dates with no data. - * - If the group exceeds the threshold we group them in an entry with 'hasData' - * set to false and 'breakLength' set to the length of the group. - * - If it doesn't exceed we keep all dates with 'hasData' set to false. - * - * The resulting array of objects has an 'index' property added to each object, - * starting from 0 which is needed to correctly position the elements after a - * .filter operation where we can no longer rely on the array index. - * - * @param dates List of dates that have data - * @param timeDensity Time density of the dates. One of day | month | year - * @returns Data for the date slider - */ -export function prepareDateSliderData( - dates: Date[], - timeDensity: DateSliderTimeDensity -) { - const domain = extent(dates, (d) => d) as Date[]; - - const dateFormat = dateFormatByTimeDensity[timeDensity]; - - const intervalFn = { - day: eachDayOfInterval, - month: eachMonthOfInterval, - year: eachYearOfInterval - }[timeDensity]; - - const searchStrs = dates.map((d) => format(d, dateFormat)); - - const allDates = intervalFn({ - start: domain[0], - end: domain.last - }); - - let data: Omit[] = []; - let noDataGroup: Omit[] = []; - - for (const date of allDates) { - const hasData = searchStrs.includes(format(date, dateFormat)); - - if (!hasData) { - noDataGroup = noDataGroup.concat({ date, hasData }); - } else { - // If we find a date with data and it has been less than 20 entries - // without data we keep them all. - if (noDataGroup.length < 20) { - data = data.concat(noDataGroup, { date, hasData }); - } else { - // Otherwise we add a break group with the first date that doesn't have - // data and store how many timesteps don't have data (breakLength) - data = data.concat( - { - date: noDataGroup[0].date, - hasData: false, - breakLength: noDataGroup.length - }, - { date, hasData } - ); - } - noDataGroup = []; - } - } - - // Add an index property which is needed to correctly position the elements - // after a .filter operation where we can no longer rely on the array index. - return data.map((d, i) => ({ ...d, index: i })); -} - -export function getZoomTranslateExtent( - data, - xScale -): [[number, number], [number, number]] { - // The translation extent should always at least encompass the full chart. - // This handle problems coming from having only 1 data point. - const end = Math.max(xScale(data.last.index), xScale.range()[1]); - return [ - [0, 0], - [end + 16, 0] - ]; -} - -export function findDate( - data: DateSliderDataItem[], - date: Date | undefined | null, - timeDensity: DateSliderTimeDensity -) { - if (!date) return undefined; - - const dateFormat = dateFormatByTimeDensity[timeDensity]; - const item = data.find( - (d) => format(d.date, dateFormat) === format(date, dateFormat) - ); - - return item; -} diff --git a/app/scripts/components/sandbox/dateslider/index.js b/app/scripts/components/sandbox/dateslider/index.js deleted file mode 100644 index 2501b8869..000000000 --- a/app/scripts/components/sandbox/dateslider/index.js +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useState } from 'react'; -import styled from 'styled-components'; -import { format } from 'date-fns'; -import { DatePicker } from '@devseed-ui/date-picker'; -import { Button } from '@devseed-ui/button'; -import { - CollecticonChevronLeftSmall, - CollecticonChevronRightSmall -} from '@devseed-ui/collecticons'; - -import Constrainer from '$styles/constrainer'; -import { mod } from '$utils/utils'; -import { PageMainContent } from '$styles/page'; -import DateSliderControl from '$components/common/dateslider'; -import { prepareDateSliderData } from '$components/common/dateslider/utils'; -import { utcString2userTzDate } from '$utils/date'; - -const Wrapper = styled.div` - position: relative; - grid-column: 1 / -1; -`; - -const Box = styled.div` - max-width: 30rem; - margin: 3rem auto; -`; - -const dates = [ - utcString2userTzDate('2021-01-01'), - utcString2userTzDate('2022-01-01'), - utcString2userTzDate('2022-02-01'), - utcString2userTzDate('2022-03-01'), - utcString2userTzDate('2022-05-01'), - utcString2userTzDate('2022-06-01'), - utcString2userTzDate('2022-07-01'), - utcString2userTzDate('2022-08-01'), - utcString2userTzDate('2022-09-01'), - utcString2userTzDate('2022-10-01'), - utcString2userTzDate('2022-11-01'), - utcString2userTzDate('2022-12-01'), - utcString2userTzDate('2023-05-01') -]; -const readyDatesD = prepareDateSliderData(dates, 'day'); -const readyDatesM = prepareDateSliderData(dates, 'month'); -const readyDatesY = prepareDateSliderData(dates, 'year'); - -function SandboxTimeseries() { - const [activeDate, setActiveDate] = useState({ - start: dates[0], - end: dates[0] - }); - - const navigateDate = (modifier = 1) => { - const idx = dates.findIndex( - (d) => format(d, 'yyyy-MM-dd') === format(activeDate.start, 'yyyy-MM-dd') - ); - const nDate = dates[mod(idx + modifier, dates.length)]; - setActiveDate({ - start: nDate, - end: nDate - }); - }; - - return ( - - - - - - - -

Month

- setActiveDate({ start: date, end: date })} - /> -
- -

Year

- setActiveDate({ start: date, end: date })} - /> -
- -

Day

- setActiveDate({ start: date, end: date })} - /> -
- -

Single

- { - // no-op - }} - /> -
-
-
-
- ); -} - -export default SandboxTimeseries; diff --git a/app/scripts/components/sandbox/index.js b/app/scripts/components/sandbox/index.js index e9b57c271..1adf2b601 100644 --- a/app/scripts/components/sandbox/index.js +++ b/app/scripts/components/sandbox/index.js @@ -10,7 +10,6 @@ import SandboxCards from './cards'; import SandboxMDXPage from './mdx-page'; import SandboxMDXScrolly from './mdx-scrollytelling'; import SandboxOverride from './override'; -import SandboxDateslider from './dateslider'; import SandboxChart from './mdx-chart'; import SandboxColors from './colors'; import SandboxMDXEditor from './mdx-editor'; @@ -75,11 +74,6 @@ const pages = [ name: 'Override', component: SandboxOverride }, - { - id: 'dateslider', - name: 'Dateslider', - component: SandboxDateslider - }, { id: 'colors', name: 'Colors',