Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
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
137 changes: 137 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,143 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units and ranges supported in `marks` are:
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸

_Ranges below 10µ are not supported by the Slider. This is a bug: https://github.com/plotly/dash/issues/1766_

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All@@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All@@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand DownExpand Up@@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All@@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading