@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
@@ -0,0 +1,168 @@
1
+ import { writable, get, derived } from "svelte/store"
2
+ import {
3
+ DefaultRowHeight,
4
+ LargeRowHeight,
5
+ MediumRowHeight,
6
+ } from "../lib/constants"
7
+
8
+ export const createStores = () => {
9
+ const focusedCellId = writable(null)
10
+ const focusedCellAPI = writable(null)
11
+ const selectedRows = writable({})
12
+ const hoveredRowId = writable(null)
13
+ const rowHeight = writable(DefaultRowHeight)
14
+ const previousFocusedRowId = writable(null)
15
+
16
+ // Derive the current focused row ID
17
+ const focusedRowId = derived(
18
+ focusedCellId,
19
+ $focusedCellId => {
20
+ return $focusedCellId?.split("-")[0]
21
+ },
22
+ null
23
+ )
24
+
25
+ return {
26
+ focusedCellId,
27
+ focusedCellAPI,
28
+ focusedRowId,
29
+ previousFocusedRowId,
30
+ selectedRows,
31
+ hoveredRowId,
32
+ rowHeight,
33
+ }
34
+ }
35
+
36
+ export const deriveStores = context => {
37
+ const {
38
+ focusedCellId,
39
+ selectedRows,
40
+ hoveredRowId,
41
+ enrichedRows,
42
+ rowLookupMap,
43
+ rowHeight,
44
+ } = context
45
+
46
+ // Derive the row that contains the selected cell
47
+ const focusedRow = derived(
48
+ [focusedCellId, rowLookupMap, enrichedRows],
49
+ ([$focusedCellId, $rowLookupMap, $enrichedRows]) => {
50
+ const rowId = $focusedCellId?.split("-")[0]
51
+
52
+ if (rowId === "new") {
53
+ // Edge case for new row
54
+ return { _id: rowId }
55
+ } else {
56
+ // All normal rows
57
+ const index = $rowLookupMap[rowId]
58
+ return $enrichedRows[index]
59
+ }
60
+ },
61
+ null
62
+ )
63
+
64
+ // Callback when leaving the grid, deselecting all focussed or selected items
65
+ const blur = () => {
66
+ focusedCellId.set(null)
67
+ selectedRows.set({})
68
+ hoveredRowId.set(null)
69
+ }
70
+
71
+ const contentLines = derived(rowHeight, $rowHeight => {
72
+ if ($rowHeight === LargeRowHeight) {
73
+ return 3
74
+ } else if ($rowHeight === MediumRowHeight) {
75
+ return 2
76
+ }
77
+ return 1
78
+ })
79
+
80
+ return {
81
+ focusedRow,
82
+ contentLines,
83
+ ui: {
84
+ actions: {
85
+ blur,
86
+ },
87
+ },
88
+ }
89
+ }
90
+
91
+ export const initialise = context => {
92
+ const {
93
+ focusedRowId,
94
+ previousFocusedRowId,
95
+ rows,
96
+ focusedCellId,
97
+ selectedRows,
98
+ hoveredRowId,
99
+ table,
100
+ rowHeight,
101
+ } = context
102
+
103
+ // Ensure we clear invalid rows from state if they disappear
104
+ rows.subscribe(() => {
105
+ const $focusedCellId = get(focusedCellId)
106
+ const $selectedRows = get(selectedRows)
107
+ const $hoveredRowId = get(hoveredRowId)
108
+ const hasRow = rows.actions.hasRow
109
+
110
+ // Check selected cell
111
+ const selectedRowId = $focusedCellId?.split("-")[0]
112
+ if (selectedRowId && !hasRow(selectedRowId)) {
113
+ focusedCellId.set(null)
114
+ }
115
+
116
+ // Check hovered row
117
+ if ($hoveredRowId && !hasRow($hoveredRowId)) {
118
+ hoveredRowId.set(null)
119
+ }
120
+
121
+ // Check selected rows
122
+ let newSelectedRows = { ...$selectedRows }
123
+ let selectedRowsNeedsUpdate = false
124
+ const selectedIds = Object.keys($selectedRows)
125
+ for (let i = 0; i < selectedIds.length; i++) {
126
+ if (!hasRow(selectedIds[i])) {
127
+ delete newSelectedRows[selectedIds[i]]
128
+ selectedRowsNeedsUpdate = true
129
+ }
130
+ }
131
+ if (selectedRowsNeedsUpdate) {
132
+ selectedRows.set(newSelectedRows)
133
+ }
134
+ })
135
+
136
+ // Remember the last focused row ID so that we can store the previous one
137
+ let lastFocusedRowId = null
138
+ focusedRowId.subscribe(id => {
139
+ previousFocusedRowId.set(lastFocusedRowId)
140
+ lastFocusedRowId = id
141
+ })
142
+
143
+ // Reset selected rows when selected cell changes
144
+ focusedCellId.subscribe(id => {
145
+ if (id) {
146
+ selectedRows.set({})
147
+ }
148
+ })
149
+
150
+ // Unset selected cell when rows are selected
151
+ selectedRows.subscribe(rows => {
152
+ if (Object.keys(rows || {}).length) {
153
+ focusedCellId.set(null)
154
+ }
155
+ })
156
+
157
+ // Remove hovered row when a cell is selected
158
+ focusedCellId.subscribe(cell => {
159
+ if (cell && get(hoveredRowId)) {
160
+ hoveredRowId.set(null)
161
+ }
162
+ })
163
+
164
+ // Pull row height from table
165
+ table.subscribe($table => {
166
+ rowHeight.set($table?.rowHeight || DefaultRowHeight)
167
+ })
168
+ }
@@ -0,0 +1,115 @@
1
+ import { writable, get, derived } from "svelte/store"
2
+
3
+ export const createStores = () => {
4
+ const users = writable([])
5
+ const userId = writable(null)
6
+
7
+ // Enrich users with unique colours
8
+ const enrichedUsers = derived(
9
+ [users, userId],
10
+ ([$users, $userId]) => {
11
+ return (
12
+ $users
13
+ .slice()
14
+ // Place current user first
15
+ .sort((a, b) => {
16
+ if (a.id === $userId) {
17
+ return -1
18
+ } else if (b.id === $userId) {
19
+ return 1
20
+ } else {
21
+ return 0
22
+ }
23
+ })
24
+ // Enrich users with colors
25
+ .map((user, idx) => {
26
+ // Generate random colour hue
27
+ let hue = 1
28
+ for (let i = 0; i < user.email.length && i < 5; i++) {
29
+ hue *= user.email.charCodeAt(i + 1)
30
+ hue /= 17
31
+ }
32
+ hue = hue % 360
33
+ const color =
34
+ idx === 0
35
+ ? "var(--spectrum-global-color-blue-400)"
36
+ : `hsl(${hue}, 50%, 40%)`
37
+
38
+ // Generate friendly label
39
+ let label = user.email
40
+ if (user.firstName) {
41
+ label = user.firstName
42
+ if (user.lastName) {
43
+ label += ` ${user.lastName}`
44
+ }
45
+ }
46
+
47
+ return {
48
+ ...user,
49
+ color,
50
+ label,
51
+ }
52
+ })
53
+ )
54
+ },
55
+ []
56
+ )
57
+
58
+ return {
59
+ users: {
60
+ ...users,
61
+ subscribe: enrichedUsers.subscribe,
62
+ },
63
+ userId,
64
+ }
65
+ }
66
+
67
+ export const deriveStores = context => {
68
+ const { users, userId } = context
69
+
70
+ // Generate a lookup map of cell ID to the user that has it selected, to make
71
+ // lookups inside cells extremely fast
72
+ const selectedCellMap = derived(
73
+ [users, userId],
74
+ ([$enrichedUsers, $userId]) => {
75
+ let map = {}
76
+ $enrichedUsers.forEach(user => {
77
+ if (user.focusedCellId && user.id !== $userId) {
78
+ map[user.focusedCellId] = user
79
+ }
80
+ })
81
+ return map
82
+ },
83
+ {}
84
+ )
85
+
86
+ const updateUser = user => {
87
+ const $users = get(users)
88
+ const index = $users.findIndex(x => x.id === user.id)
89
+ if (index === -1) {
90
+ users.set([...$users, user])
91
+ } else {
92
+ users.update(state => {
93
+ state[index] = user
94
+ return state.slice()
95
+ })
96
+ }
97
+ }
98
+
99
+ const removeUser = user => {
100
+ users.update(state => {
101
+ return state.filter(x => x.id !== user.id)
102
+ })
103
+ }
104
+
105
+ return {
106
+ users: {
107
+ ...users,
108
+ actions: {
109
+ updateUser,
110
+ removeUser,
111
+ },
112
+ },
113
+ selectedCellMap,
114
+ }
115
+ }
@@ -0,0 +1,45 @@
1
+ import { writable, get } from "svelte/store"
2
+
3
+ export const createStores = () => {
4
+ const validation = writable({})
5
+
6
+ const setError = (cellId, error) => {
7
+ if (!cellId) {
8
+ return
9
+ }
10
+ validation.update(state => ({
11
+ ...state,
12
+ [cellId]: error,
13
+ }))
14
+ }
15
+
16
+ return {
17
+ validation: {
18
+ ...validation,
19
+ actions: {
20
+ setError,
21
+ },
22
+ },
23
+ }
24
+ }
25
+
26
+ export const initialise = context => {
27
+ const { validation, previousFocusedRowId, columns, stickyColumn } = context
28
+
29
+ // Remove validation errors from previous focused row
30
+ previousFocusedRowId.subscribe(id => {
31
+ if (id) {
32
+ const $columns = get(columns)
33
+ const $stickyColumn = get(stickyColumn)
34
+ validation.update(state => {
35
+ $columns.forEach(column => {
36
+ state[`${id}-${column.name}`] = null
37
+ })
38
+ if ($stickyColumn) {
39
+ state[`${id}-${$stickyColumn.name}`] = null
40
+ }
41
+ return state
42
+ })
43
+ }
44
+ })
45
+ }
@@ -0,0 +1,141 @@
1
+ import { derived, get } from "svelte/store"
2
+ import {
3
+ MaxCellRenderHeight,
4
+ MaxCellRenderWidthOverflow,
5
+ ScrollBarSize,
6
+ } from "../lib/constants"
7
+
8
+ export const deriveStores = context => {
9
+ const {
10
+ rowHeight,
11
+ visibleColumns,
12
+ enrichedRows,
13
+ scrollTop,
14
+ scrollLeft,
15
+ width,
16
+ height,
17
+ } = context
18
+
19
+ // Derive visible rows
20
+ // Split into multiple stores containing primitives to optimise invalidation
21
+ // as much as possible
22
+ const scrolledRowCount = derived(
23
+ [scrollTop, rowHeight],
24
+ ([$scrollTop, $rowHeight]) => {
25
+ return Math.floor($scrollTop / $rowHeight)
26
+ },
27
+ 0
28
+ )
29
+ const visualRowCapacity = derived(
30
+ [height, rowHeight],
31
+ ([$height, $rowHeight]) => {
32
+ return Math.ceil($height / $rowHeight) + 1
33
+ },
34
+ 0
35
+ )
36
+ const renderedRows = derived(
37
+ [enrichedRows, scrolledRowCount, visualRowCapacity],
38
+ ([$enrichedRows, $scrolledRowCount, $visualRowCapacity]) => {
39
+ return $enrichedRows.slice(
40
+ $scrolledRowCount,
41
+ $scrolledRowCount + $visualRowCapacity
42
+ )
43
+ },
44
+ []
45
+ )
46
+
47
+ // Derive visible columns
48
+ const renderedColumns = derived(
49
+ [visibleColumns, scrollLeft, width],
50
+ ([$visibleColumns, $scrollLeft, $width]) => {
51
+ if (!$visibleColumns.length) {
52
+ return []
53
+ }
54
+ let startColIdx = 0
55
+ let rightEdge = $visibleColumns[0].width
56
+ while (
57
+ rightEdge < $scrollLeft &&
58
+ startColIdx < $visibleColumns.length - 1
59
+ ) {
60
+ startColIdx++
61
+ rightEdge += $visibleColumns[startColIdx].width
62
+ }
63
+ let endColIdx = startColIdx + 1
64
+ let leftEdge = rightEdge
65
+ while (
66
+ leftEdge < $width + $scrollLeft &&
67
+ endColIdx < $visibleColumns.length
68
+ ) {
69
+ leftEdge += $visibleColumns[endColIdx].width
70
+ endColIdx++
71
+ }
72
+ const nextRenderedColumns = $visibleColumns.slice(startColIdx, endColIdx)
73
+
74
+ // Cautiously shrink the number of rendered columns.
75
+ // This is to avoid rapidly shrinking and growing the visible column count
76
+ // which results in remounting cells
77
+ const currentCount = get(renderedColumns).length
78
+ if (currentCount === nextRenderedColumns.length + 1) {
79
+ return $visibleColumns.slice(startColIdx, endColIdx + 1)
80
+ } else {
81
+ return nextRenderedColumns
82
+ }
83
+ },
84
+ []
85
+ )
86
+
87
+ const hiddenColumnsWidth = derived(
88
+ [renderedColumns, visibleColumns],
89
+ ([$renderedColumns, $visibleColumns]) => {
90
+ const idx = $visibleColumns.findIndex(
91
+ col => col.name === $renderedColumns[0]?.name
92
+ )
93
+ let width = 0
94
+ if (idx > 0) {
95
+ for (let i = 0; i < idx; i++) {
96
+ width += $visibleColumns[i].width
97
+ }
98
+ }
99
+ return width
100
+ },
101
+ 0
102
+ )
103
+
104
+ // Determine the row index at which we should start vertically inverting cell
105
+ // dropdowns
106
+ const rowVerticalInversionIndex = derived(
107
+ [visualRowCapacity, rowHeight],
108
+ ([$visualRowCapacity, $rowHeight]) => {
109
+ return (
110
+ $visualRowCapacity - Math.ceil(MaxCellRenderHeight / $rowHeight) - 2
111
+ )
112
+ }
113
+ )
114
+
115
+ // Determine the column index at which we should start horizontally inverting
116
+ // cell dropdowns
117
+ const columnHorizontalInversionIndex = derived(
118
+ [renderedColumns, scrollLeft, width],
119
+ ([$renderedColumns, $scrollLeft, $width]) => {
120
+ const cutoff = $width + $scrollLeft - ScrollBarSize * 3
121
+ let inversionIdx = $renderedColumns.length
122
+ for (let i = $renderedColumns.length - 1; i >= 0; i--, inversionIdx--) {
123
+ const rightEdge = $renderedColumns[i].left + $renderedColumns[i].width
124
+ if (rightEdge + MaxCellRenderWidthOverflow < cutoff) {
125
+ break
126
+ }
127
+ }
128
+ return inversionIdx
129
+ }
130
+ )
131
+
132
+ return {
133
+ scrolledRowCount,
134
+ visualRowCapacity,
135
+ renderedRows,
136
+ renderedColumns,
137
+ hiddenColumnsWidth,
138
+ rowVerticalInversionIndex,
139
+ columnHorizontalInversionIndex,
140
+ }
141
+ }
@@ -1,2 +1,3 @@
1
1
  export { default as SplitPage } from "./SplitPage.svelte"
