@budibase/frontend-core 2.5.9 → 2.5.10-alpha.1
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/api/other.js +0 -9
- package/src/components/grid/cells/AttachmentCell.svelte +154 -0
- package/src/components/grid/cells/BooleanCell.svelte +44 -0
- package/src/components/grid/cells/DataCell.svelte +91 -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 +165 -0
- package/src/components/grid/cells/GutterCell.svelte +127 -0
- package/src/components/grid/cells/HeaderCell.svelte +248 -0
- package/src/components/grid/cells/JSONCell.svelte +36 -0
- package/src/components/grid/cells/LongFormCell.svelte +122 -0
- package/src/components/grid/cells/MultiSelectCell.svelte +7 -0
- package/src/components/grid/cells/NumberCell.svelte +14 -0
- package/src/components/grid/cells/OptionsCell.svelte +249 -0
- package/src/components/grid/cells/RelationshipCell.svelte +518 -0
- package/src/components/grid/cells/TextCell.svelte +111 -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/ColumnWidthButton.svelte +91 -0
- package/src/components/grid/controls/DeleteButton.svelte +66 -0
- package/src/components/grid/controls/HideColumnsButton.svelte +95 -0
- package/src/components/grid/controls/RowHeightButton.svelte +70 -0
- package/src/components/grid/controls/SortButton.svelte +129 -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 +254 -0
- package/src/components/grid/layout/GridBody.svelte +73 -0
- package/src/components/grid/layout/GridRow.svelte +59 -0
- package/src/components/grid/layout/GridScrollWrapper.svelte +81 -0
- package/src/components/grid/layout/HeaderRow.svelte +62 -0
- package/src/components/grid/layout/KeyboardShortcut.svelte +53 -0
- package/src/components/grid/layout/NewRow.svelte +334 -0
- package/src/components/grid/layout/RowCount.svelte +9 -0
- package/src/components/grid/layout/StickyColumn.svelte +176 -0
- package/src/components/grid/layout/UserAvatars.svelte +20 -0
- package/src/components/grid/lib/constants.js +14 -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 +232 -0
- package/src/components/grid/overlays/MenuOverlay.svelte +96 -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 +467 -0
- package/src/components/grid/stores/scroll.js +201 -0
- package/src/components/grid/stores/ui.js +159 -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 +145 -0
- package/src/components/index.js +1 -0
- package/src/constants.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
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/frontend-core",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.10-alpha.1",
|
|
4
4
|
"description": "Budibase frontend core libraries used in builder and client",
|
|
5
5
|
"author": "Budibase",
|
|
6
6
|
"license": "MPL-2.0",
|
|
7
7
|
"svelte": "src/index.js",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@budibase/bbui": "
|
|
10
|
-
"@budibase/shared-core": "
|
|
9
|
+
"@budibase/bbui": "2.5.10-alpha.1",
|
|
10
|
+
"@budibase/shared-core": "2.5.10-alpha.1",
|
|
11
|
+
"dayjs": "^1.11.7",
|
|
11
12
|
"lodash": "^4.17.21",
|
|
13
|
+
"socket.io-client": "^4.6.1",
|
|
12
14
|
"svelte": "^3.46.2"
|
|
13
15
|
},
|
|
14
|
-
"gitHead": "
|
|
16
|
+
"gitHead": "9fcfb0a6cd4e4003f0e420b4b180b3b069e93191"
|
|
15
17
|
}
|
package/src/api/other.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
export const buildOtherEndpoints = API => ({
|
|
2
|
-
/**
|
|
3
|
-
* TODO: find out what this is
|
|
4
|
-
*/
|
|
5
|
-
checkImportComplete: async () => {
|
|
6
|
-
return await API.get({
|
|
7
|
-
url: "/api/cloud/import/complete",
|
|
8
|
-
})
|
|
9
|
-
},
|
|
10
|
-
|
|
11
2
|
/**
|
|
12
3
|
* Gets the current environment details.
|
|
13
4
|
*/
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onMount } from "svelte"
|
|
3
|
+
import { getContext } from "svelte"
|
|
4
|
+
import { Dropzone, notifications } from "@budibase/bbui"
|
|
5
|
+
|
|
6
|
+
export let value
|
|
7
|
+
export let focused = false
|
|
8
|
+
export let onChange
|
|
9
|
+
export let readonly = false
|
|
10
|
+
export let api
|
|
11
|
+
export let invertX = false
|
|
12
|
+
export let invertY = false
|
|
13
|
+
|
|
14
|
+
const { API } = getContext("grid")
|
|
15
|
+
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
|
16
|
+
|
|
17
|
+
let isOpen = false
|
|
18
|
+
|
|
19
|
+
$: editable = focused && !readonly
|
|
20
|
+
$: {
|
|
21
|
+
if (!focused) {
|
|
22
|
+
close()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const onKeyDown = () => {
|
|
27
|
+
return isOpen
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const open = () => {
|
|
31
|
+
isOpen = true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const close = () => {
|
|
35
|
+
isOpen = false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isImage = extension => {
|
|
39
|
+
return imageExtensions.includes(extension?.toLowerCase())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleFileTooLarge = fileSizeLimit => {
|
|
43
|
+
notifications.error(
|
|
44
|
+
`Files cannot exceed ${
|
|
45
|
+
fileSizeLimit / 1000000
|
|
46
|
+
}MB. Please try again with smaller files.`
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const processFiles = async fileList => {
|
|
51
|
+
let data = new FormData()
|
|
52
|
+
for (let i = 0; i < fileList.length; i++) {
|
|
53
|
+
data.append("file", fileList[i])
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await API.uploadBuilderAttachment(data)
|
|
57
|
+
} catch (error) {
|
|
58
|
+
notifications.error("Failed to upload attachment")
|
|
59
|
+
return []
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const deleteAttachments = async fileList => {
|
|
64
|
+
try {
|
|
65
|
+
return await API.deleteBuilderAttachments(fileList)
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onMount(() => {
|
|
72
|
+
api = {
|
|
73
|
+
focus: () => open(),
|
|
74
|
+
blur: () => close(),
|
|
75
|
+
isActive: () => isOpen,
|
|
76
|
+
onKeyDown,
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<div class="attachment-cell" class:editable on:click={editable ? open : null}>
|
|
82
|
+
{#each value || [] as attachment}
|
|
83
|
+
{#if isImage(attachment.extension)}
|
|
84
|
+
<img src={attachment.url} alt={attachment.extension} />
|
|
85
|
+
{:else}
|
|
86
|
+
<div class="file" title={attachment.name}>
|
|
87
|
+
{attachment.extension}
|
|
88
|
+
</div>
|
|
89
|
+
{/if}
|
|
90
|
+
{/each}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{#if isOpen}
|
|
94
|
+
<div class="dropzone" class:invertX class:invertY>
|
|
95
|
+
<Dropzone
|
|
96
|
+
{value}
|
|
97
|
+
compact
|
|
98
|
+
on:change={e => onChange(e.detail)}
|
|
99
|
+
{processFiles}
|
|
100
|
+
{deleteAttachments}
|
|
101
|
+
{handleFileTooLarge}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
<style>
|
|
107
|
+
.attachment-cell {
|
|
108
|
+
flex: 1 1 auto;
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: row;
|
|
111
|
+
align-items: stretch;
|
|
112
|
+
padding: var(--cell-padding);
|
|
113
|
+
flex-wrap: nowrap;
|
|
114
|
+
gap: var(--cell-spacing);
|
|
115
|
+
align-self: stretch;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
user-select: none;
|
|
118
|
+
}
|
|
119
|
+
.attachment-cell.editable:hover {
|
|
120
|
+
cursor: pointer;
|
|
121
|
+
}
|
|
122
|
+
.file {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: row;
|
|
125
|
+
justify-content: flex-start;
|
|
126
|
+
align-items: center;
|
|
127
|
+
padding: 0 10px;
|
|
128
|
+
color: var(--spectrum-global-color-gray-700);
|
|
129
|
+
border: 1px solid var(--spectrum-global-color-gray-300);
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
text-transform: uppercase;
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
font-size: 12px;
|
|
134
|
+
user-select: none;
|
|
135
|
+
}
|
|
136
|
+
.dropzone {
|
|
137
|
+
position: absolute;
|
|
138
|
+
top: 100%;
|
|
139
|
+
left: 0;
|
|
140
|
+
width: 320px;
|
|
141
|
+
background: var(--background);
|
|
142
|
+
border: var(--cell-border);
|
|
143
|
+
padding: var(--cell-padding);
|
|
144
|
+
box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
|
|
145
|
+
}
|
|
146
|
+
.dropzone.invertX {
|
|
147
|
+
left: auto;
|
|
148
|
+
right: 0;
|
|
149
|
+
}
|
|
150
|
+
.dropzone.invertY {
|
|
151
|
+
transform: translateY(-100%);
|
|
152
|
+
top: 0;
|
|
153
|
+
}
|
|
154
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onMount } from "svelte"
|
|
3
|
+
import { Checkbox } from "@budibase/bbui"
|
|
4
|
+
|
|
5
|
+
export let value
|
|
6
|
+
export let focused = false
|
|
7
|
+
export let onChange
|
|
8
|
+
export let readonly = false
|
|
9
|
+
export let api
|
|
10
|
+
|
|
11
|
+
$: editable = focused && !readonly
|
|
12
|
+
|
|
13
|
+
const handleChange = e => {
|
|
14
|
+
onChange(e.detail)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const onKeyDown = e => {
|
|
18
|
+
if (e.key === "Enter") {
|
|
19
|
+
onChange(!value)
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onMount(() => {
|
|
26
|
+
api = {
|
|
27
|
+
onKeyDown,
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="boolean-cell" class:editable>
|
|
33
|
+
<Checkbox {value} on:change={handleChange} />
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.boolean-cell {
|
|
38
|
+
padding: 2px var(--cell-padding);
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
.boolean-cell.editable {
|
|
42
|
+
pointer-events: all;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from "svelte"
|
|
3
|
+
import GridCell from "./GridCell.svelte"
|
|
4
|
+
import { getCellRenderer } from "../lib/renderers"
|
|
5
|
+
import { derived, writable } from "svelte/store"
|
|
6
|
+
|
|
7
|
+
const { rows, focusedCellId, focusedCellAPI, menu, config, validation } =
|
|
8
|
+
getContext("grid")
|
|
9
|
+
|
|
10
|
+
export let highlighted
|
|
11
|
+
export let selected
|
|
12
|
+
export let rowFocused
|
|
13
|
+
export let rowIdx
|
|
14
|
+
export let focused
|
|
15
|
+
export let selectedUser
|
|
16
|
+
export let column
|
|
17
|
+
export let row
|
|
18
|
+
export let cellId
|
|
19
|
+
export let updateValue = rows.actions.updateValue
|
|
20
|
+
export let invertX = false
|
|
21
|
+
export let invertY = false
|
|
22
|
+
export let contentLines = 1
|
|
23
|
+
|
|
24
|
+
const emptyError = writable(null)
|
|
25
|
+
|
|
26
|
+
let api
|
|
27
|
+
|
|
28
|
+
// Get the error for this cell if the row is focused
|
|
29
|
+
$: error = getErrorStore(rowFocused, cellId)
|
|
30
|
+
|
|
31
|
+
// Determine if the cell is editable
|
|
32
|
+
$: readonly =
|
|
33
|
+
column.schema.autocolumn ||
|
|
34
|
+
column.schema.disabled ||
|
|
35
|
+
(!$config.allowEditRows && row._id)
|
|
36
|
+
|
|
37
|
+
// Register this cell API if the row is focused
|
|
38
|
+
$: {
|
|
39
|
+
if (focused) {
|
|
40
|
+
focusedCellAPI.set(cellAPI)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const getErrorStore = (selected, cellId) => {
|
|
45
|
+
if (!selected) {
|
|
46
|
+
return emptyError
|
|
47
|
+
}
|
|
48
|
+
return derived(validation, $validation => $validation[cellId])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cellAPI = {
|
|
52
|
+
focus: () => api?.focus?.(),
|
|
53
|
+
blur: () => api?.blur?.(),
|
|
54
|
+
isActive: () => api?.isActive?.() ?? false,
|
|
55
|
+
onKeyDown: (...params) => api?.onKeyDown(...params),
|
|
56
|
+
isReadonly: () => readonly,
|
|
57
|
+
getType: () => column.schema.type,
|
|
58
|
+
getValue: () => row[column.name],
|
|
59
|
+
setValue: value => {
|
|
60
|
+
validation.actions.setError(cellId, null)
|
|
61
|
+
updateValue(row._id, column.name, value)
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<GridCell
|
|
67
|
+
{highlighted}
|
|
68
|
+
{selected}
|
|
69
|
+
{rowIdx}
|
|
70
|
+
{focused}
|
|
71
|
+
{selectedUser}
|
|
72
|
+
{readonly}
|
|
73
|
+
error={$error}
|
|
74
|
+
on:click={() => focusedCellId.set(cellId)}
|
|
75
|
+
on:contextmenu={e => menu.actions.open(cellId, e)}
|
|
76
|
+
width={column.width}
|
|
77
|
+
>
|
|
78
|
+
<svelte:component
|
|
79
|
+
this={getCellRenderer(column)}
|
|
80
|
+
bind:api
|
|
81
|
+
value={row[column.name]}
|
|
82
|
+
schema={column.schema}
|
|
83
|
+
onChange={cellAPI.setValue}
|
|
84
|
+
{focused}
|
|
85
|
+
{readonly}
|
|
86
|
+
{invertY}
|
|
87
|
+
{invertX}
|
|
88
|
+
{contentLines}
|
|
89
|
+
/>
|
|
90
|
+
<slot />
|
|
91
|
+
</GridCell>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import dayjs from "dayjs"
|
|
3
|
+
import { CoreDatePicker, Icon } from "@budibase/bbui"
|
|
4
|
+
|
|
5
|
+
export let value
|
|
6
|
+
export let schema
|
|
7
|
+
export let onChange
|
|
8
|
+
export let focused = false
|
|
9
|
+
export let readonly = false
|
|
10
|
+
|
|
11
|
+
// adding the 0- will turn a string like 00:00:00 into a valid ISO
|
|
12
|
+
// date, but will make actual ISO dates invalid
|
|
13
|
+
$: time = new Date(`0-${value}`)
|
|
14
|
+
$: timeOnly = !isNaN(time) || schema?.timeOnly
|
|
15
|
+
$: dateOnly = schema?.dateOnly
|
|
16
|
+
$: format = timeOnly
|
|
17
|
+
? "HH:mm:ss"
|
|
18
|
+
: dateOnly
|
|
19
|
+
? "MMM D YYYY"
|
|
20
|
+
: "MMM D YYYY, HH:mm"
|
|
21
|
+
$: editable = focused && !readonly
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class="container">
|
|
25
|
+
<div class="value">
|
|
26
|
+
{#if value}
|
|
27
|
+
{dayjs(timeOnly ? time : value).format(format)}
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
{#if editable}
|
|
31
|
+
<Icon name="Calendar" />
|
|
32
|
+
{/if}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{#if editable}
|
|
36
|
+
<div class="picker">
|
|
37
|
+
<CoreDatePicker
|
|
38
|
+
{value}
|
|
39
|
+
on:change={e => onChange(e.detail)}
|
|
40
|
+
appendTo={document.documentElement}
|
|
41
|
+
enableTime={!dateOnly}
|
|
42
|
+
{timeOnly}
|
|
43
|
+
time24hr
|
|
44
|
+
ignoreTimezones={schema.ignoreTimezones}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
{/if}
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.container {
|
|
51
|
+
padding: var(--cell-padding);
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: row;
|
|
54
|
+
justify-content: space-between;
|
|
55
|
+
align-items: center;
|
|
56
|
+
flex: 1 1 auto;
|
|
57
|
+
gap: var(--cell-spacing);
|
|
58
|
+
}
|
|
59
|
+
.value {
|
|
60
|
+
flex: 1 1 auto;
|
|
61
|
+
width: 0;
|
|
62
|
+
overflow: hidden;
|
|
63
|
+
text-overflow: ellipsis;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
line-height: 20px;
|
|
66
|
+
}
|
|
67
|
+
.picker {
|
|
68
|
+
position: absolute;
|
|
69
|
+
opacity: 0;
|
|
70
|
+
}
|
|
71
|
+
.picker :global(.flatpickr) {
|
|
72
|
+
min-width: 0;
|
|
73
|
+
}
|
|
74
|
+
.picker :global(.spectrum-Textfield-input) {
|
|
75
|
+
width: 100%;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let focused = false
|
|
3
|
+
export let selected = false
|
|
4
|
+
export let highlighted = false
|
|
5
|
+
export let width = ""
|
|
6
|
+
export let selectedUser = null
|
|
7
|
+
export let error = null
|
|
8
|
+
export let rowIdx
|
|
9
|
+
export let defaultHeight = false
|
|
10
|
+
export let center = false
|
|
11
|
+
export let readonly = false
|
|
12
|
+
|
|
13
|
+
$: style = getStyle(width, selectedUser)
|
|
14
|
+
|
|
15
|
+
const getStyle = (width, selectedUser) => {
|
|
16
|
+
let style = `flex: 0 0 ${width}px;`
|
|
17
|
+
if (selectedUser) {
|
|
18
|
+
style += `--cell-color:${selectedUser.color};`
|
|
19
|
+
}
|
|
20
|
+
return style
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
class="cell"
|
|
26
|
+
class:selected
|
|
27
|
+
class:highlighted
|
|
28
|
+
class:focused
|
|
29
|
+
class:error
|
|
30
|
+
class:center
|
|
31
|
+
class:readonly
|
|
32
|
+
class:default-height={defaultHeight}
|
|
33
|
+
class:selected-other={selectedUser != null}
|
|
34
|
+
on:focus
|
|
35
|
+
on:mousedown
|
|
36
|
+
on:mouseup
|
|
37
|
+
on:click
|
|
38
|
+
on:contextmenu
|
|
39
|
+
{style}
|
|
40
|
+
data-row={rowIdx}
|
|
41
|
+
>
|
|
42
|
+
{#if error}
|
|
43
|
+
<div class="label">
|
|
44
|
+
{error}
|
|
45
|
+
</div>
|
|
46
|
+
{/if}
|
|
47
|
+
<slot />
|
|
48
|
+
{#if selectedUser && !focused}
|
|
49
|
+
<div class="label">
|
|
50
|
+
{selectedUser.label}
|
|
51
|
+
</div>
|
|
52
|
+
{/if}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<style>
|
|
56
|
+
/* Cells */
|
|
57
|
+
.cell {
|
|
58
|
+
height: var(--row-height);
|
|
59
|
+
border-bottom: var(--cell-border);
|
|
60
|
+
border-right: var(--cell-border);
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: row;
|
|
63
|
+
justify-content: flex-start;
|
|
64
|
+
align-items: flex-start;
|
|
65
|
+
color: var(--spectrum-global-color-gray-800);
|
|
66
|
+
font-size: var(--cell-font-size);
|
|
67
|
+
gap: var(--cell-spacing);
|
|
68
|
+
background: var(--cell-background);
|
|
69
|
+
position: relative;
|
|
70
|
+
width: 0;
|
|
71
|
+
--cell-color: transparent;
|
|
72
|
+
}
|
|
73
|
+
.cell.default-height {
|
|
74
|
+
height: var(--default-row-height);
|
|
75
|
+
}
|
|
76
|
+
.cell.center {
|
|
77
|
+
align-items: center;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Cell border */
|
|
81
|
+
.cell.focused:after,
|
|
82
|
+
.cell.error:after,
|
|
83
|
+
.cell.selected-other:not(.focused):after {
|
|
84
|
+
content: " ";
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: 0;
|
|
87
|
+
left: 0;
|
|
88
|
+
height: 100%;
|
|
89
|
+
width: 100%;
|
|
90
|
+
border: 2px solid var(--cell-color);
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
border-radius: 2px;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Cell border for cells with labels */
|
|
97
|
+
.cell.error:after,
|
|
98
|
+
.cell.selected-other:not(.focused):after {
|
|
99
|
+
border-radius: 0 2px 2px 2px;
|
|
100
|
+
}
|
|
101
|
+
.cell[data-row="0"].error:after,
|
|
102
|
+
.cell[data-row="0"].selected-other:not(.focused):after {
|
|
103
|
+
border-radius: 2px 2px 2px 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Cell z-index */
|
|
107
|
+
.cell.error,
|
|
108
|
+
.cell.selected-other:not(.focused) {
|
|
109
|
+
z-index: 1;
|
|
110
|
+
}
|
|
111
|
+
.cell.focused {
|
|
112
|
+
z-index: 2;
|
|
113
|
+
}
|
|
114
|
+
.cell.focused {
|
|
115
|
+
--cell-color: var(--spectrum-global-color-blue-400);
|
|
116
|
+
}
|
|
117
|
+
.cell.error {
|
|
118
|
+
--cell-color: var(--spectrum-global-color-red-500);
|
|
119
|
+
}
|
|
120
|
+
.cell.readonly {
|
|
121
|
+
--cell-color: var(--spectrum-global-color-gray-600);
|
|
122
|
+
}
|
|
123
|
+
.cell:not(.focused) {
|
|
124
|
+
user-select: none;
|
|
125
|
+
}
|
|
126
|
+
.cell:hover {
|
|
127
|
+
cursor: default;
|
|
128
|
+
}
|
|
129
|
+
.cell.highlighted:not(.focused),
|
|
130
|
+
.cell.focused.readonly {
|
|
131
|
+
--cell-background: var(--cell-background-hover);
|
|
132
|
+
}
|
|
133
|
+
.cell.selected:not(.focused) {
|
|
134
|
+
--cell-background: var(--spectrum-global-color-blue-100);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Label for additional text */
|
|
138
|
+
.label {
|
|
139
|
+
position: absolute;
|
|
140
|
+
bottom: 100%;
|
|
141
|
+
left: 0;
|
|
142
|
+
padding: 1px 4px 3px 4px;
|
|
143
|
+
margin: 0 0 -2px 0;
|
|
144
|
+
background: var(--user-color);
|
|
145
|
+
border-radius: 2px;
|
|
146
|
+
display: block;
|
|
147
|
+
color: white;
|
|
148
|
+
font-size: 12px;
|
|
149
|
+
font-weight: 600;
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
text-overflow: ellipsis;
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
user-select: none;
|
|
154
|
+
}
|
|
155
|
+
.cell[data-row="0"] .label {
|
|
156
|
+
bottom: auto;
|
|
157
|
+
top: 100%;
|
|
158
|
+
border-radius: 0 2px 2px 2px;
|
|
159
|
+
padding: 2px 4px 2px 4px;
|
|
160
|
+
margin: -2px 0 0 0;
|
|
161
|
+
}
|
|
162
|
+
.error .label {
|
|
163
|
+
background: var(--spectrum-global-color-red-500);
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { GutterWidth } from "../lib/constants"
|
|
3
|
+
import { getContext } from "svelte"
|
|
4
|
+
import { Checkbox, Icon } from "@budibase/bbui"
|
|
5
|
+
import GridCell from "./GridCell.svelte"
|
|
6
|
+
import { createEventDispatcher } from "svelte"
|
|
7
|
+
|
|
8
|
+
export let row
|
|
9
|
+
export let rowFocused = false
|
|
10
|
+
export let rowHovered = false
|
|
11
|
+
export let rowSelected = false
|
|
12
|
+
export let disableExpand = false
|
|
13
|
+
export let disableNumber = false
|
|
14
|
+
export let defaultHeight = false
|
|
15
|
+
export let disabled = false
|
|
16
|
+
|
|
17
|
+
const { config, dispatch, selectedRows } = getContext("grid")
|
|
18
|
+
const svelteDispatch = createEventDispatcher()
|
|
19
|
+
|
|
20
|
+
const select = () => {
|
|
21
|
+
svelteDispatch("select")
|
|
22
|
+
const id = row?._id
|
|
23
|
+
if (id) {
|
|
24
|
+
selectedRows.update(state => {
|
|
25
|
+
let newState = {
|
|
26
|
+
...state,
|
|
27
|
+
[id]: !state[id],
|
|
28
|
+
}
|
|
29
|
+
if (!newState[id]) {
|
|
30
|
+
delete newState[id]
|
|
31
|
+
}
|
|
32
|
+
return newState
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const expand = () => {
|
|
38
|
+
svelteDispatch("expand")
|
|
39
|
+
if (row) {
|
|
40
|
+
dispatch("edit-row", row)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<GridCell
|
|
46
|
+
width={GutterWidth}
|
|
47
|
+
highlighted={rowFocused || rowHovered}
|
|
48
|
+
selected={rowSelected}
|
|
49
|
+
{defaultHeight}
|
|
50
|
+
>
|
|
51
|
+
<div class="gutter">
|
|
52
|
+
{#if $$slots.default}
|
|
53
|
+
<slot />
|
|
54
|
+
{:else}
|
|
55
|
+
<div
|
|
56
|
+
on:click={select}
|
|
57
|
+
class="checkbox"
|
|
58
|
+
class:visible={$config.allowDeleteRows &&
|
|
59
|
+
(disableNumber || rowSelected || rowHovered || rowFocused)}
|
|
60
|
+
>
|
|
61
|
+
<Checkbox value={rowSelected} {disabled} />
|
|
62
|
+
</div>
|
|
63
|
+
{#if !disableNumber}
|
|
64
|
+
<div
|
|
65
|
+
class="number"
|
|
66
|
+
class:visible={!$config.allowDeleteRows ||
|
|
67
|
+
!(rowSelected || rowHovered || rowFocused)}
|
|
68
|
+
>
|
|
69
|
+
{row.__idx + 1}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
{/if}
|
|
73
|
+
{#if $config.allowExpandRows}
|
|
74
|
+
<div
|
|
75
|
+
class="expand"
|
|
76
|
+
class:visible={!disableExpand && (rowFocused || rowHovered)}
|
|
77
|
+
>
|
|
78
|
+
<Icon name="Maximize" hoverable size="S" on:click={expand} />
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
81
|
+
</div>
|
|
82
|
+
</GridCell>
|
|
83
|
+
|
|
84
|
+
<style>
|
|
85
|
+
.gutter {
|
|
86
|
+
flex: 1 1 auto;
|
|
87
|
+
display: grid;
|
|
88
|
+
align-items: center;
|
|
89
|
+
padding: var(--cell-padding);
|
|
90
|
+
grid-template-columns: 1fr auto;
|
|
91
|
+
gap: var(--cell-spacing);
|
|
92
|
+
}
|
|
93
|
+
.checkbox,
|
|
94
|
+
.number {
|
|
95
|
+
display: none;
|
|
96
|
+
flex-direction: row;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
align-items: center;
|
|
99
|
+
}
|
|
100
|
+
.checkbox :global(.spectrum-Checkbox) {
|
|
101
|
+
min-height: 0;
|
|
102
|
+
height: 20px;
|
|
103
|
+
}
|
|
104
|
+
.checkbox :global(.spectrum-Checkbox-box) {
|
|
105
|
+
margin: 3px 0 0 0;
|
|
106
|
+
}
|
|
107
|
+
.number {
|
|
108
|
+
color: var(--spectrum-global-color-gray-500);
|
|
109
|
+
}
|
|
110
|
+
.checkbox.visible,
|
|
111
|
+
.number.visible {
|
|
112
|
+
display: flex;
|
|
113
|
+
}
|
|
114
|
+
.expand {
|
|
115
|
+
opacity: 0;
|
|
116
|
+
margin-right: 4px;
|
|
117
|
+
}
|
|
118
|
+
.expand :global(.spectrum-Icon) {
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
}
|
|
121
|
+
.expand.visible {
|
|
122
|
+
opacity: 1;
|
|
123
|
+
}
|
|
124
|
+
.expand.visible :global(.spectrum-Icon) {
|
|
125
|
+
pointer-events: all;
|
|
126
|
+
}
|
|
127
|
+
</style>
|