@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.
Files changed (63) hide show
  1. package/package.json +6 -4
  2. package/src/components/grid/cells/AttachmentCell.svelte +153 -0
  3. package/src/components/grid/cells/BooleanCell.svelte +44 -0
  4. package/src/components/grid/cells/DataCell.svelte +86 -0
  5. package/src/components/grid/cells/DateCell.svelte +77 -0
  6. package/src/components/grid/cells/FormulaCell.svelte +5 -0
  7. package/src/components/grid/cells/GridCell.svelte +159 -0
  8. package/src/components/grid/cells/HeaderCell.svelte +237 -0
  9. package/src/components/grid/cells/JSONCell.svelte +36 -0
  10. package/src/components/grid/cells/LongFormCell.svelte +117 -0
  11. package/src/components/grid/cells/MultiSelectCell.svelte +7 -0
  12. package/src/components/grid/cells/NumberCell.svelte +7 -0
  13. package/src/components/grid/cells/OptionsCell.svelte +239 -0
  14. package/src/components/grid/cells/RelationshipCell.svelte +503 -0
  15. package/src/components/grid/cells/TextCell.svelte +110 -0
  16. package/src/components/grid/controls/AddColumnButton.svelte +16 -0
  17. package/src/components/grid/controls/AddRowButton.svelte +18 -0
  18. package/src/components/grid/controls/BetaButton.svelte +46 -0
  19. package/src/components/grid/controls/DeleteButton.svelte +72 -0
  20. package/src/components/grid/controls/HideColumnsButton.svelte +91 -0
  21. package/src/components/grid/controls/RowHeightButton.svelte +70 -0
  22. package/src/components/grid/controls/SortButton.svelte +107 -0
  23. package/src/components/grid/index.js +1 -0
  24. package/src/components/grid/layout/Avatar.svelte +24 -0
  25. package/src/components/grid/layout/Grid.svelte +250 -0
  26. package/src/components/grid/layout/GridBody.svelte +38 -0
  27. package/src/components/grid/layout/GridRow.svelte +57 -0
  28. package/src/components/grid/layout/GridScrollWrapper.svelte +84 -0
  29. package/src/components/grid/layout/HeaderRow.svelte +30 -0
  30. package/src/components/grid/layout/NewRowTop.svelte +247 -0
  31. package/src/components/grid/layout/RowCount.svelte +9 -0
  32. package/src/components/grid/layout/StickyColumn.svelte +245 -0
  33. package/src/components/grid/layout/UserAvatars.svelte +20 -0
  34. package/src/components/grid/lib/constants.js +11 -0
  35. package/src/components/grid/lib/events.js +29 -0
  36. package/src/components/grid/lib/renderers.js +29 -0
  37. package/src/components/grid/lib/utils.js +26 -0
  38. package/src/components/grid/lib/websocket.js +63 -0
  39. package/src/components/grid/overlays/KeyboardManager.svelte +180 -0
  40. package/src/components/grid/overlays/MenuOverlay.svelte +95 -0
  41. package/src/components/grid/overlays/ReorderOverlay.svelte +63 -0
  42. package/src/components/grid/overlays/ResizeOverlay.svelte +70 -0
  43. package/src/components/grid/overlays/ScrollOverlay.svelte +121 -0
  44. package/src/components/grid/stores/bounds.js +16 -0
  45. package/src/components/grid/stores/clipboard.js +34 -0
  46. package/src/components/grid/stores/columns.js +206 -0
  47. package/src/components/grid/stores/index.js +48 -0
  48. package/src/components/grid/stores/menu.js +49 -0
  49. package/src/components/grid/stores/pagination.js +24 -0
  50. package/src/components/grid/stores/reorder.js +155 -0
  51. package/src/components/grid/stores/resize.js +124 -0
  52. package/src/components/grid/stores/rows.js +462 -0
  53. package/src/components/grid/stores/scroll.js +201 -0
  54. package/src/components/grid/stores/ui.js +168 -0
  55. package/src/components/grid/stores/users.js +115 -0
  56. package/src/components/grid/stores/validation.js +45 -0
  57. package/src/components/grid/stores/viewport.js +141 -0
  58. package/src/components/index.js +1 -0
  59. package/src/fetch/DataFetch.js +27 -21
  60. package/src/fetch/QueryFetch.js +1 -1
  61. package/src/themes/midnight.css +3 -0
  62. package/src/themes/nord.css +3 -0
  63. 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.6-alpha.5",
3
+ "version": "2.5.6-alpha.7",
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": "2.5.6-alpha.5",
10
- "@budibase/shared-core": "2.5.6-alpha.5",
9
+ "@budibase/bbui": "2.5.6-alpha.7",
10
+ "@budibase/shared-core": "2.5.6-alpha.7",
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": "bd66f0f010edb2916b667bba2e408e9a1ce92f6e"
16
+ "gitHead": "c34a935f5557c60d7bbf3556942828248057dd64"
15
17
  }
