budibase/packages/standard-components/src/grid/Component.svelte

197 lines
5.1 KiB
Svelte

<script>
// Import valueSetters and custom renderers
import { number } from "./valueSetters"
import { getRenderer } from "./customRenderer"
import { isEmpty } from "lodash/fp"
import { getContext, onMount } from "svelte"
import AgGrid from "@budibase/svelte-ag-grid"
import {
TextButton as DeleteButton,
Icon,
Modal,
ModalContent,
} from "@budibase/bbui"
// These maps need to be set up to handle whatever types that are used in the tables.
const setters = new Map([["number", number]])
const SDK = getContext("sdk")
const component = getContext("component")
const { API, styleable } = SDK
export let datasource = {}
export let editable
export let theme = "alpine"
export let height = 500
export let pagination
export let detailUrl
// Add setting height as css var to allow grid to use correct height
$: gridStyles = {
...$component.styles,
normal: {
...$component.styles.normal,
["--grid-height"]: `${height}px`,
},
}
// These can never change at runtime so don't need to be reactive
let canEdit = editable && datasource && datasource.type !== "view"
let canAddDelete = editable && datasource && datasource.type === "table"
let modal
let dataLoaded = false
let data
let columnDefs
let selectedRows = []
let table
let options = {
defaultColDef: {
flex: 1,
minWidth: 150,
filter: true,
},
rowSelection: canEdit ? "multiple" : false,
suppressRowClickSelection: !canEdit,
paginationAutoPageSize: true,
pagination,
}
onMount(async () => {
if (!isEmpty(datasource)) {
data = await API.fetchDatasource(datasource)
let schema
// Get schema for datasource
// Views with "Calculate" applied provide their own schema.
// For everything else, use the tableId property to pull to table schema
if (datasource.schema) {
schema = datasource.schema
} else {
schema = (await API.fetchTableDefinition(datasource.tableId)).schema
}
columnDefs = Object.keys(schema).map((key, i) => {
return {
headerCheckboxSelection: i === 0 && canEdit,
checkboxSelection: i === 0 && canEdit,
valueSetter: setters.get(schema[key].type),
headerName: key,
field: key,
hide: shouldHideField(key),
sortable: true,
editable: canEdit && schema[key].type !== "link",
cellRenderer: getRenderer(schema[key], canEdit, SDK),
autoHeight: true,
}
})
if (detailUrl) {
columnDefs = [
...columnDefs,
{
headerName: "Detail",
field: "_id",
minWidth: 100,
width: 100,
flex: 0,
editable: false,
sortable: false,
cellRenderer: getRenderer(
{
type: "_id",
options: { detailUrl },
},
false,
SDK
),
autoHeight: true,
pinned: "left",
filter: false,
},
]
}
dataLoaded = true
}
})
const shouldHideField = name => {
if (name.startsWith("_")) return true
// always 'row'
if (name === "type") return true
// tables are always tied to a single tableId, this is irrelevant
if (name === "tableId") return true
return false
}
const handleUpdate = ({ detail }) => {
data[detail.row] = detail.data
updateRow(detail.data)
}
const updateRow = async row => {
const schema = (await API.fetchTableDefinition(row.tableId)).schema
await API.updateRow(schema, { data: row })
}
const deleteRows = async () => {
await API.deleteRows({ rows: selectedRows, tableId: datasource.name })
data = data.filter(row => !selectedRows.includes(row))
selectedRows = []
}
</script>
<svelte:head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css" />
</svelte:head>
<div class="container" use:styleable={gridStyles}>
{#if dataLoaded}
{#if canAddDelete}
<div class="controls">
{#if selectedRows.length > 0}
<DeleteButton text small on:click={modal.show()}>
<Icon name="addrow" />
Delete
{selectedRows.length}
row(s)
</DeleteButton>
{/if}
</div>
{/if}
<AgGrid
{theme}
{options}
{data}
{columnDefs}
on:update={handleUpdate}
on:select={({ detail }) => (selectedRows = detail)} />
{/if}
<Modal bind:this={modal}>
<ModalContent
title="Confirm Row Deletion"
confirmText="Delete"
onConfirm={deleteRows}>
<span>Are you sure you want to delete {selectedRows.length} row(s)?</span>
</ModalContent>
</Modal>
</div>
<style>
.container :global(.ag-pinned-left-header .ag-header-cell-label) {
justify-content: center;
}
.controls {
min-height: 15px;
margin-bottom: var(--spacing-s);
display: grid;
grid-gap: var(--spacing-s);
grid-template-columns: auto auto;
justify-content: start;
}
</style>