add time range slider

This commit is contained in:
c9s 2022-06-27 13:49:05 +08:00
parent 0ad63b55d3
commit 94a70f4fbe
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
12 changed files with 744 additions and 16 deletions

View File

@ -0,0 +1,44 @@
import PropTypes from 'prop-types'
import React from 'react'
const Handle = ({
error,
domain: [min, max],
handle: { id, value, percent = 0 },
disabled,
getHandleProps,
}) => {
const leftPosition = `${percent}%`
return (
<>
<div className='react_time_range__handle_wrapper' style={{ left: leftPosition }} {...getHandleProps(id)} />
<div
role='slider'
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
className={`react_time_range__handle_container${disabled ? '__disabled' : ''}`}
style={{ left: leftPosition }}
>
<div className={`react_time_range__handle_marker${error ? '__error' : ''}`} />
</div>
</>
)
}
Handle.propTypes = {
domain: PropTypes.array.isRequired,
handle: PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired
}).isRequired,
getHandleProps: PropTypes.func.isRequired,
disabled: PropTypes.bool,
style: PropTypes.object,
}
Handle.defaultProps = { disabled: false }
export default Handle

View File

@ -0,0 +1,32 @@
import PropTypes from 'prop-types'
import React from 'react'
const KeyboardHandle = ({ domain: [min, max], handle: { id, value, percent = 0 }, disabled, getHandleProps }) => (
<button
role='slider'
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
className='react_time_range__keyboard_handle'
style={{
left: `${percent}%`,
backgroundColor: disabled ? '#666' : '#ffc400'
}}
{...getHandleProps(id)}
/>
)
KeyboardHandle.propTypes = {
domain: PropTypes.array.isRequired,
handle: PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired
}).isRequired,
getHandleProps: PropTypes.func.isRequired,
disabled: PropTypes.bool
}
KeyboardHandle.defaultProps = { disabled: false }
export default KeyboardHandle

View File

@ -0,0 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
export const SliderRail = ({ getRailProps }) => (
<>
<div className='react_time_range__rail__outer' {...getRailProps()} />
<div className='react_time_range__rail__inner' />
</>
)
SliderRail.propTypes = { getRailProps: PropTypes.func.isRequired }
export default SliderRail

View File

@ -0,0 +1,41 @@
import { getMinutes } from 'date-fns'
import PropTypes from 'prop-types'
import React from 'react'
const Tick = ({ tick, count, format }) => {
const isFullHour = !getMinutes(tick.value)
const tickLabelStyle = {
marginLeft: `${-(100 / count) / 2}%`,
width: `${100 / count}%`,
left: `${tick.percent}%`,
}
return (
<>
<div
className={`react_time_range__tick_marker${isFullHour ? '__large' : ''}`}
style={{ left: `${tick.percent}%` }}
/>
{isFullHour && (
<div className='react_time_range__tick_label' style={tickLabelStyle}>
{format(tick.value)}
</div>
)}
</>
)
}
Tick.propTypes = {
tick: PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired
}).isRequired,
count: PropTypes.number.isRequired,
format: PropTypes.func.isRequired
}
Tick.defaultProps = { format: d => d }
export default Tick

View File

@ -0,0 +1,52 @@
import PropTypes from 'prop-types'
import React from 'react'
const getTrackConfig = ({ error, source, target, disabled }) => {
const basicStyle = {
left: `${source.percent}%`,
width: `calc(${target.percent - source.percent}% - 1px)`,
}
if (disabled) return basicStyle
const coloredTrackStyle = error
? {
backgroundColor: 'rgba(214,0,11,0.5)',
borderLeft: '1px solid rgba(214,0,11,0.5)',
borderRight: '1px solid rgba(214,0,11,0.5)',
}
: {
backgroundColor: 'rgba(98, 203, 102, 0.5)',
borderLeft: '1px solid #62CB66',
borderRight: '1px solid #62CB66',
}
return { ...basicStyle, ...coloredTrackStyle }
}
const Track = ({ error, source, target, getTrackProps, disabled }) => (
<div
className={`react_time_range__track${disabled ? '__disabled' : ''}`}
style={getTrackConfig({ error, source, target, disabled })}
{...getTrackProps()}
/>
)
Track.propTypes = {
source: PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired
}).isRequired,
target: PropTypes.shape({
id: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
percent: PropTypes.number.isRequired
}).isRequired,
getTrackProps: PropTypes.func.isRequired,
disabled: PropTypes.bool
}
Track.defaultProps = { disabled: false }
export default Track

