@budibase/frontend-core 2.7.35 → 2.7.36-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.
Files changed (46) hide show
  1. package/package.json +4 -4
  2. package/src/api/analytics.js +2 -2
  3. package/src/api/datasources.js +15 -1
  4. package/src/api/index.js +16 -2
  5. package/src/api/rows.js +3 -1
  6. package/src/components/grid/cells/AttachmentCell.svelte +3 -1
  7. package/src/components/grid/cells/GridCell.svelte +1 -1
  8. package/src/components/grid/cells/GutterCell.svelte +9 -14
  9. package/src/components/grid/cells/HeaderCell.svelte +7 -5
  10. package/src/components/grid/cells/LongFormCell.svelte +1 -1
  11. package/src/components/grid/cells/OptionsCell.svelte +4 -5
  12. package/src/components/grid/cells/RelationshipCell.svelte +22 -11
  13. package/src/components/grid/controls/AddColumnButton.svelte +1 -1
  14. package/src/components/grid/controls/BulkDeleteHandler.svelte +2 -8
  15. package/src/components/grid/controls/HideColumnsButton.svelte +9 -4
  16. package/src/components/grid/controls/SizeButton.svelte +135 -0
  17. package/src/components/grid/controls/SortButton.svelte +3 -4
  18. package/src/components/grid/layout/Grid.svelte +66 -59
  19. package/src/components/grid/layout/GridBody.svelte +3 -2
  20. package/src/components/grid/layout/GridRow.svelte +3 -2
  21. package/src/components/grid/layout/GridScrollWrapper.svelte +6 -0
  22. package/src/components/grid/layout/HeaderRow.svelte +3 -3
  23. package/src/components/grid/layout/NewRow.svelte +37 -5
  24. package/src/components/grid/layout/StickyColumn.svelte +10 -8
  25. package/src/components/grid/lib/constants.js +4 -3
  26. package/src/components/grid/lib/websocket.js +3 -2
  27. package/src/components/grid/overlays/KeyboardManager.svelte +6 -0
  28. package/src/components/grid/overlays/MenuOverlay.svelte +3 -1
  29. package/src/components/grid/overlays/ReorderOverlay.svelte +1 -1
  30. package/src/components/grid/overlays/ResizeOverlay.svelte +1 -1
  31. package/src/components/grid/overlays/ScrollOverlay.svelte +25 -2
  32. package/src/components/grid/stores/columns.js +37 -6
  33. package/src/components/grid/stores/config.js +27 -0
  34. package/src/components/grid/stores/filter.js +19 -0
  35. package/src/components/grid/stores/index.js +6 -0
  36. package/src/components/grid/stores/menu.js +15 -9
  37. package/src/components/grid/stores/rows.js +18 -16
  38. package/src/components/grid/stores/scroll.js +5 -7
  39. package/src/components/grid/stores/sort.js +27 -0
  40. package/src/components/grid/stores/ui.js +19 -4
  41. package/src/components/grid/stores/viewport.js +17 -6
  42. package/src/fetch/DataFetch.js +4 -2
  43. package/src/utils/index.js +1 -0
  44. package/src/utils/memo.js +43 -0
  45. package/src/components/grid/controls/ColumnWidthButton.svelte +0 -92
  46. package/src/components/grid/controls/RowHeightButton.svelte +0 -71
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "2.7.35",
3
+ "version": "2.7.36-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": "2.7.35",
10
- "@budibase/shared-core": "2.7.35",
9
+ "@budibase/bbui": "2.7.36-alpha.1",
10
+ "@budibase/shared-core": "2.7.36-alpha.1",
11
11
  "dayjs": "^1.11.7",
12
12
  "lodash": "^4.17.21",
13
13
  "socket.io-client": "^4.6.1",
14
14
  "svelte": "^3.46.2"
15
15
  },
16
- "gitHead": "6db1d089ee2d2df8caf513cf2cb4b22f167b91f1"
16
+ "gitHead": "8d3bbb718b4e3dea5e1da2dfa30fac231ef4fe22"
17
17
  }
