@budibase/frontend-core 2.5.9 → 2.5.10-alpha.0

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 (68) hide show
  1. package/package.json +6 -4
  2. package/src/api/other.js +0 -9
  3. package/src/components/grid/cells/AttachmentCell.svelte +154 -0
  4. package/src/components/grid/cells/BooleanCell.svelte +44 -0
  5. package/src/components/grid/cells/DataCell.svelte +91 -0
  6. package/src/components/grid/cells/DateCell.svelte +77 -0
  7. package/src/components/grid/cells/FormulaCell.svelte +5 -0
  8. package/src/components/grid/cells/GridCell.svelte +165 -0
  9. package/src/components/grid/cells/GutterCell.svelte +127 -0
  10. package/src/components/grid/cells/HeaderCell.svelte +248 -0
  11. package/src/components/grid/cells/JSONCell.svelte +36 -0
  12. package/src/components/grid/cells/LongFormCell.svelte +122 -0
  13. package/src/components/grid/cells/MultiSelectCell.svelte +7 -0
  14. package/src/components/grid/cells/NumberCell.svelte +14 -0
  15. package/src/components/grid/cells/OptionsCell.svelte +249 -0
  16. package/src/components/grid/cells/RelationshipCell.svelte +518 -0
  17. package/src/components/grid/cells/TextCell.svelte +111 -0
  18. package/src/components/grid/controls/AddColumnButton.svelte +16 -0
  19. package/src/components/grid/controls/AddRowButton.svelte +18 -0
  20. package/src/components/grid/controls/BetaButton.svelte +46 -0
  21. package/src/components/grid/controls/ColumnWidthButton.svelte +91 -0
  22. package/src/components/grid/controls/DeleteButton.svelte +66 -0
  23. package/src/components/grid/controls/HideColumnsButton.svelte +95 -0
  24. package/src/components/grid/controls/RowHeightButton.svelte +70 -0
  25. package/src/components/grid/controls/SortButton.svelte +129 -0
  26. package/src/components/grid/index.js +1 -0
  27. package/src/components/grid/layout/Avatar.svelte +24 -0
  28. package/src/components/grid/layout/Grid.svelte +254 -0
  29. package/src/components/grid/layout/GridBody.svelte +73 -0
  30. package/src/components/grid/layout/GridRow.svelte +59 -0
  31. package/src/components/grid/layout/GridScrollWrapper.svelte +81 -0
  32. package/src/components/grid/layout/HeaderRow.svelte +62 -0
  33. package/src/components/grid/layout/KeyboardShortcut.svelte +53 -0
  34. package/src/components/grid/layout/NewRow.svelte +334 -0
  35. package/src/components/grid/layout/RowCount.svelte +9 -0
  36. package/src/components/grid/layout/StickyColumn.svelte +176 -0
  37. package/src/components/grid/layout/UserAvatars.svelte +20 -0
  38. package/src/components/grid/lib/constants.js +14 -0
  39. package/src/components/grid/lib/events.js +29 -0
  40. package/src/components/grid/lib/renderers.js +29 -0
  41. package/src/components/grid/lib/utils.js +26 -0
  42. package/src/components/grid/lib/websocket.js +63 -0
  43. package/src/components/grid/overlays/KeyboardManager.svelte +232 -0
  44. package/src/components/grid/overlays/MenuOverlay.svelte +96 -0
  45. package/src/components/grid/overlays/ReorderOverlay.svelte +63 -0
  46. package/src/components/grid/overlays/ResizeOverlay.svelte +70 -0
  47. package/src/components/grid/overlays/ScrollOverlay.svelte +121 -0
  48. package/src/components/grid/stores/bounds.js +16 -0
  49. package/src/components/grid/stores/clipboard.js +34 -0
  50. package/src/components/grid/stores/columns.js +206 -0
  51. package/src/components/grid/stores/index.js +48 -0
  52. package/src/components/grid/stores/menu.js +49 -0
  53. package/src/components/grid/stores/pagination.js +24 -0
  54. package/src/components/grid/stores/reorder.js +155 -0
  55. package/src/components/grid/stores/resize.js +124 -0
  56. package/src/components/grid/stores/rows.js +467 -0
  57. package/src/components/grid/stores/scroll.js +201 -0
  58. package/src/components/grid/stores/ui.js +159 -0
  59. package/src/components/grid/stores/users.js +115 -0
  60. package/src/components/grid/stores/validation.js +45 -0
  61. package/src/components/grid/stores/viewport.js +145 -0
  62. package/src/components/index.js +1 -0
  63. package/src/constants.js +1 -0
  64. package/src/fetch/DataFetch.js +27 -21
  65. package/src/fetch/QueryFetch.js +1 -1
  66. package/src/themes/midnight.css +3 -0
  67. package/src/themes/nord.css +3 -0
  68. package/src/utils/utils.js +20 -0