View File

@ -0,0 +1,256 @@
import React from 'react'
import PropTypes from 'prop-types'
import {scaleTime} from 'd3-scale'
import {Handles, Rail, Slider, Ticks, Tracks} from 'react-compound-slider'
import {
addHours,
addMinutes,
differenceInMilliseconds,
endOfToday,
format,
isAfter,
isBefore,
set,
startOfToday,
} from 'date-fns'
import SliderRail from './components/SliderRail'
import Track from './components/Track'
import Tick from './components/Tick'
import Handle from './components/Handle'
const getTimelineConfig = (timelineStart, timelineLength) => (date) => {
const percent = differenceInMilliseconds(date, timelineStart) / timelineLength * 100
const value = Number(format(date, 'T'))
return {percent, value}
}
const getFormattedBlockedIntervals = (blockedDates = [], [startTime, endTime]) => {
if (!blockedDates.length) return null
const timelineLength = differenceInMilliseconds(endTime, startTime)
const getConfig = getTimelineConfig(startTime, timelineLength)
const formattedBlockedDates = blockedDates.map((interval, index) => {
let {start, end} = interval
if (isBefore(start, startTime)) start = startTime
if (isAfter(end, endTime)) end = endTime
const source = getConfig(start)
const target = getConfig(end)
return {id: `blocked-track-${index}`, source, target}
})
return formattedBlockedDates
}
const getNowConfig = ([startTime, endTime]) => {
const timelineLength = differenceInMilliseconds(endTime, startTime)
const getConfig = getTimelineConfig(startTime, timelineLength)
const source = getConfig(new Date())
const target = getConfig(addMinutes(new Date(), 1))
return {id: 'now-track', source, target}
}
class TimeRange extends React.Component {
get disabledIntervals() {
return getFormattedBlockedIntervals(this.props.disabledIntervals, this.props.timelineInterval)
}
get now() {
return getNowConfig(this.props.timelineInterval)
}
onChange = newTime => {
const formattedNewTime = newTime.map(t => new Date(t))
if (this.props.onChange) {
this.props.onChange(formattedNewTime)
}
}
checkIsSelectedIntervalNotValid = ([start, end], source, target) => {
const {value: startInterval} = source
const {value: endInterval} = target
if (startInterval > start && endInterval <= end || startInterval >= start && endInterval < end)
return true
if (start >= startInterval && end <= endInterval) return true
const isStartInBlockedInterval = start > startInterval && start < endInterval && end >= endInterval
const isEndInBlockedInterval = end < endInterval && end > startInterval && start <= startInterval
return isStartInBlockedInterval || isEndInBlockedInterval
}
onUpdate = newTime => {
const {onUpdate} = this.props
if (!onUpdate) {
return
}
const disabledIntervals = this.disabledIntervals
if (disabledIntervals?.length) {
const isValuesNotValid = disabledIntervals.some(({source, target}) =>
this.checkIsSelectedIntervalNotValid(newTime, source, target))
const formattedNewTime = newTime.map(t => new Date(t))
onUpdate({error: isValuesNotValid, time: formattedNewTime})
return
}
const formattedNewTime = newTime.map(t => new Date(t))
onUpdate({error: false, time: formattedNewTime})
}
getDateTicks = () => {
const {timelineInterval, ticksNumber} = this.props
return scaleTime().domain(timelineInterval).ticks(ticksNumber).map(t => +t)
}
render() {
const {
sliderRailClassName,
timelineInterval,
selectedInterval,
containerClassName,
error,
step,
showNow,
formatTick,
mode,
} = this.props
const domain = timelineInterval.map(t => Number(t))
const disabledIntervals = this.disabledIntervals
return (
<div className={containerClassName || 'react_time_range__time_range_container'}>
<Slider
mode={mode}
step={step}
domain={domain}
onUpdate={this.onUpdate}
onChange={this.onChange}
values={selectedInterval?.map(t => +t)}
rootStyle={{position: 'relative', width: '100%'}}
>
<Rail>
{({getRailProps}) =>
<SliderRail className={sliderRailClassName} getRailProps={getRailProps}/>}
</Rail>
<Handles>
{({handles, getHandleProps}) => (
<>
{handles.map(handle => (
<Handle
error={error}
key={handle.id}
handle={handle}
domain={domain}
getHandleProps={getHandleProps}
/>
))}
</>
)}
</Handles>
<Tracks left={false} right={false}>
{({tracks, getTrackProps}) => (
<>
{tracks?.map(({id, source, target}) =>
<Track
error={error}
key={id}
source={source}
target={target}
getTrackProps={getTrackProps}
/>
)}
</>
)}
</Tracks>
{disabledIntervals?.length && (
<Tracks left={false} right={false}>
{({getTrackProps}) => (
<>
{disabledIntervals.map(({id, source, target}) => (
<Track
key={id}
source={source}
target={target}
getTrackProps={getTrackProps}
disabled
/>
))}
</>
)}
</Tracks>
)}
{showNow && (
<Tracks left={false} right={false}>
{({getTrackProps}) => (
<Track
key={this.now?.id}
source={this.now?.source}
target={this.now?.target}
getTrackProps={getTrackProps}
/>
)}
</Tracks>
)}
<Ticks values={this.getDateTicks()}>
{({ticks}) => (
<>
{ticks.map(tick => (
<Tick
key={tick.id}
tick={tick}
count={ticks.length}
format={formatTick}
/>
))}
</>
)}
</Ticks>
</Slider>
</div>
)
}
}
TimeRange.propTypes = {
ticksNumber: PropTypes.number.isRequired,
selectedInterval: PropTypes.arrayOf(PropTypes.object),
timelineInterval: PropTypes.arrayOf(PropTypes.object),
disabledIntervals: PropTypes.arrayOf(PropTypes.object),
containerClassName: PropTypes.string,
sliderRailClassName: PropTypes.string,
step: PropTypes.number,
formatTick: PropTypes.func,
}
TimeRange.defaultProps = {
selectedInterval: [
set(new Date(), {minutes: 0, seconds: 0, milliseconds: 0}),
set(addHours(new Date(), 1), {minutes: 0, seconds: 0, milliseconds: 0})
],
timelineInterval: [startOfToday(), endOfToday()],
formatTick: ms => format(new Date(ms), 'HH:mm'),
disabledIntervals: [],
step: 1000 * 60 * 30,
ticksNumber: 48,
error: false,
mode: 3,
}
export default TimeRange