@@ -7,11 +7,11 @@ export const buildAnalyticsEndpoints = API => ({
7
7
  url: "/api/bbtel",
8
8
  })
9
9
  },
10
- analyticsPing: async ({ source }) => {
10
+ analyticsPing: async ({ source, embedded }) => {
11
11
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
12
12
  return await API.post({
13
13
  url: "/api/bbtel/ping",
14
- body: { source, timezone },
14
+ body: { source, timezone, embedded },
15
15
  })
16
16
  },
17
17
  })
@@ -26,13 +26,16 @@ export const buildDatasourceEndpoints = API => ({
26
26
  * Creates a datasource
27
27
  * @param datasource the datasource to create
28
28
  * @param fetchSchema whether to fetch the schema or not
29
+ * @param tablesFilter a list of tables to actually fetch rather than simply
30
+ * all that are accessible.
29
31
  */
30
- createDatasource: async ({ datasource, fetchSchema }) => {
32
+ createDatasource: async ({ datasource, fetchSchema, tablesFilter }) => {
31
33
  return await API.post({
32
34
  url: "/api/datasources",
33
35
  body: {
34
36
  datasource,
35
37
  fetchSchema,
38
+ tablesFilter,
36
39
  },
37
40
  })
38
41
  },
@@ -69,4 +72,15 @@ export const buildDatasourceEndpoints = API => ({
69
72
  body: { datasource },
70
73
  })
71
74
  },
75
+
76
+ /**
77
+ * Fetch table names available within the datasource, for filtering out undesired tables
78
+ * @param datasource the datasource configuration to use for fetching tables
79
+ */
80
+ fetchInfoForDatasource: async datasource => {
81
+ return await API.post({
82
+ url: `/api/datasources/info`,
83
+ body: { datasource },
84
+ })
85
+ },
72
86
  })
package/src/api/index.js CHANGED
@@ -75,7 +75,11 @@ export const createAPIClient = config => {
75
75
  let cache = {}
76
76
 
77
77
  // Generates an error object from an API response
78
- const makeErrorFromResponse = async (response, method) => {
78
+ const makeErrorFromResponse = async (
79
+ response,
80
+ method,
81
+ suppressErrors = false
82
+ ) => {
79
83
  // Try to read a message from the error
80
84
  let message = response.statusText
81
85
  let json = null
@@ -96,6 +100,7 @@ export const createAPIClient = config => {
96
100
  url: response.url,
97
101
  method,
98
102
  handled: true,
103
+ suppressErrors,
99
104
  }
100
105
  }
101
106
 
@@ -119,6 +124,7 @@ export const createAPIClient = config => {
119
124
  json = true,
120
125
  external = false,
121
126
  parseResponse,
127
+ suppressErrors = false,
122
128
  }) => {
123
129
  // Ensure we don't do JSON processing if sending a GET request
124
130
  json = json && method !== "GET"
@@ -174,7 +180,7 @@ export const createAPIClient = config => {
174
180
  }
175
181
  } else {
176
182
  delete cache[url]
177
- throw await makeErrorFromResponse(response, method)
183
+ throw await makeErrorFromResponse(response, method, suppressErrors)
178
184
  }
179
185
  }
180
186
 
@@ -228,6 +234,14 @@ export const createAPIClient = config => {
228
234
  invalidateCache: () => {
229
235
  cache = {}
230
236
  },
237
+
238
+ // Generic utility to extract the current app ID. Assumes that any client
239
+ // that exists in an app context will be attaching our app ID header.
240
+ getAppID: () => {
241
+ let headers = {}
242
+ config?.attachHeaders(headers)
243
+ return headers?.["x-budibase-app-id"]
244
+ },
231
245
  }
232
246
 
233
247
  // Attach all endpoints
