Skip to content

Commit 49fde0d

Browse files
committed
Merge branch 'v4' into release/4.0.0rc4
2 parents e15fd5f + 098ea68 commit 49fde0d

File tree

16 files changed

+559
-22
lines changed

16 files changed

+559
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
88
- Modernized `dcc.Tabs`
99
- Modernized `dcc.DatePickerSingle` and `dcc.DatePickerRange`
1010
- DatePicker calendars can now accept translations as an external script, either with Dash's `external_scripts` or from the assets folder. See [documentation](https://date-fns.org/v4.1.0/docs/CDN) for the underlying library that supports this.
11+
- New `dcc.Button` component that mirrors `html.Button` but with default styles applied
1112

1213
## Changed
1314
- `dcc.Tab` now accepts a `width` prop which can be a pixel or percentage width for an individual tab.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import {ButtonProps} from '../types';
3+
import './css/button.css';
4+
5+
/**
6+
* Similar to dash.html.Button, but with theming and styles applied.
7+
*/
8+
const Button = ({
9+
setProps,
10+
n_blur = 0,
11+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
12+
n_blur_timestamp = -1,
13+
n_clicks = 0,
14+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
15+
n_clicks_timestamp = -1,
16+
type = 'button',
17+
className,
18+
children,
19+
...props
20+
}: ButtonProps) => {
21+
const ctx = window.dash_component_api.useDashContext();
22+
const isLoading = ctx.useLoading();
23+
24+
return (
25+
<button
26+
data-dash-is-loading={isLoading || undefined}
27+
className={'dash-button ' + (className ?? '')}
28+
onBlur={() => {
29+
setProps({
30+
n_blur: n_blur + 1,
31+
n_blur_timestamp: Date.now(),
32+
});
33+
}}
34+
onClick={() => {
35+
setProps({
36+
n_clicks: n_clicks + 1,
37+
n_clicks_timestamp: Date.now(),
38+
});
39+
}}
40+
type={type}
41+
{...props}
42+
>
43+
{children}
44+
</button>
45+
);
46+
};
47+
48+
export default Button;

