@budibase/bbui 1.0.0 → 1.0.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/LICENSE +375 -0
- package/README.md +19 -17
- package/dist/bbui.es.js +2 -0
- package/dist/bbui.es.js.map +1 -0
- package/package.json +68 -26
- package/src/ActionButton/ActionButton.svelte +83 -0
- package/src/ActionGroup/ActionGroup.svelte +26 -0
- package/src/ActionMenu/ActionMenu.svelte +43 -0
- package/src/Actions/ClickOutside.svench.svx +30 -0
- package/src/Actions/PositionDropdown.svench.svx +81 -0
- package/src/Actions/autoresize_textarea.js +14 -0
- package/src/Actions/click_outside.js +18 -0
- package/src/Actions/position_dropdown.js +66 -0
- package/src/Avatar/Avatar.svelte +49 -0
- package/src/Badge/Badge.svelte +29 -0
- package/src/Button/Button.svelte +52 -0
- package/src/Button/Button.svench +138 -0
- package/src/ButtonGroup/ButtonGroup.svelte +19 -0
- package/src/ClearButton/ClearButton.svelte +19 -0
- package/src/ColorPicker/ColorPicker.svelte +292 -0
- package/src/DetailSummary/DetailSummary.svelte +83 -0
- package/src/Divider/Divider.svelte +25 -0
- package/src/Drawer/Drawer.svelte +85 -0
- package/src/Drawer/Drawer.svench +42 -0
- package/src/Drawer/DrawerContent.svelte +46 -0
- package/src/Form/Checkbox.svelte +23 -0
- package/src/Form/Combobox.svelte +44 -0
- package/src/Form/Core/Checkbox.svelte +67 -0
- package/src/Form/Core/Combobox.svelte +156 -0
- package/src/Form/Core/DatePicker.svelte +183 -0
- package/src/Form/Core/Dropzone.svelte +393 -0
- package/src/Form/Core/Multiselect.svelte +88 -0
- package/src/Form/Core/Picker.svelte +241 -0
- package/src/Form/Core/RadioGroup.svelte +47 -0
- package/src/Form/Core/Search.svelte +101 -0
- package/src/Form/Core/Select.svelte +81 -0
- package/src/Form/Core/Stepper.svelte +172 -0
- package/src/Form/Core/Switch.svelte +35 -0
- package/src/Form/Core/TextArea.svelte +60 -0
- package/src/Form/Core/TextField.svelte +104 -0
- package/src/Form/Core/index.js +12 -0
- package/src/Form/DatePicker.svelte +33 -0
- package/src/Form/Dropzone.svelte +34 -0
- package/src/Form/Field.svelte +41 -0
- package/src/Form/FieldLabel.svelte +27 -0
- package/src/Form/Input.svelte +38 -45
- package/src/Form/Multiselect.svelte +39 -0
- package/src/Form/RadioGroup.svelte +38 -0
- package/src/Form/Search.svelte +32 -0
- package/src/Form/Select.svelte +45 -43
- package/src/Form/Stepper.svelte +45 -0
- package/src/Form/TextArea.svelte +25 -44
- package/src/Form/Toggle.svelte +23 -0
- package/src/Icon/Icon.svelte +47 -0
- package/src/InlineAlert/InlineAlert.svelte +56 -0
- package/src/Label/Label.svelte +67 -0
- package/src/Layout/Layout.svelte +75 -0
- package/src/Layout/Page.svelte +25 -0
- package/src/Link/Link.svelte +22 -0
- package/src/List/Items/DetailSummary.svench +53 -0
- package/src/Menu/Item.svelte +45 -0
- package/src/Menu/Menu.svelte +7 -0
- package/src/Menu/Menu.svench +19 -0
- package/src/Menu/Section.svelte +10 -0
- package/src/Menu/Separator.svelte +1 -0
- package/src/Modal/Content.svelte +6 -0
- package/src/Modal/CustomContent.svelte +38 -0
- package/src/Modal/Modal.svelte +133 -0
- package/src/Modal/Modal.svench +117 -0
- package/src/Modal/ModalContent.svelte +139 -0
- package/src/Modal/QuizModal.svelte +54 -0
- package/src/Notification/Notification.svelte +20 -0
- package/src/Notification/NotificationDisplay.svelte +36 -0
- package/src/Pagination/Pagination.svelte +57 -0
- package/src/Popover/Popover.svelte +52 -0
- package/src/Popover/Popover.svench +121 -0
- package/src/ProgressBar/ProgressBar.svelte +54 -0
- package/src/ProgressCircle/ProgressCircle.svelte +62 -0
- package/src/SideNavigation/Item.svelte +45 -0
- package/src/SideNavigation/Navigation.svelte +12 -0
- package/src/StatusLight/StatusLight.svelte +41 -0
- package/src/Stores/Notifications.svench.svx +78 -0
- package/src/Stores/notifications.js +56 -0
- package/src/Table/ArrayRenderer.svelte +17 -0
- package/src/Table/AttachmentRenderer.svelte +55 -0
- package/src/Table/BooleanRenderer.svelte +42 -0
- package/src/Table/CellRenderer.svelte +37 -0
- package/src/Table/DateTimeRenderer.svelte +13 -0
- package/src/Table/InternalRenderer.svelte +28 -0
- package/src/Table/RelationshipRenderer.svelte +40 -0
- package/src/Table/SelectEditRenderer.svelte +16 -0
- package/src/Table/StringRenderer.svelte +13 -0
- package/src/Table/Table.svelte +506 -0
- package/src/Table/index.js +1 -0
- package/src/Tabs/Tab.svelte +66 -0
- package/src/Tabs/Tabs.svelte +105 -0
- package/src/Tags/Tag.svelte +43 -0
- package/src/Tags/Tags.svelte +7 -0
- package/src/Tooltip/Tooltip.svelte +24 -0
- package/src/TreeView/Item.svelte +41 -0
- package/src/TreeView/Tree.svelte +16 -0
- package/src/Typography/Body.svelte +19 -0
- package/src/Typography/Code.svelte +10 -0
- package/src/Typography/Detail.svelte +15 -0
- package/src/Typography/Heading.svelte +16 -0
- package/src/bbui.css +96 -0
- package/src/context.js +3 -0
- package/src/index.js +75 -10
- package/src/spectrum-icons-rollup.js +31 -0
- package/src/spectrum-icons-vite.js +31 -0
- package/src/utils/helpers.js +8 -0
- package/dist/bundle.css +0 -6
- package/dist/bundle.css.map +0 -18
- package/dist/index.js +0 -919
- package/dist/index.mjs +0 -906
- package/src/Branding/Budibase-logo.svelte +0 -68
- package/src/Branding/Logo.svelte +0 -16
- package/src/Branding/Logo.svench +0 -8
- package/src/Form/Button.svelte +0 -52
- package/src/Form/Button.svench +0 -26
- package/src/Form/Input.svench +0 -8
- package/src/Form/Select.svench +0 -8
- package/src/Form/TextArea.svench +0 -8
- package/src/Links/Home.svelte +0 -49
- package/src/Links/Home.svench +0 -19
- package/src/actions/autoresize_textarea.js +0 -14
- package/src/icons/Contribution.svelte +0 -14
- package/src/index.svench.svx +0 -35
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { createEventDispatcher } from "svelte"
|
|
3
|
+
import "@spectrum-css/table/dist/index-vars.css"
|
|
4
|
+
import CellRenderer from "./CellRenderer.svelte"
|
|
5
|
+
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
|
6
|
+
import { cloneDeep } from "lodash"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The expected schema is our normal couch schemas for our tables.
|
|
10
|
+
* Each field schema can be enriched with a few extra properties to customise
|
|
11
|
+
* the behaviour.
|
|
12
|
+
* All of these are optional and do not need to be added.
|
|
13
|
+
* displayName: Overrides the field name displayed as the column title
|
|
14
|
+
* sortable: Set to false to disable sorting data by a certain column
|
|
15
|
+
* editable: Set to false to disable editing a certain column if the
|
|
16
|
+
* allowEditColumns prop is true
|
|
17
|
+
*/
|
|
18
|
+
export let data = []
|
|
19
|
+
export let schema = {}
|
|
20
|
+
export let showAutoColumns = false
|
|
21
|
+
export let rowCount = 0
|
|
22
|
+
export let quiet = false
|
|
23
|
+
export let loading = false
|
|
24
|
+
export let allowSelectRows = true
|
|
25
|
+
export let allowEditRows = true
|
|
26
|
+
export let allowEditColumns = true
|
|
27
|
+
export let selectedRows = []
|
|
28
|
+
export let editColumnTitle = "Edit"
|
|
29
|
+
export let customRenderers = []
|
|
30
|
+
export let disableSorting = false
|
|
31
|
+
|
|
32
|
+
const dispatch = createEventDispatcher()
|
|
33
|
+
|
|
34
|
+
// Config
|
|
35
|
+
const rowHeight = 55
|
|
36
|
+
const headerHeight = 36
|
|
37
|
+
const rowPreload = 5
|
|
38
|
+
|
|
39
|
+
// Sorting state
|
|
40
|
+
let sortColumn
|
|
41
|
+
let sortOrder
|
|
42
|
+
|
|
43
|
+
// Table state
|
|
44
|
+
let height = 0
|
|
45
|
+
let loaded = false
|
|
46
|
+
$: schema = fixSchema(schema)
|
|
47
|
+
$: if (!loading) loaded = true
|
|
48
|
+
$: rows = data ?? []
|
|
49
|
+
$: visibleRowCount = getVisibleRowCount(loaded, height, rows.length, rowCount)
|
|
50
|
+
$: contentStyle = getContentStyle(visibleRowCount, rowCount)
|
|
51
|
+
$: sortedRows = sortRows(rows, sortColumn, sortOrder)
|
|
52
|
+
$: fields = getFields(schema, showAutoColumns)
|
|
53
|
+
$: showEditColumn = allowEditRows || allowSelectRows
|
|
54
|
+
|
|
55
|
+
// Scrolling state
|
|
56
|
+
let timeout
|
|
57
|
+
let nextScrollTop = 0
|
|
58
|
+
let scrollTop = 0
|
|
59
|
+
$: firstVisibleRow = calculateFirstVisibleRow(scrollTop)
|
|
60
|
+
$: lastVisibleRow = calculateLastVisibleRow(
|
|
61
|
+
firstVisibleRow,
|
|
62
|
+
visibleRowCount,
|
|
63
|
+
rows.length
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// Reset state when data changes
|
|
67
|
+
$: rows.length, reset()
|
|
68
|
+
const reset = () => {
|
|
69
|
+
nextScrollTop = 0
|
|
70
|
+
scrollTop = 0
|
|
71
|
+
clearTimeout(timeout)
|
|
72
|
+
timeout = null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const fixSchema = schema => {
|
|
76
|
+
let fixedSchema = {}
|
|
77
|
+
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
|
|
78
|
+
if (typeof fieldSchema === "string") {
|
|
79
|
+
fixedSchema[fieldName] = {
|
|
80
|
+
type: fieldSchema,
|
|
81
|
+
name: fieldName,
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
fixedSchema[fieldName] = {
|
|
85
|
+
...fieldSchema,
|
|
86
|
+
name: fieldName,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
return fixedSchema
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const getVisibleRowCount = (loaded, height, allRows, rowCount) => {
|
|
94
|
+
if (!loaded) {
|
|
95
|
+
return rowCount || 0
|
|
96
|
+
}
|
|
97
|
+
if (rowCount) {
|
|
98
|
+
return Math.min(allRows, rowCount)
|
|
99
|
+
}
|
|
100
|
+
return Math.min(allRows, Math.ceil(height / rowHeight))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const getContentStyle = (visibleRows, rowCount) => {
|
|
104
|
+
if (!rowCount || !visibleRows) {
|
|
105
|
+
return ""
|
|
106
|
+
}
|
|
107
|
+
return `height: ${headerHeight + visibleRows * (rowHeight + 1)}px;`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const sortRows = (rows, sortColumn, sortOrder) => {
|
|
111
|
+
if (!sortColumn || !sortOrder || disableSorting) {
|
|
112
|
+
return rows
|
|
113
|
+
}
|
|
114
|
+
return rows.slice().sort((a, b) => {
|
|
115
|
+
const colA = a[sortColumn]
|
|
116
|
+
const colB = b[sortColumn]
|
|
117
|
+
if (sortOrder === "Descending") {
|
|
118
|
+
return colA > colB ? -1 : 1
|
|
119
|
+
} else {
|
|
120
|
+
return colA > colB ? 1 : -1
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const sortBy = fieldSchema => {
|
|
126
|
+
if (fieldSchema.sortable === false) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
if (fieldSchema.name === sortColumn) {
|
|
130
|
+
sortOrder = sortOrder === "Descending" ? "Ascending" : "Descending"
|
|
131
|
+
} else {
|
|
132
|
+
sortColumn = fieldSchema.name
|
|
133
|
+
sortOrder = "Descending"
|
|
134
|
+
}
|
|
135
|
+
dispatch("sort", { column: sortColumn, order: sortOrder })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const getDisplayName = schema => {
|
|
139
|
+
let name = schema?.displayName
|
|
140
|
+
if (schema && name === undefined) {
|
|
141
|
+
name = schema.name
|
|
142
|
+
}
|
|
143
|
+
return name || ""
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const getFields = (schema, showAutoColumns) => {
|
|
147
|
+
let columns = []
|
|
148
|
+
let autoColumns = []
|
|
149
|
+
Object.entries(schema || {}).forEach(([field, fieldSchema]) => {
|
|
150
|
+
if (!field || !fieldSchema) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
if (!fieldSchema?.autocolumn) {
|
|
154
|
+
columns.push(fieldSchema)
|
|
155
|
+
} else if (showAutoColumns) {
|
|
156
|
+
autoColumns.push(fieldSchema)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
return columns
|
|
160
|
+
.sort((a, b) => {
|
|
161
|
+
const orderA = a.order || Number.MAX_SAFE_INTEGER
|
|
162
|
+
const orderB = b.order || Number.MAX_SAFE_INTEGER
|
|
163
|
+
const nameA = getDisplayName(a)
|
|
164
|
+
const nameB = getDisplayName(b)
|
|
165
|
+
if (orderA !== orderB) {
|
|
166
|
+
return orderA < orderB ? orderA : orderB
|
|
167
|
+
}
|
|
168
|
+
return nameA < nameB ? a : b
|
|
169
|
+
})
|
|
170
|
+
.concat(autoColumns)
|
|
171
|
+
.map(column => column.name)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const onScroll = event => {
|
|
175
|
+
nextScrollTop = event.target.scrollTop
|
|
176
|
+
if (timeout) {
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
timeout = setTimeout(() => {
|
|
180
|
+
scrollTop = nextScrollTop
|
|
181
|
+
timeout = null
|
|
182
|
+
}, 50)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const calculateFirstVisibleRow = scrollTop => {
|
|
186
|
+
return Math.max(Math.floor(scrollTop / (rowHeight + 1)) - rowPreload, 0)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const calculateLastVisibleRow = (firstRow, visibleRowCount, allRowCount) => {
|
|
190
|
+
if (visibleRowCount === 0) {
|
|
191
|
+
return -1
|
|
192
|
+
}
|
|
193
|
+
return Math.min(firstRow + visibleRowCount + 2 * rowPreload, allRowCount)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const editColumn = (e, field) => {
|
|
197
|
+
e.stopPropagation()
|
|
198
|
+
dispatch("editcolumn", field)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const editRow = (e, row) => {
|
|
202
|
+
e.stopPropagation()
|
|
203
|
+
dispatch("editrow", cloneDeep(row))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const toggleSelectRow = row => {
|
|
207
|
+
if (!allowSelectRows) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
if (selectedRows.includes(row)) {
|
|
211
|
+
selectedRows = selectedRows.filter(selectedRow => selectedRow !== row)
|
|
212
|
+
} else {
|
|
213
|
+
selectedRows = [...selectedRows, row]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
<div class="wrapper" bind:offsetHeight={height}>
|
|
219
|
+
{#if !loaded}
|
|
220
|
+
<div class="loading" style={contentStyle} />
|
|
221
|
+
{:else}
|
|
222
|
+
<div
|
|
223
|
+
on:scroll={onScroll}
|
|
224
|
+
class:quiet
|
|
225
|
+
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
|
226
|
+
class="container"
|
|
227
|
+
>
|
|
228
|
+
<div style={contentStyle}>
|
|
229
|
+
<table class="spectrum-Table" class:spectrum-Table--quiet={quiet}>
|
|
230
|
+
{#if fields.length}
|
|
231
|
+
<thead class="spectrum-Table-head">
|
|
232
|
+
<tr>
|
|
233
|
+
{#if showEditColumn}
|
|
234
|
+
<th class="spectrum-Table-headCell">
|
|
235
|
+
<div class="spectrum-Table-headCell-content">
|
|
236
|
+
{editColumnTitle || ""}
|
|
237
|
+
</div>
|
|
238
|
+
</th>
|
|
239
|
+
{/if}
|
|
240
|
+
{#each fields as field}
|
|
241
|
+
<th
|
|
242
|
+
class="spectrum-Table-headCell"
|
|
243
|
+
class:is-sortable={schema[field].sortable !== false}
|
|
244
|
+
class:is-sorted-desc={sortColumn === field &&
|
|
245
|
+
sortOrder === "Descending"}
|
|
246
|
+
class:is-sorted-asc={sortColumn === field &&
|
|
247
|
+
sortOrder === "Ascending"}
|
|
248
|
+
on:click={() => sortBy(schema[field])}
|
|
249
|
+
>
|
|
250
|
+
<div class="spectrum-Table-headCell-content">
|
|
251
|
+
<div class="title">{getDisplayName(schema[field])}</div>
|
|
252
|
+
{#if schema[field]?.autocolumn}
|
|
253
|
+
<svg
|
|
254
|
+
class="spectrum-Icon spectrum-Table-autoIcon"
|
|
255
|
+
focusable="false"
|
|
256
|
+
>
|
|
257
|
+
<use xlink:href="#spectrum-icon-18-MagicWand" />
|
|
258
|
+
</svg>
|
|
259
|
+
{/if}
|
|
260
|
+
{#if sortColumn === field}
|
|
261
|
+
<svg
|
|
262
|
+
class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon"
|
|
263
|
+
focusable="false"
|
|
264
|
+
aria-hidden="true"
|
|
265
|
+
>
|
|
266
|
+
<use xlink:href="#spectrum-css-icon-Arrow100" />
|
|
267
|
+
</svg>
|
|
268
|
+
{/if}
|
|
269
|
+
{#if allowEditColumns && schema[field]?.editable !== false}
|
|
270
|
+
<svg
|
|
271
|
+
class="spectrum-Icon spectrum-Table-editIcon"
|
|
272
|
+
focusable="false"
|
|
273
|
+
on:click={e => editColumn(e, field)}
|
|
274
|
+
>
|
|
275
|
+
<use xlink:href="#spectrum-icon-18-Edit" />
|
|
276
|
+
</svg>
|
|
277
|
+
{/if}
|
|
278
|
+
</div>
|
|
279
|
+
</th>
|
|
280
|
+
{/each}
|
|
281
|
+
</tr>
|
|
282
|
+
</thead>
|
|
283
|
+
{/if}
|
|
284
|
+
<tbody class="spectrum-Table-body">
|
|
285
|
+
{#if sortedRows?.length && fields.length}
|
|
286
|
+
{#each sortedRows as row, idx}
|
|
287
|
+
<tr
|
|
288
|
+
on:click={() => dispatch("click", row)}
|
|
289
|
+
on:click={() => toggleSelectRow(row)}
|
|
290
|
+
class="spectrum-Table-row"
|
|
291
|
+
class:hidden={idx < firstVisibleRow || idx > lastVisibleRow}
|
|
292
|
+
>
|
|
293
|
+
{#if idx >= firstVisibleRow && idx <= lastVisibleRow}
|
|
294
|
+
{#if showEditColumn}
|
|
295
|
+
<td
|
|
296
|
+
class="spectrum-Table-cell spectrum-Table-cell--divider"
|
|
297
|
+
>
|
|
298
|
+
<div class="spectrum-Table-cell-content">
|
|
299
|
+
<SelectEditRenderer
|
|
300
|
+
data={row}
|
|
301
|
+
selected={selectedRows.includes(row)}
|
|
302
|
+
onToggleSelection={() => toggleSelectRow(row)}
|
|
303
|
+
onEdit={e => editRow(e, row)}
|
|
304
|
+
{allowSelectRows}
|
|
305
|
+
{allowEditRows}
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
</td>
|
|
309
|
+
{/if}
|
|
310
|
+
{#each fields as field}
|
|
311
|
+
<td
|
|
312
|
+
class="spectrum-Table-cell"
|
|
313
|
+
class:spectrum-Table-cell--divider={!!schema[field]
|
|
314
|
+
.divider}
|
|
315
|
+
>
|
|
316
|
+
<div class="spectrum-Table-cell-content">
|
|
317
|
+
<CellRenderer
|
|
318
|
+
{customRenderers}
|
|
319
|
+
{row}
|
|
320
|
+
schema={schema[field]}
|
|
321
|
+
value={row[field]}
|
|
322
|
+
on:clickrelationship
|
|
323
|
+
>
|
|
324
|
+
<slot />
|
|
325
|
+
</CellRenderer>
|
|
326
|
+
</div>
|
|
327
|
+
</td>
|
|
328
|
+
{/each}
|
|
329
|
+
{/if}
|
|
330
|
+
</tr>
|
|
331
|
+
{/each}
|
|
332
|
+
{:else}
|
|
333
|
+
<tr class="placeholder-row">
|
|
334
|
+
{#if showEditColumn}
|
|
335
|
+
<td class="placeholder-offset" />
|
|
336
|
+
{/if}
|
|
337
|
+
{#each fields as field}
|
|
338
|
+
<td />
|
|
339
|
+
{/each}
|
|
340
|
+
<div class="placeholder" class:has-fields={fields.length > 0}>
|
|
341
|
+
<div class="placeholder-content">
|
|
342
|
+
<svg
|
|
343
|
+
class="spectrum-Icon spectrum-Icon--sizeXXL"
|
|
344
|
+
focusable="false"
|
|
345
|
+
>
|
|
346
|
+
<use xlink:href="#spectrum-icon-18-Table" />
|
|
347
|
+
</svg>
|
|
348
|
+
<div>No rows found</div>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</tr>
|
|
352
|
+
{/if}
|
|
353
|
+
</tbody>
|
|
354
|
+
</table>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
{/if}
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<style>
|
|
361
|
+
.wrapper {
|
|
362
|
+
background-color: var(--spectrum-alias-background-color-secondary);
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
position: relative;
|
|
365
|
+
z-index: 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.container {
|
|
369
|
+
height: 100%;
|
|
370
|
+
position: relative;
|
|
371
|
+
overflow: auto;
|
|
372
|
+
}
|
|
373
|
+
.container.quiet {
|
|
374
|
+
border: none;
|
|
375
|
+
}
|
|
376
|
+
table {
|
|
377
|
+
width: 100%;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.spectrum-Table-headCell .spectrum-Icon {
|
|
381
|
+
pointer-events: all;
|
|
382
|
+
margin-left: var(
|
|
383
|
+
--spectrum-table-header-sort-icon-gap,
|
|
384
|
+
var(--spectrum-global-dimension-size-125)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
.spectrum-Table-editIcon,
|
|
388
|
+
.spectrum-Table-autoIcon {
|
|
389
|
+
width: var(--spectrum-global-dimension-size-150);
|
|
390
|
+
height: var(--spectrum-global-dimension-size-150);
|
|
391
|
+
}
|
|
392
|
+
.spectrum-Table-editIcon {
|
|
393
|
+
opacity: 0;
|
|
394
|
+
}
|
|
395
|
+
.spectrum-Table-headCell:hover .spectrum-Table-editIcon {
|
|
396
|
+
opacity: 1;
|
|
397
|
+
transition: opacity 0.2s ease;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
th {
|
|
401
|
+
vertical-align: middle;
|
|
402
|
+
height: var(--header-height);
|
|
403
|
+
position: sticky;
|
|
404
|
+
top: 0;
|
|
405
|
+
z-index: 2;
|
|
406
|
+
background-color: var(--spectrum-alias-background-color-secondary);
|
|
407
|
+
border-bottom: 1px solid
|
|
408
|
+
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
|
409
|
+
}
|
|
410
|
+
.spectrum-Table-headCell-content {
|
|
411
|
+
white-space: nowrap;
|
|
412
|
+
display: flex;
|
|
413
|
+
flex-direction: row;
|
|
414
|
+
justify-content: flex-start;
|
|
415
|
+
align-items: center;
|
|
416
|
+
user-select: none;
|
|
417
|
+
}
|
|
418
|
+
.spectrum-Table-headCell-content .title {
|
|
419
|
+
overflow: hidden;
|
|
420
|
+
text-overflow: ellipsis;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.placeholder-row {
|
|
424
|
+
position: relative;
|
|
425
|
+
height: 150px;
|
|
426
|
+
}
|
|
427
|
+
.placeholder-row td {
|
|
428
|
+
border-top: none !important;
|
|
429
|
+
border-bottom: none !important;
|
|
430
|
+
}
|
|
431
|
+
.placeholder-offset {
|
|
432
|
+
width: 1px;
|
|
433
|
+
}
|
|
434
|
+
.placeholder {
|
|
435
|
+
top: 0;
|
|
436
|
+
height: 100%;
|
|
437
|
+
left: 0;
|
|
438
|
+
width: 100%;
|
|
439
|
+
position: absolute;
|
|
440
|
+
display: flex;
|
|
441
|
+
flex-direction: row;
|
|
442
|
+
justify-content: center;
|
|
443
|
+
align-items: center;
|
|
444
|
+
}
|
|
445
|
+
.placeholder.has-fields {
|
|
446
|
+
top: var(--header-height);
|
|
447
|
+
height: calc(100% - var(--header-height));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.placeholder-content {
|
|
451
|
+
padding: 20px;
|
|
452
|
+
display: flex;
|
|
453
|
+
flex-direction: column;
|
|
454
|
+
justify-content: center;
|
|
455
|
+
align-items: center;
|
|
456
|
+
color: var(
|
|
457
|
+
--spectrum-table-cell-text-color,
|
|
458
|
+
var(--spectrum-alias-text-color)
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
.placeholder-content div {
|
|
462
|
+
margin-top: 10px;
|
|
463
|
+
font-size: var(
|
|
464
|
+
--spectrum-table-cell-text-size,
|
|
465
|
+
var(--spectrum-alias-font-size-default)
|
|
466
|
+
);
|
|
467
|
+
text-align: center;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
tbody {
|
|
471
|
+
z-index: 1;
|
|
472
|
+
}
|
|
473
|
+
tbody tr {
|
|
474
|
+
height: var(--row-height);
|
|
475
|
+
}
|
|
476
|
+
tbody tr.hidden {
|
|
477
|
+
height: calc(var(--row-height) + 1px);
|
|
478
|
+
}
|
|
479
|
+
td {
|
|
480
|
+
padding-top: 0;
|
|
481
|
+
padding-bottom: 0;
|
|
482
|
+
border-bottom: none;
|
|
483
|
+
border-top: 1px solid
|
|
484
|
+
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
|
485
|
+
border-radius: 0;
|
|
486
|
+
}
|
|
487
|
+
tr:first-child td {
|
|
488
|
+
border-top: none;
|
|
489
|
+
}
|
|
490
|
+
tr:last-child td {
|
|
491
|
+
border-bottom: 1px solid
|
|
492
|
+
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid));
|
|
493
|
+
}
|
|
494
|
+
td.spectrum-Table-cell--divider {
|
|
495
|
+
width: 1px;
|
|
496
|
+
}
|
|
497
|
+
.spectrum-Table-cell-content {
|
|
498
|
+
height: var(--row-height);
|
|
499
|
+
white-space: nowrap;
|
|
500
|
+
display: flex;
|
|
501
|
+
flex-direction: row;
|
|
502
|
+
justify-content: flex-start;
|
|
503
|
+
align-items: center;
|
|
504
|
+
gap: 4px;
|
|
505
|
+
}
|
|
506
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Table } from "./Table.svelte"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext, onMount, createEventDispatcher } from "svelte"
|
|
3
|
+
import Portal from "svelte-portal"
|
|
4
|
+
export let title
|
|
5
|
+
export let icon = ""
|
|
6
|
+
|
|
7
|
+
const dispatch = createEventDispatcher()
|
|
8
|
+
const selected = getContext("tab")
|
|
9
|
+
let tab
|
|
10
|
+
let tabInfo
|
|
11
|
+
|
|
12
|
+
const setTabInfo = () => {
|
|
13
|
+
// If the tabs are being rendered inside a component which uses
|
|
14
|
+
// a svelte transition to enter, then this initial getBoundingClientRect
|
|
15
|
+
// will return an incorrect position.
|
|
16
|
+
// We just need to get this off the main thread to fix this, by using
|
|
17
|
+
// a 0ms timeout.
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
tabInfo = tab.getBoundingClientRect()
|
|
20
|
+
if ($selected.title === title) {
|
|
21
|
+
$selected.info = tabInfo
|
|
22
|
+
}
|
|
23
|
+
}, 0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onMount(() => {
|
|
27
|
+
setTabInfo()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const onClick = () => {
|
|
31
|
+
$selected = { ...$selected, title, info: tab.getBoundingClientRect() }
|
|
32
|
+
dispatch("click")
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div
|
|
37
|
+
bind:this={tab}
|
|
38
|
+
on:click={onClick}
|
|
39
|
+
class:is-selected={$selected.title === title}
|
|
40
|
+
class="spectrum-Tabs-item"
|
|
41
|
+
class:emphasized={$selected.title === title && $selected.emphasized}
|
|
42
|
+
tabindex="0"
|
|
43
|
+
>
|
|
44
|
+
{#if icon}
|
|
45
|
+
<svg
|
|
46
|
+
class="spectrum-Icon spectrum-Icon--sizeM"
|
|
47
|
+
focusable="false"
|
|
48
|
+
aria-hidden="true"
|
|
49
|
+
aria-label="Folder"
|
|
50
|
+
>
|
|
51
|
+
<use xlink:href="#spectrum-icon-18-{icon}" />
|
|
52
|
+
</svg>
|
|
53
|
+
{/if}
|
|
54
|
+
<span class="spectrum-Tabs-itemLabel">{title}</span>
|
|
55
|
+
</div>
|
|
56
|
+
{#if $selected.title === title}
|
|
57
|
+
<Portal target=".spectrum-Tabs-content-{$selected.id}">
|
|
58
|
+
<slot />
|
|
59
|
+
</Portal>
|
|
60
|
+
{/if}
|
|
61
|
+
|
|
62
|
+
<style>
|
|
63
|
+
.emphasized {
|
|
64
|
+
color: var(--spectrum-global-color-blue-600);
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import "@spectrum-css/tabs/dist/index-vars.css"
|
|
3
|
+
import { writable } from "svelte/store"
|
|
4
|
+
import { onMount, setContext, createEventDispatcher } from "svelte"
|
|
5
|
+
|
|
6
|
+
export let selected
|
|
7
|
+
export let vertical = false
|
|
8
|
+
export let noPadding = false
|
|
9
|
+
export let quiet = false
|
|
10
|
+
export let emphasized = false
|
|
11
|
+
|
|
12
|
+
let _id = id()
|
|
13
|
+
const tab = writable({ title: selected, id: _id, emphasized })
|
|
14
|
+
setContext("tab", tab)
|
|
15
|
+
|
|
16
|
+
let container
|
|
17
|
+
|
|
18
|
+
const dispatch = createEventDispatcher()
|
|
19
|
+
|
|
20
|
+
$: {
|
|
21
|
+
if ($tab.title !== selected) {
|
|
22
|
+
selected = $tab.title
|
|
23
|
+
dispatch("select", selected)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let top, left, width, height
|
|
28
|
+
$: calculateIndicatorLength($tab)
|
|
29
|
+
$: calculateIndicatorOffset($tab)
|
|
30
|
+
|
|
31
|
+
function calculateIndicatorLength() {
|
|
32
|
+
if (!vertical) {
|
|
33
|
+
width = $tab.info?.width + "px"
|
|
34
|
+
height = $tab.info?.height
|
|
35
|
+
} else {
|
|
36
|
+
height = $tab.info?.height + 4 + "px"
|
|
37
|
+
width = $tab.info?.width
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function calculateIndicatorOffset() {
|
|
42
|
+
if (!vertical) {
|
|
43
|
+
left = $tab.info?.left - container?.getBoundingClientRect().left + "px"
|
|
44
|
+
} else {
|
|
45
|
+
top = $tab.info?.top - container?.getBoundingClientRect().top + "px"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onMount(() => {
|
|
50
|
+
calculateIndicatorLength()
|
|
51
|
+
calculateIndicatorOffset()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
function id() {
|
|
55
|
+
return "_" + Math.random().toString(36).substr(2, 9)
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<div
|
|
60
|
+
bind:this={container}
|
|
61
|
+
class:quiet
|
|
62
|
+
class="selected-border spectrum-Tabs {quiet &&
|
|
63
|
+
'spectrum-Tabs--quiet'} spectrum-Tabs--{vertical
|
|
64
|
+
? 'vertical'
|
|
65
|
+
: 'horizontal'}"
|
|
66
|
+
>
|
|
67
|
+
<slot />
|
|
68
|
+
{#if $tab.info}
|
|
69
|
+
<div
|
|
70
|
+
class="spectrum-Tabs-selectionIndicator indicator-transition"
|
|
71
|
+
style="{emphasized &&
|
|
72
|
+
'background-color: var(--spectrum-global-color-blue-400)'}; width: {width}; height: {height}; left: {left}; top: {top};"
|
|
73
|
+
/>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div
|
|
78
|
+
class="spectrum-Tabs-content spectrum-Tabs-content-{_id}"
|
|
79
|
+
class:noPadding
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<style>
|
|
83
|
+
.quiet {
|
|
84
|
+
border-bottom: none !important;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.spectrum-Tabs {
|
|
88
|
+
padding-left: var(--spacing-xl);
|
|
89
|
+
padding-right: var(--spacing-xl);
|
|
90
|
+
position: relative;
|
|
91
|
+
border-bottom: var(--border-light);
|
|
92
|
+
}
|
|
93
|
+
.spectrum-Tabs-content {
|
|
94
|
+
margin-top: var(--spectrum-global-dimension-static-size-150);
|
|
95
|
+
}
|
|
96
|
+
.indicator-transition {
|
|
97
|
+
transition: all 200ms;
|
|
98
|
+
}
|
|
99
|
+
.spectrum-Tabs--horizontal .spectrum-Tabs-selectionIndicator {
|
|
100
|
+
bottom: 0 !important;
|
|
101
|
+
}
|
|
102
|
+
.noPadding {
|
|
103
|
+
margin: 0;
|
|
104
|
+
}
|
|
105
|
+
</style>
|