View File

@ -0,0 +1,126 @@
$react-time-range--gray: #C8CACC;
$react-time-range--highlight-tap: #000000;
$react-time-range--rail-bg: #F5F7FA;
$react-time-range--handle-bg: #FFFFFF;
$react-time-range--handle-bg--disabled: #666;
$react-time-range--track--valid: rgb(98, 203, 102);
$react-time-range--track--not-valid: rgb(214, 0, 11);
$react-time-range--tick-label: #77828C;
$react-time-range--track--disabled: repeating-linear-gradient( -45deg, transparent, transparent 3px, #D0D3D7 4px, #D0D3D7 2px);
.react_time_range__time_range_container {
padding: 30px 52px 0 52px;
height: 70px;
// width: 90%;
box-sizing: border-box;
}
.react_time_range__keyboard_handle {
position: absolute;
transform: translate(-50%, -50%);
z-index: 3;
width: 24px;
height: 24px;
border-radius: 50%;
box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, 0.3);
}
.react_time_range__track {
position: absolute;
transform: translate(0%, -50%);
height: 50px;
cursor: pointer;
transition: background-color .15s ease-in-out, border-color .15s ease;
z-index: 3;
&__disabled {
@extend .react_time_range__track;
z-index: 1;
border-left: 1px solid $react-time-range--gray;
border-right: 1px solid $react-time-range--gray;
background: $react-time-range--track--disabled;
}
}
.react_time_range__rail {
&__outer {
position: absolute;
width: 100%;
height: 50px;
transform: translate(0%, -50%);
cursor: pointer;
}
&__inner {
position: absolute;
width: 100%;
height: 50px;
transform: translate(0%, -50%);
pointer-events: none;
background-color: $react-time-range--rail-bg;
border-bottom: 1px solid $react-time-range--gray;
}
}
.react_time_range__handle {
&_wrapper {
position: absolute;
transform: translate(-50%, -50%);
-webkit-tap-highlight-color: $react-time-range--highlight-tap;
z-index: 6;
width: 24px;
height: 24px;
cursor: pointer;
background-color: transparent;
}
&_container {
position: absolute;
display: flex;
transform: translate(-50%, -50%);
z-index: 4;
width: 10px;
height: 24px;
border-radius: 4px;
box-shadow: 0 0 3px rgba(0,0,0, 0.4);
background-color: $react-time-range--handle-bg;
&__disabled {
@extend .react_time_range__handle_container;
background-color: $react-time-range--handle-bg--disabled;
}
}
&_marker {
width: 2px;
height: 12px;
margin: auto;
border-radius: 2px;
background-color: $react-time-range--track--valid;
transition: background-color .2s ease;
&__error {
@extend .react_time_range__handle_marker;
background-color: $react-time-range--track--not-valid;
}
}
}
.react_time_range__tick {
&_marker {
position: absolute;
margin-top: 20px;
width: 1px;
height: 5px;
background-color: $react-time-range--gray;
z-index: 2;
&__large {
@extend .react_time_range__tick_marker;
margin-top: 15px;
height: 10px;
}
}
&_label {
position: absolute;
margin-top: 28px;
font-size: 10px;
text-align: center;
z-index: 2;
color: $react-time-range--tick-label;
font-family: sans-serif;
}
}

