Commit 90907486 authored by Duggan, John's avatar Duggan, John
Browse files

Improve checkbox UX

parent bcd8666e
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ class DataSelector(datagrid.VGrid):
        self._select_strategy = select_strategy
        self._show_user_directories = show_user_directories

        self._revogrid_id = f"nova__dataselector_{self._next_id}_rv"
        self._state_name = f"nova__dataselector_{self._next_id}_state"
        self._facilities_name = f"nova__dataselector_{self._next_id}_facilities"
        self._instruments_name = f"nova__dataselector_{self._next_id}_instruments"
@@ -87,6 +88,9 @@ class DataSelector(datagrid.VGrid):
        self._datafiles_name = f"nova__dataselector_{self._next_id}_datafiles"

        self._flush_state = f"flushState('{self._v_model_name_in_state}');"
        self._reset_rv_grid = client.JSEval(
            exec=f"window.rvUpdateCheckboxes('{self._v_model}', '{self._datafiles_name}')"
        ).exec
        self._reset_state = client.JSEval(exec=f"{self._v_model} = []; {self._flush_state}").exec

        self.create_model(facility, instrument)
@@ -145,10 +149,13 @@ class DataSelector(datagrid.VGrid):
                        f"   datafiles_key: '{self._datafiles_name}',"
                        f"   model_key: '{self._v_model}',"
                        "    name: 'Available Datafiles',"
                        "    prop: 'title'"
                        "    prop: 'title',"
                        f"   state_key: '{self._v_model_name_in_state}',"
                        "}]",
                    ),
                    frame_size=10,
                    hide_attribution=True,
                    id=self._revogrid_id,
                    readonly=True,
                    stretch=True,
                    source=(self._datafiles_name,),
@@ -160,6 +167,12 @@ class DataSelector(datagrid.VGrid):
                if "update_modelValue" not in kwargs:
                    self.update_modelValue = self._flush_state

                # Sets up some JavaScript event handlers when the component is mounted.
                with self:
                    client.ClientTriggers(
                        mounted=f"window.rvOnMount('{self._revogrid_id}', '{self._v_model}', '{self._datafiles_name}');"
                    )

            with cast(
                vuetify.VSelect,
                InputField(
@@ -199,6 +212,7 @@ class DataSelector(datagrid.VGrid):

    def reset(self, _: Any = None) -> None:
        self._reset_state()
        self._reset_rv_grid()

    def set_state(
        self, facility: Optional[str] = None, instrument: Optional[str] = None, experiment: Optional[str] = None
+52 −10
Original line number Diff line number Diff line
window.rvOnMount = function(id, modelKey, dataKey) {
    const grid = document.querySelector(`#${id}`)
    grid.addEventListener('viewportscroll', () => {
        window.rvUpdateCheckboxes(modelKey, dataKey)
    })
}

window.rvUpdateCheckboxes = function(modelKey, dataKey) {
    const trameState = window.trame.state.state
    const modelValue = _.get(trameState, modelKey)
    const availableData = _.get(trameState, dataKey)
    const selectAllCheckbox = document.querySelector(".header-content input")
    const rowCheckboxes = document.querySelectorAll(".rgCell")

    if (selectAllCheckbox === null) {
        return
    }

    if (modelValue.length === 0) {
        selectAllCheckbox.checked = false
        selectAllCheckbox.indeterminate = false
    } else if (modelValue.length === availableData.length) {
        selectAllCheckbox.checked = true
        selectAllCheckbox.indeterminate = false
    } else {
        selectAllCheckbox.checked = false
        selectAllCheckbox.indeterminate = true
    }

    rowCheckboxes.forEach((element) => {
        input = element.querySelector('input')
        rowIndex = element.dataset.rgrow
        input.checked = modelValue.includes(availableData[rowIndex].path)
    })
}

window.rvCellTemplate = function(createElement, props) {
    const inputVNode = createElement('input', {
        type: 'checkbox',
        onChange: (e) => {
            const state = window.trame.state.state
            const modelKey = props.column.model_key
            const trameState = window.trame.state.state
            const modelValue = _.get(trameState, props.column.model_key)
            const path = props.data[props.rowIndex].path
            const index = _.get(window.trame.state.state, modelKey).indexOf(path)
            const index = modelValue.indexOf(path)

            // We need to assign instead of modifying in place in order for the Trame watcher to pick up changes.
            if (e.target.checked && index < 0) {
                _.get(state, modelKey).push(path)
                _.set(trameState, props.column.model_key, _.concat(modelValue, path))
            } else if (index >= 0) {
                _.get(state, modelKey).splice(index, 1)
                _.set(trameState, props.column.model_key, modelValue.toSpliced(index, 1))
            }

            window.trame.state.dirty('config')
            // Update the UI
            window.rvUpdateCheckboxes(props.column.model_key, props.column.datafiles_key)
            window.trame.state.dirty(props.column.state_key)
        },
    })

@@ -24,15 +63,18 @@ window.rvColumnTemplate = function (createElement, props) {
    const inputVNode = createElement('input', {
        type: 'checkbox',
        onChange: (e) => {
            const state = window.trame.state.state
            const trameState = window.trame.state.state
            const availableData = _.get(trameState, props.datafiles_key)

            if (e.target.checked) {
                _.set(state, 'config.selected_files', _.get(state, props.datafiles_key).map((item) => item.path))
                _.set(trameState, 'config.selected_files', availableData.map((item) => item.path))
            } else {
                _.set(state, 'config.selected_files', [])
                _.set(trameState, 'config.selected_files', [])
            }

            window.trame.state.dirty('config')
            // Update the UI
            window.rvUpdateCheckboxes(props.model_key, props.datafiles_key)
            window.trame.state.dirty(props.state_key)
        },
    })