Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, {lazy, Suspense} from 'react';
import {PersistedProps, PersistenceTypes, DateRangeSliderProps} from '../types';
import dateRangeSlider from '../utils/LazyLoader/dateRangeSlider';

import './css/datesliders.css';

const RealDateRangeSlider = lazy(dateRangeSlider);

/**
* A date range slider component.
* Used for specifying a range of dates with optional disabled date indicators
* and calendar-aware stepping.
*/
export default function DateRangeSlider({
updatemode = 'mouseup',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
persisted_props = [PersistedProps.value],
// eslint-disable-next-line @typescript-eslint/no-unused-vars
persistence_type = PersistenceTypes.local,
// eslint-disable-next-line no-magic-numbers
verticalHeight = 400,
allow_direct_input = true,
...props
}: DateRangeSliderProps) {

Check warning on line 24 in components/dash-core-components/src/components/DateRangeSlider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ55VHnoEMqJBb_Rbb9N&open=AZ55VHnoEMqJBb_Rbb9N&pullRequest=3802
return (
<Suspense fallback={null}>
<RealDateRangeSlider
updatemode={updatemode}
verticalHeight={verticalHeight}
allow_direct_input={allow_direct_input}
{...props}
/>
</Suspense>
);
}

DateRangeSlider.dashPersistence = {
persisted_props: [PersistedProps.value],
persistence_type: PersistenceTypes.local,
};
174 changes: 174 additions & 0 deletions components/dash-core-components/src/components/DateSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React, {
lazy,
Suspense,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import {omit} from 'ramda';
import DatePickerSingle from '../components/DatePickerSingle';
import {
PersistedProps,
PersistenceTypes,
DateSliderProps,
DateRangeSliderProps,
} from '../types';
import {strAsDate, snapToStep} from '../utils/calendar/helpers';
import dateRangeSlider from '../utils/LazyLoader/dateRangeSlider';
import './css/datesliders.css';

const RealSlider = lazy(dateRangeSlider);
const narrowWindow = 500;

/**
* A slider component for selecting a single date.
* This is a wrapper around DateRangeSlider that handles date values.
*/
export default function DateSlider({
updatemode = 'mouseup',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
persisted_props = [PersistedProps.value],
// eslint-disable-next-line @typescript-eslint/no-unused-vars
persistence_type = PersistenceTypes.local,
// eslint-disable-next-line no-magic-numbers
verticalHeight = 400,
allow_direct_input = true,
setProps,
value,
drag_value,
id,
vertical = false,
min,
max,
display_format,
...props
}: DateSliderProps) {

Check warning on line 47 in components/dash-core-components/src/components/DateSlider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=plotly_dash&issues=AZ55VHkpEMqJBb_Rbb9M&open=AZ55VHkpEMqJBb_Rbb9M&pullRequest=3802
const [resetKey, setResetKey] = useState(0);
const [isNarrow, setIsNarrow] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);

// Convert single date value to array for DateRangeSlider
const mappedValue: DateRangeSliderProps['value'] = useMemo(() => {
return value ? [value] : value;
}, [value]);

// Convert single date drag value to array for DateRangeSlider
const mappedDragValue: DateRangeSliderProps['drag_value'] = useMemo(() => {
return drag_value ? [drag_value] : undefined;
}, [drag_value]);

const mappedSetProps: DateRangeSliderProps['setProps'] = useCallback(
newProps => {
const {value, drag_value} = newProps;
const mappedProps: Partial<DateSliderProps> = omit(
['value', 'drag_value', 'setProps'],
newProps
);
if ('value' in newProps) {
mappedProps.value = value ? value[0] : value;
}
if ('drag_value' in newProps) {
mappedProps.drag_value = drag_value
? drag_value[0]
: drag_value;
}
setProps(mappedProps);
},
[setProps]
);

const handleDateInputChange = useCallback(
(dateStr: `${string}-${string}-${string}` | undefined) => {
if (!dateStr) {
setProps({
value:
(min as `${string}-${string}-${string}`) ?? undefined,
});
return;
}

const inputDate = strAsDate(dateStr);
if (inputDate && props.step && props.step_unit) {
const parsedMin = strAsDate(min);
const snapped = snapToStep(
inputDate,
parsedMin ?? inputDate,
props.step,
props.step_unit
);
if (snapped.getTime() !== inputDate.getTime()) {
setResetKey(k => k + 1); // rejeitar
return;
}
}

const hasNoChange = value === dateStr;
if (hasNoChange) {
setResetKey(k => k + 1);
} else {
setProps({value: dateStr});
}
},
[value, setProps, min, props.step, props.step_unit]
);

// Resize Logic
useEffect(() => {
if (!containerRef.current) {
return undefined;
}
const observer = new ResizeObserver(([entry]) => {
setIsNarrow(entry.contentRect.width < narrowWindow);
});
observer.observe(containerRef.current);
return () => observer.disconnect();
}, []);

return (
<div
ref={containerRef}
className="date-slider-container"
data-vertical={vertical}
data-narrow={isNarrow}
>
<div className="dash-slider-wrapper">
<Suspense fallback={null}>
<RealSlider
key={resetKey}
id={id}
updatemode={updatemode}
verticalHeight={verticalHeight}
allow_direct_input={false}
vertical={vertical}
min={min}
max={max}
display_format={display_format}
value={mappedValue}
drag_value={mappedDragValue}
setProps={mappedSetProps}
{...props}
/>
</Suspense>
</div>
{allow_direct_input && (
<div className="dash-range-slider-min-input">
<DatePickerSingle
key={`date-input-${resetKey}`}
date={value ?? undefined}
setProps={({date}) => handleDateInputChange(date)}
min_date_allowed={min}
max_date_allowed={max}
display_format={display_format}
/>
</div>
)}
</div>
);
}