package/src/api/rows.js CHANGED
@@ -16,14 +16,16 @@ export const buildRowEndpoints = API => ({
16
16
  /**
17
17
  * Creates or updates a row in a table.
18
18
  * @param row the row to save
19
+ * @param suppressErrors whether or not to suppress error notifications
19
20
  */
20
- saveRow: async row => {
21
+ saveRow: async (row, suppressErrors = false) => {
21
22
  if (!row?.tableId) {
22
23
  return
23
24
  }
24
25
  return await API.post({
25
26
  url: `/api/${row.tableId}/rows`,
26
27
  body: row,
28
+ suppressErrors,
27
29
  })
28
30
  },
29
31
 
@@ -138,10 +138,12 @@
138
138
  top: 100%;
139
139
  left: 0;
140
140
  width: 320px;
141
- background: var(--background);
141
+ background: var(--grid-background-alt);
142
142
  border: var(--cell-border);
143
143
  padding: var(--cell-padding);
144
144
  box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
145
+ border-bottom-left-radius: 2px;
146
+ border-bottom-right-radius: 2px;
145
147
  }
146
148
  .dropzone.invertX {
147
149
  left: auto;
@@ -132,7 +132,7 @@
132
132
  --cell-color: var(--user-color);
133
133
  }
134
134
  .cell.focused {
135
- --cell-color: var(--spectrum-global-color-blue-400);
135
+ --cell-color: var(--accent-color);
136
136
  }
137
137
  .cell.error {
138
138
  --cell-color: var(--spectrum-global-color-red-500);
@@ -9,7 +9,7 @@
9
9
  export let rowFocused = false
10
10
  export let rowHovered = false
11
11
  export let rowSelected = false
12
- export let disableExpand = false
12
+ export let expandable = false
13
13
  export let disableNumber = false
14
14
  export let defaultHeight = false
15
15
  export let disabled = false
@@ -24,13 +24,6 @@
24
24
  selectedRows.actions.toggleRow(id)
25
25
  }
26
26
  }
27
-
28
- const expand = () => {
29
- svelteDispatch("expand")
30
- if (row) {
31
- dispatch("edit-row", row)
32
- }
33
- }
34
27
  </script>
35
28
 
36
29
  <GridCell
@@ -70,12 +63,14 @@
70
63
  color="var(--spectrum-global-color-red-400)"
71
64
  />
72
65
  </div>
73
- {:else 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} />
66
+ {:else}
67
+ <div class="expand" class:visible={$config.allowExpandRows && expandable}>
68
+ <Icon
69
+ size="S"
70
+ name="Maximize"
71
+ hoverable
72
+ on:click={() => svelteDispatch("expand")}
73
+ />
79
74
  </div>
80
75
  {/if}
81
76
  </div>
@@ -163,7 +163,7 @@
163
163
  <MenuItem
164
164
  icon="Edit"
165
165
  on:click={editColumn}
166
- disabled={!$config.allowEditColumns || column.schema.disabled}
166
+ disabled={!$config.allowSchemaChanges || column.schema.disabled}
167
167
  >
168
168
  Edit column
169
169
  </MenuItem>
@@ -171,7 +171,7 @@
171
171
  icon="Label"
172
172
  on:click={makeDisplayColumn}
173
173
  disabled={idx === "sticky" ||
174
- !$config.allowEditColumns ||
174
+ !$config.allowSchemaChanges ||
175
175
  bannedDisplayColumnTypes.includes(column.schema.type)}
176
176
  >
177
177
  Use as display column
@@ -197,10 +197,12 @@
197
197
  Move right
198
198
  </MenuItem>
199
199
  <MenuItem
200
- disabled={idx === "sticky"}
200
+ disabled={idx === "sticky" || !$config.showControls}
201
201
  icon="VisibilityOff"
202
- on:click={hideColumn}>Hide column</MenuItem
202
+ on:click={hideColumn}
203
203
  >
204
+ Hide column
205
+ </MenuItem>
204
206
  </Menu>
205
207
  </Popover>
206
208
 
@@ -218,7 +220,7 @@
218
220
  .header-cell :global(.cell) {
219
221
  padding: 0 var(--cell-padding);
220
222
  gap: calc(2 * var(--cell-spacing));
221
- background: var(--spectrum-global-color-gray-100);
223
+ background: var(--grid-background-alt);
222
224
  }
223
225
 
224
226
  .name {
@@ -102,7 +102,7 @@
102
102
  top: 0;
103
103
  left: 0;
104
104
  width: calc(100% + var(--max-cell-render-width-overflow));
105
- height: var(--max-cell-render-height);
105
+ height: calc(var(--row-height) + var(--max-cell-render-height));
106
106
  z-index: 1;
107
107
  border-radius: 2px;
108
108
  resize: none;
@@ -132,10 +132,7 @@
132
132
  {option}
133
133
  </div>
134
134
  {#if values.includes(option)}
135
- <Icon
136
- name="Checkmark"
137
- color="var(--spectrum-global-color-blue-400)"
138
- />
135
+ <Icon name="Checkmark" color="var(--accent-color)" />
139
136
  {/if}
140
137
  </div>
141
138
  {/each}
@@ -223,6 +220,8 @@
223
220
  overflow-y: auto;
224
221
  border: var(--cell-border);
225
222
  box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
223
+ border-bottom-left-radius: 2px;
224
+ border-bottom-right-radius: 2px;
226
225
  }
227
226
  .options.invertX {
228
227
  left: auto;
@@ -240,7 +239,7 @@
240
239
  justify-content: space-between;
241
240
  align-items: center;
242
241
  gap: var(--cell-spacing);
243
- background-color: var(--background);
242
+ background-color: var(--grid-background-alt);
244
243
  }
245
244
  .option:hover,
246
245
  .option.focused {
@@ -42,6 +42,8 @@
42
42
  let candidateIndex
43
43
  let lastSearchId
44
44
  let searching = false
45
+ let valuesHeight = 0
46
+ let container
45
47
 
46
48
  $: oneRowOnly = schema?.relationshipType === "one-to-many"
47
49
  $: editable = focused && !readonly
@@ -138,6 +140,7 @@
138
140
 
139
141
  const open = async () => {
140
142
  isOpen = true
143
+ valuesHeight = container.getBoundingClientRect().height
141
144
 
142
145
  // Find the primary display for the related table
143
146
  if (!primaryDisplay) {
@@ -242,8 +245,14 @@
242
245
  })
243
246
  </script>
244
247
 
245
- <div class="wrapper" class:editable class:focused style="--color:{color};">
246
- <div class="container">
248
+ <div
249
+ class="wrapper"
250
+ class:editable
251
+ class:focused
252
+ class:invertY
253
+ style="--color:{color};"
254
+ >
255
+ <div class="container" bind:this={container}>
247
256
  <div
248
257
  class="values"
249
258
  class:wrap={editable || contentLines > 1}
@@ -290,6 +299,7 @@
290
299
  class:invertY
291
300
  on:wheel|stopPropagation
292
301
  use:clickOutside={close}
302
+ style="--values-height:{valuesHeight}px;"
293
303
  >
294
304
  <div class="search">
295
305
  <Input
@@ -319,11 +329,7 @@
319
329
  </span>
320
330
  </div>
321
331
  {#if isRowSelected(row)}
322
- <Icon
323
- size="S"
324
- name="Checkmark"
325
- color="var(--spectrum-global-color-blue-400)"
326
- />
332
+ <Icon size="S" name="Checkmark" color="var(--accent-color)" />
327
333
  {/if}
328
334
  </div>
329
335
  {/each}
@@ -340,7 +346,7 @@
340
346
  min-height: var(--row-height);
341
347
  max-height: var(--row-height);
342
348
  overflow: hidden;
343
- --max-relationship-height: 120px;
349
+ --max-relationship-height: 96px;
344
350
  }
345
351
  .wrapper.focused {
346
352
  position: absolute;
@@ -352,6 +358,10 @@
352
358
  max-height: none;
353
359
  overflow: visible;
354
360
  }
361
+ .wrapper.invertY {
362
+ top: auto;
363
+ bottom: 0;
364
+ }
355
365
 
356
366
  .container {
357
367
  min-height: var(--row-height);
@@ -450,16 +460,17 @@
450
460
  left: 0;
451
461
  width: 100%;
452
462
  max-height: calc(
453
- var(--max-cell-render-height) + var(--row-height) -
454
- var(--max-relationship-height)
463
+ var(--max-cell-render-height) + var(--row-height) - var(--values-height)
455
464
  );
456
- background: var(--background);
465
+ background: var(--grid-background-alt);
457
466
  border: var(--cell-border);
458
467
  box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15);
459
468
  display: flex;
460
469
  flex-direction: column;
461
470
  align-items: stretch;
462
471
  padding: 0 0 8px 0;
472
+ border-bottom-left-radius: 2px;
473
+ border-bottom-right-radius: 2px;
463
474
  }
464
475
  .dropdown.invertY {
465
476
  transform: translateY(-100%);
@@ -10,7 +10,7 @@
10
10
  quiet
11
11
  size="M"
12
12
  on:click={() => dispatch("add-column")}
13
- disabled={!$config.allowAddColumns}
13
+ disabled={!$config.allowSchemaChanges}
14
14
  >
15
15
  Add column
16
16
  </ActionButton>
@@ -6,15 +6,9 @@
6
6
 
7
7
  let modal
8
8
 
9
- $: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
9
+ $: selectedRowCount = Object.values($selectedRows).length
10
10
  $: rowsToDelete = Object.entries($selectedRows)
11
- .map(entry => {
12
- if (entry[1] === true) {
13
- return $rows.find(x => x._id === entry[0])
14
- } else {
15
- return null
16
- }
17
- })
11
+ .map(entry => $rows.find(x => x._id === entry[0]))
18
12
  .filter(x => x != null)
19
13
 
20
14
  // Deletion callback when confirmed
@@ -3,12 +3,13 @@
3
3
  import { ActionButton, Popover, Toggle, Icon } from "@budibase/bbui"
4
4
  import { getColumnIcon } from "../lib/utils"
5
5
 
6
- const { columns, stickyColumn, compact } = getContext("grid")
6
+ const { columns, stickyColumn } = getContext("grid")
7
7
 
8
8
  let open = false
9
9
  let anchor
10
10
 
11
11
  $: anyHidden = $columns.some(col => !col.visible)
12
+ $: text = getText($columns)
12
13
 
13
14
  const toggleVisibility = (column, visible) => {
14
15
  columns.update(state => {
@@ -38,6 +39,11 @@
38
39
  })
39
40
  columns.actions.saveChanges()
40
41
  }
42
+
43
+ const getText = columns => {
44
+ const hidden = columns.filter(col => !col.visible).length
45
+ return hidden ? `Hide columns (${hidden})` : "Hide columns"
46
+ }
41
47
  </script>
42
48
 
43
49
  <div bind:this={anchor}>
@@ -48,13 +54,12 @@
48
54
  on:click={() => (open = !open)}
49
55
  selected={open || anyHidden}
50
56
  disabled={!$columns.length}
51
- tooltip={$compact ? "Columns" : ""}
52
57
  >
53
- {$compact ? "" : "Columns"}
58
+ {text}
54
59
  </ActionButton>
55
60
  </div>
56
61
 
57
- <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
62
+ <Popover bind:open {anchor} align="left">
58
63
  <div class="content">
59
64
  <div class="columns">
60
65
  {#if $stickyColumn}
@@ -0,0 +1,135 @@
1
+ <script>
2
+ import { getContext } from "svelte"
3
+ import { ActionButton, Popover, Label } from "@budibase/bbui"
4
+ import {
5
+ DefaultColumnWidth,
6
+ LargeRowHeight,
7
+ MediumRowHeight,
8
+ SmallRowHeight,
9
+ } from "../lib/constants"
10
+
11
+ const { stickyColumn, columns, rowHeight, table } = getContext("grid")
12
+
13
+ // Some constants for column width options
14
+ const smallColSize = 120
15
+ const mediumColSize = DefaultColumnWidth
16
+ const largeColSize = DefaultColumnWidth * 1.5
17
+
18
+ // Row height sizes
19
+ const rowSizeOptions = [
20
+ {
21
+ label: "Small",
22
+ size: SmallRowHeight,
23
+ },
24
+ {
25
+ label: "Medium",
26
+ size: MediumRowHeight,
27
+ },
28
+ {
29
+ label: "Large",
30
+ size: LargeRowHeight,
31
+ },
32
+ ]
33
+
34
+ let open = false
35
+ let anchor
36
+
37
+ // Column width sizes
38
+ $: allCols = $columns.concat($stickyColumn ? [$stickyColumn] : [])
39
+ $: allSmall = allCols.every(col => col.width === smallColSize)
40
+ $: allMedium = allCols.every(col => col.width === mediumColSize)
41
+ $: allLarge = allCols.every(col => col.width === largeColSize)
42
+ $: custom = !allSmall && !allMedium && !allLarge
43
+ $: columnSizeOptions = [
44
+ {
45
+ label: "Small",
46
+ size: smallColSize,
47
+ selected: allSmall,
48
+ },
49
+ {
50
+ label: "Medium",
51
+ size: mediumColSize,
52
+ selected: allMedium,
53
+ },
54
+ {
55
+ label: "Large",
56
+ size: largeColSize,
57
+ selected: allLarge,
58
+ },
59
+ ]
60
+
61
+ const changeRowHeight = height => {
62
+ columns.actions.saveTable({
63
+ ...$table,
64
+ rowHeight: height,
65
+ })
66
+ }
67
+ </script>
68
+
69
+ <div bind:this={anchor}>
70
+ <ActionButton
71
+ icon="MoveUpDown"
72
+ quiet
73
+ size="M"
74
+ on:click={() => (open = !open)}
75
+ selected={open}
76
+ disabled={!allCols.length}
77
+ >
78
+ Size
79
+ </ActionButton>
80
+ </div>
81
+
82
+ <Popover bind:open {anchor} align="left">
83
+ <div class="content">
84
+ <div class="size">
85
+ <Label>Row height</Label>
86
+ <div class="options">
87
+ {#each rowSizeOptions as option}
88
+ <ActionButton
89
+ quiet
90
+ selected={$rowHeight === option.size}
91
+ on:click={() => changeRowHeight(option.size)}
92
+ >
93
+ {option.label}
94
+ </ActionButton>
95
+ {/each}
96
+ </div>
97
+ </div>
98
+ <div class="size">
99
+ <Label>Column width</Label>
100
+ <div class="options">
101
+ {#each columnSizeOptions as option}
102
+ <ActionButton
103
+ quiet
104
+ on:click={() => columns.actions.changeAllColumnWidths(option.size)}
105
+ selected={option.selected}
106
+ >
107
+ {option.label}
108
+ </ActionButton>
109
+ {/each}
110
+ {#if custom}
111
+ <ActionButton selected={custom} quiet>Custom</ActionButton>
112
+ {/if}
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </Popover>
117
+
118
+ <style>
119
+ .content {
120
+ padding: 12px;
121
+ }
122
+ .size {
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: 8px;
126
+ }
127
+ .size:first-child {
128
+ margin-bottom: 16px;
129
+ }
130
+ .options {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 8px;
134
+ }
135
+ </style>
@@ -2,7 +2,7 @@
2
2
  import { getContext } from "svelte"
3
3
  import { ActionButton, Popover, Select } from "@budibase/bbui"
4
4
 
5
- const { sort, columns, stickyColumn, compact } = getContext("grid")
5
+ const { sort, columns, stickyColumn } = getContext("grid")
6
6
 
7
7
  let open = false
8
8
  let anchor
@@ -90,13 +90,12 @@
90
90
  on:click={() => (open = !open)}
91
91
  selected={open}
92
92
  disabled={!columnOptions.length}
93
- tooltip={$compact ? "Sort" : ""}
94
93
  >
95
- {$compact ? "" : "Sort"}
94
+ Sort
96
95
  </ActionButton>
97
96
  </div>
98
97
 
99
- <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
98
+ <Popover bind:open {anchor} align="left">
100
99
  <div class="content">
101
100
  <Select
102
101
  placeholder={null}