add row selection functionality to tables
This commit is contained in:
parent
0829723d99
commit
350edc2aec
|
@ -3,6 +3,7 @@
|
|||
import "@spectrum-css/table/dist/index-vars.css"
|
||||
import CellRenderer from "./CellRenderer.svelte"
|
||||
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
||||
import Checkbox from "../Form/Checkbox.svelte"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { deepGet } from "../utils/helpers"
|
||||
|
||||
|
@ -29,7 +30,7 @@
|
|||
export let editColumnTitle = "Edit"
|
||||
export let customRenderers = []
|
||||
export let disableSorting = false
|
||||
|
||||
export let allowSelectAllRows = false
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
// Config
|
||||
|
@ -204,12 +205,27 @@
|
|||
dispatch("editrow", cloneDeep(row))
|
||||
}
|
||||
|
||||
const toggleSelectAll = e => {
|
||||
if (!allowSelectAllRows) {
|
||||
return
|
||||
}
|
||||
if (e.detail) {
|
||||
selectedRows = rows
|
||||
} else {
|
||||
selectedRows = []
|
||||
}
|
||||
}
|
||||
|
||||
const toggleSelectRow = row => {
|
||||
if (!allowSelectRows) {
|
||||
return
|
||||
}
|
||||
if (selectedRows.includes(row)) {
|
||||
selectedRows = selectedRows.filter(selectedRow => selectedRow !== row)
|
||||
if (
|
||||
selectedRows.findIndex(selectedRow => selectedRow._id === row._id) === 0
|
||||
) {
|
||||
selectedRows = selectedRows.filter(
|
||||
selectedRow => selectedRow._id !== row._id
|
||||
)
|
||||
} else {
|
||||
selectedRows = [...selectedRows, row]
|
||||
}
|
||||
|
@ -234,7 +250,11 @@
|
|||
{#if showEditColumn}
|
||||
<th class="spectrum-Table-headCell">
|
||||
<div class="spectrum-Table-headCell-content">
|
||||
{editColumnTitle || ""}
|
||||
{#if allowSelectAllRows}
|
||||
<Checkbox on:change={toggleSelectAll} />
|
||||
{:else}
|
||||
{editColumnTitle || ""}
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
{/if}
|
||||
|
@ -299,7 +319,9 @@
|
|||
<div class="spectrum-Table-cell-content">
|
||||
<SelectEditRenderer
|
||||
data={row}
|
||||
selected={selectedRows.includes(row)}
|
||||
selected={selectedRows.findIndex(
|
||||
selectedRow => selectedRow._id === row._id
|
||||
) !== -1}
|
||||
onToggleSelection={() => toggleSelectRow(row)}
|
||||
onEdit={e => editRow(e, row)}
|
||||
{allowSelectRows}
|
||||
|
@ -364,6 +386,7 @@
|
|||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container {
|
||||
|
|
|
@ -35,12 +35,14 @@ export const getBindableProperties = (asset, componentId) => {
|
|||
const urlBindings = getUrlBindings(asset)
|
||||
const deviceBindings = getDeviceBindings()
|
||||
const stateBindings = getStateBindings()
|
||||
const rowBindings = getRowBindings()
|
||||
return [
|
||||
...contextBindings,
|
||||
...urlBindings,
|
||||
...stateBindings,
|
||||
...userBindings,
|
||||
...deviceBindings,
|
||||
...rowBindings,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -321,13 +323,33 @@ const getDeviceBindings = () => {
|
|||
return bindings
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all row bindings that are globally available.
|
||||
*/
|
||||
const getRowBindings = () => {
|
||||
let bindings = []
|
||||
if (get(store).clientFeatures?.rowSelection) {
|
||||
const safeState = makePropSafe("rowSelection")
|
||||
bindings = [
|
||||
{
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe("row")}`,
|
||||
readableBinding: "Rows",
|
||||
},
|
||||
]
|
||||
|
||||
return bindings
|
||||
}
|
||||
return bindings
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all state bindings that are globally available.
|
||||
*/
|
||||
const getStateBindings = () => {
|
||||
let bindings = []
|
||||
if (get(store).clientFeatures?.state) {
|
||||
const safeState = makePropSafe("state")
|
||||
if (get(store).clientFeatures?.rowSelection) {
|
||||
const safeState = makePropSafe("rowSelection")
|
||||
bindings = getAllStateVariables().map(key => ({
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe(key)}`,
|
||||
|
|
|
@ -42,6 +42,7 @@ const INITIAL_FRONTEND_STATE = {
|
|||
intelligentLoading: false,
|
||||
deviceAwareness: false,
|
||||
state: false,
|
||||
rowSelection: false,
|
||||
customThemes: false,
|
||||
devicePreview: false,
|
||||
messagePassing: false,
|
||||
|
|
|
@ -66,7 +66,6 @@ export function createAppStore() {
|
|||
}
|
||||
|
||||
async function update(appId, value) {
|
||||
console.log({ value })
|
||||
const response = await api.put(`/api/applications/${appId}`, { ...value })
|
||||
if (response.status === 200) {
|
||||
store.update(state => {
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"state": true,
|
||||
"customThemes": true,
|
||||
"devicePreview": true,
|
||||
"messagePassing": true
|
||||
"messagePassing": true,
|
||||
"rowSelection": true
|
||||
},
|
||||
"layout": {
|
||||
"name": "Layout",
|
||||
|
@ -2707,6 +2708,13 @@
|
|||
"key": "showAutoColumns",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Allow row selection",
|
||||
"key": "allowSelectRows",
|
||||
"defaultValue": false
|
||||
},
|
||||
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Link table rows",
|
||||
|
@ -2961,6 +2969,11 @@
|
|||
"label": "Show auto columns",
|
||||
"key": "showAutoColumns"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Allow row selection",
|
||||
"key": "allowSelectRows"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"label": "Link table rows",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import UserBindingsProvider from "components/context/UserBindingsProvider.svelte"
|
||||
import DeviceBindingsProvider from "components/context/DeviceBindingsProvider.svelte"
|
||||
import StateBindingsProvider from "components/context/StateBindingsProvider.svelte"
|
||||
import RowSelectionProvider from "components/context/RowSelectionProvider.svelte"
|
||||
import SettingsBar from "components/preview/SettingsBar.svelte"
|
||||
import SelectionIndicator from "components/preview/SelectionIndicator.svelte"
|
||||
import HoverIndicator from "components/preview/HoverIndicator.svelte"
|
||||
|
@ -90,59 +91,61 @@
|
|||
<UserBindingsProvider>
|
||||
<DeviceBindingsProvider>
|
||||
<StateBindingsProvider>
|
||||
<!-- Settings bar can be rendered outside of device preview -->
|
||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||
{#key $builderStore.selectedComponentId}
|
||||
{#if $builderStore.inBuilder}
|
||||
<SettingsBar />
|
||||
{/if}
|
||||
{/key}
|
||||
<RowSelectionProvider>
|
||||
<!-- Settings bar can be rendered outside of device preview -->
|
||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||
{#key $builderStore.selectedComponentId}
|
||||
{#if $builderStore.inBuilder}
|
||||
<SettingsBar />
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<!-- Clip boundary for selection indicators -->
|
||||
<div
|
||||
id="clip-root"
|
||||
class:preview={$builderStore.inBuilder}
|
||||
class:tablet-preview={$builderStore.previewDevice === "tablet"}
|
||||
class:mobile-preview={$builderStore.previewDevice === "mobile"}
|
||||
>
|
||||
<!-- Actual app -->
|
||||
<div id="app-root">
|
||||
<CustomThemeWrapper>
|
||||
{#key $screenStore.activeLayout._id}
|
||||
<Component
|
||||
isLayout
|
||||
instance={$screenStore.activeLayout.props}
|
||||
/>
|
||||
{/key}
|
||||
<!-- Clip boundary for selection indicators -->
|
||||
<div
|
||||
id="clip-root"
|
||||
class:preview={$builderStore.inBuilder}
|
||||
class:tablet-preview={$builderStore.previewDevice === "tablet"}
|
||||
class:mobile-preview={$builderStore.previewDevice === "mobile"}
|
||||
>
|
||||
<!-- Actual app -->
|
||||
<div id="app-root">
|
||||
<CustomThemeWrapper>
|
||||
{#key $screenStore.activeLayout._id}
|
||||
<Component
|
||||
isLayout
|
||||
instance={$screenStore.activeLayout.props}
|
||||
/>
|
||||
{/key}
|
||||
|
||||
<!--
|
||||
<!--
|
||||
Flatpickr needs to be inside the theme wrapper.
|
||||
It also needs its own container because otherwise it hijacks
|
||||
key events on the whole page. It is painful to work with.
|
||||
-->
|
||||
<div id="flatpickr-root" />
|
||||
<div id="flatpickr-root" />
|
||||
|
||||
<!-- Modal container to ensure they sit on top -->
|
||||
<div class="modal-container" />
|
||||
<!-- Modal container to ensure they sit on top -->
|
||||
<div class="modal-container" />
|
||||
|
||||
<!-- Layers on top of app -->
|
||||
<NotificationDisplay />
|
||||
<ConfirmationDisplay />
|
||||
<PeekScreenDisplay />
|
||||
</CustomThemeWrapper>
|
||||
</div>
|
||||
<!-- Layers on top of app -->
|
||||
<NotificationDisplay />
|
||||
<ConfirmationDisplay />
|
||||
<PeekScreenDisplay />
|
||||
</CustomThemeWrapper>
|
||||
</div>
|
||||
|
||||
<!-- Selection indicators should be bounded by device -->
|
||||
<!--
|
||||
<!-- Selection indicators should be bounded by device -->
|
||||
<!--
|
||||
We don't want to key these by componentID as they control their own
|
||||
re-mounting to avoid flashes.
|
||||
-->
|
||||
{#if $builderStore.inBuilder}
|
||||
<SelectionIndicator />
|
||||
<HoverIndicator />
|
||||
<DNDHandler />
|
||||
{/if}
|
||||
</div>
|
||||
{#if $builderStore.inBuilder}
|
||||
<SelectionIndicator />
|
||||
<HoverIndicator />
|
||||
<DNDHandler />
|
||||
{/if}
|
||||
</div>
|
||||
</RowSelectionProvider>
|
||||
</StateBindingsProvider>
|
||||
</DeviceBindingsProvider>
|
||||
</UserBindingsProvider>
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
export let linkURL
|
||||
export let linkColumn
|
||||
export let linkPeek
|
||||
export let allowSelectRows
|
||||
|
||||
const component = getContext("component")
|
||||
const { styleable, getAction, ActionTypes, routeStore } = getContext("sdk")
|
||||
const { styleable, getAction, ActionTypes, routeStore, rowSelectionStore } =
|
||||
getContext("sdk")
|
||||
const customColumnKey = `custom-${Math.random()}`
|
||||
const customRenderers = [
|
||||
{
|
||||
|
@ -24,7 +26,7 @@
|
|||
component: SlotRenderer,
|
||||
},
|
||||
]
|
||||
|
||||
let selectedRows = []
|
||||
$: hasChildren = $component.children
|
||||
$: loading = dataProvider?.loading ?? false
|
||||
$: data = dataProvider?.rows || []
|
||||
|
@ -36,6 +38,9 @@
|
|||
ActionTypes.SetDataProviderSorting
|
||||
)
|
||||
|
||||
$: {
|
||||
rowSelectionStore.actions.update(selectedRows)
|
||||
}
|
||||
const getFields = (schema, customColumns, showAutoColumns) => {
|
||||
// Check for an invalid column selection
|
||||
let invalid = false
|
||||
|
@ -112,7 +117,9 @@
|
|||
{rowCount}
|
||||
{quiet}
|
||||
{customRenderers}
|
||||
allowSelectRows={false}
|
||||
{allowSelectRows}
|
||||
bind:selectedRows
|
||||
allowSelectAllRows={true}
|
||||
allowEditRows={false}
|
||||
allowEditColumns={false}
|
||||
showAutoColumns={true}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
import Provider from "./Provider.svelte"
|
||||
import { rowSelectionStore } from "stores"
|
||||
</script>
|
||||
|
||||
<Provider key="rowSelection" data={$rowSelectionStore}>
|
||||
<slot />
|
||||
</Provider>
|
|
@ -6,6 +6,7 @@ import {
|
|||
screenStore,
|
||||
builderStore,
|
||||
uploadStore,
|
||||
rowSelectionStore,
|
||||
} from "stores"
|
||||
import { styleable } from "utils/styleable"
|
||||
import { linkable } from "utils/linkable"
|
||||
|
@ -19,6 +20,7 @@ export default {
|
|||
authStore,
|
||||
notificationStore,
|
||||
routeStore,
|
||||
rowSelectionStore,
|
||||
screenStore,
|
||||
builderStore,
|
||||
uploadStore,
|
||||
|
|
|
@ -10,7 +10,7 @@ export { peekStore } from "./peek"
|
|||
export { stateStore } from "./state"
|
||||
export { themeStore } from "./theme"
|
||||
export { uploadStore } from "./uploads.js"
|
||||
|
||||
export { rowSelectionStore } from "./rowSelection.js"
|
||||
// Context stores are layered and duplicated, so it is not a singleton
|
||||
export { createContextStore } from "./context"
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { writable } from "svelte/store"
|
||||
|
||||
const createRowSelectionStore = () => {
|
||||
const store = writable([])
|
||||
|
||||
function update(rows) {
|
||||
console.log(rows)
|
||||
store.update(state => {
|
||||
state = rows
|
||||
return state
|
||||
})
|
||||
}
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: {
|
||||
update,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const rowSelectionStore = createRowSelectionStore()
|
Loading…
Reference in New Issue