2
2
  export { default as TestimonialPage } from "./TestimonialPage.svelte"
3
+ export { Grid } from "./grid"
@@ -21,11 +21,11 @@ export default class DataFetch {
21
21
  this.API = null
22
22
 
23
23
  // Feature flags
24
- this.featureStore = writable({
24
+ this.features = {
25
25
  supportsSearch: false,
26
26
  supportsSort: false,
27
27
  supportsPagination: false,
28
- })
28
+ }
29
29
 
30
30
  // Config
31
31
  this.options = {
@@ -56,6 +56,7 @@ export default class DataFetch {
56
56
  pageNumber: 0,
57
57
  cursor: null,
58
58
  cursors: [],
59
+ resetKey: Math.random(),
59
60
  })
60
61
 
61
62
  // Merge options with their default values
@@ -81,17 +82,14 @@ export default class DataFetch {
81
82
  this.prevPage = this.prevPage.bind(this)
82
83
 
83
84
  // Derive certain properties to return
84
- this.derivedStore = derived(
85
- [this.store, this.featureStore],
86
- ([$store, $featureStore]) => {
87
- return {
88
- ...$store,
89
- ...$featureStore,
90
- hasNextPage: this.hasNextPage($store),
91
- hasPrevPage: this.hasPrevPage($store),
92
- }
85
+ this.derivedStore = derived(this.store, $store => {
86
+ return {
87
+ ...$store,
88
+ ...this.features,
89
+ hasNextPage: this.hasNextPage($store),
90
+ hasPrevPage: this.hasPrevPage($store),
93
91
  }
94
- )
92
+ })
95
93
 
96
94
  // Mark as loaded if we have no datasource
97
95
  if (!this.options.datasource) {
@@ -120,11 +118,11 @@ export default class DataFetch {
120
118
  // Fetch datasource definition and determine feature flags
121
119
  const definition = await this.getDefinition(datasource)
122
120
  const features = this.determineFeatureFlags(definition)
123
- this.featureStore.set({
121
+ this.features = {
124
122
  supportsSearch: !!features?.supportsSearch,
125
123
  supportsSort: !!features?.supportsSort,
126
124
  supportsPagination: paginate && !!features?.supportsPagination,
127
- })
125
+ }
128
126
 
129
127
  // Fetch and enrich schema
130
128
  let schema = this.getSchema(datasource, definition)
@@ -138,11 +136,17 @@ export default class DataFetch {
138
136
  this.options.sortOrder = "ascending"
139
137
  }
140
138
 
141
- // If no sort column, use the first field in the schema
139
+ // If no sort column, use the primary display and fallback to first column
142
140
  if (!this.options.sortColumn) {
143
- this.options.sortColumn = Object.keys(schema)[0]
141
+ let newSortColumn
142
+ if (definition?.primaryDisplay && schema[definition.primaryDisplay]) {
143
+ newSortColumn = definition.primaryDisplay
144
+ } else {
145
+ newSortColumn = Object.keys(schema)[0]
146
+ }
147
+ this.options.sortColumn = newSortColumn
144
148
  }
145
- const { sortColumn } = this.options
149
+ const { sortOrder, sortColumn } = this.options
146
150
 
147
151
  // Determine what sort type to use
148
152
  let sortType = "string"
@@ -167,6 +171,8 @@ export default class DataFetch {
167
171
  loading: true,
168
172
  cursors: [],
169
173
  cursor: null,
174
+ sortOrder,
175
+ sortColumn,
170
176
  }))
171
177
 
172
178
  // Actually fetch data
@@ -180,6 +186,7 @@ export default class DataFetch {
180
186
  info: page.info,
181
187
  cursors: paginate && page.hasNextPage ? [null, page.cursor] : [null],
182
188
  error: page.error,
189
+ resetKey: Math.random(),
183
190
  }))
184
191
  }
