diff --git a/apps/backtest-report/components/TimeRangeSlider/components/Handle.js b/apps/backtest-report/components/TimeRangeSlider/components/Handle.js
new file mode 100644
index 000000000..338fb0133
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/components/Handle.js
@@ -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 (
+ <>
+
+
+ >
+ )
+}
+
+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
diff --git a/apps/backtest-report/components/TimeRangeSlider/components/KeyboardHandle.js b/apps/backtest-report/components/TimeRangeSlider/components/KeyboardHandle.js
new file mode 100644
index 000000000..077312bda
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/components/KeyboardHandle.js
@@ -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 }) => (
+
+)
+
+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
diff --git a/apps/backtest-report/components/TimeRangeSlider/components/SliderRail.js b/apps/backtest-report/components/TimeRangeSlider/components/SliderRail.js
new file mode 100644
index 000000000..5cfcef111
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/components/SliderRail.js
@@ -0,0 +1,13 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+export const SliderRail = ({ getRailProps }) => (
+ <>
+
+
+ >
+)
+
+SliderRail.propTypes = { getRailProps: PropTypes.func.isRequired }
+
+export default SliderRail
diff --git a/apps/backtest-report/components/TimeRangeSlider/components/Tick.js b/apps/backtest-report/components/TimeRangeSlider/components/Tick.js
new file mode 100644
index 000000000..078f7539c
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/components/Tick.js
@@ -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 (
+ <>
+
+ {isFullHour && (
+
+ {format(tick.value)}
+
+ )}
+ >
+ )
+}
+
+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
diff --git a/apps/backtest-report/components/TimeRangeSlider/components/Track.js b/apps/backtest-report/components/TimeRangeSlider/components/Track.js
new file mode 100644
index 000000000..275d55651
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/components/Track.js
@@ -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 }) => (
+
+)
+
+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
diff --git a/apps/backtest-report/components/TimeRangeSlider/index.jsx b/apps/backtest-report/components/TimeRangeSlider/index.jsx
new file mode 100644
index 000000000..b1674b5ac
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/index.jsx
@@ -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 (
+
+ +t)}
+ rootStyle={{position: 'relative', width: '100%'}}
+ >
+
+ {({getRailProps}) =>
+ }
+
+
+
+ {({handles, getHandleProps}) => (
+ <>
+ {handles.map(handle => (
+
+ ))}
+ >
+ )}
+
+
+
+ {({tracks, getTrackProps}) => (
+ <>
+ {tracks?.map(({id, source, target}) =>
+
+ )}
+ >
+ )}
+
+
+ {disabledIntervals?.length && (
+
+ {({getTrackProps}) => (
+ <>
+ {disabledIntervals.map(({id, source, target}) => (
+
+ ))}
+ >
+ )}
+
+ )}
+
+ {showNow && (
+
+ {({getTrackProps}) => (
+
+ )}
+
+ )}
+
+
+ {({ticks}) => (
+ <>
+ {ticks.map(tick => (
+
+ ))}
+ >
+ )}
+
+
+
+ )
+ }
+}
+
+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
diff --git a/apps/backtest-report/components/TimeRangeSlider/index.scss b/apps/backtest-report/components/TimeRangeSlider/index.scss
new file mode 100644
index 000000000..6b96f7376
--- /dev/null
+++ b/apps/backtest-report/components/TimeRangeSlider/index.scss
@@ -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;
+ }
+}
diff --git a/apps/backtest-report/components/TradingViewChart.tsx b/apps/backtest-report/components/TradingViewChart.tsx
index d22e76636..80ef68ea4 100644
--- a/apps/backtest-report/components/TradingViewChart.tsx
+++ b/apps/backtest-report/components/TradingViewChart.tsx
@@ -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([]);
+ 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)
+ 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 (
@@ -558,6 +585,15 @@ const TradingViewChart = (props: TradingViewChartProps) => {
+ {
+ console.log("selectedTimeRange", tr)
+ setSelectedTimeRange(tr)
+ }}
+ />
+
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);
}}/>
@@ -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"
})
}
diff --git a/apps/backtest-report/package.json b/apps/backtest-report/package.json
index dcc8a8d0d..36a268ab2 100644
--- a/apps/backtest-report/package.json
+++ b/apps/backtest-report/package.json
@@ -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"
}
}
diff --git a/apps/backtest-report/pages/_app.tsx b/apps/backtest-report/pages/_app.tsx
index 300e8a516..ae7758bd0 100644
--- a/apps/backtest-report/pages/_app.tsx
+++ b/apps/backtest-report/pages/_app.tsx
@@ -1,4 +1,6 @@
import '../styles/globals.css'
+import '../components/TimeRangeSlider/index.scss'
+
import type {AppProps} from 'next/app'
import Head from 'next/head';
diff --git a/apps/backtest-report/pages/_document.tsx b/apps/backtest-report/pages/_document.tsx
index 93210acd4..4fef35250 100644
--- a/apps/backtest-report/pages/_document.tsx
+++ b/apps/backtest-report/pages/_document.tsx
@@ -22,7 +22,7 @@ class MyDocument extends Document {
styles: [
<>
{initialProps.styles}
-
+
>
],
};
diff --git a/apps/backtest-report/yarn.lock b/apps/backtest-report/yarn.lock
index 6659a6e51..1733d3467 100644
--- a/apps/backtest-report/yarn.lock
+++ b/apps/backtest-report/yarn.lock
@@ -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==