components/dash-core-components/src/components/RadioItems.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ export default function RadioItems({
5454
options={sanitizedOptions}
5555
selected={isNil(value) ? [] : [value]}
5656
onSelectionChange={selection => {
57-
setProps({value: selection[selection.length - 1]});
57+
if (selection.length) {
58+
setProps({value: selection[selection.length - 1]});
59+
}
5860
}}
5961
{...stylingProps}
6062
/>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.dash-button {
2+
line-height: 32px;
3+
background: color-mix(
4+
in srgb,
5+
var(--Dash-Fill-Interactive-Strong) 5%,
6+
transparent
7+
);
8+
color: var(--Dash-Fill-Interactive-Strong);
9+
padding: 0 calc(var(--Dash-Spacing) * 2);
10+
border-radius: 4px;
11+
border: 1px solid var(--Dash-Fill-Interactive-Strong);
12+
box-sizing: border-box;
13+
vertical-align: middle;
14+
}
15+
16+
/* Hover state - stronger background */
17+
.dash-button:hover {
18+
cursor: pointer;
19+
background: var(--Dash-Fill-Inverse-Strong);
20+
color: var(--Dash-Fill-Interactive-Strong);
21+
}
22+
23+
/* Active state (clicking) - inverted colors */
24+
.dash-button:active {
25+
background: var(--Dash-Fill-Interactive-Strong);
26+
color: var(--Dash-Fill-Inverse-Strong);
27+
}
28+
29+
/* Keyboard focus - inverted colors */
30+
.dash-button:focus-visible {
31+
outline: none;
32+
background: var(--Dash-Fill-Interactive-Strong);
33+
color: var(--Dash-Fill-Inverse-Strong);
34+
}
35+
36+
/* Hover after keyboard focus - keep inverted but acknowledge hover */
37+
.dash-button:focus-visible:hover {
38+
background: var(--Dash-Fill-Interactive-Strong);
39+
color: var(--Dash-Fill-Inverse-Strong);
40+
}
41+
42+
/* Active state after keyboard focus - inverted colors */
43+
.dash-button:focus-visible:active {
44+
background: var(--Dash-Fill-Interactive-Strong);
45+
color: var(--Dash-Fill-Inverse-Strong);
46+
}
47+
48+
/* Disabled state */
49+
.dash-button:disabled {
50+
opacity: 0.6;
51+
cursor: not-allowed;
52+
background: var(--Dash-Fill-Disabled);
53+
color: var(--Dash-Text-Strong);
54+
border-color: var(--Dash-Stroke-Weak);
55+
}

components/dash-core-components/src/components/css/datepickers.css

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@
2020
align-items: center;
2121
gap: calc(var(--Dash-Spacing) * 2);
2222
box-sizing: border-box;
23-
padding: 0 calc(var(--Dash-Spacing) * 2);
23+
padding: 0 12px;
2424
}
2525

26-
.dash-datepicker-input-wrapper:has(:nth-child(3)) {
27-
grid-template-columns: auto 1fr auto;
26+
.dash-datepicker-input-wrapper:has(> :nth-child(2)) {
27+
grid-template-columns: 1fr auto;
2828
}
2929

30-
.dash-datepicker-input-wrapper:has(:nth-child(4)) {
31-
grid-template-columns: auto 1fr auto auto;
30+
.dash-datepicker-input-wrapper:has(> :nth-child(3)) {
31+
grid-template-columns: 1fr auto auto;
3232
}
3333

34-
.dash-datepicker-input-wrapper:has(:nth-child(5)) {
35-
grid-template-columns: auto auto auto 1fr auto;
34+
.dash-datepicker-input-wrapper:has(> :nth-child(4)) {
35+
grid-template-columns: auto auto 1fr auto;
3636
}
3737

38-
.dash-datepicker-input-wrapper:has(:nth-child(6)) {
39-
grid-template-columns: auto auto auto 1fr auto auto;
38+
.dash-datepicker-input-wrapper:has(> :nth-child(5)) {
39+
grid-template-columns: auto auto 1fr auto auto;
4040
}
4141

4242
.dash-datepicker-input-wrapper,
@@ -55,6 +55,7 @@
5555
border-radius: 4px;
5656
padding: 0;
5757
background-color: inherit;
58+
color: inherit;
5859
}
5960

6061
.dash-datepicker-input::selection,

components/dash-core-components/src/components/css/dropdown.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
grid-template-columns: auto 1fr;
2020
justify-items: start;
2121
align-items: center;
22-
gap: 8px;
22+
gap: calc(var(--Dash-Spacing) * 2);
2323
}
2424

25-
.dash-dropdown-grid-container:has(:nth-child(3)) {
25+
.dash-dropdown-grid-container:has(> :nth-child(3)) {
2626
grid-template-columns: auto 1fr auto;
2727
}
2828

29-
.dash-dropdown-grid-container:has(:nth-child(4)) {
29+
.dash-dropdown-grid-container:has(> :nth-child(4)) {
3030
grid-template-columns: auto 1fr auto auto;
3131
}
3232

components/dash-core-components/src/fragments/DatePickerRange.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as Popover from '@radix-ui/react-popover';
33
import {
44
ArrowLeftIcon,
55
ArrowRightIcon,
6-
CalendarIcon,
76
CaretDownIcon,
87
Cross1Icon,
98
} from '@radix-ui/react-icons';
@@ -136,6 +135,12 @@ const DatePickerRange = ({
136135
start_date: dateAsStr(internalStartDate),
137136
end_date: dateAsStr(internalEndDate),
138137
});
138+
} else if (!internalStartDate && !internalEndDate) {
139+
// Both dates cleared - send undefined for both
140+
setProps({
141+
start_date: dateAsStr(internalStartDate),
142+
end_date: dateAsStr(internalEndDate),
143+
});
139144
} else if (updatemode === 'singledate' && internalStartDate) {
140145
// Only start changed - send just that one
141146
setProps({start_date: dateAsStr(internalStartDate)});
@@ -333,7 +338,6 @@ const DatePickerRange = ({
333338
}
334339
}}
335340
>
336-
<CalendarIcon className="dash-datepicker-trigger-icon" />
337341
<AutosizeInput
338342
inputRef={node => {
339343
startInputRef.current = node;

components/dash-core-components/src/fragments/DatePickerSingle.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import * as Popover from '@radix-ui/react-popover';
3-
import {CalendarIcon, CaretDownIcon, Cross1Icon} from '@radix-ui/react-icons';
3+
import {CaretDownIcon, Cross1Icon} from '@radix-ui/react-icons';
44
import AutosizeInput from 'react-input-autosize';
55
import uuid from 'uniqid';
66

@@ -170,7 +170,6 @@ const DatePickerSingle = ({
170170
}
171171
}}
172172
>
173-
<CalendarIcon className="dash-datepicker-trigger-icon" />
174173
<AutosizeInput
175174
inputRef={node => {
176175
inputRef.current = node;

components/dash-core-components/src/fragments/Dropdown.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ const Dropdown = (props: DropdownProps) => {
264264
searchInputRef.current.focus();
265265
}
266266
});
267-
}, [isOpen, multi, displayOptions, sanitizedValues]);
267+
}, [isOpen, multi, displayOptions]);
268268

269269
// Handle keyboard navigation in popover
270270
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
@@ -289,7 +289,7 @@ const Dropdown = (props: DropdownProps) => {
289289
}
290290

291291
const focusableElements = e.currentTarget.querySelectorAll(
292-
'input[type="search"], input[type="checkbox"]:not([disabled])'
292+
'input[type="search"], input:not([disabled])'
293293
) as NodeListOf<HTMLElement>;
294294

295295
// Don't interfere with the event if there aren't any options that the user can interact with
@@ -505,6 +505,7 @@ const Dropdown = (props: DropdownProps) => {
505505
options={displayOptions}
506506
selected={sanitizedValues}
507507
onSelectionChange={updateSelection}
508+
inputType={multi ? 'checkbox' : 'radio'}
508509
className="dash-dropdown-options"
509510
optionClassName="dash-dropdown-option"
510511
optionStyle={{

components/dash-core-components/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable import/prefer-default-export */
2+
import Button from './components/Button';
23
import Checklist from './components/Checklist';
34
import Clipboard from './components/Clipboard.react';
45
import ConfirmDialog from './components/ConfirmDialog.react';
@@ -28,6 +29,7 @@ import Upload from './components/Upload.react';
2829
import './components/css/dcc.css';
2930

3031
export {
32+
Button,
3133
Checklist,
3234
Clipboard,
3335
ConfirmDialog,

0 commit comments

Comments
 (0)