@@ -0,0 +1,153 @@
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
+ onKeyDown,
76
+ }
77
+ })
78
+ </script>
79
+
80
+ <div class="attachment-cell" class:editable on:click={editable ? open : null}>
81
+ {#each value || [] as attachment}
82
+ {#if isImage(attachment.extension)}
83
+ <img src={attachment.url} alt={attachment.extension} />
84
+ {:else}
85
+ <div class="file" title={attachment.name}>
86
+ {attachment.extension}
87
+ </div>
88
+ {/if}
89
+ {/each}
90
+ </div>
91
+
92
+ {#if isOpen}
93
+ <div class="dropzone" class:invertX class:invertY>
94
+ <Dropzone
95
+ {value}
96
+ compact
97
+ on:change={e => onChange(e.detail)}
98
+ {processFiles}
99
+ {deleteAttachments}
100
+ {handleFileTooLarge}
101
+ />
102
+ </div>
103
+ {/if}
104
+
105
+ <style>
106
+ .attachment-cell {
107
+ flex: 1 1 auto;
108
+ display: flex;
109
+ flex-direction: row;
110
+ align-items: stretch;
111
+ padding: var(--cell-padding);
112
+ flex-wrap: nowrap;
113
+ gap: var(--cell-spacing);
114
+ align-self: stretch;
115
+ overflow: hidden;
116
+ user-select: none;
117
+ }
118
+ .attachment-cell.editable:hover {
119
+ cursor: pointer;
120
+ }
121
+ .file {
122
+ display: flex;
123
+ flex-direction: row;
124
+ justify-content: flex-start;
125
+ align-items: center;
126
+ padding: 0 10px;
127
+ color: var(--spectrum-global-color-gray-700);
128
+ border: 1px solid var(--spectrum-global-color-gray-300);
129
+ border-radius: 4px;
130
+ text-transform: uppercase;
131
+ font-weight: 600;
132
+ font-size: 12px;
133
+ user-select: none;
134
+ }
135
+ .dropzone {
136
+ position: absolute;
137
+ top: 100%;
138
+ left: 0;
139
+ width: 320px;
140
+ background: var(--background);
141
+ border: var(--cell-border);
142
+ padding: var(--cell-padding);
143
+ box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
144
+ }
145
+ .dropzone.invertX {
146
+ left: auto;
147
+ right: 0;
148
+ }
149
+ .dropzone.invertY {
150
+ transform: translateY(-100%);
151
+ top: 0;
152
+ }
153
+ </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,86 @@
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
+
23
+ const emptyError = writable(null)
24
+
25
+ let api
26
+
27
+ // Get the error for this cell if the row is focused
28
+ $: error = getErrorStore(rowFocused, cellId)
29
+
30
+ // Determine if the cell is editable
31
+ $: readonly =
32
+ column.schema.autocolumn ||
33
+ column.schema.disabled ||
34
+ (!$config.allowEditRows && row._id)
35
+
36
+ // Register this cell API if the row is focused
37
+ $: {
38
+ if (focused) {
39
+ focusedCellAPI.set(cellAPI)
40
+ }
41
+ }
42
+
43
+ const getErrorStore = (selected, cellId) => {
44
+ if (!selected) {
45
+ return emptyError
46
+ }
47
+ return derived(validation, $validation => $validation[cellId])
48
+ }
49
+
50
+ const cellAPI = {
51
+ focus: () => api?.focus(),
52
+ blur: () => api?.blur(),
53
+ onKeyDown: (...params) => api?.onKeyDown(...params),
54
+ isReadonly: () => readonly,
55
+ getType: () => column.schema.type,
56
+ getValue: () => row[column.name],
57
+ setValue: value => {
58
+ validation.actions.setError(cellId, null)
59
+ updateValue(row._id, column.name, value)
60
+ },
61
+ }
62
+ </script>
63
+
64
+ <GridCell
65
+ {highlighted}
66
+ {selected}
67
+ {rowIdx}
68
+ {focused}
69
+ {selectedUser}
70
+ error={$error}
71
+ on:click={() => focusedCellId.set(cellId)}
72
+ on:contextmenu={e => menu.actions.open(cellId, e)}
73
+ width={column.width}
74
+ >
75
+ <svelte:component
76
+ this={getCellRenderer(column)}
77
+ bind:api
78
+ value={row[column.name]}
79
+ schema={column.schema}
80
+ onChange={cellAPI.setValue}
81
+ {focused}
82
+ {readonly}
83
+ {invertY}
84
+ {invertX}
85
+ />
86
+ </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,5 @@
1
+ <script>
2
+ import TextCell from "./TextCell.svelte"
3
+ </script>
4
+
5
+ <TextCell {...$$props} readonly />
@@ -0,0 +1,159 @@
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
+
12
+ $: style = getStyle(width, selectedUser)
13
+
14
+ const getStyle = (width, selectedUser) => {
15
+ let style = `flex: 0 0 ${width}px;`
16
+ if (selectedUser) {
17
+ style += `--cell-color:${selectedUser.color};`
18
+ }
19
+ return style
20
+ }
21
+ </script>
22
+
23
+ <div
24
+ class="cell"
25
+ class:selected
26
+ class:highlighted
27
+ class:focused
28
+ class:error
29
+ class:center
30
+ class:default-height={defaultHeight}
31
+ class:selected-other={selectedUser != null}
32
+ on:focus
33
+ on:mousedown
34
+ on:mouseup
35
+ on:click
36
+ on:contextmenu
37
+ {style}
38
+ data-row={rowIdx}
39
+ >
40
+ {#if error}
41
+ <div class="label">
42
+ {error}
43
+ </div>
44
+ {/if}
45
+ <slot />
46
+ {#if selectedUser && !focused}
47
+ <div class="label">
48
+ {selectedUser.label}
49
+ </div>
50
+ {/if}
51
+ </div>
52
+
53
+ <style>
54
+ /* Cells */
55
+ .cell {
56
+ height: var(--row-height);
57
+ border-bottom: var(--cell-border);
58
+ border-right: var(--cell-border);
59
+ display: flex;
60
+ flex-direction: row;
61
+ justify-content: flex-start;
62
+ align-items: flex-start;
63
+ color: var(--spectrum-global-color-gray-800);
64
+ font-size: var(--cell-font-size);
65
+ gap: var(--cell-spacing);
66
+ background: var(--cell-background);
67
+ position: relative;
68
+ width: 0;
69
+ --cell-color: transparent;
70
+ }
71
+ .cell.default-height {
72
+ height: var(--default-row-height);
73
+ }
74
+ .cell.center {
75
+ align-items: center;
76
+ }
77
+
78
+ /* Cell border */
79
+ .cell.focused:after,
80
+ .cell.error:after,
81
+ .cell.selected-other:not(.focused):after {
82
+ content: " ";
83
+ position: absolute;
84
+ top: 0;
85
+ left: 0;
86
+ height: 100%;
87
+ width: 100%;
88
+ border: 2px solid var(--cell-color);
89
+ pointer-events: none;
90
+ border-radius: 2px;
91
+ box-sizing: border-box;
92
+ }
93
+
94
+ /* Cell border for cells with labels */
95
+ .cell.error:after,
96
+ .cell.selected-other:not(.focused):after {
97
+ border-radius: 0 2px 2px 2px;
98
+ }
99
+ .cell[data-row="0"].error:after,
100
+ .cell[data-row="0"].selected-other:not(.focused):after {
101
+ border-radius: 2px 2px 2px 0;
102
+ }
103
+
104
+ /* Cell z-index */
105
+ .cell.error,
106
+ .cell.selected-other:not(.focused) {
107
+ z-index: 1;
108
+ }
109
+ .cell.focused {
110
+ z-index: 2;
111
+ }
112
+ .cell.focused {
113
+ --cell-color: var(--spectrum-global-color-blue-400);
114
+ }
115
+ .cell.error {
116
+ --cell-color: var(--spectrum-global-color-red-500);
117
+ }
118
+ .cell:not(.focused) {
119
+ user-select: none;
120
+ }
121
+ .cell:hover {
122
+ cursor: default;
123
+ }
124
+ .cell.highlighted:not(.focused) {
125
+ --cell-background: var(--cell-background-hover);
126
+ }
127
+ .cell.selected:not(.focused) {
128
+ --cell-background: var(--spectrum-global-color-blue-100);
129
+ }
130
+
131
+ /* Label for additional text */
132
+ .label {
133
+ position: absolute;
134
+ bottom: 100%;
135
+ left: 0;
136
+ padding: 1px 4px 3px 4px;
137
+ margin: 0 0 -2px 0;
138
+ background: var(--user-color);
139
+ border-radius: 2px;
140
+ display: block;
141
+ color: white;
142
+ font-size: 12px;
143
+ font-weight: 600;
144
+ white-space: nowrap;
145
+ text-overflow: ellipsis;
146
+ overflow: hidden;
147
+ user-select: none;
148
+ }
149
+ .cell[data-row="0"] .label {
150
+ bottom: auto;
151
+ top: 100%;
152
+ border-radius: 0 2px 2px 2px;
153
+ padding: 2px 4px 2px 4px;
154
+ margin: -2px 0 0 0;
155
+ }
156
+ .error .label {
157
+ background: var(--spectrum-global-color-red-500);
158
+ }
159
+ </style>