@@ -0,0 +1,248 @@
1
+ <script>
2
+ import { getContext } from "svelte"
3
+ import GridCell from "./GridCell.svelte"
4
+ import { Icon, Popover, Menu, MenuItem } from "@budibase/bbui"
5
+ import { getColumnIcon } from "../lib/utils"
6
+
7
+ export let column
8
+ export let idx
9
+ export let orderable = true
10
+
11
+ const {
12
+ reorder,
13
+ isReordering,
14
+ isResizing,
15
+ rand,
16
+ sort,
17
+ renderedColumns,
18
+ dispatch,
19
+ config,
20
+ ui,
21
+ columns,
22
+ } = getContext("grid")
23
+
24
+ const bannedDisplayColumnTypes = [
25
+ "link",
26
+ "array",
27
+ "attachment",
28
+ "boolean",
29
+ "formula",
30
+ "json",
31
+ ]
32
+
33
+ let anchor
34
+ let open = false
35
+ let timeout
36
+
37
+ $: sortedBy = column.name === $sort.column
38
+ $: canMoveLeft = orderable && idx > 0
39
+ $: canMoveRight = orderable && idx < $renderedColumns.length - 1
40
+ $: ascendingLabel = column.schema?.type === "number" ? "low-high" : "A-Z"
41
+ $: descendingLabel = column.schema?.type === "number" ? "high-low" : "Z-A"
42
+
43
+ const editColumn = () => {
44
+ dispatch("edit-column", column.schema)
45
+ open = false
46
+ }
47
+
48
+ const onMouseDown = e => {
49
+ if (e.button === 0 && orderable) {
50
+ timeout = setTimeout(() => {
51
+ reorder.actions.startReordering(column.name, e)
52
+ }, 200)
53
+ }
54
+ }
55
+
56
+ const onMouseUp = e => {
57
+ if (e.button === 0 && orderable) {
58
+ clearTimeout(timeout)
59
+ }
60
+ }
61
+
62
+ const onContextMenu = e => {
63
+ e.preventDefault()
64
+ ui.actions.blur()
65
+ open = !open
66
+ }
67
+
68
+ const sortAscending = () => {
69
+ sort.set({
70
+ column: column.name,
71
+ order: "ascending",
72
+ })
73
+ open = false
74
+ }
75
+
76
+ const sortDescending = () => {
77
+ sort.set({
78
+ column: column.name,
79
+ order: "descending",
80
+ })
81
+ open = false
82
+ }
83
+
84
+ const moveLeft = () => {
85
+ reorder.actions.moveColumnLeft(column.name)
86
+ open = false
87
+ }
88
+
89
+ const moveRight = () => {
90
+ reorder.actions.moveColumnRight(column.name)
91
+ open = false
92
+ }
93
+
94
+ const makeDisplayColumn = () => {
95
+ columns.actions.changePrimaryDisplay(column.name)
96
+ open = false
97
+ }
98
+
99
+ const hideColumn = () => {
100
+ columns.update(state => {
101
+ const index = state.findIndex(col => col.name === column.name)
102
+ state[index].visible = false
103
+ return state.slice()
104
+ })
105
+ columns.actions.saveChanges()
106
+ open = false
107
+ }
108
+ </script>
109
+
110
+ <div
111
+ class="header-cell"
112
+ class:open
113
+ style="flex: 0 0 {column.width}px;"
114
+ bind:this={anchor}
115
+ class:disabled={$isReordering || $isResizing}
116
+ class:sticky={idx === "sticky"}
117
+ >
118
+ <GridCell
119
+ on:mousedown={onMouseDown}
120
+ on:mouseup={onMouseUp}
121
+ on:contextmenu={onContextMenu}
122
+ width={column.width}
123
+ left={column.left}
124
+ defaultHeight
125
+ center
126
+ >
127
+ <Icon
128
+ size="S"
129
+ name={getColumnIcon(column)}
130
+ color={`var(--spectrum-global-color-gray-600)`}
131
+ />
132
+ <div class="name">
133
+ {column.label}
134
+ </div>
135
+ {#if sortedBy}
136
+ <div class="sort-indicator">
137
+ <Icon
138
+ size="S"
139
+ name={$sort.order === "descending" ? "SortOrderDown" : "SortOrderUp"}
140
+ color="var(--spectrum-global-color-gray-600)"
141
+ />
142
+ </div>
143
+ {/if}
144
+ <div class="more" on:click={() => (open = true)}>
145
+ <Icon
146
+ size="S"
147
+ name="MoreVertical"
148
+ color="var(--spectrum-global-color-gray-600)"
149
+ />
150
+ </div>
151
+ </GridCell>
152
+ </div>
153
+
154
+ <Popover
155
+ bind:open
156
+ {anchor}
157
+ align="right"
158
+ offset={0}
159
+ popoverTarget={document.getElementById(`grid-${rand}`)}
160
+ animate={false}
161
+ >
162
+ <Menu>
163
+ <MenuItem
164
+ icon="Edit"
165
+ on:click={editColumn}
166
+ disabled={!$config.allowEditColumns || column.schema.disabled}
167
+ >
168
+ Edit column
169
+ </MenuItem>
170
+ <MenuItem
171
+ icon="Label"
172
+ on:click={makeDisplayColumn}
173
+ disabled={idx === "sticky" ||
174
+ !$config.allowEditColumns ||
175
+ bannedDisplayColumnTypes.includes(column.schema.type)}
176
+ >
177
+ Use as display column
178
+ </MenuItem>
179
+ <MenuItem
180
+ icon="SortOrderUp"
181
+ on:click={sortAscending}
182
+ disabled={column.name === $sort.column && $sort.order === "ascending"}
183
+ >
184
+ Sort {ascendingLabel}
185
+ </MenuItem>
186
+ <MenuItem
187
+ icon="SortOrderDown"
188
+ on:click={sortDescending}
189
+ disabled={column.name === $sort.column && $sort.order === "descending"}
190
+ >
191
+ Sort {descendingLabel}
192
+ </MenuItem>
193
+ <MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
194
+ Move left
195
+ </MenuItem>
196
+ <MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}>
197
+ Move right
198
+ </MenuItem>
199
+ <MenuItem icon="VisibilityOff" on:click={hideColumn}>Hide column</MenuItem>
200
+ </Menu>
201
+ </Popover>
202
+
203
+ <style>
204
+ .header-cell {
205
+ display: flex;
206
+ }
207
+ .header-cell:not(.sticky):hover,
208
+ .header-cell:not(.sticky) :global(.cell:hover) {
209
+ cursor: grab;
210
+ }
211
+ .header-cell.disabled {
212
+ pointer-events: none;
213
+ }
214
+ .header-cell :global(.cell) {
215
+ padding: 0 var(--cell-padding);
216
+ gap: calc(2 * var(--cell-spacing));
217
+ background: var(--spectrum-global-color-gray-100);
218
+ }
219
+
220
+ .name {
221
+ flex: 1 1 auto;
222
+ width: 0;
223
+ white-space: nowrap;
224
+ text-overflow: ellipsis;
225
+ overflow: hidden;
226
+ }
227
+
228
+ .more {
229
+ display: none;
230
+ padding: 4px;
231
+ margin: 0 -4px;
232
+ }
233
+ .header-cell.open .more,
234
+ .header-cell:hover .more {
235
+ display: block;
236
+ }
237
+ .more:hover {
238
+ cursor: pointer;
239
+ }
240
+ .more:hover :global(.spectrum-Icon) {
241
+ color: var(--spectrum-global-color-gray-800) !important;
242
+ }
243
+
244
+ .header-cell.open .sort-indicator,
245
+ .header-cell:hover .sort-indicator {
246
+ display: none;
247
+ }
248
+ </style>
@@ -0,0 +1,36 @@
1
+ <script>
2
+ import LongFormCell from "./LongFormCell.svelte"
3
+
4
+ export let onChange
5
+ export let value
6
+ export let api
7
+
8
+ $: stringified = getStringifiedValue(value)
9
+
10
+ const getStringifiedValue = value => {
11
+ if (!value) {
12
+ return value
13
+ }
14
+ try {
15
+ return JSON.stringify(value, null, 2)
16
+ } catch (error) {
17
+ return null
18
+ }
19
+ }
20
+
21
+ const parse = value => {
22
+ const trimmed = value?.trim()
23
+ if (!trimmed) {
24
+ onChange(null)
25
+ return
26
+ }
27
+ try {
28
+ const parsed = JSON.parse(trimmed)
29
+ onChange(parsed)
30
+ } catch (error) {
31
+ // Swallow
32
+ }
33
+ }
34
+ </script>
35
+
36
+ <LongFormCell {...$$props} bind:api value={stringified} onChange={parse} />
@@ -0,0 +1,122 @@
1
+ <script>
2
+ import { onMount, tick } from "svelte"
3
+ import { clickOutside } 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
+ export let invertX = false
11
+ export let invertY = false
12
+
13
+ let textarea
14
+ let isOpen = false
15
+
16
+ $: editable = focused && !readonly
17
+ $: {
18
+ if (!focused) {
19
+ isOpen = false
20
+ }
21
+ }
22
+
23
+ const handleChange = e => {
24
+ onChange(e.target.value)
25
+ }
26
+
27
+ const onKeyDown = () => {
28
+ return isOpen
29
+ }
30
+
31
+ const open = async () => {
32
+ isOpen = true
33
+ await tick()
34
+ textarea.focus()
35
+ if (value?.length > 100) {
36
+ textarea.setSelectionRange(0, 0)
37
+ }
38
+ }
39
+
40
+ const close = () => {
41
+ textarea?.blur()
42
+ isOpen = false
43
+ }
44
+
45
+ onMount(() => {
46
+ api = {
47
+ focus: () => open(),
48
+ blur: () => close(),
49
+ isActive: () => isOpen,
50
+ onKeyDown,
51
+ }
52
+ })
53
+ </script>
54
+
55
+ {#if isOpen}
56
+ <textarea
57
+ class:invertX
58
+ class:invertY
59
+ bind:this={textarea}
60
+ value={value || ""}
61
+ on:change={handleChange}
62
+ on:wheel|stopPropagation
63
+ spellcheck="false"
64
+ use:clickOutside={close}
65
+ />
66
+ {:else}
67
+ <div class="long-form-cell" on:click={editable ? open : null} class:editable>
68
+ <div class="value">
69
+ {value || ""}
70
+ </div>
71
+ </div>
72
+ {/if}
73
+
74
+ <style>
75
+ .long-form-cell {
76
+ flex: 1 1 auto;
77
+ padding: var(--cell-padding);
78
+ align-self: stretch;
79
+ display: flex;
80
+ align-items: flex-start;
81
+ overflow: hidden;
82
+ }
83
+ .long-form-cell.editable:hover {
84
+ cursor: text;
85
+ }
86
+ .value {
87
+ display: -webkit-box;
88
+ -webkit-line-clamp: var(--content-lines);
89
+ -webkit-box-orient: vertical;
90
+ overflow: hidden;
91
+ line-height: 20px;
92
+ }
93
+ textarea {
94
+ padding: var(--cell-padding);
95
+ margin: 0;
96
+ border: 2px solid var(--cell-color);
97
+ background: var(--cell-background);
98
+ font-size: var(--cell-font-size);
99
+ font-family: var(--font-sans);
100
+ color: inherit;
101
+ position: absolute;
102
+ top: 0;
103
+ left: 0;
104
+ width: calc(100% + var(--max-cell-render-width-overflow));
105
+ height: var(--max-cell-render-height);
106
+ z-index: 1;
107
+ border-radius: 2px;
108
+ resize: none;
109
+ line-height: 20px;
110
+ }
111
+ textarea.invertX {
112
+ left: auto;
113
+ right: 0;
114
+ }
115
+ textarea.invertY {
116
+ transform: translateY(-100%);
117
+ top: calc(100% + 1px);
118
+ }
119
+ textarea:focus {
120
+ outline: none;
121
+ }
122
+ </style>
@@ -0,0 +1,7 @@
1
+ <script>
2
+ import OptionsCell from "./OptionsCell.svelte"
3
+
4
+ export let api
5
+ </script>
6
+
7
+ <OptionsCell bind:api {...$$props} multi />
@@ -0,0 +1,14 @@
1
+ <script>
2
+ import TextCell from "./TextCell.svelte"
3
+
4
+ export let api
5
+ export let onChange
6
+
7
+ const numberOnChange = value => {
8
+ const float = parseFloat(value)
9
+ const newValue = isNaN(float) ? null : float
10
+ onChange(newValue)
11
+ }
12
+ </script>
13
+
14
+ <TextCell {...$$props} onChange={numberOnChange} bind:api type="number" />
@@ -0,0 +1,249 @@
1
+ <script>
2
+ import { Icon, clickOutside } from "@budibase/bbui"
3
+ import { getColor } from "../lib/utils"
4
+ import { onMount } from "svelte"
5
+
6
+ export let value
7
+ export let schema
8
+ export let onChange
9
+ export let focused = false
10
+ export let multi = false
11
+ export let readonly = false
12
+ export let api
13
+ export let invertX = false
14
+ export let invertY = false
15
+ export let contentLines = 1
16
+
17
+ let isOpen = false
18
+ let focusedOptionIdx = null
19
+
20
+ $: options = schema?.constraints?.inclusion || []
21
+ $: editable = focused && !readonly
22
+ $: values = Array.isArray(value) ? value : [value].filter(x => x != null)
23
+ $: {
24
+ // Close when deselected
25
+ if (!focused) {
26
+ close()
27
+ }
28
+ }
29
+
30
+ const open = () => {
31
+ isOpen = true
32
+ focusedOptionIdx = 0
33
+ }
34
+
35
+ const close = () => {
36
+ isOpen = false
37
+ }
38
+
39
+ const getOptionColor = value => {
40
+ const index = value ? options.indexOf(value) : null
41
+ return getColor(index)
42
+ }
43
+
44
+ const toggleOption = option => {
45
+ if (!multi) {
46
+ onChange(option === value ? null : option)
47
+ close()
48
+ } else {
49
+ const sanitizedValues = values.filter(x => options.includes(x))
50
+ if (sanitizedValues.includes(option)) {
51
+ onChange(sanitizedValues.filter(x => x !== option))
52
+ } else {
53
+ onChange([...sanitizedValues, option])
54
+ }
55
+ }
56
+ }
57
+
58
+ const onKeyDown = e => {
59
+ if (!isOpen) {
60
+ return false
61
+ }
62
+ e.preventDefault()
63
+ if (e.key === "ArrowDown") {
64
+ focusedOptionIdx = Math.min(focusedOptionIdx + 1, options.length - 1)
65
+ } else if (e.key === "ArrowUp") {
66
+ focusedOptionIdx = Math.max(focusedOptionIdx - 1, 0)
67
+ } else if (e.key === "Enter") {
68
+ toggleOption(options[focusedOptionIdx])
69
+ }
70
+ return true
71
+ }
72
+
73
+ onMount(() => {
74
+ api = {
75
+ focus: open,
76
+ blur: close,
77
+ isActive: () => isOpen,
78
+ onKeyDown,
79
+ }
80
+ })
81
+ </script>
82
+
83
+ <div
84
+ class="container"
85
+ class:multi
86
+ class:editable
87
+ class:open
88
+ on:click|self={editable ? open : null}
89
+ >
90
+ <div
91
+ class="values"
92
+ class:wrap={contentLines > 1}
93
+ on:click={editable ? open : null}
94
+ >
95
+ {#each values as val}
96
+ {@const color = getOptionColor(val)}
97
+ {#if color}
98
+ <div class="badge text" style="--color: {color}">
99
+ <span>
100
+ {val}
101
+ </span>
102
+ </div>
103
+ {:else}
104
+ <div class="text">
105
+ {val || ""}
106
+ </div>
107
+ {/if}
108
+ {/each}
109
+ </div>
110
+ {#if editable}
111
+ <div class="arrow" on:click={open}>
112
+ <Icon name="ChevronDown" />
113
+ </div>
114
+ {/if}
115
+ {#if isOpen}
116
+ <div
117
+ class="options"
118
+ class:invertX
119
+ class:invertY
120
+ on:wheel={e => e.stopPropagation()}
121
+ use:clickOutside={close}
122
+ >
123
+ {#each options as option, idx}
124
+ {@const color = getOptionColor(option)}
125
+ <div
126
+ class="option"
127
+ on:click={() => toggleOption(option)}
128
+ class:focused={focusedOptionIdx === idx}
129
+ on:mouseenter={() => (focusedOptionIdx = idx)}
130
+ >
131
+ <div class="badge text" style="--color: {color}">
132
+ {option}
133
+ </div>
134
+ {#if values.includes(option)}
135
+ <Icon
136
+ name="Checkmark"
137
+ color="var(--spectrum-global-color-blue-400)"
138
+ />
139
+ {/if}
140
+ </div>
141
+ {/each}
142
+ </div>
143
+ {/if}
144
+ </div>
145
+
146
+ <style>
147
+ .container {
148
+ display: flex;
149
+ flex-direction: row;
150
+ justify-content: space-between;
151
+ align-items: flex-start;
152
+ align-self: stretch;
153
+ flex: 1 1 auto;
154
+ overflow: hidden;
155
+ }
156
+ .container.editable:hover {
157
+ cursor: pointer;
158
+ }
159
+ .values {
160
+ display: flex;
161
+ flex-direction: row;
162
+ justify-content: flex-start;
163
+ align-items: flex-start;
164
+ flex: 1 1 auto;
165
+ grid-column-gap: var(--cell-spacing);
166
+ grid-row-gap: var(--cell-padding);
167
+ overflow: hidden;
168
+ padding: var(--cell-padding);
169
+ flex-wrap: nowrap;
170
+ }
171
+ .values.wrap {
172
+ flex-wrap: wrap;
173
+ }
174
+ .text {
175
+ overflow: hidden;
176
+ text-overflow: ellipsis;
177
+ white-space: nowrap;
178
+ }
179
+ .multi .text {
180
+ flex: 0 0 auto;
181
+ }
182
+ .badge {
183
+ padding: 0 var(--cell-padding);
184
+ background: var(--color);
185
+ border-radius: var(--cell-padding);
186
+ user-select: none;
187
+ display: flex;
188
+ align-items: center;
189
+ gap: var(--cell-spacing);
190
+ height: 20px;
191
+ max-width: 100%;
192
+ }
193
+ .badge span {
194
+ text-overflow: ellipsis;
195
+ overflow: hidden;
196
+ white-space: nowrap;
197
+ }
198
+ .arrow {
199
+ position: absolute;
200
+ right: 0;
201
+ top: 0;
202
+ height: 100%;
203
+ bottom: 2px;
204
+ padding: 0 6px 0 16px;
205
+ display: grid;
206
+ place-items: center;
207
+ background: linear-gradient(
208
+ to right,
209
+ transparent 0%,
210
+ var(--cell-background) 40%
211
+ );
212
+ }
213
+ .options {
214
+ min-width: calc(100% + 2px);
215
+ position: absolute;
216
+ top: 100%;
217
+ left: -1px;
218
+ display: flex;
219
+ flex-direction: column;
220
+ justify-content: flex-start;
221
+ align-items: stretch;
222
+ max-height: var(--max-cell-render-height);
223
+ overflow-y: auto;
224
+ border: var(--cell-border);
225
+ box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
226
+ }
227
+ .options.invertX {
228
+ left: auto;
229
+ right: 0;
230
+ }
231
+ .options.invertY {
232
+ transform: translateY(-100%);
233
+ top: 0;
234
+ }
235
+ .option {
236
+ flex: 0 0 var(--default-row-height);
237
+ padding: 0 var(--cell-padding);
238
+ display: flex;
239
+ flex-direction: row;
240
+ justify-content: space-between;
241
+ align-items: center;
242
+ gap: var(--cell-spacing);
243
+ background-color: var(--background);
244
+ }
245
+ .option:hover,
246
+ .option.focused {
247
+ background-color: var(--spectrum-global-color-gray-200);
248
+ }
249
+ </style>