Skip to content
Merged
14 changes: 12 additions & 2 deletions components/dash-core-components/src/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ function Input({
(direction: 'increment' | 'decrement') => {
const currentValue = parseFloat(input.current.value) || 0;
const stepAsNum = parseFloat(step as string) || 1;

// Count decimal places to avoid floating point precision issues
const decimalPlaces = (stepAsNum.toString().split('.')[1] || '')
.length;

const newValue =
direction === 'increment'
? currentValue + stepAsNum
Expand All @@ -196,8 +201,13 @@ function Input({
);
}

input.current.value = constrainedValue.toString();
setValue(constrainedValue.toString());
// Round to the step's decimal precision
const roundedValue = parseFloat(
constrainedValue.toFixed(decimalPlaces)
);

input.current.value = roundedValue.toString();
setValue(roundedValue.toString());
onEvent();
},
[step, props.min, props.max, onEvent]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,75 @@
z-index: 500;
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
0px 10px 20px -15px var(--Dash-Shading-Weak);
overscroll-behavior: contain;
}

.dash-datepicker
[data-radix-popper-content-wrapper]:has(.dash-datepicker-portal) {
transform: none !important;
}

.dash-datepicker-portal {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
max-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
background: var(--Dash-Shading-Strong);
border: none;
box-shadow: none;
overflow: visible;
padding: 0;
pointer-events: none;
}

.dash-datepicker-portal .dash-datepicker-calendar-wrapper {
background: var(--Dash-Fill-Inverse-Strong);
border-radius: var(--Dash-Spacing);
border: 1px solid var(--Dash-Stroke-Strong);
padding: 16px;
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
0px 10px 20px -15px var(--Dash-Shading-Weak);
z-index: 1;
pointer-events: auto;
width: fit-content;
max-width: 95vw;
}

.dash-datepicker-fullscreen {
pointer-events: auto;
background: var(--Dash-Fill-Inverse-Strong);
}

.dash-datepicker-close-button {
position: fixed;
top: calc(var(--Dash-Spacing) * 2);
right: calc(var(--Dash-Spacing) * 2);
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background: var(--Dash-Fill-Inverse-Strong);
border: none;
border-radius: var(--Dash-Spacing);
color: var(--Dash-Text-Strong);
cursor: pointer;
z-index: 501;
pointer-events: auto;
}

.dash-datepicker-close-button:hover {
background: var(--Dash-Fill-Weak);
color: var(--Dash-Fill-Interactive-Strong);
}

.dash-datepicker-close-button:focus {
outline: 2px solid var(--Dash-Fill-Interactive-Strong);
outline-offset: 2px;
}

.dash-datepicker-calendar-wrapper {
Expand All @@ -155,6 +224,7 @@
display: flex;
align-items: flex-start;
gap: calc(var(--Dash-Spacing) * 4);
flex-wrap: wrap;
}

.dash-datepicker-controls {
Expand Down Expand Up @@ -226,8 +296,3 @@
width: 20px;
height: 20px;
}

/* Override Radix's position: fixed to use position: absolute when using custom container */
div[data-radix-popper-content-wrapper]:has(.dash-datepicker-content) {
position: absolute !important;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
z-index: 500;
box-shadow: 0px 10px 38px -10px var(--Dash-Shading-Strong),
0px 10px 20px -15px var(--Dash-Shading-Weak);
overscroll-behavior: contain;
}

.dash-dropdown-value-count,
Expand Down Expand Up @@ -235,8 +236,3 @@
.dash-dropdown-wrapper {
position: relative;
}

/* Override Radix's position: fixed to use position: absolute when using custom container */
div[data-radix-popper-content-wrapper]:has(.dash-dropdown-content) {
position: absolute !important;
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const DatePickerRange = ({
end_date_id,
start_date_placeholder_text = 'Start Date',
end_date_placeholder_text = 'End Date',
with_portal = false,
with_full_screen_portal = false,
}: DatePickerRangeProps) => {
const [internalStartDate, setInternalStartDate] = useState(
strAsDate(start_date)
Expand Down Expand Up @@ -102,6 +104,7 @@ const DatePickerRange = ({
const startInputRef = useRef<HTMLInputElement | null>(null);
const endInputRef = useRef<HTMLInputElement | null>(null);
const calendarRef = useRef<CalendarHandle>(null);
const hasPortal = with_portal || with_full_screen_portal;

useEffect(() => {
setInternalStartDate(strAsDate(start_date));
Expand Down Expand Up @@ -333,7 +336,7 @@ const DatePickerRange = ({
id={start_date_id || accessibleId}
inputClassName="dash-datepicker-input dash-datepicker-start-date"
value={startInputValue}
onChange={e => setStartInputValue(e.target.value)}
onChange={e => setStartInputValue(e.target?.value)}
onKeyDown={handleStartInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
Expand All @@ -354,7 +357,7 @@ const DatePickerRange = ({
id={end_date_id || accessibleId + '-end-date'}
inputClassName="dash-datepicker-input dash-datepicker-end-date"
value={endInputValue}
onChange={e => setEndInputValue(e.target.value)}
onChange={e => setEndInputValue(e.target?.value)}
onKeyDown={handleEndInputKeyDown}
onFocus={() => {
if (isCalendarOpen) {
Expand All @@ -381,12 +384,21 @@ const DatePickerRange = ({

<Popover.Portal container={containerRef.current}>
<Popover.Content
className="dash-datepicker-content"
align="start"
sideOffset={5}
collisionBoundary={containerRef.current?.closest(
'#_dash-app-content'
)}
className={`dash-datepicker-content${
hasPortal ? ' dash-datepicker-portal' : ''
}${
with_full_screen_portal
? ' dash-datepicker-fullscreen'
: ''
}`}
align={hasPortal ? 'center' : 'start'}
sideOffset={hasPortal ? 0 : 5}
avoidCollisions={!hasPortal}
onInteractOutside={
with_full_screen_portal
? e => e.preventDefault()
: undefined
}
onOpenAutoFocus={e => e.preventDefault()}
onCloseAutoFocus={e => {
e.preventDefault();
Expand All @@ -407,6 +419,15 @@ const DatePickerRange = ({
}
}}
>
{with_full_screen_portal && (
<button
className="dash-datepicker-close-button"
onClick={() => setIsCalendarOpen(false)}
aria-label="Close calendar"
>
<Cross1Icon />
</button>
)}
<Calendar
ref={calendarRef}
initialVisibleDate={initialCalendarDate}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const DatePickerSingle = ({
day_size = 34,
number_of_months_shown = 1,
calendar_orientation,
with_portal = false,
with_full_screen_portal = false,
}: DatePickerSingleProps) => {
const [internalDate, setInternalDate] = useState(strAsDate(date));
const direction = is_RTL
Expand All @@ -61,6 +63,7 @@ const DatePickerSingle = ({
const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement | null>(null);
const calendarRef = useRef<CalendarHandle>(null);
const hasPortal = with_portal || with_full_screen_portal;

useEffect(() => {
setInternalDate(strAsDate(date));
Expand Down Expand Up @@ -177,7 +180,7 @@ const DatePickerSingle = ({
id={accessibleId}
inputClassName="dash-datepicker-input dash-datepicker-end-date"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
onChange={e => setInputValue(e.target?.value)}
onKeyDown={handleInputKeyDown}
placeholder={placeholder}
disabled={disabled}
Expand All @@ -200,12 +203,21 @@ const DatePickerSingle = ({

<Popover.Portal container={containerRef.current}>
<Popover.Content
className="dash-datepicker-content"
align="start"
sideOffset={5}
collisionBoundary={containerRef.current?.closest(
'#_dash-app-content'
)}
className={`dash-datepicker-content${
hasPortal ? ' dash-datepicker-portal' : ''
}${
with_full_screen_portal
? ' dash-datepicker-fullscreen'
: ''
}`}
align={hasPortal ? 'center' : 'start'}
sideOffset={hasPortal ? 0 : 5}
avoidCollisions={!hasPortal}
onInteractOutside={
with_full_screen_portal
? e => e.preventDefault()
: undefined
}
onOpenAutoFocus={e => e.preventDefault()}
onCloseAutoFocus={e => {
e.preventDefault();
Expand All @@ -215,6 +227,15 @@ const DatePickerSingle = ({
}
}}
>
{with_full_screen_portal && (
<button
className="dash-datepicker-close-button"
onClick={() => setIsCalendarOpen(false)}
aria-label="Close calendar"
>
<Cross1Icon />
</button>
)}
<Calendar
ref={calendarRef}
initialVisibleDate={initialMonth}
Expand Down
10 changes: 5 additions & 5 deletions components/dash-core-components/src/fragments/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ const Dropdown = (props: DropdownProps) => {
}
}}
className={`dash-dropdown ${className ?? ''}`}
style={style}
aria-labelledby={`${accessibleId}-value-count ${accessibleId}-value`}
aria-haspopup="listbox"
aria-expanded={isOpen}
Expand Down Expand Up @@ -458,9 +457,6 @@ const Dropdown = (props: DropdownProps) => {
className="dash-dropdown-content"
align="start"
sideOffset={5}
collisionBoundary={positioningContainerRef.current?.closest(
'#_dash-app-content'
)}
onOpenAutoFocus={e => e.preventDefault()}
onKeyDown={handleKeyDown}
style={{
Expand Down Expand Up @@ -543,7 +539,11 @@ const Dropdown = (props: DropdownProps) => {
);

return (
<div ref={positioningContainerRef} className="dash-dropdown-wrapper">
<div
ref={positioningContainerRef}
className="dash-dropdown-wrapper"
style={style}
>
{popover}
</div>
);
Expand Down
Loading