restructuring, more styling updates on table, renaming
This commit is contained in:
parent
137c3ccdec
commit
341843af84
|
@ -66,7 +66,7 @@
|
||||||
"@budibase/bbui": "^1.44.1",
|
"@budibase/bbui": "^1.44.1",
|
||||||
"@budibase/client": "^0.2.6",
|
"@budibase/client": "^0.2.6",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@budibase/svelte-ag-grid": "^0.0.15",
|
"@budibase/svelte-ag-grid": "^0.0.16",
|
||||||
"@fortawesome/fontawesome-free": "^5.14.0",
|
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@svelteschool/svelte-forms": "^0.7.0",
|
"@svelteschool/svelte-forms": "^0.7.0",
|
||||||
|
|
|
@ -1,233 +0,0 @@
|
||||||
<script>
|
|
||||||
import { goto, params } from "@sveltech/routify"
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import fsort from "fast-sort"
|
|
||||||
import getOr from "lodash/fp/getOr"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import { Button, Icon } from "@budibase/bbui"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import AttachmentList from "./AttachmentList.svelte"
|
|
||||||
import TablePagination from "./TablePagination.svelte"
|
|
||||||
import CreateEditRowModal from "./modals/CreateEditRowModal.svelte"
|
|
||||||
import RowPopover from "./buttons/CreateRowButton.svelte"
|
|
||||||
import ColumnPopover from "./buttons/CreateColumnButton.svelte"
|
|
||||||
import ViewPopover from "./buttons/CreateViewButton.svelte"
|
|
||||||
import ColumnHeaderPopover from "./popovers/ColumnPopover.svelte"
|
|
||||||
import EditRowPopover from "./popovers/RowPopover.svelte"
|
|
||||||
import CalculationPopover from "./buttons/CalculateButton.svelte"
|
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
|
||||||
|
|
||||||
const ITEMS_PER_PAGE = 10
|
|
||||||
|
|
||||||
export let schema = []
|
|
||||||
export let data = []
|
|
||||||
export let title
|
|
||||||
export let allowEditing = false
|
|
||||||
export let loading = false
|
|
||||||
|
|
||||||
let currentPage = 0
|
|
||||||
|
|
||||||
$: columns = schema ? Object.keys(schema) : []
|
|
||||||
$: sort = $backendUiStore.sort
|
|
||||||
$: sorted = sort ? fsort(data)[sort.direction](sort.column) : data
|
|
||||||
$: paginatedData =
|
|
||||||
sorted && sorted.length
|
|
||||||
? sorted.slice(
|
|
||||||
currentPage * ITEMS_PER_PAGE,
|
|
||||||
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
|
||||||
)
|
|
||||||
: []
|
|
||||||
$: tableId = data?.length ? data[0].tableId : null
|
|
||||||
|
|
||||||
function selectRelationship(row, fieldName) {
|
|
||||||
if (!row?.[fieldName]?.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
$goto(
|
|
||||||
`/${$params.application}/backend/table/${tableId}/relationship/${row._id}/${fieldName}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<div class="table-controls">
|
|
||||||
<h2 class="title">
|
|
||||||
<span>{title}</span>
|
|
||||||
{#if loading}
|
|
||||||
<div transition:fade>
|
|
||||||
<Spinner size="10" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</h2>
|
|
||||||
<div class="popovers">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<table class="bb-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{#if allowEditing}
|
|
||||||
<th class="edit-header">
|
|
||||||
<div>Edit</div>
|
|
||||||
</th>
|
|
||||||
{/if}
|
|
||||||
{#each columns as header}
|
|
||||||
<th>
|
|
||||||
{#if allowEditing}
|
|
||||||
<ColumnHeaderPopover field={schema[header]} />
|
|
||||||
{:else}
|
|
||||||
<div class="header">{header}</div>
|
|
||||||
{/if}
|
|
||||||
</th>
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#if paginatedData.length === 0}
|
|
||||||
{#if allowEditing}
|
|
||||||
<td class="no-border">No data.</td>
|
|
||||||
{/if}
|
|
||||||
{#each columns as header, idx}
|
|
||||||
<td class="no-border">
|
|
||||||
{#if idx === 0 && !allowEditing}No data.{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#each paginatedData as row}
|
|
||||||
<tr>
|
|
||||||
{#if allowEditing}
|
|
||||||
<td>
|
|
||||||
<EditRowPopover {row} />
|
|
||||||
</td>
|
|
||||||
{/if}
|
|
||||||
{#each columns as header}
|
|
||||||
<td>
|
|
||||||
{#if schema[header].type === 'link'}
|
|
||||||
<div
|
|
||||||
class:link={row[header] && row[header].length}
|
|
||||||
on:click={() => selectRelationship(row, header)}>
|
|
||||||
{row[header] ? row[header].length : 0}
|
|
||||||
related row(s)
|
|
||||||
</div>
|
|
||||||
{:else if schema[header].type === 'attachment'}
|
|
||||||
<AttachmentList files={row[header] || []} />
|
|
||||||
{:else}{getOr('', header, row)}{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<TablePagination
|
|
||||||
{data}
|
|
||||||
bind:currentPage
|
|
||||||
pageItemCount={paginatedData.length}
|
|
||||||
{ITEMS_PER_PAGE} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
margin-top: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.title > span {
|
|
||||||
margin-right: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: 1px solid var(--grey-4);
|
|
||||||
background: #fff;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
background: var(--grey-3);
|
|
||||||
border: 1px solid var(--grey-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
thead th {
|
|
||||||
color: var(--ink);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
transition: 0.5s all;
|
|
||||||
vertical-align: middle;
|
|
||||||
height: 48px;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead th:hover {
|
|
||||||
color: var(--blue);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
max-width: 200px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--grey-4);
|
|
||||||
white-space: nowrap;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: var(--spacing-l) var(--spacing-m);
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
td.no-border {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr {
|
|
||||||
border-bottom: 1px solid var(--grey-4);
|
|
||||||
transition: 0.3s background-color;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:hover {
|
|
||||||
background: var(--grey-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-controls {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popovers {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.popovers > div) {
|
|
||||||
margin-right: var(--spacing-m);
|
|
||||||
margin-bottom: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-header {
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit-header:hover {
|
|
||||||
cursor: default;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
color: var(--grey-6);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -3,10 +3,15 @@
|
||||||
import { goto, params } from "@sveltech/routify"
|
import { goto, params } from "@sveltech/routify"
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
import Spinner from "components/common/Spinner.svelte"
|
||||||
import AgGrid from "@budibase/svelte-ag-grid"
|
import AgGrid from "@budibase/svelte-ag-grid"
|
||||||
import { getRenderer, editRowRenderer } from "./cells/cellRenderers";
|
import {
|
||||||
|
getRenderer,
|
||||||
|
editRowRenderer,
|
||||||
|
} from "./cells/cellRenderers"
|
||||||
|
import TableLoadingOverlay from "./TableLoadingOverlay"
|
||||||
import TableHeader from "./TableHeader"
|
import TableHeader from "./TableHeader"
|
||||||
|
import "@budibase/svelte-ag-grid/dist/index.css"
|
||||||
|
|
||||||
export let schema = []
|
export let schema = {}
|
||||||
export let data = []
|
export let data = []
|
||||||
export let title
|
export let title
|
||||||
export let allowEditing = false
|
export let allowEditing = false
|
||||||
|
@ -28,9 +33,12 @@
|
||||||
pagination: true,
|
pagination: true,
|
||||||
enableRangeSelection: true,
|
enableRangeSelection: true,
|
||||||
popupParent: document.body,
|
popupParent: document.body,
|
||||||
|
components: {
|
||||||
|
customLoadingOverlay: TableLoadingOverlay,
|
||||||
|
},
|
||||||
|
loadingOverlayComponent: "customLoadingOverlay",
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor
|
|
||||||
$: {
|
$: {
|
||||||
let result = []
|
let result = []
|
||||||
if (allowEditing) {
|
if (allowEditing) {
|
||||||
|
@ -43,25 +51,31 @@
|
||||||
suppressMenu: true,
|
suppressMenu: true,
|
||||||
minWidth: 75,
|
minWidth: 75,
|
||||||
width: 75,
|
width: 75,
|
||||||
cellRenderer: editRowRenderer
|
cellRenderer: editRowRenderer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
columnDefs = [...result, ...Object.keys(schema || {}).map(key => ({
|
|
||||||
|
for (let key in schema) {
|
||||||
|
result.push({
|
||||||
headerComponent: TableHeader,
|
headerComponent: TableHeader,
|
||||||
headerComponentParams: {
|
headerComponentParams: {
|
||||||
field: schema[key]
|
field: schema[key],
|
||||||
|
editable: allowEditing,
|
||||||
},
|
},
|
||||||
headerName: key,
|
headerName: key,
|
||||||
field: key,
|
field: key,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
cellRenderer: getRenderer(schema[key], true),
|
cellRenderer: getRenderer(schema[key], true),
|
||||||
cellRendererParams: {
|
cellRendererParams: {
|
||||||
selectRelationship
|
selectRelationship,
|
||||||
},
|
},
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
}))]
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
columnDefs = result
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRelationship(row, fieldName) {
|
function selectRelationship(row, fieldName) {
|
||||||
|
@ -76,24 +90,12 @@
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="table-controls">
|
<div class="table-controls">
|
||||||
<h2 class="title">
|
<h2 class="title"><span>{title}</span></h2>
|
||||||
<span>{title}</span>
|
|
||||||
{#if loading}
|
|
||||||
<div transition:fade>
|
|
||||||
<Spinner size="10" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</h2>
|
|
||||||
<div class="popovers">
|
<div class="popovers">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AgGrid
|
<AgGrid {theme} {options} {data} {columnDefs} {loading} />
|
||||||
{theme}
|
|
||||||
{options}
|
|
||||||
{data}
|
|
||||||
{columnDefs}
|
|
||||||
/>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -125,6 +127,15 @@
|
||||||
margin-bottom: var(--spacing-xl);
|
margin-bottom: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.ag-menu) {
|
||||||
|
border: var(--border-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ag-popup-child) {
|
||||||
|
border-radius: var(--border-radius-m) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.ag-header-cell-text) {
|
:global(.ag-header-cell-text) {
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -132,13 +143,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.ag-filter) {
|
:global(.ag-filter) {
|
||||||
padding: var(--spacing-l);
|
padding: var(--spacing-s);
|
||||||
outline: none;
|
outline: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
border: var(--border-dark);
|
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
font-family: var(--font-sans) !important;
|
||||||
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,28 +157,43 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.ag-simple-filter-body-wrapper > *) {
|
||||||
|
margin-bottom: var(--spacing-m) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ag-select) {
|
||||||
|
height: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.ag-menu input) {
|
:global(.ag-menu input) {
|
||||||
color: var(--ink) !important;
|
color: var(--ink) !important;
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s) !important;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: var(--grey-2) !important;
|
background-color: var(--grey-2) !important;
|
||||||
padding: var(--spacing-m);
|
padding: var(--spacing-m);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
border: var(--border-transparent);
|
border: var(--border-transparent) !important;
|
||||||
|
transition: 0.2s all;
|
||||||
|
}
|
||||||
|
:global(.ag-menu input:focus) {
|
||||||
|
border: var(--border-blue) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.ag-picker-field-display) {
|
:global(.ag-picker-field-display) {
|
||||||
color: var(--ink) !important;
|
color: var(--ink) !important;
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s) !important;
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s) !important;
|
||||||
border: none;
|
|
||||||
background-color: var(--grey-2) !important;
|
background-color: var(--grey-2) !important;
|
||||||
padding: var(--spacing-m);
|
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
border: var(--border-transparent);
|
border: var(--border-transparent) !important;
|
||||||
transition: all 0.2s ease-in-out;
|
}
|
||||||
|
|
||||||
|
:global(.ag-picker-field-wrapper) {
|
||||||
|
background: var(--grey-2) !important;
|
||||||
|
border: var(--border-transparent) !important;
|
||||||
|
padding: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { Modal, ModalContent } from "@budibase/bbui"
|
import { Modal, ModalContent } from "@budibase/bbui"
|
||||||
import CreateEditColumnPopover from "../popovers/CreateEditColumnPopover.svelte"
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
const SORT_ICON_MAP = {
|
const SORT_ICON_MAP = {
|
||||||
asc: "ri-arrow-down-fill",
|
asc: "ri-arrow-down-fill",
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
export let enableSorting = true
|
export let enableSorting = true
|
||||||
export let showColumnMenu
|
export let showColumnMenu
|
||||||
export let progressSort
|
export let progressSort
|
||||||
|
export let editable
|
||||||
|
|
||||||
let menuButton
|
let menuButton
|
||||||
let sortDirection = ""
|
let sortDirection = ""
|
||||||
|
@ -50,13 +51,15 @@
|
||||||
showCancelButton={false}
|
showCancelButton={false}
|
||||||
showConfirmButton={false}
|
showConfirmButton={false}
|
||||||
title={`Edit Column: ${field.name}`}>
|
title={`Edit Column: ${field.name}`}>
|
||||||
<CreateEditColumnPopover onClosed={modal.hide} {field} />
|
<CreateEditColumn onClosed={modal.hide} {field} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
<div>
|
<div>
|
||||||
|
{#if editable}
|
||||||
<span on:click|stopPropagation={showModal}>
|
<span on:click|stopPropagation={showModal}>
|
||||||
<i class="ri-pencil-line" />
|
<i class="ri-pencil-line" />
|
||||||
</span>
|
</span>
|
||||||
|
{/if}
|
||||||
<span on:click|stopPropagation={toggleMenu} bind:this={menuButton}>
|
<span on:click|stopPropagation={toggleMenu} bind:this={menuButton}>
|
||||||
<i class="ri-filter-line" />
|
<i class="ri-filter-line" />
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,46 +1,9 @@
|
||||||
// // the column the header is for
|
|
||||||
// column: Column;
|
|
||||||
|
|
||||||
// // the name to display for the column. if the column is using a headerValueGetter,
|
|
||||||
// // the displayName will take this into account.
|
|
||||||
// displayName: string;
|
|
||||||
|
|
||||||
// // whether sorting is enabled for the column. only put sort logic into
|
|
||||||
// // your header if this is true.
|
|
||||||
// enableSorting: boolean;
|
|
||||||
|
|
||||||
// // whether menu is enabled for the column. only display a menu button
|
|
||||||
// // in your header if this is true.
|
|
||||||
// enableMenu: boolean;
|
|
||||||
|
|
||||||
// // callback to progress the sort for this column.
|
|
||||||
// // the grid will decide the next sort direction eg ascending, descending or 'no sort'.
|
|
||||||
// // pass multiSort=true if you want to do a multi sort (eg user has shift held down when
|
|
||||||
// // they click)
|
|
||||||
// progressSort(multiSort: boolean): void;
|
|
||||||
|
|
||||||
// // callback to set the sort for this column.
|
|
||||||
// // pass the sort direction to use ignoring the current sort eg one of 'asc', 'desc' or null
|
|
||||||
// // (for no sort). pass multiSort=true if you want to do a multi sort (eg user has shift held
|
|
||||||
// // down when they click)
|
|
||||||
// setSort(sort: string, multiSort?: boolean): void;
|
|
||||||
|
|
||||||
// // callback to request the grid to show the column menu.
|
|
||||||
// // pass in the html element of the column menu to have the
|
|
||||||
// // grid position the menu over the button.
|
|
||||||
// showColumnMenu(menuButton: HTMLElement): void;
|
|
||||||
|
|
||||||
// // The grid API
|
|
||||||
// api: any;
|
|
||||||
import TableHeader from "./TableHeader.svelte"
|
import TableHeader from "./TableHeader.svelte"
|
||||||
|
|
||||||
export default class TableHeaderWrapper {
|
export default class TableHeaderWrapper {
|
||||||
constructor() {
|
constructor() {}
|
||||||
// foo
|
|
||||||
}
|
|
||||||
|
|
||||||
init(params) {
|
init(params) {
|
||||||
console.log("init", params)
|
|
||||||
this.agParams = params
|
this.agParams = params
|
||||||
this.container = document.createElement("div")
|
this.container = document.createElement("div")
|
||||||
this.container.style.height = "100%"
|
this.container.style.height = "100%"
|
||||||
|
@ -50,12 +13,12 @@ export default class TableHeaderWrapper {
|
||||||
target: this.container,
|
target: this.container,
|
||||||
props: params,
|
props: params,
|
||||||
})
|
})
|
||||||
this.eGui = this.container
|
this.gui = this.container
|
||||||
}
|
}
|
||||||
|
|
||||||
// can get called more than once, you should return the HTML element
|
// can get called more than once, you should return the HTML element
|
||||||
getGui() {
|
getGui() {
|
||||||
return this.eGui
|
return this.gui
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets called when a new Column Definition has been set for this header
|
// gets called when a new Column Definition has been set for this header
|
||||||
|
@ -65,11 +28,8 @@ export default class TableHeaderWrapper {
|
||||||
target: this.container,
|
target: this.container,
|
||||||
props: params,
|
props: params,
|
||||||
})
|
})
|
||||||
console.log("Refreshing", params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional method, gets called once, when component is destroyed
|
// optional method, gets called once, when component is destroyed
|
||||||
destroy() {
|
destroy() {}
|
||||||
console.log("Destroy")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script>
|
||||||
|
import Spinner from "components/common/Spinner.svelte"
|
||||||
|
import { fade } from "svelte/transition"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="ag-overlay-loading-center loading-container">
|
||||||
|
<div transition:fade class="loading-overlay">
|
||||||
|
<img
|
||||||
|
height="30"
|
||||||
|
width="30"
|
||||||
|
src="/_builder/assets/bb-logo.svg"
|
||||||
|
alt="Budibase icon" />
|
||||||
|
<span> Loading Your Data </span>
|
||||||
|
<Spinner size="12" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.loading-overlay {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-overlay > * {
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,17 @@
|
||||||
|
import LoadingOverlay from "./LoadingOverlay.svelte"
|
||||||
|
|
||||||
|
export default class LoadingOverlayWrapper {
|
||||||
|
init(params) {
|
||||||
|
this.gui = document.createElement("div")
|
||||||
|
new LoadingOverlay({
|
||||||
|
target: this.gui,
|
||||||
|
props: {
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getGui() {
|
||||||
|
return this.gui
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,136 +0,0 @@
|
||||||
<script>
|
|
||||||
export let data
|
|
||||||
export let currentPage = 0
|
|
||||||
export let pageItemCount
|
|
||||||
export let ITEMS_PER_PAGE
|
|
||||||
|
|
||||||
let numPages = 0
|
|
||||||
$: numPages = Math.ceil((data?.length ?? 0) / ITEMS_PER_PAGE)
|
|
||||||
$: displayAllPages = numPages <= 10
|
|
||||||
$: pagesAroundCurrent = getPagesAroundCurrent(currentPage, numPages)
|
|
||||||
|
|
||||||
const next = () => {
|
|
||||||
if (currentPage + 1 === numPages) return
|
|
||||||
currentPage = currentPage + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const previous = () => {
|
|
||||||
if (currentPage == 0) return
|
|
||||||
currentPage = currentPage - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectPage = page => {
|
|
||||||
currentPage = page
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPagesAroundCurrent(current, max) {
|
|
||||||
const start = Math.max(current - 2, 1)
|
|
||||||
const end = Math.min(current + 2, max - 2)
|
|
||||||
let pages = []
|
|
||||||
for (let i = start; i <= end; i++) {
|
|
||||||
pages.push(i)
|
|
||||||
}
|
|
||||||
return pages
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="pagination">
|
|
||||||
<div class="pagination__buttons">
|
|
||||||
<button on:click={previous} disabled={currentPage === 0}><</button>
|
|
||||||
{#if displayAllPages}
|
|
||||||
{#each Array(numPages) as _, idx}
|
|
||||||
<button
|
|
||||||
class:selected={idx === currentPage}
|
|
||||||
on:click={() => selectPage(idx)}>
|
|
||||||
{idx + 1}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<button class:selected={currentPage === 0} on:click={() => selectPage(0)}>
|
|
||||||
1
|
|
||||||
</button>
|
|
||||||
{#if currentPage > 3}<button disabled>...</button>{/if}
|
|
||||||
{#each pagesAroundCurrent as idx}
|
|
||||||
<button
|
|
||||||
class:selected={idx === currentPage}
|
|
||||||
on:click={() => selectPage(idx)}>
|
|
||||||
{idx + 1}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
{#if currentPage < numPages - 4}<button disabled>...</button>{/if}
|
|
||||||
<button
|
|
||||||
class:selected={currentPage === numPages - 1}
|
|
||||||
on:click={() => selectPage(numPages - 1)}>
|
|
||||||
{numPages}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
<button
|
|
||||||
on:click={next}
|
|
||||||
disabled={currentPage === numPages - 1 || numPages === 0}>
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{#if numPages > 1}
|
|
||||||
Showing
|
|
||||||
{ITEMS_PER_PAGE * currentPage + 1}
|
|
||||||
-
|
|
||||||
{ITEMS_PER_PAGE * currentPage + pageItemCount}
|
|
||||||
of
|
|
||||||
{data.length}
|
|
||||||
rows
|
|
||||||
{:else if numPages === 1}Showing all {data.length} row(s){/if}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.pagination {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination__buttons {
|
|
||||||
display: flex;
|
|
||||||
border: 1px solid var(--grey-4);
|
|
||||||
border-radius: var(--border-radius-s);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination__buttons button {
|
|
||||||
display: inline-block;
|
|
||||||
padding: var(--spacing-s) 0;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
background: #fff;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
border-right: 1px solid var(--grey-4);
|
|
||||||
text-transform: capitalize;
|
|
||||||
min-width: 20px;
|
|
||||||
transition: 0.3s background-color;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
color: var(--grey-6);
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
.pagination__buttons button:last-child {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
.pagination__buttons button:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
}
|
|
||||||
.pagination__buttons button.selected {
|
|
||||||
background: var(--blue);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: var(--font-size-s);
|
|
||||||
margin: var(--spacing-xl) 0;
|
|
||||||
color: var(--grey-6);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -6,7 +6,7 @@
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import CreateEditColumnPopover from "../popovers/CreateEditColumnPopover.svelte"
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
let fieldName
|
let fieldName
|
||||||
|
@ -23,6 +23,6 @@
|
||||||
showCancelButton={false}
|
showCancelButton={false}
|
||||||
showConfirmButton={false}
|
showConfirmButton={false}
|
||||||
title={'Create Column'}>
|
title={'Create Column'}>
|
||||||
<CreateEditColumnPopover onClosed={modal.hide} />
|
<CreateEditColumn onClosed={modal.hide} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
: 0
|
: 0
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class:link={count} on:click={() => selectRelationship(row, columnName)}>
|
||||||
class:link={count}
|
|
||||||
on:click={() => selectRelationship(row, columnName)}>
|
|
||||||
{count}
|
{count}
|
||||||
related row(s)
|
related row(s)
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import AttachmentList from "./AttachmentCell.svelte"
|
import AttachmentList from "./AttachmentCell.svelte"
|
||||||
import EditRowPopover from "../popovers/RowPopover.svelte"
|
import EditRowPopover from "../popovers/RowPopover.svelte"
|
||||||
import RelationshipDisplay from "./RelationshipCell.svelte"
|
import RelationshipDisplay from "./RelationshipCell.svelte"
|
||||||
|
import LoadingOverlay from "./LoadingOverlay.svelte"
|
||||||
|
|
||||||
const renderers = {
|
const renderers = {
|
||||||
attachment: attachmentRenderer,
|
attachment: attachmentRenderer,
|
||||||
|
@ -28,7 +29,6 @@ export function editRowRenderer(params) {
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
function attachmentRenderer(options, constraints, editable) {
|
function attachmentRenderer(options, constraints, editable) {
|
||||||
return params => {
|
return params => {
|
||||||
|
@ -57,7 +57,7 @@ function linkedRowRenderer() {
|
||||||
props: {
|
props: {
|
||||||
row: params.data,
|
row: params.data,
|
||||||
columnName: params.column.colId,
|
columnName: params.column.colId,
|
||||||
selectRelationship: params.selectRelationship
|
selectRelationship: params.selectRelationship,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { Input, Button, Select, Toggle } from "@budibase/bbui"
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Select,
|
|
||||||
Toggle,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
|
@ -160,10 +155,10 @@
|
||||||
bind:value={field.fieldName} />
|
bind:value={field.fieldName} />
|
||||||
{/if}
|
{/if}
|
||||||
<footer>
|
<footer>
|
||||||
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
{#if originalName}
|
{#if originalName}
|
||||||
<Button red on:click={confirmDelete}>Delete Column</Button>
|
<Button red on:click={confirmDelete}>Delete Column</Button>
|
||||||
{/if}
|
{/if}
|
||||||
<Button secondary on:click={onClosed}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveColumn}>Save Column</Button>
|
<Button primary on:click={saveColumn}>Save Column</Button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
|
@ -1,145 +0,0 @@
|
||||||
<script>
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
|
||||||
import { FIELDS } from "constants/backend"
|
|
||||||
import CreateEditColumnPopover from "./CreateEditColumnPopover.svelte"
|
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
|
||||||
import { notifier } from "../../../../builderStore/store/notifications"
|
|
||||||
|
|
||||||
export let field
|
|
||||||
|
|
||||||
let anchor
|
|
||||||
let dropdown
|
|
||||||
let editing
|
|
||||||
let confirmDeleteDialog
|
|
||||||
|
|
||||||
$: sortColumn = $backendUiStore.sort && $backendUiStore.sort.column
|
|
||||||
$: sortDirection = $backendUiStore.sort && $backendUiStore.sort.direction
|
|
||||||
$: type = field?.type
|
|
||||||
|
|
||||||
function showEditor() {
|
|
||||||
editing = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideEditor() {
|
|
||||||
dropdown.hide()
|
|
||||||
editing = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDelete() {
|
|
||||||
dropdown.hide()
|
|
||||||
confirmDeleteDialog.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteColumn() {
|
|
||||||
if (field.name === $backendUiStore.selectedTable.primaryDisplay) {
|
|
||||||
notifier.danger("You cannot delete the display column")
|
|
||||||
} else {
|
|
||||||
backendUiStore.actions.tables.deleteField(field)
|
|
||||||
notifier.success("Column deleted")
|
|
||||||
}
|
|
||||||
hideEditor()
|
|
||||||
}
|
|
||||||
|
|
||||||
function sort(direction, column) {
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
if (direction !== "none") {
|
|
||||||
state.sort = { direction, column }
|
|
||||||
} else {
|
|
||||||
state.sort = undefined
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
hideEditor()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="container" bind:this={anchor} on:click={dropdown.show}>
|
|
||||||
<span>{field.name}</span>
|
|
||||||
<Icon name="arrowdown" />
|
|
||||||
</div>
|
|
||||||
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
|
||||||
{#if editing}
|
|
||||||
<h5>Edit Column</h5>
|
|
||||||
<CreateEditColumnPopover onClosed={hideEditor} {field} />
|
|
||||||
{:else}
|
|
||||||
<ul>
|
|
||||||
{#if type !== 'link'}
|
|
||||||
<li data-cy="edit-column-header" on:click={showEditor}>
|
|
||||||
<Icon name="edit" />
|
|
||||||
Edit
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
<li data-cy="delete-column-header" on:click={showDelete}>
|
|
||||||
<Icon name="delete" />
|
|
||||||
Delete
|
|
||||||
</li>
|
|
||||||
{#if sortDirection === 'desc' || sortDirection === 'asc'}
|
|
||||||
<li on:click={() => sort('none', field.name)}>
|
|
||||||
<Icon name="close" />
|
|
||||||
Remove sort
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
{#if sortDirection === 'desc' || sortColumn !== field.name}
|
|
||||||
<li on:click={() => sort('asc', field.name)}>
|
|
||||||
<Icon name="sortascending" />
|
|
||||||
Sort A - Z
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
{#if sortDirection === 'asc' || sortColumn !== field.name}
|
|
||||||
<li on:click={() => sort('desc', field.name)}>
|
|
||||||
<Icon name="sortdescending" />
|
|
||||||
Sort Z - A
|
|
||||||
</li>
|
|
||||||
{/if}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</DropdownMenu>
|
|
||||||
<ConfirmDialog
|
|
||||||
bind:this={confirmDeleteDialog}
|
|
||||||
body={`Are you sure you wish to delete this column? Your data will be deleted and this action cannot be undone.`}
|
|
||||||
okText="Delete Column"
|
|
||||||
onOk={deleteColumn}
|
|
||||||
title="Confirm Delete" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding: var(--spacing-s) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
color: var(--ink);
|
|
||||||
padding: var(--spacing-s) var(--spacing-m);
|
|
||||||
margin: auto 0px;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
li:hover {
|
|
||||||
background-color: var(--grey-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
li:active {
|
|
||||||
color: var(--blue);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
|
|
@ -30,9 +30,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div on:click={showModal}>
|
<div on:click={showModal}><i class="ri-more-line" /></div>
|
||||||
<i class="ri-more-line" />
|
|
||||||
</div>
|
|
||||||
<!-- <DropdownMenu bind:this={dropdown} {anchor} align="left">
|
<!-- <DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
<ul>
|
<ul>
|
||||||
<li data-cy="edit-row" on:click={showModal}>
|
<li data-cy="edit-row" on:click={showModal}>
|
||||||
|
|
|
@ -731,10 +731,10 @@
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
||||||
|
|
||||||
"@budibase/svelte-ag-grid@^0.0.15":
|
"@budibase/svelte-ag-grid@^0.0.16":
|
||||||
version "0.0.15"
|
version "0.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/svelte-ag-grid/-/svelte-ag-grid-0.0.15.tgz#d115243e54b2d00624794afa07fbea4f45c2824d"
|
resolved "https://registry.yarnpkg.com/@budibase/svelte-ag-grid/-/svelte-ag-grid-0.0.16.tgz#1b91dc1e27dad034b827dc7b258fa16d3d3bf68f"
|
||||||
integrity sha512-1DYrbv07urntexv8nTHsKcgseQOaw6rVNMU0kLQXoDqHJncyyBc9bSh5qCgitAYvKtJ2GhT/Lj/v84z8Kb2fdQ==
|
integrity sha512-Yxnfe03Mo7VhuB4wJSGNoc8jaorH9lertptPt2halef9Z93kkYwdwpldnWVzQT07YdX6soPaVLhupxKrI5Hvtw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ag-grid-community "^24.0.0"
|
ag-grid-community "^24.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue