plotly / dash-table

OBSOLETE: now part of https://github.com/plotly/dash
https://dash.plotly.com
MIT License
419 stars 74 forks source link

Row selector checkbox styling #856

Open ghost opened 3 years ago

ghost commented 3 years ago

The checkboxes for row selection are inserted as <input> elements only, which cannot be styled reliably across browsers, making it very difficult to create a look and feel of the DataTable that is consistent with the other Dash components.

The aim is to make the row selector checkboxes styleable with CSS -- like the second checkbox from the bottom in this screenshot:

image

The fix for this is very simple - adding a <label> tag immediately after the <input> allows for full css-only customization of the checkboxes by hiding the input control and styling the label. The input element needs an ID that the label can reference, but that's all, really.

To make this use the existing Dash-Bootstrap checkbox component styling, a few classes and a wrapper element are needed, but the principle is the same.

Before:

<td class="dash-select-cell">
  <input type="checkbox" name="row-select-datatable-foo" />
</td>

After:

<td class="dash-select-cell">
  <div class="custom-checkbox custom-control" style="left: 1rem;">
    <input id="row-select-datatable-foo-1" class="custom-control-input" type="checkbox" name="row-select-datatable-foo" />
    <label for="row-select-datatable-foo-1" class="custom-control-label" />
  </div>
</td>

Implementation notes:

  1. The left: 1rem style is needed to counteract the padding:0 override that Dash sets on div elements that match td > div:not(.cell-markdown). Of course this could be done with a change to the reset stylesheet instead of a style property on the tag, but the above is just an example.

  2. The whole change can be implemented by modifying the src\dash-table\derived\cell\operations.tsx script.

  3. To avoid potentially-unwanted style changes in existing deployments, the styleable HTML could be made optional.

  4. Yes I am aware that checkboxes can be styles cross-browser using prefixed -appearance: none, but as long as the Dash-Bootstrap components are using the input+label method, I would still propose following the same method here

ghost commented 3 years ago

A simple implementation (without the 'optional' setting) could look something like this:

function rowSelectCell(
    id: string,
    idx: number,
    rowSelectable: Selection,
    selectedRows: number[],
    setProps: SetProps,
    data: Data
) {
    return (
        <td
            key='select'
            className='dash-select-cell'
            style={{
                width: `30px`,
                maxWidth: `30px`,
                minWidth: `30px`,
                textAlign: 'center'
            }}
        >
            <div
                className='custom-checkbox custom-control'
                style={{
                    left: `1rem`
                }}
            >
                <input
                    type={rowSelectable === 'single' ? 'radio' : 'checkbox'}
                    style={{verticalAlign: 'middle'}}
                    name={`row-select-${id}`}
                    id={`row-select-${id}-${idx}`}
                    className='custom-control-input'
                    checked={R.includes(idx, selectedRows)}
                    onChange={() => {
                        const newSelectedRows =
                            rowSelectable === 'single'
                                ? [idx]
                                : R.ifElse(
                                    R.includes(idx),
                                    R.without([idx]),
                                    R.append(idx)
                                )(selectedRows);
                        setProps({
                            selected_rows: newSelectedRows,
                            selected_row_ids: R.map(
                                i => data[i].id,
                                newSelectedRows
                            )
                        });
                    }}
                />
                <label
                    htmlFor={`row-select-${id}-${idx}`}
                    className='custom-control-label'
                />
            </div>
        </td>
    );
}
OnDraKoon commented 3 years ago

Hi, I am interested in customizing the checkboxes in Dash DataTable, but I don't know how to proceed even with the manual provided above. Could I write the change just simply in css? Could you please help me on how to achieve the desired change?

Thank you,

Andrew.

JensRoland commented 3 years ago

The above is not a manual for achieving it with the current DataTable implementation; rather, it is a proposal to change the DataTable implementation to allow standard methods to work.

However, as mentioned, you can do it with the current DataTable version by using -appearance: none in your CSS (See CanIUse.com page for compatibility and remember to prefix it as needed), like so:

.dash-select-cell input[type=checkbox] {
  appearance: none;
  cursor: pointer;
  width: 200px;
  height: 200px;
  background-image: url("https://static.thenounproject.com/png/2303901-200.png");
  background-repeat: no-repeat;
  background-position: center center;
}

.dash-select-cell input[type=checkbox]:checked {
  background-image: url("https://static.thenounproject.com/png/747863-200.png");
}

See demo and code: https://jsfiddle.net/1apdztk3/

OnDraKoon commented 3 years ago

Hello Jens, thank you so much for replying and giving me the advice! I have pasted the code to my css, but now I can't see any of the check boxes actually - see the screenshot attached: Screenshot 2021-08-12 170855

In case I can make this work, how would it be done similarly for the check boxes in the header?

Thanks for help!

Andrew

JensRoland commented 3 years ago

You need to change the styles to use your own images / styling. The example css uses 200x200px images which will not fit in the Dash table

OnDraKoon commented 3 years ago

Hmm.. I took the same check box picture(s) and decrease their size to 12px times 12px and converted it to .svg. It should be sufficiently small, but no check boxes are till visible : -/. The png version of the box: Picture1 chec_box_png