View File

@ -2,12 +2,12 @@ import React, {useEffect, useRef, useState} from 'react';
import {tsvParse} from "d3-dsv";
import {Checkbox, Group, SegmentedControl, Table} from '@mantine/core';
// https://github.com/tradingview/lightweight-charts/issues/543
// const createChart = dynamic(() => import('lightweight-charts'));
import {createChart, CrosshairMode, MouseEventParams, TimeRange} from 'lightweight-charts';
import {ReportSummary} from "../types";
import moment from "moment";
import TimeRangeSlider from './TimeRangeSlider';
const parseKline = () => {
return (d: any) => {
@ -34,6 +34,23 @@ const parseKline = () => {
};
};
const selectKLines = (klines: KLine[], startTime: Date, endTime: Date): KLine[] => {
const selected = [];
for (let i = 0; i < klines.length; i++) {
const k = klines[i]
if (k.startTime < startTime) {
continue
}
if (k.startTime > endTime) {
break
}
selected.push(k)
}
return selected
}
const parseOrder = () => {
return (d: any) => {
@ -383,6 +400,13 @@ const TradingViewChart = (props: TradingViewChartProps) => {
const [showPositionAverageCost, setShowPositionAverageCost] = useState(false);
const [orders, setOrders] = useState<Order[]>([]);
const reportTimeRange = [
new Date(props.reportSummary.startTime),
new Date(props.reportSummary.endTime),
]
const [selectedTimeRange, setSelectedTimeRange] = useState(reportTimeRange)
const [timeRange, setTimeRange] = useState(reportTimeRange);
useEffect(() => {
if (!chartContainerRef.current || chartContainerRef.current.children.length > 0) {
return;
@ -412,7 +436,10 @@ const TradingViewChart = (props: TradingViewChartProps) => {
}
const kLinesFetcher = fetchKLines(props.basePath, props.runID, props.symbol, currentInterval, new Date(props.reportSummary.startTime), new Date(props.reportSummary.endTime)).then((klines) => {
chartData.klines = removeDuplicatedKLines(klines as Array<KLine>)
if (klines) {
chartData.allKLines = removeDuplicatedKLines(klines)
chartData.klines = selectKLines(chartData.allKLines, selectedTimeRange[0], selectedTimeRange[1])
}
});
fetchers.push(kLinesFetcher);
@ -537,7 +564,7 @@ const TradingViewChart = (props: TradingViewChartProps) => {
}
};
}, [props.runID, props.reportSummary, currentInterval, showPositionBase, showPositionAverageCost])
}, [props.runID, props.reportSummary, currentInterval, showPositionBase, showPositionAverageCost, selectedTimeRange])
return (
<div>
@ -558,6 +585,15 @@ const TradingViewChart = (props: TradingViewChartProps) => {
</div>
<TimeRangeSlider
selectedInterval={selectedTimeRange}
timelineInterval={timeRange}
onChange={(tr: any) => {
console.log("selectedTimeRange", tr)
setSelectedTimeRange(tr)
}}
/>
<Group>
<Checkbox label="Show Canceled" checked={showCanceledOrders}
onChange={(event) => setShowCanceledOrders(event.currentTarget.checked)}/>
@ -575,7 +611,7 @@ const TradingViewChart = (props: TradingViewChartProps) => {
console.log("orderTime", orderTime)
console.log("visibleRange", visibleRange)
console.log("setVisibleRange", from, to, to - from)
chart.current.timeScale().setVisibleRange({ from, to } as TimeRange);
chart.current.timeScale().setVisibleRange({from, to} as TimeRange);
// chart.current.timeScale().scrollToPosition(20, true);
}}/>
</div>
@ -592,7 +628,7 @@ const OrderListTable = (props: OrderListTableProps) => {
let orders = props.orders;
if (!props.showCanceled) {
orders = orders.filter((order : Order) => {
orders = orders.filter((order: Order) => {
return order.status != "CANCELED"
})
}

View File

@ -15,11 +15,14 @@
"@mantine/next": "^4.2.5",
"d3-dsv": "^3.0.1",
"d3-format": "^3.1.0",
"d3-scale": "^4.0.2",
"d3-time-format": "^4.1.0",
"date-fns": "^2.28.0",
"lightweight-charts": "^3.8.0",
"moment": "^2.29.3",
"next": "12.1.6",
"react": "18.1.0",
"react-compound-slider": "^3.4.0",
"react-dom": "18.1.0",
"tabler-icons-react": "^1.48.0"
},
@ -33,6 +36,7 @@
"eslint": "8.14.0",
"eslint-config-next": "12.1.6",
"next-transpile-modules": "^9.0.0",
"sass": "^1.53.0",
"typescript": "4.6.4"
}
}

View File

@ -1,4 +1,6 @@
import '../styles/globals.css'
import '../components/TimeRangeSlider/index.scss'
import type {AppProps} from 'next/app'
import Head from 'next/head';

View File

@ -22,7 +22,7 @@ class MyDocument extends Document {
styles: [
<>
{initialProps.styles}
<ServerStyles html={initialProps.html} server={stylesServer}/>
<ServerStyles key="server-styles" html={initialProps.html} server={stylesServer}/>
</>
],
};

View File

@ -17,6 +17,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.12.5":
version "7.18.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
dependencies:
regenerator-runtime "^0.13.4"
"@emotion/cache@11.7.1", "@emotion/cache@^11.7.1":
version "11.7.1"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"
@ -515,6 +522,14 @@ ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@ -584,6 +599,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -592,7 +612,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^3.0.2:
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@ -630,6 +650,21 @@ chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
"chokidar@>=3.0.0 <4.0.0":
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
clsx@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
@ -686,13 +721,25 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33"
integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==
"d3-array@2 - 3":
"d3-array@2 - 3", "d3-array@2.10.0 - 3":
version "3.1.6"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.6.tgz#0342c835925826f49b4d16eb7027aec334ffc97d"
integrity sha512-DCbBBNuKOeiR9h04ySRBMW52TFVc91O9wJziuyXw6Ztmy8D3oZbmCkOO3UHKC7ceNJsN2Mavo9+vwV8EAEUXzA==
dependencies:
internmap "1 - 2"
d3-array@^2.8.0:
version "2.12.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81"
integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==
dependencies:
internmap "^1.0.0"
"d3-color@1 - 3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
d3-dsv@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
@ -702,19 +749,37 @@ d3-dsv@^3.0.1:
iconv-lite "0.6"
rw "1"
d3-format@^3.1.0:
"d3-format@1 - 3", d3-format@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
d3-time-format@^4.1.0:
"d3-interpolate@1.2.0 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
dependencies:
d3-color "1 - 3"
d3-scale@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
dependencies:
d3-array "2.10.0 - 3"
d3-format "1 - 3"
d3-interpolate "1.2.0 - 3"
d3-time "2.1.1 - 3"
d3-time-format "2 - 4"
"d3-time-format@2 - 4", d3-time-format@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
dependencies:
d3-time "1 - 3"
"d3-time@1 - 3":
"d3-time@1 - 3", "d3-time@2.1.1 - 3":
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975"
integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==
@ -726,6 +791,11 @@ damerau-levenshtein@^1.0.7:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
date-fns@^2.28.0:
version "2.28.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -1178,6 +1248,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@ -1220,7 +1295,7 @@ get-symbol-description@^1.0.0:
call-bind "^1.0.2"
get-intrinsic "^1.1.1"
glob-parent@^5.1.2:
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@ -1376,6 +1451,11 @@ ignore@^5.2.0:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
immutable@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
import-fresh@^3.0.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@ -1421,6 +1501,11 @@ internal-slot@^1.0.3:
resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
internmap@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95"
integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
@ -1428,6 +1513,13 @@ is-bigint@^1.0.1:
dependencies:
has-bigints "^1.0.1"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-boolean-object@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
@ -1460,7 +1552,7 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@ -1720,6 +1812,11 @@ next@12.1.6:
"@next/swc-win32-ia32-msvc" "12.1.6"
"@next/swc-win32-x64-msvc" "12.1.6"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@ -1860,7 +1957,7 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.3.1:
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@ -1903,6 +2000,15 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
react-compound-slider@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-3.4.0.tgz#0367befe1367bb7968b38d0cbf07db6192b3c57e"
integrity sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==
dependencies:
"@babel/runtime" "^7.12.5"
d3-array "^2.8.0"
warning "^4.0.3"
react-dom@18.1.0:
version "18.1.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f"
@ -1973,6 +2079,13 @@ readable-stream@~1.0.17, readable-stream@~1.0.27-1:
isarray "0.0.1"
string_decoder "~0.10.x"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
@ -2048,6 +2161,15 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sass@^1.53.0:
version "1.53.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.53.0.tgz#eab73a7baac045cc57ddc1d1ff501ad2659952eb"
integrity sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
scheduler@^0.22.0:
version "0.22.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"
@ -2093,7 +2215,7 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
source-map-js@^1.0.1:
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@ -2313,7 +2435,7 @@ v8-compile-cache@^2.0.3:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
warning@^4.0.2:
warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==