From 641bc0fbdbc550667029d5938cb93967c61c9405 Mon Sep 17 00:00:00 2001 From: glsdown Date: Sat, 28 Aug 2021 18:15:51 +0100 Subject: [PATCH 1/5] Fixed button group radio doc example --- .../components/button_group.md | 18 +++--------- .../components/button_group/radios.R | 5 ++-- .../components/button_group/radios.jl | 5 ++-- .../components/button_group/radios.py | 5 ++-- docs/static/docs.css | 29 ++++++------------- 5 files changed, 22 insertions(+), 40 deletions(-) diff --git a/docs/components_page/components/button_group.md b/docs/components_page/components/button_group.md index 564b277c4..22e4cba0a 100644 --- a/docs/components_page/components/button_group.md +++ b/docs/components_page/components/button_group.md @@ -29,36 +29,26 @@ Stack buttons in the `ButtonGroup` vertically by setting `vertical=True`. ## RadioItems as ButtonGroup -Sometimes you might like to use a `ButtonGroup` to let the user choose from a set of options, where the currently chosen option is indicated by that button being marked as "active". Since this requires that buttons respond to other buttons in the group being clicked on, it can be a little awkward to achieve when each button is a separate Dash component. Instead it is easier to use `RadioItems` and do some customisation of the styling. Most of the customisation is achieved with the `label_class_name` and `label_checked_class_name` props of the `RadioItems` component, though some additional CSS is required (see below). +Sometimes you might like to use a `ButtonGroup` to let the user choose from a set of options, where the currently chosen option is indicated by that button being marked as "active". Since this requires that buttons respond to other buttons in the group being clicked on, it can be a little awkward to achieve when each button is a separate Dash component. Instead it is easier to use `RadioItems` and do some customisation of the styling. Most of the customisation is achieved with the `input_class_name`, `label_class_name` and `label_checked_class_name` props of the `RadioItems` component, though some additional CSS is required (see below). {{example:components/button_group/radios.py:button_group}} ```css -/* Turn off existing buttons */ -.radio-group .custom-control-input ~ .custom-control-label::before { - content: none; -} - -.radio-group .custom-radio .custom-control-input ~ .custom-control-label::after { - content: none; -} - /* restyle radio items */ -.radio-group .custom-control { +.radio-group .form-check { padding-left: 0; } -.radio-group .btn-group > .custom-control:not(:last-child) > .btn { +.radio-group .btn-group > .form-check:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } -.radio-group .btn-group > .custom-control:not(:first-child) > .btn { +.radio-group .btn-group > .form-check:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; } ``` - {{apidoc:src/components/ButtonGroup.js}} diff --git a/docs/components_page/components/button_group/radios.R b/docs/components_page/components/button_group/radios.R index 6d4c322f9..47970a544 100644 --- a/docs/components_page/components/button_group/radios.R +++ b/docs/components_page/components/button_group/radios.R @@ -6,8 +6,9 @@ button_group <- htmlDiv( dbcRadioItems( id = "radios", class_name = "btn-group", - label_class_name = "btn btn-secondary", - label_checked_class_name = "active", + input_class_name="btn-check", + label_class_name = "btn btn-outline-secondary", + label_checked_class_name = "active btn-secondary", options = list( list(label = "Option 1", value = 1), list(label = "Option 2", value = 2), diff --git a/docs/components_page/components/button_group/radios.jl b/docs/components_page/components/button_group/radios.jl index 422608151..cc05c2d59 100644 --- a/docs/components_page/components/button_group/radios.jl +++ b/docs/components_page/components/button_group/radios.jl @@ -5,8 +5,9 @@ button_group = html_div( dbc_radioitems( id = "radios", class_name = "btn-group", - label_class_name = "btn btn-secondary", - label_checked_class_name = "active", + input_class_name="btn-check", + label_class_name = "btn btn-outline-secondary", + label_checked_class_name = "active btn-secondary", options = [ Dict("label" => "Option 1", "value" => 1), Dict("label" => "Option 2", "value" => 2), diff --git a/docs/components_page/components/button_group/radios.py b/docs/components_page/components/button_group/radios.py index 224269e59..503ca7d81 100644 --- a/docs/components_page/components/button_group/radios.py +++ b/docs/components_page/components/button_group/radios.py @@ -7,8 +7,9 @@ dbc.RadioItems( id="radios", class_name="btn-group", - label_class_name="btn btn-secondary", - label_checked_class_name="active", + input_class_name="btn-check", + label_class_name="btn btn-outline-secondary", + label_checked_class_name="active btn-secondary", options=[ {"label": "Option 1", "value": 1}, {"label": "Option 2", "value": 2}, diff --git a/docs/static/docs.css b/docs/static/docs.css index 575bb2d73..75d3a066f 100644 --- a/docs/static/docs.css +++ b/docs/static/docs.css @@ -31,6 +31,10 @@ span.hljs-meta { color: #66d9ef; } +a { + text-decoration: none; +} + .demo-layout-container { height: 600px; margin-right: 0rem; @@ -134,8 +138,8 @@ span.hljs-meta { /* Custom checkbox example CSS */ #checklist-selected-style - .custom-control-input:checked - ~ .custom-control-label::before { + .form-check-input:checked + ~ .form-check-label::before { background-color: #fa7268; border-color: #ea6258; } @@ -152,29 +156,14 @@ span.hljs-meta { } /* radio button group example */ -.button-group-demo - .radio-group - .custom-control-input - ~ .custom-control-label::before { - content: none; -} - -.button-group-demo - .radio-group - .custom-radio - .custom-control-input - ~ .custom-control-label::after { - content: none; -} - -.button-group-demo .radio-group .custom-control { +.button-group-demo .radio-group .form-check { padding-left: 0; } .button-group-demo .radio-group .btn-group - > .custom-control:not(:last-child) + > .form-check:not(:last-child) > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; @@ -183,7 +172,7 @@ span.hljs-meta { .button-group-demo .radio-group .btn-group - > .custom-control:not(:first-child) + > .form-check:not(:first-child) > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; From 19236e525a78706fe9f48e569cbbd6277089c761 Mon Sep 17 00:00:00 2001 From: glsdown Date: Sat, 28 Aug 2021 18:32:56 +0100 Subject: [PATCH 2/5] Add input checked style and class_name --- docs/components_page/components/input.md | 16 ++----- .../components/input/selected_styles.R | 36 +++++++++++---- .../components/input/selected_styles.jl | 36 +++++++++++---- .../components/input/selected_styles.py | 36 +++++++++++---- docs/static/docs.css | 8 ---- src/components/input/Checklist.js | 44 ++++++++++++++++++- src/components/input/RadioItems.js | 41 ++++++++++++++++- 7 files changed, 168 insertions(+), 49 deletions(-) diff --git a/docs/components_page/components/input.md b/docs/components_page/components/input.md index d72144174..2e2c1bdfd 100644 --- a/docs/components_page/components/input.md +++ b/docs/components_page/components/input.md @@ -73,16 +73,7 @@ Set `inline=True` to make the radio items or checklists fit next to each other o ## Checked item styles -Use the `labelCheckedStyle` and `label_checked_class_name` arguments to apply different styles to the labels of checked items. When using custom inputs you can override the styles of the inputs using custom CSS. See the below example. - -```css -#checklist-selected-style - .custom-control-input:checked - ~ .custom-control-label::before { - background-color: #fa7268; - border-color: #ea6258; -} -``` +Use the `input_checked_style`, `input_checked_class_name`, `label_checked_style` and `label_checked_class_name` arguments to apply different styles to the labels of checked items. {{example:components/input/selected_styles.py:checklist}} @@ -98,13 +89,12 @@ When using `Input` with `type="color"`, the user may specify a color, either by Note that the color picker presentation may vary substantially from one browser and/or platform to another. -As you drag the selector around the color picker, notice that the text color is smoothly updated. While this is a nice feature, it may cause a performance issue in your app, because the callback fires continuously. +As you drag the selector around the color picker, notice that the text color is smoothly updated. While this is a nice feature, it may cause a performance issue in your app, because the callback fires continuously. -This is a great use-case for a [Dash clientside callback](https://dash.plotly.com/clientside-callbacks). This example uses a clientside callback so the callback runs directly in the browser instead of making requests to the Dash server. For your reference, the regular Dash callback is shown as a comment. +This is a great use-case for a [Dash clientside callback](https://dash.plotly.com/clientside-callbacks). This example uses a clientside callback so the callback runs directly in the browser instead of making requests to the Dash server. For your reference, the regular Dash callback is shown as a comment. {{example:components/input/colorpicker.py:colorpicker}} - {{apidoc:src/components/input/Input.js}} {{apidoc:src/components/input/Textarea.js}} diff --git a/docs/components_page/components/input/selected_styles.R b/docs/components_page/components/input/selected_styles.R index 949bcd1c8..a65cbcdef 100644 --- a/docs/components_page/components/input/selected_styles.R +++ b/docs/components_page/components/input/selected_styles.R @@ -1,11 +1,31 @@ library(dashBootstrapComponents) +library(dashHtmlComponents) -checklist <- dbcChecklist( - id = "checklist-selected-style", - options = list( - list(label = "Option 1", value = 1), - list(label = "Option 2", value = 2), - list(label = "Option 3", value = 3) - ), - labelCheckedStyle = list(color = "red") +checklist <- htmlDiv( + list( + dbcChecklist( + id = "checklist-selected-style", + options = list( + list(label = "Option 1", value = 1), + list(label = "Option 2", value = 2), + list(label = "Option 3", value = 3) + ), + label_checked_style=list("color" = "red"), + input_checked_style=list( + "backgroundColor" = "#fa7268", + "borderColor" = "#ea6258" + ) + ), + htmlHr(), + dbcRadioItems( + id="radio-selected-style", + options = list( + list(label = "Option 1", value = 1), + list(label = "Option 2", value = 2), + list(label = "Option 3", value = 3) + ), + label_checked_class_name="text-success", + input_checked_class_name="border border-success bg-success" + ) + ) ) diff --git a/docs/components_page/components/input/selected_styles.jl b/docs/components_page/components/input/selected_styles.jl index c6752110d..8021eb0b0 100644 --- a/docs/components_page/components/input/selected_styles.jl +++ b/docs/components_page/components/input/selected_styles.jl @@ -1,11 +1,31 @@ using DashBootstrapComponents +using DashHtmlComponents -checklist = dbc_checklist( - id = "checklist-selected-style", - options = [ - Dict("label" => "Option 1", "value" => 1), - Dict("label" => "Option 2", "value" => 2), - Dict("label" => "Option 3", "value" => 3), - ], - labelCheckedStyle = Dict("color" => "red"), +checklist = html_div( + [ + dbc_checklist( + id = "checklist-selected-style", + options = [ + Dict("label" => "Option 1", "value" => 1), + Dict("label" => "Option 2", "value" => 2), + Dict("label" => "Option 3", "value" => 3), + ], + label_checked_style=Dict("color" => "red"), + input_checked_style=Dict( + "backgroundColor" => "#fa7268", + "borderColor" => "#ea6258", + ), + ), + html_hr(), + dbc_radioitems( + id="radio-selected-style", + options=[ + Dict("label" => "Option 1", "value" => 1), + Dict("label" => "Option 2", "value" => 2), + Dict("label" => "Option 3", "value" => 3), + ], + label_checked_class_name="text-success", + input_checked_class_name="border border-success bg-success", + ) + ] ); diff --git a/docs/components_page/components/input/selected_styles.py b/docs/components_page/components/input/selected_styles.py index cf705c410..8fbee5459 100644 --- a/docs/components_page/components/input/selected_styles.py +++ b/docs/components_page/components/input/selected_styles.py @@ -1,11 +1,31 @@ import dash_bootstrap_components as dbc +import dash_html_components as html -checklist = dbc.Checklist( - id="checklist-selected-style", - options=[ - {"label": "Option 1", "value": 1}, - {"label": "Option 2", "value": 2}, - {"label": "Option 3", "value": 3}, - ], - labelCheckedStyle={"color": "red"}, +checklist = html.Div( + [ + dbc.Checklist( + id="checklist-selected-style", + options=[ + {"label": "Option 1", "value": 1}, + {"label": "Option 2", "value": 2}, + {"label": "Option 3", "value": 3}, + ], + label_checked_style={"color": "red"}, + input_checked_style={ + "backgroundColor": "#fa7268", + "borderColor": "#ea6258", + }, + ), + html.Hr(), + dbc.RadioItems( + id="radio-selected-style", + options=[ + {"label": "Option 1", "value": 1}, + {"label": "Option 2", "value": 2}, + {"label": "Option 3", "value": 3}, + ], + label_checked_class_name="text-success", + input_checked_class_name="border border-success bg-success", + ), + ] ) diff --git a/docs/static/docs.css b/docs/static/docs.css index 75d3a066f..04667fa4a 100644 --- a/docs/static/docs.css +++ b/docs/static/docs.css @@ -136,14 +136,6 @@ a { display: none; } -/* Custom checkbox example CSS */ -#checklist-selected-style - .form-check-input:checked - ~ .form-check-label::before { - background-color: #fa7268; - border-color: #ea6258; -} - /* invalid input styling */ #styled-numeric-input input:invalid { border-color: #dc3545; diff --git a/src/components/input/Checklist.js b/src/components/input/Checklist.js index 34aeb4987..404ba7033 100644 --- a/src/components/input/Checklist.js +++ b/src/components/input/Checklist.js @@ -27,8 +27,12 @@ const Checklist = props => { id, inputClassName, input_class_name, + inputCheckedClassName, + input_checked_class_name, inputStyle, input_style, + inputCheckedStyle, + input_checked_style, labelClassName, label_class_name, labelCheckedClassName, @@ -45,6 +49,13 @@ const Checklist = props => { const checked = includes(option.value, value); + const mergedInputStyle = checked + ? { + ...(input_style || inputStyle), + ...(input_checked_style || inputCheckedStyle) + } + : input_style || inputStyle; + const mergedLabelStyle = checked ? { ...(label_style || labelStyle), @@ -70,10 +81,11 @@ const Checklist = props => { checked={checked} className={classNames( 'form-check-input', - input_class_name || inputClassName + input_class_name || inputClassName, + checked && (input_checked_class_name || inputCheckedClassName) )} disabled={Boolean(option.disabled)} - style={input_style || inputStyle} + style={mergedInputStyle} type="checkbox" onChange={() => { let newValue; @@ -205,6 +217,20 @@ Checklist.propTypes = { */ inputStyle: PropTypes.object, + /** + * Additional inline style arguments to apply to elements on checked + * items. + */ + input_checked_style: PropTypes.object, + + /** + * **DEPRECATED** Use `input_checked_style` instead. + * + * Additional inline style arguments to apply to elements on checked + * items. + */ + inputCheckedStyle: PropTypes.object, + /** * The class of the checkbox element */ @@ -217,6 +243,20 @@ Checklist.propTypes = { */ inputClassName: PropTypes.string, + /** + * Additional CSS classes to apply to the element when the + * corresponding checkbox is checked. + */ + input_checked_class_name: PropTypes.string, + + /** + * **DEPRECATED** Use `input_checked_class_name` instead. + * + * Additional CSS classes to apply to the element when the + * corresponding checkbox is checked. + */ + inputCheckedClassName: PropTypes.string, + /** * Inline style arguments to apply to the