DateSlider.dashPersistence = {
persisted_props: [PersistedProps.value],
persistence_type: PersistenceTypes.local,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
.date-slider-container,
.date-range-slider-container {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
}

.date-slider-container[data-vertical='true'],
.date-range-slider-container[data-vertical='true'] {
flex-direction: column;
align-items: stretch;
}

.dash-range-slider-max-input {
order: 1;
max-width: 140px;
}

.dash-range-slider-min-input {
text-align: center;
max-width: 140px;
}

.dash-slider-wrapper {
flex: 1;
min-width: 0;
width: 100%;
}

.dash-date-range-slider-wrapper {
position: relative;
flex: 1;
padding: 0 28px;
box-sizing: border-box;
min-width: 0;
width: 100%;
}

/* Slider */
.date-slider-container[data-narrow='true'] .dash-range-slider-min-input {
display: none;
}

@container (max-width: 130px) {
.date-slider-container[data-vertical='true'] .dash-range-slider-min-input {
display: none !important;
}
}

/* Range Slider */
.date-range-slider-container[data-narrow='true'] .dash-range-slider-min-input,
.date-range-slider-container[data-narrow='true'] .dash-range-slider-max-input {
display: none;
}

@container (max-width: 130px) {
.date-range-slider-container[data-vertical='true'] .dash-range-slider-min-input,
.date-range-slider-container[data-vertical='true'] .dash-range-slider-max-input {
display: none !important;
}
}

.dash-slider-tooltip {
display: none;
position: absolute;
border-radius: var(--Dash-Spacing);
padding: calc(var(--Dash-Spacing) * 3);
font-size: 12px;
line-height: 1;
box-shadow: 0 0 8px var(--Dash-Shading-Strong);
background-color: var(--Dash-Fill-Inverse-Strong);
user-select: none;
z-index: 1000;
fill: var(--Dash-Fill-Inverse-Strong);
white-space: nowrap;
}

.dash-slider-mark {
position: absolute;
font-size: 12px;
height: 12px;
line-height: 12px;
color: var(--Dash-Text-Strong);
white-space: nowrap;
pointer-events: none;
z-index: 10;
transform: translateX(
max(-50%, calc(0px - var(--dash-mark-offset, 0px)))
);
}

.dash-datepicker {
width: 140px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,4 @@
.dash-slider-container .dash-range-slider-max-input {
display: none;
}
}
}
Loading
Loading