@budibase/frontend-core 2.5.6-alpha.1 → 2.5.6-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -4
- package/src/components/grid/cells/AttachmentCell.svelte +153 -0
- package/src/components/grid/cells/BooleanCell.svelte +44 -0
- package/src/components/grid/cells/DataCell.svelte +86 -0
- package/src/components/grid/cells/DateCell.svelte +77 -0
- package/src/components/grid/cells/FormulaCell.svelte +5 -0
- package/src/components/grid/cells/GridCell.svelte +159 -0
- package/src/components/grid/cells/HeaderCell.svelte +237 -0
- package/src/components/grid/cells/JSONCell.svelte +36 -0
- package/src/components/grid/cells/LongFormCell.svelte +117 -0
- package/src/components/grid/cells/MultiSelectCell.svelte +7 -0
- package/src/components/grid/cells/NumberCell.svelte +7 -0
- package/src/components/grid/cells/OptionsCell.svelte +239 -0
- package/src/components/grid/cells/RelationshipCell.svelte +503 -0
- package/src/components/grid/cells/TextCell.svelte +110 -0
- package/src/components/grid/controls/AddColumnButton.svelte +16 -0
- package/src/components/grid/controls/AddRowButton.svelte +18 -0
- package/src/components/grid/controls/BetaButton.svelte +46 -0
- package/src/components/grid/controls/DeleteButton.svelte +72 -0
- package/src/components/grid/controls/HideColumnsButton.svelte +91 -0
- package/src/components/grid/controls/RowHeightButton.svelte +70 -0
- package/src/components/grid/controls/SortButton.svelte +107 -0
- package/src/components/grid/index.js +1 -0
- package/src/components/grid/layout/Avatar.svelte +24 -0
- package/src/components/grid/layout/Grid.svelte +250 -0
- package/src/components/grid/layout/GridBody.svelte +38 -0
- package/src/components/grid/layout/GridRow.svelte +57 -0
- package/src/components/grid/layout/GridScrollWrapper.svelte +84 -0
- package/src/components/grid/layout/HeaderRow.svelte +30 -0
- package/src/components/grid/layout/NewRowTop.svelte +247 -0
- package/src/components/grid/layout/RowCount.svelte +9 -0
- package/src/components/grid/layout/StickyColumn.svelte +245 -0
- package/src/components/grid/layout/UserAvatars.svelte +20 -0
- package/src/components/grid/lib/constants.js +11 -0
- package/src/components/grid/lib/events.js +29 -0
- package/src/components/grid/lib/renderers.js +29 -0
- package/src/components/grid/lib/utils.js +26 -0
- package/src/components/grid/lib/websocket.js +63 -0
- package/src/components/grid/overlays/KeyboardManager.svelte +180 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +95 -0
- package/src/components/grid/overlays/ReorderOverlay.svelte +63 -0
- package/src/components/grid/overlays/ResizeOverlay.svelte +70 -0
- package/src/components/grid/overlays/ScrollOverlay.svelte +121 -0
- package/src/components/grid/stores/bounds.js +16 -0
- package/src/components/grid/stores/clipboard.js +34 -0
- package/src/components/grid/stores/columns.js +206 -0
- package/src/components/grid/stores/index.js +48 -0
- package/src/components/grid/stores/menu.js +49 -0
- package/src/components/grid/stores/pagination.js +24 -0
- package/src/components/grid/stores/reorder.js +155 -0
- package/src/components/grid/stores/resize.js +124 -0
- package/src/components/grid/stores/rows.js +462 -0
- package/src/components/grid/stores/scroll.js +201 -0
- package/src/components/grid/stores/ui.js +168 -0
- package/src/components/grid/stores/users.js +115 -0
- package/src/components/grid/stores/validation.js +45 -0
- package/src/components/grid/stores/viewport.js +141 -0
- package/src/components/index.js +1 -0
- package/src/fetch/DataFetch.js +27 -21
- package/src/fetch/QueryFetch.js +1 -1
- package/src/themes/midnight.css +3 -0
- package/src/themes/nord.css +3 -0
- package/src/utils/utils.js +20 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
|
+
ModalContent,
|
|
5
|
+
ActionButton,
|
|
6
|
+
notifications,
|
|
7
|
+
} from "@budibase/bbui"
|
|
8
|
+
import { getContext } from "svelte"
|
|
9
|
+
|
|
10
|
+
const { selectedRows, rows, config } = getContext("grid")
|
|
11
|
+
|
|
12
|
+
let modal
|
|
13
|
+
|
|
14
|
+
$: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
|
|
15
|
+
$: rowsToDelete = Object.entries($selectedRows)
|
|
16
|
+
.map(entry => {
|
|
17
|
+
if (entry[1] === true) {
|
|
18
|
+
return $rows.find(x => x._id === entry[0])
|
|
19
|
+
} else {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
.filter(x => x != null)
|
|
24
|
+
|
|
25
|
+
// Deletion callback when confirmed
|
|
26
|
+
const performDeletion = async () => {
|
|
27
|
+
const count = rowsToDelete.length
|
|
28
|
+
await rows.actions.deleteRows(rowsToDelete)
|
|
29
|
+
notifications.success(`Deleted ${count} row${count === 1 ? "" : "s"}`)
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#if selectedRowCount}
|
|
34
|
+
<div class="delete-button" data-ignore-click-outside="true">
|
|
35
|
+
<ActionButton
|
|
36
|
+
icon="Delete"
|
|
37
|
+
size="S"
|
|
38
|
+
on:click={modal.show}
|
|
39
|
+
disabled={!$config.allowEditRows}
|
|
40
|
+
>
|
|
41
|
+
Delete {selectedRowCount} row{selectedRowCount === 1 ? "" : "s"}
|
|
42
|
+
</ActionButton>
|
|
43
|
+
</div>
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
46
|
+
<Modal bind:this={modal}>
|
|
47
|
+
<ModalContent
|
|
48
|
+
title="Delete rows"
|
|
49
|
+
confirmText="Continue"
|
|
50
|
+
cancelText="Cancel"
|
|
51
|
+
onConfirm={performDeletion}
|
|
52
|
+
size="M"
|
|
53
|
+
>
|
|
54
|
+
Are you sure you want to delete {selectedRowCount}
|
|
55
|
+
row{selectedRowCount === 1 ? "" : "s"}?
|
|
56
|
+
</ModalContent>
|
|
57
|
+
</Modal>
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.delete-button :global(.spectrum-ActionButton:not(:disabled) *) {
|
|
61
|
+
color: var(--spectrum-global-color-red-400);
|
|
62
|
+
}
|
|
63
|
+
.delete-button :global(.spectrum-ActionButton:not(:disabled)) {
|
|
64
|
+
border-color: var(--spectrum-global-color-red-400);
|
|
65
|
+
}
|
|
66
|
+
/*.delete-button.disabled :global(.spectrum-ActionButton *) {*/
|
|
67
|
+
/* color: var(--spectrum-global-color-gray-600);*/
|
|
68
|
+
/*}*/
|
|
69
|
+
/*.delete-button.disabled :global(.spectrum-ActionButton) {*/
|
|
70
|
+
/* border-color: var(--spectrum-global-color-gray-400);*/
|
|
71
|
+
/*}*/
|
|
72
|
+
</style>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import { ActionButton, Popover, Toggle } from "@budibase/bbui"
|
|
4
|
+
|
|
5
|
+
const { columns } = getContext("grid")
|
|
6
|
+
|
|
7
|
+
let open = false
|
|
8
|
+
let anchor
|
|
9
|
+
|
|
10
|
+
$: anyHidden = $columns.some(col => !col.visible)
|
|
11
|
+
|
|
12
|
+
const toggleVisibility = (column, visible) => {
|
|
13
|
+
columns.update(state => {
|
|
14
|
+
const index = state.findIndex(col => col.name === column.name)
|
|
15
|
+
state[index].visible = visible
|
|
16
|
+
return state.slice()
|
|
17
|
+
})
|
|
18
|
+
columns.actions.saveChanges()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const showAll = () => {
|
|
22
|
+
columns.update(state => {
|
|
23
|
+
return state.map(col => ({
|
|
24
|
+
...col,
|
|
25
|
+
visible: true,
|
|
26
|
+
}))
|
|
27
|
+
})
|
|
28
|
+
columns.actions.saveChanges()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const hideAll = () => {
|
|
32
|
+
columns.update(state => {
|
|
33
|
+
return state.map(col => ({
|
|
34
|
+
...col,
|
|
35
|
+
visible: false,
|
|
36
|
+
}))
|
|
37
|
+
})
|
|
38
|
+
columns.actions.saveChanges()
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<div bind:this={anchor}>
|
|
43
|
+
<ActionButton
|
|
44
|
+
icon="VisibilityOff"
|
|
45
|
+
quiet
|
|
46
|
+
size="M"
|
|
47
|
+
on:click={() => (open = !open)}
|
|
48
|
+
selected={open || anyHidden}
|
|
49
|
+
disabled={!$columns.length}
|
|
50
|
+
>
|
|
51
|
+
Hide columns
|
|
52
|
+
</ActionButton>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<Popover bind:open {anchor} align="left">
|
|
56
|
+
<div class="content">
|
|
57
|
+
<div class="columns">
|
|
58
|
+
{#each $columns as column}
|
|
59
|
+
<Toggle
|
|
60
|
+
size="S"
|
|
61
|
+
value={column.visible}
|
|
62
|
+
on:change={e => toggleVisibility(column, e.detail)}
|
|
63
|
+
/>
|
|
64
|
+
<span>{column.name}</span>
|
|
65
|
+
{/each}
|
|
66
|
+
</div>
|
|
67
|
+
<div class="buttons">
|
|
68
|
+
<ActionButton on:click={showAll}>Show all</ActionButton>
|
|
69
|
+
<ActionButton on:click={hideAll}>Hide all</ActionButton>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</Popover>
|
|
73
|
+
|
|
74
|
+
<style>
|
|
75
|
+
.content {
|
|
76
|
+
padding: 12px 12px;
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
gap: 12px;
|
|
80
|
+
}
|
|
81
|
+
.buttons {
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-direction: row;
|
|
84
|
+
gap: 8px;
|
|
85
|
+
}
|
|
86
|
+
.columns {
|
|
87
|
+
display: grid;
|
|
88
|
+
align-items: center;
|
|
89
|
+
grid-template-columns: auto 1fr;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import { ActionButton, Popover } from "@budibase/bbui"
|
|
4
|
+
import {
|
|
5
|
+
LargeRowHeight,
|
|
6
|
+
MediumRowHeight,
|
|
7
|
+
SmallRowHeight,
|
|
8
|
+
} from "../lib/constants"
|
|
9
|
+
|
|
10
|
+
const { rowHeight, columns, table } = getContext("grid")
|
|
11
|
+
const sizeOptions = [
|
|
12
|
+
{
|
|
13
|
+
label: "Small",
|
|
14
|
+
size: SmallRowHeight,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "Medium",
|
|
18
|
+
size: MediumRowHeight,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Large",
|
|
22
|
+
size: LargeRowHeight,
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
let open = false
|
|
27
|
+
let anchor
|
|
28
|
+
|
|
29
|
+
const changeRowHeight = height => {
|
|
30
|
+
columns.actions.saveTable({
|
|
31
|
+
...$table,
|
|
32
|
+
rowHeight: height,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<div bind:this={anchor}>
|
|
38
|
+
<ActionButton
|
|
39
|
+
icon="LineHeight"
|
|
40
|
+
quiet
|
|
41
|
+
size="M"
|
|
42
|
+
on:click={() => (open = !open)}
|
|
43
|
+
selected={open}
|
|
44
|
+
>
|
|
45
|
+
Row height
|
|
46
|
+
</ActionButton>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<Popover bind:open {anchor} align="left">
|
|
50
|
+
<div class="content">
|
|
51
|
+
{#each sizeOptions as option}
|
|
52
|
+
<ActionButton
|
|
53
|
+
quiet
|
|
54
|
+
selected={$rowHeight === option.size}
|
|
55
|
+
on:click={() => changeRowHeight(option.size)}
|
|
56
|
+
>
|
|
57
|
+
{option.label}
|
|
58
|
+
</ActionButton>
|
|
59
|
+
{/each}
|
|
60
|
+
</div>
|
|
61
|
+
</Popover>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.content {
|
|
65
|
+
padding: 12px;
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 8px;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import { ActionButton, Popover, Select } from "@budibase/bbui"
|
|
4
|
+
|
|
5
|
+
const { sort, visibleColumns, stickyColumn } = getContext("grid")
|
|
6
|
+
const orderOptions = [
|
|
7
|
+
{ label: "A-Z", value: "ascending" },
|
|
8
|
+
{ label: "Z-A", value: "descending" },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
let open = false
|
|
12
|
+
let anchor
|
|
13
|
+
|
|
14
|
+
$: columnOptions = getColumnOptions($stickyColumn, $visibleColumns)
|
|
15
|
+
$: checkValidSortColumn($sort.column, $stickyColumn, $visibleColumns)
|
|
16
|
+
|
|
17
|
+
const getColumnOptions = (stickyColumn, columns) => {
|
|
18
|
+
let options = []
|
|
19
|
+
if (stickyColumn) {
|
|
20
|
+
options.push(stickyColumn.name)
|
|
21
|
+
}
|
|
22
|
+
return [...options, ...columns.map(col => col.name)]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const updateSortColumn = e => {
|
|
26
|
+
sort.update(state => ({
|
|
27
|
+
...state,
|
|
28
|
+
column: e.detail,
|
|
29
|
+
}))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const updateSortOrder = e => {
|
|
33
|
+
sort.update(state => ({
|
|
34
|
+
...state,
|
|
35
|
+
order: e.detail,
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Ensure we never have a sort column selected that is not visible
|
|
40
|
+
const checkValidSortColumn = (sortColumn, stickyColumn, visibleColumns) => {
|
|
41
|
+
if (!sortColumn) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
sortColumn !== stickyColumn?.name &&
|
|
46
|
+
!visibleColumns.some(col => col.name === sortColumn)
|
|
47
|
+
) {
|
|
48
|
+
if (stickyColumn) {
|
|
49
|
+
sort.update(state => ({
|
|
50
|
+
...state,
|
|
51
|
+
column: stickyColumn.name,
|
|
52
|
+
}))
|
|
53
|
+
} else {
|
|
54
|
+
sort.update(state => ({
|
|
55
|
+
...state,
|
|
56
|
+
column: visibleColumns[0]?.name,
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<div bind:this={anchor}>
|
|
64
|
+
<ActionButton
|
|
65
|
+
icon="SortOrderDown"
|
|
66
|
+
quiet
|
|
67
|
+
size="M"
|
|
68
|
+
on:click={() => (open = !open)}
|
|
69
|
+
selected={open || $sort.column}
|
|
70
|
+
disabled={!columnOptions.length}
|
|
71
|
+
>
|
|
72
|
+
Sort
|
|
73
|
+
</ActionButton>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<Popover bind:open {anchor} align="left">
|
|
77
|
+
<div class="content">
|
|
78
|
+
<Select
|
|
79
|
+
placeholder={null}
|
|
80
|
+
value={$sort.column}
|
|
81
|
+
options={columnOptions}
|
|
82
|
+
autoWidth
|
|
83
|
+
on:change={updateSortColumn}
|
|
84
|
+
label="Column"
|
|
85
|
+
/>
|
|
86
|
+
<Select
|
|
87
|
+
placeholder={null}
|
|
88
|
+
value={$sort.order}
|
|
89
|
+
options={orderOptions}
|
|
90
|
+
autoWidth
|
|
91
|
+
on:change={updateSortOrder}
|
|
92
|
+
label="Order"
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
</Popover>
|
|
96
|
+
|
|
97
|
+
<style>
|
|
98
|
+
.content {
|
|
99
|
+
padding: 6px 12px 12px 12px;
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 8px;
|
|
103
|
+
}
|
|
104
|
+
.content :global(.spectrum-Picker) {
|
|
105
|
+
width: 140px;
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Grid } from "./layout/Grid.svelte"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let user
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="user" style="background:{user.color};" title={user.email}>
|
|
6
|
+
{user.email[0]}
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
div {
|
|
11
|
+
width: 24px;
|
|
12
|
+
height: 24px;
|
|
13
|
+
display: grid;
|
|
14
|
+
place-items: center;
|
|
15
|
+
color: white;
|
|
16
|
+
border-radius: 50%;
|
|
17
|
+
font-size: 12px;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
text-transform: uppercase;
|
|
20
|
+
}
|
|
21
|
+
div:hover {
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { setContext } from "svelte"
|
|
3
|
+
import { writable } from "svelte/store"
|
|
4
|
+
import { fade } from "svelte/transition"
|
|
5
|
+
import { clickOutside, ProgressCircle } from "@budibase/bbui"
|
|
6
|
+
import { createEventManagers } from "../lib/events"
|
|
7
|
+
import { createAPIClient } from "../../../api"
|
|
8
|
+
import { attachStores } from "../stores"
|
|
9
|
+
import DeleteButton from "../controls/DeleteButton.svelte"
|
|
10
|
+
import BetaButton from "../controls/BetaButton.svelte"
|
|
11
|
+
import GridBody from "./GridBody.svelte"
|
|
12
|
+
import ResizeOverlay from "../overlays/ResizeOverlay.svelte"
|
|
13
|
+
import ReorderOverlay from "../overlays/ReorderOverlay.svelte"
|
|
14
|
+
import HeaderRow from "./HeaderRow.svelte"
|
|
15
|
+
import ScrollOverlay from "../overlays/ScrollOverlay.svelte"
|
|
16
|
+
import MenuOverlay from "../overlays/MenuOverlay.svelte"
|
|
17
|
+
import StickyColumn from "./StickyColumn.svelte"
|
|
18
|
+
import UserAvatars from "./UserAvatars.svelte"
|
|
19
|
+
import KeyboardManager from "../overlays/KeyboardManager.svelte"
|
|
20
|
+
import SortButton from "../controls/SortButton.svelte"
|
|
21
|
+
import AddColumnButton from "../controls/AddColumnButton.svelte"
|
|
22
|
+
import HideColumnsButton from "../controls/HideColumnsButton.svelte"
|
|
23
|
+
import AddRowButton from "../controls/AddRowButton.svelte"
|
|
24
|
+
import RowHeightButton from "../controls/RowHeightButton.svelte"
|
|
25
|
+
import {
|
|
26
|
+
MaxCellRenderHeight,
|
|
27
|
+
MaxCellRenderWidthOverflow,
|
|
28
|
+
GutterWidth,
|
|
29
|
+
DefaultRowHeight,
|
|
30
|
+
} from "../lib/constants"
|
|
31
|
+
|
|
32
|
+
export let API = null
|
|
33
|
+
export let tableId = null
|
|
34
|
+
export let schemaOverrides = null
|
|
35
|
+
export let allowAddRows = true
|
|
36
|
+
export let allowAddColumns = true
|
|
37
|
+
export let allowEditColumns = true
|
|
38
|
+
export let allowExpandRows = true
|
|
39
|
+
export let allowEditRows = true
|
|
40
|
+
export let allowDeleteRows = true
|
|
41
|
+
|
|
42
|
+
// Unique identifier for DOM nodes inside this instance
|
|
43
|
+
const rand = Math.random()
|
|
44
|
+
|
|
45
|
+
// State stores
|
|
46
|
+
const tableIdStore = writable(tableId)
|
|
47
|
+
const schemaOverridesStore = writable(schemaOverrides)
|
|
48
|
+
const config = writable({
|
|
49
|
+
allowAddRows,
|
|
50
|
+
allowAddColumns,
|
|
51
|
+
allowEditColumns,
|
|
52
|
+
allowExpandRows,
|
|
53
|
+
allowEditRows,
|
|
54
|
+
allowDeleteRows,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Build up context
|
|
58
|
+
let context = {
|
|
59
|
+
API: API || createAPIClient(),
|
|
60
|
+
rand,
|
|
61
|
+
config,
|
|
62
|
+
tableId: tableIdStore,
|
|
63
|
+
schemaOverrides: schemaOverridesStore,
|
|
64
|
+
}
|
|
65
|
+
context = { ...context, ...createEventManagers() }
|
|
66
|
+
context = attachStores(context)
|
|
67
|
+
|
|
68
|
+
// Reference some stores for local use
|
|
69
|
+
const {
|
|
70
|
+
isResizing,
|
|
71
|
+
isReordering,
|
|
72
|
+
ui,
|
|
73
|
+
loaded,
|
|
74
|
+
loading,
|
|
75
|
+
rowHeight,
|
|
76
|
+
contentLines,
|
|
77
|
+
} = context
|
|
78
|
+
|
|
79
|
+
// Keep stores up to date
|
|
80
|
+
$: tableIdStore.set(tableId)
|
|
81
|
+
$: schemaOverridesStore.set(schemaOverrides)
|
|
82
|
+
$: config.set({
|
|
83
|
+
allowAddRows,
|
|
84
|
+
allowAddColumns,
|
|
85
|
+
allowEditColumns,
|
|
86
|
+
allowExpandRows,
|
|
87
|
+
allowEditRows,
|
|
88
|
+
allowDeleteRows,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Set context for children to consume
|
|
92
|
+
setContext("grid", context)
|
|
93
|
+
|
|
94
|
+
// Expose ability to retrieve context externally for external control
|
|
95
|
+
export const getContext = () => context
|
|
96
|
+
|
|
97
|
+
// Initialise websocket for multi-user
|
|
98
|
+
// onMount(() => createWebsocket(context))
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<div
|
|
102
|
+
class="grid"
|
|
103
|
+
id="grid-{rand}"
|
|
104
|
+
class:is-resizing={$isResizing}
|
|
105
|
+
class:is-reordering={$isReordering}
|
|
106
|
+
style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
|
|
107
|
+
>
|
|
108
|
+
<div class="controls">
|
|
109
|
+
<div class="controls-left">
|
|
110
|
+
<AddRowButton />
|
|
111
|
+
<AddColumnButton />
|
|
112
|
+
<slot name="controls" />
|
|
113
|
+
<RowHeightButton />
|
|
114
|
+
<HideColumnsButton />
|
|
115
|
+
<SortButton />
|
|
116
|
+
</div>
|
|
117
|
+
<div class="controls-right">
|
|
118
|
+
<DeleteButton />
|
|
119
|
+
<UserAvatars />
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
{#if $loaded}
|
|
123
|
+
<div class="grid-data-outer" use:clickOutside={ui.actions.blur}>
|
|
124
|
+
<div class="grid-data-inner">
|
|
125
|
+
<StickyColumn />
|
|
126
|
+
<div class="grid-data-content">
|
|
127
|
+
<HeaderRow />
|
|
128
|
+
<GridBody />
|
|
129
|
+
</div>
|
|
130
|
+
<div class="overlays">
|
|
131
|
+
<ResizeOverlay />
|
|
132
|
+
<ReorderOverlay />
|
|
133
|
+
<BetaButton />
|
|
134
|
+
<ScrollOverlay />
|
|
135
|
+
<MenuOverlay />
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
{/if}
|
|
140
|
+
{#if $loading}
|
|
141
|
+
<div in:fade|local={{ duration: 130 }} class="grid-loading">
|
|
142
|
+
<ProgressCircle />
|
|
143
|
+
</div>
|
|
144
|
+
{/if}
|
|
145
|
+
<KeyboardManager />
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<style>
|
|
149
|
+
.grid {
|
|
150
|
+
flex: 1 1 auto;
|
|
151
|
+
display: flex;
|
|
152
|
+
flex-direction: column;
|
|
153
|
+
justify-content: flex-start;
|
|
154
|
+
align-items: stretch;
|
|
155
|
+
position: relative;
|
|
156
|
+
overflow: hidden;
|
|
157
|
+
background: var(--cell-background);
|
|
158
|
+
|
|
159
|
+
/* Variables */
|
|
160
|
+
--cell-background: var(--spectrum-global-color-gray-50);
|
|
161
|
+
--cell-background-hover: var(--spectrum-global-color-gray-100);
|
|
162
|
+
--cell-padding: 8px;
|
|
163
|
+
--cell-spacing: 4px;
|
|
164
|
+
--cell-border: 1px solid var(--spectrum-global-color-gray-200);
|
|
165
|
+
--cell-font-size: 14px;
|
|
166
|
+
--controls-height: 50px;
|
|
167
|
+
}
|
|
168
|
+
.grid,
|
|
169
|
+
.grid :global(*) {
|
|
170
|
+
box-sizing: border-box;
|
|
171
|
+
}
|
|
172
|
+
.grid.is-resizing :global(*) {
|
|
173
|
+
cursor: col-resize !important;
|
|
174
|
+
}
|
|
175
|
+
.grid.is-reordering :global(*) {
|
|
176
|
+
cursor: grabbing !important;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.grid-data-outer,
|
|
180
|
+
.grid-data-inner {
|
|
181
|
+
flex: 1 1 auto;
|
|
182
|
+
display: flex;
|
|
183
|
+
justify-items: flex-start;
|
|
184
|
+
align-items: stretch;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
}
|
|
187
|
+
.grid-data-outer {
|
|
188
|
+
height: 0;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
}
|
|
191
|
+
.grid-data-inner {
|
|
192
|
+
flex-direction: row;
|
|
193
|
+
position: relative;
|
|
194
|
+
}
|
|
195
|
+
.grid-data-content {
|
|
196
|
+
flex: 1 1 auto;
|
|
197
|
+
overflow: hidden;
|
|
198
|
+
display: flex;
|
|
199
|
+
flex-direction: column;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Controls */
|
|
203
|
+
.controls {
|
|
204
|
+
height: var(--controls-height);
|
|
205
|
+
display: flex;
|
|
206
|
+
flex-direction: row;
|
|
207
|
+
justify-content: space-between;
|
|
208
|
+
align-items: center;
|
|
209
|
+
border-bottom: 2px solid var(--spectrum-global-color-gray-200);
|
|
210
|
+
padding: var(--cell-padding);
|
|
211
|
+
gap: var(--cell-spacing);
|
|
212
|
+
background: var(--background);
|
|
213
|
+
}
|
|
214
|
+
.controls-left,
|
|
215
|
+
.controls-right {
|
|
216
|
+
display: flex;
|
|
217
|
+
flex-direction: row;
|
|
218
|
+
justify-content: flex-start;
|
|
219
|
+
align-items: center;
|
|
220
|
+
gap: var(--cell-spacing);
|
|
221
|
+
}
|
|
222
|
+
.controls-right {
|
|
223
|
+
gap: 12px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Overlays */
|
|
227
|
+
.overlays {
|
|
228
|
+
z-index: 10;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Loading */
|
|
232
|
+
.grid-loading {
|
|
233
|
+
position: absolute;
|
|
234
|
+
width: 100%;
|
|
235
|
+
height: 100%;
|
|
236
|
+
display: grid;
|
|
237
|
+
place-items: center;
|
|
238
|
+
z-index: 10;
|
|
239
|
+
}
|
|
240
|
+
.grid-loading:before {
|
|
241
|
+
content: "";
|
|
242
|
+
position: absolute;
|
|
243
|
+
top: 0;
|
|
244
|
+
left: 0;
|
|
245
|
+
width: 100%;
|
|
246
|
+
height: 100%;
|
|
247
|
+
background: var(--background);
|
|
248
|
+
opacity: 0.6;
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext, onMount } from "svelte"
|
|
3
|
+
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
|
4
|
+
import GridRow from "./GridRow.svelte"
|
|
5
|
+
|
|
6
|
+
const { bounds, renderedRows, rowVerticalInversionIndex } = getContext("grid")
|
|
7
|
+
|
|
8
|
+
let body
|
|
9
|
+
|
|
10
|
+
onMount(() => {
|
|
11
|
+
// Observe and record the height of the body
|
|
12
|
+
const observer = new ResizeObserver(() => {
|
|
13
|
+
bounds.set(body.getBoundingClientRect())
|
|
14
|
+
})
|
|
15
|
+
observer.observe(body)
|
|
16
|
+
return () => {
|
|
17
|
+
observer.disconnect()
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div bind:this={body} class="grid-body">
|
|
23
|
+
<GridScrollWrapper scrollHorizontally scrollVertically wheelInteractive>
|
|
24
|
+
{#each $renderedRows as row, idx}
|
|
25
|
+
<GridRow {row} {idx} invertY={idx >= $rowVerticalInversionIndex} />
|
|
26
|
+
{/each}
|
|
27
|
+
</GridScrollWrapper>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.grid-body {
|
|
32
|
+
display: block;
|
|
33
|
+
position: relative;
|
|
34
|
+
cursor: default;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
flex: 1 1 auto;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import DataCell from "../cells/DataCell.svelte"
|
|
4
|
+
|
|
5
|
+
export let row
|
|
6
|
+
export let idx
|
|
7
|
+
export let invertY = false
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
focusedCellId,
|
|
11
|
+
reorder,
|
|
12
|
+
selectedRows,
|
|
13
|
+
renderedColumns,
|
|
14
|
+
hoveredRowId,
|
|
15
|
+
selectedCellMap,
|
|
16
|
+
focusedRow,
|
|
17
|
+
columnHorizontalInversionIndex,
|
|
18
|
+
} = getContext("grid")
|
|
19
|
+
|
|
20
|
+
$: rowSelected = !!$selectedRows[row._id]
|
|
21
|
+
$: rowHovered = $hoveredRowId === row._id
|
|
22
|
+
$: rowFocused = $focusedRow?._id === row._id
|
|
23
|
+
$: reorderSource = $reorder.sourceColumn
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
class="row"
|
|
28
|
+
on:focus
|
|
29
|
+
on:mouseenter={() => ($hoveredRowId = row._id)}
|
|
30
|
+
on:mouseleave={() => ($hoveredRowId = null)}
|
|
31
|
+
>
|
|
32
|
+
{#each $renderedColumns as column, columnIdx (column.name)}
|
|
33
|
+
{@const cellId = `${row._id}-${column.name}`}
|
|
34
|
+
<DataCell
|
|
35
|
+
{cellId}
|
|
36
|
+
{column}
|
|
37
|
+
{row}
|
|
38
|
+
{invertY}
|
|
39
|
+
{rowFocused}
|
|
40
|
+
invertX={columnIdx >= $columnHorizontalInversionIndex}
|
|
41
|
+
highlighted={rowHovered || rowFocused || reorderSource === column.name}
|
|
42
|
+
selected={rowSelected}
|
|
43
|
+
rowIdx={idx}
|
|
44
|
+
focused={$focusedCellId === cellId}
|
|
45
|
+
selectedUser={$selectedCellMap[cellId]}
|
|
46
|
+
width={column.width}
|
|
47
|
+
/>
|
|
48
|
+
{/each}
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
.row {
|
|
53
|
+
width: 0;
|
|
54
|
+
display: flex;
|
|
55
|
+
height: var(--row-height);
|
|
56
|
+
}
|
|
57
|
+
</style>
|