@budibase/frontend-core 2.5.6-alpha.5 → 2.5.6-alpha.7
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,84 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import { domDebounce } from "../../../utils/utils"
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
rowHeight,
|
|
7
|
+
scroll,
|
|
8
|
+
focusedCellId,
|
|
9
|
+
renderedRows,
|
|
10
|
+
maxScrollTop,
|
|
11
|
+
maxScrollLeft,
|
|
12
|
+
bounds,
|
|
13
|
+
hoveredRowId,
|
|
14
|
+
hiddenColumnsWidth,
|
|
15
|
+
} = getContext("grid")
|
|
16
|
+
|
|
17
|
+
export let scrollVertically = false
|
|
18
|
+
export let scrollHorizontally = false
|
|
19
|
+
export let wheelInteractive = false
|
|
20
|
+
|
|
21
|
+
$: style = generateStyle($scroll, $rowHeight, $hiddenColumnsWidth)
|
|
22
|
+
|
|
23
|
+
const generateStyle = (scroll, rowHeight, hiddenWidths) => {
|
|
24
|
+
const offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0
|
|
25
|
+
const offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0
|
|
26
|
+
return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handles a wheel even and updates the scroll offsets
|
|
30
|
+
const handleWheel = e => {
|
|
31
|
+
e.preventDefault()
|
|
32
|
+
const modifier = e.ctrlKey || e.metaKey
|
|
33
|
+
let x = modifier ? e.deltaY : e.deltaX
|
|
34
|
+
let y = modifier ? e.deltaX : e.deltaY
|
|
35
|
+
debouncedHandleWheel(x, y, e.clientY)
|
|
36
|
+
}
|
|
37
|
+
const debouncedHandleWheel = domDebounce((deltaX, deltaY, clientY) => {
|
|
38
|
+
const { top, left } = $scroll
|
|
39
|
+
|
|
40
|
+
// Calculate new scroll top
|
|
41
|
+
let newScrollTop = top + deltaY
|
|
42
|
+
newScrollTop = Math.max(0, Math.min(newScrollTop, $maxScrollTop))
|
|
43
|
+
|
|
44
|
+
// Calculate new scroll left
|
|
45
|
+
let newScrollLeft = left + deltaX
|
|
46
|
+
newScrollLeft = Math.max(0, Math.min(newScrollLeft, $maxScrollLeft))
|
|
47
|
+
|
|
48
|
+
// Update state
|
|
49
|
+
scroll.set({
|
|
50
|
+
left: scrollHorizontally ? newScrollLeft : left,
|
|
51
|
+
top: scrollVertically ? newScrollTop : top,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Hover row under cursor
|
|
55
|
+
const y = clientY - $bounds.top + (newScrollTop % $rowHeight)
|
|
56
|
+
const hoveredRow = $renderedRows[Math.floor(y / $rowHeight)]
|
|
57
|
+
hoveredRowId.set(hoveredRow?._id)
|
|
58
|
+
})
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<div
|
|
62
|
+
class="outer"
|
|
63
|
+
on:wheel={wheelInteractive ? handleWheel : null}
|
|
64
|
+
on:click|self={() => ($focusedCellId = null)}
|
|
65
|
+
>
|
|
66
|
+
<div {style} class="inner">
|
|
67
|
+
<slot />
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
.outer {
|
|
73
|
+
min-width: 100%;
|
|
74
|
+
min-height: 100%;
|
|
75
|
+
height: 100%;
|
|
76
|
+
width: 100%;
|
|
77
|
+
max-height: 100%;
|
|
78
|
+
max-width: 100%;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
}
|
|
81
|
+
.inner {
|
|
82
|
+
position: absolute;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
|
4
|
+
import HeaderCell from "../cells/HeaderCell.svelte"
|
|
5
|
+
|
|
6
|
+
const { renderedColumns } = getContext("grid")
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div class="header">
|
|
10
|
+
<GridScrollWrapper scrollHorizontally>
|
|
11
|
+
<div class="row">
|
|
12
|
+
{#each $renderedColumns as column, idx}
|
|
13
|
+
<HeaderCell {column} {idx} />
|
|
14
|
+
{/each}
|
|
15
|
+
</div>
|
|
16
|
+
</GridScrollWrapper>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<style>
|
|
20
|
+
.header {
|
|
21
|
+
background: var(--background);
|
|
22
|
+
border-bottom: var(--cell-border);
|
|
23
|
+
position: relative;
|
|
24
|
+
height: var(--default-row-height);
|
|
25
|
+
z-index: 1;
|
|
26
|
+
}
|
|
27
|
+
.row {
|
|
28
|
+
display: flex;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import GridCell from "../cells/GridCell.svelte"
|
|
3
|
+
import { getContext, onMount } from "svelte"
|
|
4
|
+
import { Icon, Button } from "@budibase/bbui"
|
|
5
|
+
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
|
6
|
+
import DataCell from "../cells/DataCell.svelte"
|
|
7
|
+
import { fly } from "svelte/transition"
|
|
8
|
+
import { GutterWidth } from "../lib/constants"
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
hoveredRowId,
|
|
12
|
+
focusedCellId,
|
|
13
|
+
stickyColumn,
|
|
14
|
+
scroll,
|
|
15
|
+
config,
|
|
16
|
+
dispatch,
|
|
17
|
+
visibleColumns,
|
|
18
|
+
rows,
|
|
19
|
+
showHScrollbar,
|
|
20
|
+
tableId,
|
|
21
|
+
subscribe,
|
|
22
|
+
scrollLeft,
|
|
23
|
+
} = getContext("grid")
|
|
24
|
+
|
|
25
|
+
let isAdding = false
|
|
26
|
+
let newRow = {}
|
|
27
|
+
let touched = false
|
|
28
|
+
|
|
29
|
+
$: firstColumn = $stickyColumn || $visibleColumns[0]
|
|
30
|
+
$: rowHovered = $hoveredRowId === "new"
|
|
31
|
+
$: rowFocused = $focusedCellId?.startsWith("new-")
|
|
32
|
+
$: width = GutterWidth + ($stickyColumn?.width || 0)
|
|
33
|
+
$: $tableId, (isAdding = false)
|
|
34
|
+
|
|
35
|
+
const addRow = async () => {
|
|
36
|
+
// Create row
|
|
37
|
+
const savedRow = await rows.actions.addRow(newRow, 0)
|
|
38
|
+
if (savedRow) {
|
|
39
|
+
// Select the first cell if possible
|
|
40
|
+
if (firstColumn) {
|
|
41
|
+
$focusedCellId = `${savedRow._id}-${firstColumn.name}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Reset state
|
|
45
|
+
isAdding = false
|
|
46
|
+
scroll.set({
|
|
47
|
+
left: 0,
|
|
48
|
+
top: 0,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const cancel = () => {
|
|
54
|
+
isAdding = false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const startAdding = () => {
|
|
58
|
+
newRow = {}
|
|
59
|
+
isAdding = true
|
|
60
|
+
if (firstColumn) {
|
|
61
|
+
$focusedCellId = `new-${firstColumn.name}`
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const updateValue = (rowId, columnName, val) => {
|
|
66
|
+
touched = true
|
|
67
|
+
newRow[columnName] = val
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const addViaModal = () => {
|
|
71
|
+
isAdding = false
|
|
72
|
+
dispatch("add-row")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onMount(() => subscribe("add-row-inline", startAdding))
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<!-- Only show new row functionality if we have any columns -->
|
|
79
|
+
{#if isAdding}
|
|
80
|
+
<div class="container" transition:fly={{ y: 20, duration: 130 }}>
|
|
81
|
+
<div class="content" class:above-scrollbar={$showHScrollbar}>
|
|
82
|
+
<div
|
|
83
|
+
class="new-row"
|
|
84
|
+
on:mouseenter={() => ($hoveredRowId = "new")}
|
|
85
|
+
on:mouseleave={() => ($hoveredRowId = null)}
|
|
86
|
+
>
|
|
87
|
+
<div
|
|
88
|
+
class="sticky-column"
|
|
89
|
+
style="flex: 0 0 {width}px"
|
|
90
|
+
class:scrolled={$scrollLeft > 0}
|
|
91
|
+
>
|
|
92
|
+
<GridCell width={GutterWidth} {rowHovered} {rowFocused}>
|
|
93
|
+
<div class="gutter">
|
|
94
|
+
<div class="number">1</div>
|
|
95
|
+
{#if $config.allowExpandRows}
|
|
96
|
+
<Icon
|
|
97
|
+
name="Maximize"
|
|
98
|
+
size="S"
|
|
99
|
+
hoverable
|
|
100
|
+
on:click={addViaModal}
|
|
101
|
+
/>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
</GridCell>
|
|
105
|
+
{#if $stickyColumn}
|
|
106
|
+
{@const cellId = `new-${$stickyColumn.name}`}
|
|
107
|
+
<DataCell
|
|
108
|
+
{cellId}
|
|
109
|
+
column={$stickyColumn}
|
|
110
|
+
row={newRow}
|
|
111
|
+
{rowHovered}
|
|
112
|
+
focused={$focusedCellId === cellId}
|
|
113
|
+
{rowFocused}
|
|
114
|
+
width={$stickyColumn.width}
|
|
115
|
+
{updateValue}
|
|
116
|
+
rowIdx={0}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<GridScrollWrapper scrollHorizontally wheelInteractive>
|
|
122
|
+
<div class="row">
|
|
123
|
+
{#each $visibleColumns as column}
|
|
124
|
+
{@const cellId = `new-${column.name}`}
|
|
125
|
+
{#key cellId}
|
|
126
|
+
<DataCell
|
|
127
|
+
{cellId}
|
|
128
|
+
{column}
|
|
129
|
+
row={newRow}
|
|
130
|
+
{rowHovered}
|
|
131
|
+
focused={$focusedCellId === cellId}
|
|
132
|
+
{rowFocused}
|
|
133
|
+
width={column.width}
|
|
134
|
+
{updateValue}
|
|
135
|
+
rowIdx={0}
|
|
136
|
+
/>
|
|
137
|
+
{/key}
|
|
138
|
+
{/each}
|
|
139
|
+
</div>
|
|
140
|
+
</GridScrollWrapper>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="buttons">
|
|
144
|
+
<Button size="M" cta on:click={addRow}>Save</Button>
|
|
145
|
+
<Button size="M" secondary newStyles on:click={cancel}>Cancel</Button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
149
|
+
|
|
150
|
+
<style>
|
|
151
|
+
.container {
|
|
152
|
+
pointer-events: none;
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: var(--row-height);
|
|
155
|
+
left: 0;
|
|
156
|
+
width: 100%;
|
|
157
|
+
padding-bottom: 800px;
|
|
158
|
+
display: flex;
|
|
159
|
+
flex-direction: column;
|
|
160
|
+
align-items: stretch;
|
|
161
|
+
}
|
|
162
|
+
.container:before {
|
|
163
|
+
position: absolute;
|
|
164
|
+
content: "";
|
|
165
|
+
left: 0;
|
|
166
|
+
top: 0;
|
|
167
|
+
height: 100%;
|
|
168
|
+
width: 100%;
|
|
169
|
+
background: var(--cell-background);
|
|
170
|
+
opacity: 0.8;
|
|
171
|
+
z-index: -1;
|
|
172
|
+
}
|
|
173
|
+
.content {
|
|
174
|
+
pointer-events: all;
|
|
175
|
+
background: var(--background);
|
|
176
|
+
border-bottom: var(--cell-border);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.new-row {
|
|
180
|
+
display: flex;
|
|
181
|
+
bottom: 0;
|
|
182
|
+
left: 0;
|
|
183
|
+
width: 100%;
|
|
184
|
+
transition: margin-bottom 130ms ease-out;
|
|
185
|
+
}
|
|
186
|
+
.new-row :global(.cell) {
|
|
187
|
+
--cell-background: var(--background) !important;
|
|
188
|
+
border-bottom: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.sticky-column {
|
|
192
|
+
display: flex;
|
|
193
|
+
z-index: 1;
|
|
194
|
+
position: relative;
|
|
195
|
+
}
|
|
196
|
+
/* Don't show borders between cells in the sticky column */
|
|
197
|
+
.sticky-column :global(.cell:not(:last-child)) {
|
|
198
|
+
border-right: none;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.row {
|
|
202
|
+
width: 0;
|
|
203
|
+
display: flex;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Add shadow when scrolled */
|
|
207
|
+
.sticky-column.scrolled {
|
|
208
|
+
/*box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.1);*/
|
|
209
|
+
}
|
|
210
|
+
.sticky-column.scrolled:after {
|
|
211
|
+
content: "";
|
|
212
|
+
width: 10px;
|
|
213
|
+
height: 100%;
|
|
214
|
+
background: linear-gradient(to right, rgba(0, 0, 0, 0.05), transparent);
|
|
215
|
+
left: 100%;
|
|
216
|
+
top: 0;
|
|
217
|
+
position: absolute;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Styles for gutter */
|
|
221
|
+
.gutter {
|
|
222
|
+
flex: 1 1 auto;
|
|
223
|
+
display: grid;
|
|
224
|
+
align-items: center;
|
|
225
|
+
padding: var(--cell-padding);
|
|
226
|
+
grid-template-columns: 1fr auto;
|
|
227
|
+
gap: var(--cell-spacing);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Floating buttons */
|
|
231
|
+
.buttons {
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: row;
|
|
234
|
+
gap: 8px;
|
|
235
|
+
margin: 24px 0 0 var(--gutter-width);
|
|
236
|
+
pointer-events: all;
|
|
237
|
+
align-self: flex-start;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.number {
|
|
241
|
+
display: flex;
|
|
242
|
+
flex-direction: row;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
align-items: center;
|
|
245
|
+
color: var(--spectrum-global-color-gray-500);
|
|
246
|
+
}
|
|
247
|
+
</style>
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import { Checkbox, Icon } from "@budibase/bbui"
|
|
4
|
+
import GridCell from "../cells/GridCell.svelte"
|
|
5
|
+
import DataCell from "../cells/DataCell.svelte"
|
|
6
|
+
import GridScrollWrapper from "./GridScrollWrapper.svelte"
|
|
7
|
+
import HeaderCell from "../cells/HeaderCell.svelte"
|
|
8
|
+
import { GutterWidth } from "../lib/constants"
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
rows,
|
|
12
|
+
selectedRows,
|
|
13
|
+
stickyColumn,
|
|
14
|
+
renderedRows,
|
|
15
|
+
focusedCellId,
|
|
16
|
+
hoveredRowId,
|
|
17
|
+
config,
|
|
18
|
+
selectedCellMap,
|
|
19
|
+
focusedRow,
|
|
20
|
+
dispatch,
|
|
21
|
+
scrollLeft,
|
|
22
|
+
} = getContext("grid")
|
|
23
|
+
|
|
24
|
+
$: rowCount = $rows.length
|
|
25
|
+
$: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
|
|
26
|
+
$: width = GutterWidth + ($stickyColumn?.width || 0)
|
|
27
|
+
|
|
28
|
+
const selectAll = () => {
|
|
29
|
+
const allSelected = selectedRowCount === rowCount
|
|
30
|
+
if (allSelected) {
|
|
31
|
+
$selectedRows = {}
|
|
32
|
+
} else {
|
|
33
|
+
let allRows = {}
|
|
34
|
+
$rows.forEach(row => {
|
|
35
|
+
allRows[row._id] = true
|
|
36
|
+
})
|
|
37
|
+
$selectedRows = allRows
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const selectRow = id => {
|
|
42
|
+
selectedRows.update(state => {
|
|
43
|
+
let newState = {
|
|
44
|
+
...state,
|
|
45
|
+
[id]: !state[id],
|
|
46
|
+
}
|
|
47
|
+
if (!newState[id]) {
|
|
48
|
+
delete newState[id]
|
|
49
|
+
}
|
|
50
|
+
return newState
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<div
|
|
56
|
+
class="sticky-column"
|
|
57
|
+
style="flex: 0 0 {width}px"
|
|
58
|
+
class:scrolled={$scrollLeft > 0}
|
|
59
|
+
>
|
|
60
|
+
<div class="header row">
|
|
61
|
+
<GridCell width={GutterWidth} defaultHeight center>
|
|
62
|
+
<div class="gutter">
|
|
63
|
+
<div class="checkbox visible">
|
|
64
|
+
{#if $config.allowDeleteRows}
|
|
65
|
+
<div on:click={selectAll}>
|
|
66
|
+
<Checkbox
|
|
67
|
+
value={rowCount && selectedRowCount === rowCount}
|
|
68
|
+
disabled={!$renderedRows.length}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
73
|
+
{#if $config.allowExpandRows}
|
|
74
|
+
<div class="expand">
|
|
75
|
+
<Icon name="Maximize" size="S" />
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
</GridCell>
|
|
80
|
+
|
|
81
|
+
{#if $stickyColumn}
|
|
82
|
+
<HeaderCell column={$stickyColumn} orderable={false} idx="sticky" />
|
|
83
|
+
{/if}
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="content" on:mouseleave={() => ($hoveredRowId = null)}>
|
|
87
|
+
<GridScrollWrapper scrollVertically wheelInteractive>
|
|
88
|
+
{#each $renderedRows as row, idx}
|
|
89
|
+
{@const rowSelected = !!$selectedRows[row._id]}
|
|
90
|
+
{@const rowHovered = $hoveredRowId === row._id}
|
|
91
|
+
{@const rowFocused = $focusedRow?._id === row._id}
|
|
92
|
+
{@const cellId = `${row._id}-${$stickyColumn?.name}`}
|
|
93
|
+
<div
|
|
94
|
+
class="row"
|
|
95
|
+
on:mouseenter={() => ($hoveredRowId = row._id)}
|
|
96
|
+
on:mouseleave={() => ($hoveredRowId = null)}
|
|
97
|
+
>
|
|
98
|
+
<GridCell
|
|
99
|
+
width={GutterWidth}
|
|
100
|
+
highlighted={rowFocused || rowHovered}
|
|
101
|
+
selected={rowSelected}
|
|
102
|
+
>
|
|
103
|
+
<div class="gutter">
|
|
104
|
+
<div
|
|
105
|
+
on:click={() => selectRow(row._id)}
|
|
106
|
+
class="checkbox"
|
|
107
|
+
class:visible={$config.allowDeleteRows &&
|
|
108
|
+
(rowSelected || rowHovered || rowFocused)}
|
|
109
|
+
>
|
|
110
|
+
<Checkbox value={rowSelected} />
|
|
111
|
+
</div>
|
|
112
|
+
<div
|
|
113
|
+
class="number"
|
|
114
|
+
class:visible={!$config.allowDeleteRows ||
|
|
115
|
+
!(rowSelected || rowHovered || rowFocused)}
|
|
116
|
+
>
|
|
117
|
+
{row.__idx + 1}
|
|
118
|
+
</div>
|
|
119
|
+
{#if $config.allowExpandRows}
|
|
120
|
+
<div class="expand" class:visible={rowFocused || rowHovered}>
|
|
121
|
+
<Icon
|
|
122
|
+
name="Maximize"
|
|
123
|
+
hoverable
|
|
124
|
+
size="S"
|
|
125
|
+
on:click={() => {
|
|
126
|
+
dispatch("edit-row", row)
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
{/if}
|
|
131
|
+
</div>
|
|
132
|
+
</GridCell>
|
|
133
|
+
{#if $stickyColumn}
|
|
134
|
+
<DataCell
|
|
135
|
+
{row}
|
|
136
|
+
{cellId}
|
|
137
|
+
{rowFocused}
|
|
138
|
+
selected={rowSelected}
|
|
139
|
+
highlighted={rowHovered || rowFocused}
|
|
140
|
+
rowIdx={idx}
|
|
141
|
+
focused={$focusedCellId === cellId}
|
|
142
|
+
selectedUser={$selectedCellMap[cellId]}
|
|
143
|
+
width={$stickyColumn.width}
|
|
144
|
+
column={$stickyColumn}
|
|
145
|
+
/>
|
|
146
|
+
{/if}
|
|
147
|
+
</div>
|
|
148
|
+
{/each}
|
|
149
|
+
</GridScrollWrapper>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<style>
|
|
154
|
+
.sticky-column {
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
position: relative;
|
|
158
|
+
z-index: 2;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Add right border */
|
|
162
|
+
.sticky-column:before {
|
|
163
|
+
position: absolute;
|
|
164
|
+
content: "";
|
|
165
|
+
width: 0;
|
|
166
|
+
height: 100%;
|
|
167
|
+
left: calc(100% - 1px);
|
|
168
|
+
top: 0;
|
|
169
|
+
border-left: var(--cell-border);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Add shadow when scrolled */
|
|
173
|
+
.sticky-column.scrolled:after {
|
|
174
|
+
position: absolute;
|
|
175
|
+
content: "";
|
|
176
|
+
width: 8px;
|
|
177
|
+
height: 100%;
|
|
178
|
+
left: 100%;
|
|
179
|
+
top: 0;
|
|
180
|
+
opacity: 1;
|
|
181
|
+
background: linear-gradient(to right, var(--drop-shadow), transparent);
|
|
182
|
+
z-index: -1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Don't show borders between cells in the sticky column */
|
|
186
|
+
.sticky-column :global(.cell:not(:last-child)) {
|
|
187
|
+
border-right: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.header {
|
|
191
|
+
z-index: 1;
|
|
192
|
+
}
|
|
193
|
+
.header :global(.cell) {
|
|
194
|
+
background: var(--spectrum-global-color-gray-100);
|
|
195
|
+
}
|
|
196
|
+
.row {
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: row;
|
|
199
|
+
justify-content: flex-start;
|
|
200
|
+
align-items: stretch;
|
|
201
|
+
}
|
|
202
|
+
.content {
|
|
203
|
+
position: relative;
|
|
204
|
+
flex: 1 1 auto;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Styles for gutter */
|
|
208
|
+
.gutter {
|
|
209
|
+
flex: 1 1 auto;
|
|
210
|
+
display: grid;
|
|
211
|
+
align-items: center;
|
|
212
|
+
padding: var(--cell-padding);
|
|
213
|
+
grid-template-columns: 1fr auto;
|
|
214
|
+
gap: var(--cell-spacing);
|
|
215
|
+
}
|
|
216
|
+
.checkbox,
|
|
217
|
+
.number {
|
|
218
|
+
display: none;
|
|
219
|
+
flex-direction: row;
|
|
220
|
+
justify-content: center;
|
|
221
|
+
align-items: center;
|
|
222
|
+
}
|
|
223
|
+
.checkbox :global(.spectrum-Checkbox) {
|
|
224
|
+
min-height: 0;
|
|
225
|
+
height: 20px;
|
|
226
|
+
}
|
|
227
|
+
.checkbox :global(.spectrum-Checkbox-box) {
|
|
228
|
+
margin: 3px 0 0 0;
|
|
229
|
+
}
|
|
230
|
+
.number {
|
|
231
|
+
color: var(--spectrum-global-color-gray-500);
|
|
232
|
+
}
|
|
233
|
+
.checkbox.visible,
|
|
234
|
+
.number.visible {
|
|
235
|
+
display: flex;
|
|
236
|
+
}
|
|
237
|
+
.expand {
|
|
238
|
+
opacity: 0;
|
|
239
|
+
pointer-events: none;
|
|
240
|
+
}
|
|
241
|
+
.expand.visible {
|
|
242
|
+
opacity: 1;
|
|
243
|
+
pointer-events: all;
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import Avatar from "./Avatar.svelte"
|
|
4
|
+
|
|
5
|
+
const { users } = getContext("grid")
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div class="users">
|
|
9
|
+
{#each $users as user}
|
|
10
|
+
<Avatar {user} />
|
|
11
|
+
{/each}
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
.users {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: row;
|
|
18
|
+
gap: 8px;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const Padding = 276
|
|
2
|
+
export const MaxCellRenderHeight = 252
|
|
3
|
+
export const MaxCellRenderWidthOverflow = 200
|
|
4
|
+
export const ScrollBarSize = 8
|
|
5
|
+
export const GutterWidth = 72
|
|
6
|
+
export const DefaultColumnWidth = 200
|
|
7
|
+
export const MinColumnWidth = 100
|
|
8
|
+
export const SmallRowHeight = 36
|
|
9
|
+
export const MediumRowHeight = 64
|
|
10
|
+
export const LargeRowHeight = 92
|
|
11
|
+
export const DefaultRowHeight = SmallRowHeight
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createEventDispatcher } from "svelte"
|
|
2
|
+
|
|
3
|
+
export const createEventManagers = () => {
|
|
4
|
+
const svelteDispatch = createEventDispatcher()
|
|
5
|
+
let subscribers = {}
|
|
6
|
+
|
|
7
|
+
// Dispatches an event, notifying subscribers and also emitting a normal
|
|
8
|
+
// svelte event
|
|
9
|
+
const dispatch = (event, payload) => {
|
|
10
|
+
svelteDispatch(event, payload)
|
|
11
|
+
const subs = subscribers[event] || []
|
|
12
|
+
for (let i = 0; i < subs.length; i++) {
|
|
13
|
+
subs[i](payload)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Subscribes to events
|
|
18
|
+
const subscribe = (event, callback) => {
|
|
19
|
+
const subs = subscribers[event] || []
|
|
20
|
+
subscribers[event] = [...subs, callback]
|
|
21
|
+
|
|
22
|
+
// Return unsubscribe function
|
|
23
|
+
return () => {
|
|
24
|
+
subscribers[event] = subscribers[event].filter(cb => cb !== callback)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { dispatch, subscribe }
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import OptionsCell from "../cells/OptionsCell.svelte"
|
|
2
|
+
import DateCell from "../cells/DateCell.svelte"
|
|
3
|
+
import MultiSelectCell from "../cells/MultiSelectCell.svelte"
|
|
4
|
+
import NumberCell from "../cells/NumberCell.svelte"
|
|
5
|
+
import RelationshipCell from "../cells/RelationshipCell.svelte"
|
|
6
|
+
import TextCell from "../cells/TextCell.svelte"
|
|
7
|
+
import LongFormCell from "../cells/LongFormCell.svelte"
|
|
8
|
+
import BooleanCell from "../cells/BooleanCell.svelte"
|
|
9
|
+
import FormulaCell from "../cells/FormulaCell.svelte"
|
|
10
|
+
import JSONCell from "../cells/JSONCell.svelte"
|
|
11
|
+
import AttachmentCell from "../cells/AttachmentCell.svelte"
|
|
12
|
+
|
|
13
|
+
const TypeComponentMap = {
|
|
14
|
+
text: TextCell,
|
|
15
|
+
options: OptionsCell,
|
|
16
|
+
datetime: DateCell,
|
|
17
|
+
barcodeqr: TextCell,
|
|
18
|
+
longform: LongFormCell,
|
|
19
|
+
array: MultiSelectCell,
|
|
20
|
+
number: NumberCell,
|
|
21
|
+
boolean: BooleanCell,
|
|
22
|
+
attachment: AttachmentCell,
|
|
23
|
+
link: RelationshipCell,
|
|
24
|
+
formula: FormulaCell,
|
|
25
|
+
json: JSONCell,
|
|
26
|
+
}
|
|
27
|
+
export const getCellRenderer = column => {
|
|
28
|
+
return TypeComponentMap[column?.schema?.type] || TextCell
|
|
29
|
+
}
|