185
192
 
@@ -189,23 +196,22 @@ export default class DataFetch {
189
196
  async getPage() {
190
197
  const { sortColumn, sortOrder, sortType, limit } = this.options
191
198
  const { query } = get(this.store)
192
- const features = get(this.featureStore)
193
199
 
194
200
  // Get the actual data
195
201
  let { rows, info, hasNextPage, cursor, error } = await this.getData()
196
202
 
197
203
  // If we don't support searching, do a client search
198
- if (!features.supportsSearch) {
204
+ if (!this.features.supportsSearch) {
199
205
  rows = runLuceneQuery(rows, query)
200
206
  }
201
207
 
202
208
  // If we don't support sorting, do a client-side sort
203
- if (!features.supportsSort) {
209
+ if (!this.features.supportsSort) {
204
210
  rows = luceneSort(rows, sortColumn, sortOrder, sortType)
205
211
  }
206
212
 
207
213
  // If we don't support pagination, do a client-side limit
208
- if (!features.supportsPagination) {
214
+ if (!this.features.supportsPagination) {
209
215
  rows = luceneLimit(rows, limit)
210
216
  }
211
217
 
@@ -31,7 +31,7 @@ export default class QueryFetch extends DataFetch {
31
31
 
32
32
  async getData() {
33
33
  const { datasource, limit, paginate } = this.options
34
- const { supportsPagination } = get(this.featureStore)
34
+ const { supportsPagination } = this.features
35
35
  const { cursor, definition } = get(this.store)
36
36
  const type = definition?.fields?.pagination?.type
37
37
 
@@ -13,6 +13,9 @@
13
13
  --spectrum-global-color-gray-800: hsl(var(--hue), var(--sat), 85%);
14
14
  --spectrum-global-color-gray-900: hsl(var(--hue), var(--sat), 95%);
15
15
 
16
+ /* Custom additions */
16
17
  --modal-background: var(--spectrum-global-color-gray-50);
18
+ --drop-shadow: rgba(0, 0, 0, 0.25) !important;
19
+ --spectrum-global-color-blue-100: rgba(35, 40, 50) !important;
17
20
  }
18
21
 
@@ -46,5 +46,8 @@
46
46
  --spectrum-alias-highlight-active: rgba(169, 177, 193, 0.1);
47
47
  --spectrum-alias-background-color-hover-overlay: rgba(169, 177, 193, 0.1);
48
48
 
49
+ /* Custom additions */
49
50
  --modal-background: var(--spectrum-global-color-gray-50);
51
+ --drop-shadow: rgba(0, 0, 0, 0.15) !important;
52
+ --spectrum-global-color-blue-100: rgb(56, 65, 84) !important;
50
53
  }
@@ -86,3 +86,23 @@ export const throttle = (callback, minDelay = 1000) => {
86
86
  }
87
87
  return invoke
88
88
  }
89
+
90
+ /**
91
+ * Utility to debounce DOM activities using requestAnimationFrame
92
+ * @param callback the function to run
93
+ * @returns {Function}
94
+ */
95
+ export const domDebounce = callback => {
96
+ let active = false
97
+ let lastParams
98
+ return (...params) => {
99
+ lastParams = params
100
+ if (!active) {
101
+ active = true
102
+ requestAnimationFrame(() => {
103
+ callback(...lastParams)
104
+ active = false
105
+ })
106
+ }
107
+ }
108
+ }