Add data provider component and refactor component setting types
This commit is contained in:
parent
5bbb2b388d
commit
a51f5c73c4
|
@ -35,7 +35,7 @@ export const getDataProviderComponents = (asset, componentId) => {
|
|||
// Filter by only data provider components
|
||||
return path.filter(component => {
|
||||
const def = store.actions.components.getDefinition(component._component)
|
||||
return def?.dataProvider
|
||||
return def?.dataContext != null
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -101,15 +101,16 @@ const getContextBindings = (asset, componentId) => {
|
|||
|
||||
// Create bindings for each data provider
|
||||
dataProviders.forEach(component => {
|
||||
const isForm = component._component.endsWith("/form")
|
||||
const datasource = getDatasourceForProvider(component)
|
||||
let tableName, schema
|
||||
const def = store.actions.components.getDefinition(component._component)
|
||||
const contextDefinition = def.dataContext
|
||||
let schema
|
||||
|
||||
// Forms are an edge case which do not need table schemas
|
||||
if (isForm) {
|
||||
if (contextDefinition.type === "form") {
|
||||
schema = buildFormSchema(component)
|
||||
tableName = "Fields"
|
||||
} else {
|
||||
const datasource = getDatasourceForProvider(component)
|
||||
if (!datasource) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[
|
||||
"container",
|
||||
"dataprovider",
|
||||
"datagrid",
|
||||
"list",
|
||||
"button",
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import { Select } from "@budibase/bbui"
|
||||
import { currentAsset, store } from "builderStore"
|
||||
import { findComponentPath } from "builderStore/storeUtils"
|
||||
|
||||
export let value
|
||||
|
||||
$: path = findComponentPath($currentAsset.props, $store.selectedComponentId)
|
||||
$: console.log(path)
|
||||
$: providers = path.filter(
|
||||
component =>
|
||||
component._component === "@budibase/standard-components/dataprovider"
|
||||
)
|
||||
</script>
|
||||
|
||||
<Select thin secondary {value} on:change>
|
||||
<option value="">Choose option</option>
|
||||
{#if providers}
|
||||
{#each providers as component}
|
||||
<option value={component._id}>{component._instanceName}</option>
|
||||
{/each}
|
||||
{/if}
|
||||
</Select>
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import DatasourceSelect from "./DatasourceSelect.svelte"
|
||||
import DataSourceSelect from "./DataSourceSelect.svelte"
|
||||
|
||||
const otherSources = [{ name: "Custom", label: "Custom" }]
|
||||
</script>
|
||||
|
||||
<DatasourceSelect on:change {...$$props} showAllQueries={true} {otherSources} />
|
||||
<DataSourceSelect on:change {...$$props} showAllQueries={true} {otherSources} />
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
import OptionSelect from "./PropertyControls/OptionSelect.svelte"
|
||||
import Checkbox from "./PropertyControls/Checkbox.svelte"
|
||||
import TableSelect from "./PropertyControls/TableSelect.svelte"
|
||||
import DatasourceSelect from "./PropertyControls/DatasourceSelect.svelte"
|
||||
import DataSourceSelect from "./PropertyControls/DataSourceSelect.svelte"
|
||||
import DataProviderSelect from "./PropertyControls/DataProviderSelect.svelte"
|
||||
import FieldSelect from "./PropertyControls/FieldSelect.svelte"
|
||||
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
|
||||
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
|
||||
|
@ -61,7 +62,8 @@
|
|||
const controlMap = {
|
||||
text: Input,
|
||||
select: OptionSelect,
|
||||
datasource: DatasourceSelect,
|
||||
dataSource: DataSourceSelect,
|
||||
dataProvider: DataProviderSelect,
|
||||
detailScreen: DetailScreenSelect,
|
||||
boolean: Checkbox,
|
||||
number: Input,
|
||||
|
|
|
@ -7,31 +7,31 @@ import { executeQuery } from "./queries"
|
|||
/**
|
||||
* Fetches all rows for a particular Budibase data source.
|
||||
*/
|
||||
export const fetchDatasource = async datasource => {
|
||||
if (!datasource || !datasource.type) {
|
||||
export const fetchDatasource = async dataSource => {
|
||||
if (!dataSource || !dataSource.type) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Fetch all rows in data source
|
||||
const { type, tableId, fieldName } = datasource
|
||||
const { type, tableId, fieldName } = dataSource
|
||||
let rows = []
|
||||
if (type === "table") {
|
||||
rows = await fetchTableData(tableId)
|
||||
} else if (type === "view") {
|
||||
rows = await fetchViewData(datasource)
|
||||
rows = await fetchViewData(dataSource)
|
||||
} else if (type === "query") {
|
||||
// Set the default query params
|
||||
let parameters = cloneDeep(datasource.queryParams || {})
|
||||
for (let param of datasource.parameters) {
|
||||
let parameters = cloneDeep(dataSource.queryParams || {})
|
||||
for (let param of dataSource.parameters) {
|
||||
if (!parameters[param.name]) {
|
||||
parameters[param.name] = param.default
|
||||
}
|
||||
}
|
||||
rows = await executeQuery({ queryId: datasource._id, parameters })
|
||||
rows = await executeQuery({ queryId: dataSource._id, parameters })
|
||||
} else if (type === "link") {
|
||||
rows = await fetchRelationshipData({
|
||||
rowId: datasource.rowId,
|
||||
tableId: datasource.rowTableId,
|
||||
rowId: dataSource.rowId,
|
||||
tableId: dataSource.rowTableId,
|
||||
fieldName,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { notificationStore, datasourceStore } from "../store"
|
||||
import { notificationStore, dataSourceStore } from "../store"
|
||||
import API from "./api"
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ export const executeQuery = async ({ queryId, parameters }) => {
|
|||
notificationStore.danger("An error has occurred")
|
||||
} else if (!query.readable) {
|
||||
notificationStore.success("Query executed successfully")
|
||||
datasourceStore.actions.invalidateDatasource(query.datasourceId)
|
||||
dataSourceStore.actions.invalidateDataSource(query.datasourceId)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { notificationStore, datasourceStore } from "../store"
|
||||
import { notificationStore, dataSourceStore } from "../store"
|
||||
import API from "./api"
|
||||
import { fetchTableDefinition } from "./tables"
|
||||
|
||||
|
@ -31,7 +31,7 @@ export const saveRow = async row => {
|
|||
: notificationStore.success("Row saved")
|
||||
|
||||
// Refresh related datasources
|
||||
datasourceStore.actions.invalidateDatasource(row.tableId)
|
||||
dataSourceStore.actions.invalidateDataSource(row.tableId)
|
||||
|
||||
return res
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export const updateRow = async row => {
|
|||
: notificationStore.success("Row updated")
|
||||
|
||||
// Refresh related datasources
|
||||
datasourceStore.actions.invalidateDatasource(row.tableId)
|
||||
dataSourceStore.actions.invalidateDataSource(row.tableId)
|
||||
|
||||
return res
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export const deleteRow = async ({ tableId, rowId, revId }) => {
|
|||
: notificationStore.success("Row deleted")
|
||||
|
||||
// Refresh related datasources
|
||||
datasourceStore.actions.invalidateDatasource(tableId)
|
||||
dataSourceStore.actions.invalidateDataSource(tableId)
|
||||
|
||||
return res
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ export const deleteRows = async ({ tableId, rows }) => {
|
|||
: notificationStore.success(`${rows.length} row(s) deleted`)
|
||||
|
||||
// Refresh related datasources
|
||||
datasourceStore.actions.invalidateDatasource(tableId)
|
||||
dataSourceStore.actions.invalidateDataSource(tableId)
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
{
|
||||
type: ActionTypes.RefreshDatasource,
|
||||
callback: () => authStore.actions.fetchUser(),
|
||||
metadata: { datasource: { type: "table", tableId: TableNames.USERS } },
|
||||
metadata: { dataSource: { type: "table", tableId: TableNames.USERS } },
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { getContext, setContext, onMount } from "svelte"
|
||||
import { datasourceStore, createContextStore } from "../store"
|
||||
import { dataSourceStore, createContextStore } from "../store"
|
||||
import { ActionTypes } from "../constants"
|
||||
import { generate } from "shortid"
|
||||
|
||||
|
@ -31,9 +31,9 @@
|
|||
// Register any "refresh datasource" actions with a singleton store
|
||||
// so we can easily refresh data at all levels for any datasource
|
||||
if (type === ActionTypes.RefreshDatasource) {
|
||||
const { datasource } = metadata || {}
|
||||
datasourceStore.actions.registerDatasource(
|
||||
datasource,
|
||||
const { dataSource } = metadata || {}
|
||||
dataSourceStore.actions.registerDataSource(
|
||||
dataSource,
|
||||
instanceId,
|
||||
callback
|
||||
)
|
||||
|
@ -48,7 +48,7 @@
|
|||
instanceId = generate()
|
||||
|
||||
// Unregister all datasource instances when unmounting this provider
|
||||
return () => datasourceStore.actions.unregisterInstance(instanceId)
|
||||
return () => dataSourceStore.actions.unregisterInstance(instanceId)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { notificationStore } from "./notification"
|
||||
|
||||
export const createDataSourceStore = () => {
|
||||
const store = writable([])
|
||||
|
||||
// Registers a new dataSource instance
|
||||
const registerDataSource = (dataSource, instanceId, refresh) => {
|
||||
if (!dataSource || !instanceId || !refresh) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a list of all relevant dataSource IDs which would require that
|
||||
// this dataSource is refreshed
|
||||
let dataSourceIds = []
|
||||
|
||||
// Extract table ID
|
||||
if (dataSource.type === "table" || dataSource.type === "view") {
|
||||
if (dataSource.tableId) {
|
||||
dataSourceIds.push(dataSource.tableId)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract both table IDs from both sides of the relationship
|
||||
else if (dataSource.type === "link") {
|
||||
if (dataSource.rowTableId) {
|
||||
dataSourceIds.push(dataSource.rowTableId)
|
||||
}
|
||||
if (dataSource.tableId) {
|
||||
dataSourceIds.push(dataSource.tableId)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the dataSource ID (not the query ID) for queries
|
||||
else if (dataSource.type === "query") {
|
||||
if (dataSource.dataSourceId) {
|
||||
dataSourceIds.push(dataSource.dataSourceId)
|
||||
}
|
||||
}
|
||||
|
||||
// Store configs for each relevant dataSource ID
|
||||
if (dataSourceIds.length) {
|
||||
store.update(state => {
|
||||
dataSourceIds.forEach(id => {
|
||||
state.push({
|
||||
dataSourceId: id,
|
||||
instanceId,
|
||||
refresh,
|
||||
})
|
||||
})
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Removes all registered dataSource instances belonging to a particular
|
||||
// instance ID
|
||||
const unregisterInstance = instanceId => {
|
||||
store.update(state => {
|
||||
return state.filter(instance => instance.instanceId !== instanceId)
|
||||
})
|
||||
}
|
||||
|
||||
// Invalidates a specific dataSource ID by refreshing all instances
|
||||
// which depend on data from that dataSource
|
||||
const invalidateDataSource = dataSourceId => {
|
||||
const relatedInstances = get(store).filter(instance => {
|
||||
return instance.dataSourceId === dataSourceId
|
||||
})
|
||||
if (relatedInstances?.length) {
|
||||
notificationStore.blockNotifications(1000)
|
||||
}
|
||||
relatedInstances?.forEach(instance => {
|
||||
instance.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { registerDataSource, unregisterInstance, invalidateDataSource },
|
||||
}
|
||||
}
|
||||
|
||||
export const dataSourceStore = createDataSourceStore()
|
|
@ -1,84 +0,0 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { notificationStore } from "./notification"
|
||||
|
||||
export const createDatasourceStore = () => {
|
||||
const store = writable([])
|
||||
|
||||
// Registers a new datasource instance
|
||||
const registerDatasource = (datasource, instanceId, refresh) => {
|
||||
if (!datasource || !instanceId || !refresh) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a list of all relevant datasource IDs which would require that
|
||||
// this datasource is refreshed
|
||||
let datasourceIds = []
|
||||
|
||||
// Extract table ID
|
||||
if (datasource.type === "table" || datasource.type === "view") {
|
||||
if (datasource.tableId) {
|
||||
datasourceIds.push(datasource.tableId)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract both table IDs from both sides of the relationship
|
||||
else if (datasource.type === "link") {
|
||||
if (datasource.rowTableId) {
|
||||
datasourceIds.push(datasource.rowTableId)
|
||||
}
|
||||
if (datasource.tableId) {
|
||||
datasourceIds.push(datasource.tableId)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the datasource ID (not the query ID) for queries
|
||||
else if (datasource.type === "query") {
|
||||
if (datasource.datasourceId) {
|
||||
datasourceIds.push(datasource.datasourceId)
|
||||
}
|
||||
}
|
||||
|
||||
// Store configs for each relevant datasource ID
|
||||
if (datasourceIds.length) {
|
||||
store.update(state => {
|
||||
datasourceIds.forEach(id => {
|
||||
state.push({
|
||||
datasourceId: id,
|
||||
instanceId,
|
||||
refresh,
|
||||
})
|
||||
})
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Removes all registered datasource instances belonging to a particular
|
||||
// instance ID
|
||||
const unregisterInstance = instanceId => {
|
||||
store.update(state => {
|
||||
return state.filter(instance => instance.instanceId !== instanceId)
|
||||
})
|
||||
}
|
||||
|
||||
// Invalidates a specific datasource ID by refreshing all instances
|
||||
// which depend on data from that datasource
|
||||
const invalidateDatasource = datasourceId => {
|
||||
const relatedInstances = get(store).filter(instance => {
|
||||
return instance.datasourceId === datasourceId
|
||||
})
|
||||
if (relatedInstances?.length) {
|
||||
notificationStore.blockNotifications(1000)
|
||||
}
|
||||
relatedInstances?.forEach(instance => {
|
||||
instance.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { registerDatasource, unregisterInstance, invalidateDatasource },
|
||||
}
|
||||
}
|
||||
|
||||
export const datasourceStore = createDatasourceStore()
|
|
@ -3,7 +3,7 @@ export { notificationStore } from "./notification"
|
|||
export { routeStore } from "./routes"
|
||||
export { screenStore } from "./screens"
|
||||
export { builderStore } from "./builder"
|
||||
export { datasourceStore } from "./datasource"
|
||||
export { dataSourceStore } from "./dataSource"
|
||||
|
||||
// Context stores are layered and duplicated, so it is not a singleton
|
||||
export { createContextStore } from "./context"
|
||||
|
|
|
@ -84,13 +84,11 @@
|
|||
"icon": "ri-list-check-2",
|
||||
"styleable": true,
|
||||
"hasChildren": true,
|
||||
"dataProvider": true,
|
||||
"actions": ["RefreshDatasource"],
|
||||
"settings": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"type": "dataProvider",
|
||||
"label": "Data",
|
||||
"key": "datasource"
|
||||
"key": "dataProviderId"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -103,7 +101,11 @@
|
|||
"label": "Filtering",
|
||||
"key": "filter"
|
||||
}
|
||||
]
|
||||
],
|
||||
"dataContext": {
|
||||
"type": "schema",
|
||||
"dataProviderSetting": "dataProviderId"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"name": "Search",
|
||||
|
@ -1472,5 +1474,44 @@
|
|||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"dataprovider": {
|
||||
"name": "Data Provider",
|
||||
"icon": "ri-database-2-line",
|
||||
"styleable": false,
|
||||
"hasChildren": true,
|
||||
"settings": [
|
||||
{
|
||||
"type": "dataSource",
|
||||
"label": "Data",
|
||||
"key": "dataSource"
|
||||
},
|
||||
{
|
||||
"type": "filter",
|
||||
"label": "Filtering",
|
||||
"key": "filter"
|
||||
}
|
||||
],
|
||||
"dataContext": {
|
||||
"type": "static",
|
||||
"values": [
|
||||
{
|
||||
"label": "Rows",
|
||||
"key": "rows"
|
||||
},
|
||||
{
|
||||
"label": "Rows Length",
|
||||
"key": "rowsLength"
|
||||
},
|
||||
{
|
||||
"label": "Loading",
|
||||
"key": "loading"
|
||||
},
|
||||
{
|
||||
"label": "Loaded",
|
||||
"key": "loaded"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let dataSource
|
||||
export let filter
|
||||
export let sortColumn
|
||||
export let sortOrder
|
||||
export let limit
|
||||
|
||||
const { API, styleable, Provider, ActionTypes } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
// Loading flag every time data is being fetched
|
||||
let loading = false
|
||||
|
||||
// Loading flag for the initial load
|
||||
let loaded = false
|
||||
|
||||
let allRows = []
|
||||
$: fetchData(dataSource)
|
||||
$: filteredRows = filterRows(allRows, filter)
|
||||
$: sortedRows = sortRows(filteredRows, sortColumn, sortOrder)
|
||||
$: rows = limitRows(sortedRows, limit)
|
||||
$: {
|
||||
console.log(allRows)
|
||||
console.log(filteredRows)
|
||||
console.log(sortedRows)
|
||||
console.log(rows)
|
||||
}
|
||||
|
||||
$: actions = [
|
||||
{
|
||||
type: ActionTypes.RefreshDatasource,
|
||||
callback: () => fetchData(dataSource),
|
||||
metadata: { dataSource },
|
||||
},
|
||||
]
|
||||
$: dataContext = {
|
||||
rows,
|
||||
rowsLength: rows.length,
|
||||
loading,
|
||||
loaded,
|
||||
}
|
||||
|
||||
const fetchData = async dataSource => {
|
||||
loading = true
|
||||
allRows = await API.fetchDatasource(dataSource)
|
||||
loading = false
|
||||
loaded = false
|
||||
}
|
||||
|
||||
const filterRows = (rows, filter) => {
|
||||
if (!Object.keys(filter || {}).length) {
|
||||
return rows
|
||||
}
|
||||
let filteredData = [...rows]
|
||||
Object.entries(filter).forEach(([field, value]) => {
|
||||
if (value != null && value !== "") {
|
||||
filteredData = filteredData.filter(row => {
|
||||
return row[field] === value
|
||||
})
|
||||
}
|
||||
})
|
||||
return filteredData
|
||||
}
|
||||
|
||||
const sortRows = (rows, sortColumn, sortOrder) => {
|
||||
if (!sortColumn || !sortOrder) {
|
||||
return rows
|
||||
}
|
||||
return rows.slice().sort((a, b) => {
|
||||
const colA = a[sortColumn]
|
||||
const colB = b[sortColumn]
|
||||
if (sortOrder === "descending") {
|
||||
return colA > colB ? -1 : 1
|
||||
} else {
|
||||
return colA > colB ? 1 : -1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const limitRows = (rows, limit) => {
|
||||
const numLimit = parseFloat(limit)
|
||||
if (isNaN(numLimit)) {
|
||||
return rows
|
||||
}
|
||||
return rows.slice(0, numLimit)
|
||||
}
|
||||
</script>
|
||||
|
||||
<Provider {actions} data={dataContext}>
|
||||
<slot />
|
||||
</Provider>
|
|
@ -1,68 +1,32 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { isEmpty } from "lodash/fp"
|
||||
|
||||
export let datasource
|
||||
export let dataProviderId
|
||||
export let noRowsMessage
|
||||
export let filter
|
||||
|
||||
const { API, styleable, Provider, builderStore, ActionTypes } = getContext(
|
||||
"sdk"
|
||||
)
|
||||
const { API, styleable, builderStore, Provider } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
let rows = []
|
||||
let loaded = false
|
||||
const context = getContext("context")
|
||||
|
||||
$: fetchData(datasource)
|
||||
$: filteredRows = filterRows(rows, filter)
|
||||
$: actions = [
|
||||
{
|
||||
type: ActionTypes.RefreshDatasource,
|
||||
callback: () => fetchData(datasource),
|
||||
metadata: { datasource },
|
||||
},
|
||||
]
|
||||
|
||||
const fetchData = async datasource => {
|
||||
if (!isEmpty(datasource)) {
|
||||
rows = await API.fetchDatasource(datasource)
|
||||
}
|
||||
loaded = true
|
||||
}
|
||||
|
||||
const filterRows = (rows, filter) => {
|
||||
if (!Object.keys(filter || {}).length) {
|
||||
return rows
|
||||
}
|
||||
let filteredData = [...rows]
|
||||
Object.entries(filter).forEach(([field, value]) => {
|
||||
if (value != null && value !== "") {
|
||||
filteredData = filteredData.filter(row => {
|
||||
return row[field] === value
|
||||
})
|
||||
}
|
||||
})
|
||||
return filteredData
|
||||
}
|
||||
$: data = context[dataProviderId]?.rows ?? []
|
||||
$: loaded = context[dataProviderId]?.loaded ?? false
|
||||
</script>
|
||||
|
||||
<Provider {actions}>
|
||||
<div use:styleable={$component.styles}>
|
||||
{#if filteredRows.length > 0}
|
||||
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||
<p><i class="ri-image-line" />Add some components to display.</p>
|
||||
{:else}
|
||||
{#each filteredRows as row}
|
||||
<Provider data={row}>
|
||||
<slot />
|
||||
</Provider>
|
||||
{/each}
|
||||
{/if}
|
||||
{:else if loaded && noRowsMessage}
|
||||
<p><i class="ri-list-check-2" />{noRowsMessage}</p>
|
||||
<div use:styleable={$component.styles}>
|
||||
{#if data.length > 0}
|
||||
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||
<p><i class="ri-image-line" />Add some components to display.</p>
|
||||
{:else}
|
||||
{#each data as row}
|
||||
<Provider data={row}>
|
||||
<slot />
|
||||
</Provider>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</Provider>
|
||||
{:else if loaded && noRowsMessage}
|
||||
<p><i class="ri-list-check-2" />{noRowsMessage}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
p {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { loadSpectrumIcons } from "./spectrum-icons"
|
|||
loadSpectrumIcons()
|
||||
|
||||
export { default as container } from "./Container.svelte"
|
||||
export { default as dataprovider } from "./DataProvider.svelte"
|
||||
export { default as datagrid } from "./grid/Component.svelte"
|
||||
export { default as screenslot } from "./ScreenSlot.svelte"
|
||||
export { default as button } from "./Button.svelte"
|
||||
|
|
Loading…
Reference in New Issue