Add lots of new work on client library rewrite
This commit is contained in:
parent
3a981ac256
commit
e11656fa24
|
@ -15,17 +15,17 @@
|
|||
"svelte-spa-router": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@budibase/standard-components": "^0.3.8",
|
||||
"@rollup/plugin-alias": "^3.1.1",
|
||||
"@rollup/plugin-commonjs": "^16.0.0",
|
||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"jsdom": "^16.0.1",
|
||||
"rollup": "^2.11.2",
|
||||
"rollup-plugin-alias": "^2.2.0",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
"rollup": "^2.33.2",
|
||||
"rollup-plugin-node-builtins": "^2.1.2",
|
||||
"rollup-plugin-node-globals": "^1.4.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-svelte": "^6.1.1",
|
||||
"svelte": "3.29.0",
|
||||
"svelte-jester": "^1.0.6"
|
||||
"svelte": "^3.29.0"
|
||||
},
|
||||
"gitHead": "e4e053cb6ff9a0ddc7115b44ccaa24b8ec41fb9a"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import resolve from "rollup-plugin-node-resolve"
|
||||
import commonjs from "rollup-plugin-commonjs"
|
||||
import alias from "@rollup/plugin-alias"
|
||||
import commonjs from "@rollup/plugin-commonjs"
|
||||
import resolve from "@rollup/plugin-node-resolve"
|
||||
import builtins from "rollup-plugin-node-builtins"
|
||||
import nodeglobals from "rollup-plugin-node-globals"
|
||||
import svelte from "rollup-plugin-svelte"
|
||||
import alias from "rollup-plugin-alias"
|
||||
import path from "path"
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH
|
||||
|
@ -13,13 +12,7 @@ export default {
|
|||
input: "src/index.js",
|
||||
output: [
|
||||
{
|
||||
sourcemap: true,
|
||||
format: "iife",
|
||||
name: "app",
|
||||
file: `./dist/budibase-client.js`,
|
||||
},
|
||||
{
|
||||
file: "dist/budibase-client.esm.mjs",
|
||||
file: "dist/budibase-client.js",
|
||||
format: "esm",
|
||||
sourcemap: "inline",
|
||||
},
|
||||
|
@ -45,7 +38,6 @@ export default {
|
|||
}),
|
||||
commonjs(),
|
||||
builtins(),
|
||||
nodeglobals(),
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
import { onMount } from "svelte"
|
||||
import { componentStore } from "../store"
|
||||
import Component from "./Component.svelte"
|
||||
import { getValidProps } from "../utils"
|
||||
|
||||
let frontendDefinition
|
||||
let loaded = false
|
||||
$: pageProps = frontendDefinition?.page?.props
|
||||
$: pageDefinition = frontendDefinition?.page?.props
|
||||
|
||||
onMount(async () => {
|
||||
frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
||||
|
@ -17,9 +16,5 @@
|
|||
</script>
|
||||
|
||||
{#if loaded}
|
||||
<Component
|
||||
component={pageProps._component}
|
||||
props={getValidProps(pageProps)}
|
||||
children={pageProps._children}
|
||||
styles={pageProps._styles.normal} />
|
||||
<Component definition={pageDefinition} />
|
||||
{/if}
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
<script>
|
||||
import { componentStore } from "../store"
|
||||
import { buildStyle, getValidProps } from "../utils"
|
||||
import { getValidProps } from "../utils"
|
||||
|
||||
export let props
|
||||
export let children
|
||||
export let component
|
||||
export let styles
|
||||
export let definition = {}
|
||||
|
||||
$: componentConstructor = componentStore.actions.getComponent(component)
|
||||
$: console.log("Rendering: " + component)
|
||||
$: componentProps = getValidProps(definition)
|
||||
$: children = definition._children
|
||||
$: componentName = extractComponentName(definition._component)
|
||||
$: constructor = componentStore.actions.getComponent(componentName)
|
||||
$: id = `${componentName}-${definition._id}`
|
||||
$: styles = { ...definition._styles, id }
|
||||
|
||||
// Extracts the actual component name from the library name
|
||||
function extractComponentName(name) {
|
||||
console.log(name)
|
||||
if (name == null) {
|
||||
console.log(definition)
|
||||
}
|
||||
const split = name.split("/")
|
||||
return split[split.length - 1]
|
||||
}
|
||||
|
||||
$: console.log("Rendering: " + componentName)
|
||||
</script>
|
||||
|
||||
{#if componentConstructor}
|
||||
<div
|
||||
style={buildStyle(styles)}
|
||||
data-bb-component={component}
|
||||
data-bb-name={component._instanceName}>
|
||||
<svelte:component this={componentConstructor} {...props}>
|
||||
{#if children && children.length}
|
||||
{#each children as child}
|
||||
<svelte:self
|
||||
props={getValidProps(child)}
|
||||
component={child._component}
|
||||
children={child._children}
|
||||
styles={child._styles.normal} />
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:component>
|
||||
</div>
|
||||
{#if constructor}
|
||||
<svelte:component this={constructor} {...componentProps} {styles}>
|
||||
{#if children && children.length}
|
||||
{#each children as child}
|
||||
<svelte:self definition={child} />
|
||||
{/each}
|
||||
{/if}
|
||||
</svelte:component>
|
||||
{/if}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { onMount, setContext } from "svelte"
|
||||
import Router from "svelte-spa-router"
|
||||
import { routeStore, screenStore } from "@budibase/component-sdk"
|
||||
import { routeStore, screenStore, styleable } from "@budibase/component-sdk"
|
||||
import Screen from "./Screen.svelte"
|
||||
|
||||
export let styles
|
||||
let routes
|
||||
|
||||
onMount(() => {
|
||||
|
@ -14,17 +15,23 @@
|
|||
await routeStore.actions.fetchRoutes()
|
||||
await screenStore.actions.fetchScreens()
|
||||
routes = {}
|
||||
$routeStore.forEach(route => {
|
||||
$routeStore.routes.forEach(route => {
|
||||
routes[route.path] = Screen
|
||||
})
|
||||
|
||||
// Add catch-all route so that we serve the Screen component always
|
||||
routes["*"] = Screen
|
||||
}
|
||||
|
||||
function test(a, b) {
|
||||
console.log(a)
|
||||
console.log(b)
|
||||
function onRouteLoading({ detail }) {
|
||||
routeStore.actions.setActiveRoute(detail.route)
|
||||
}
|
||||
|
||||
setContext("test", 123)
|
||||
</script>
|
||||
|
||||
{#if routes}
|
||||
<Router {routes} on:routeEvent={test} />
|
||||
<div use:styleable={styles}>
|
||||
<Router on:routeLoading={onRouteLoading} {routes} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
<script>
|
||||
import { screenStore } from "@budibase/component-sdk"
|
||||
import { location } from "svelte-spa-router"
|
||||
import { screenStore, routeStore } from "@budibase/component-sdk"
|
||||
import Component from "./Component.svelte"
|
||||
import { getValidProps } from "../utils"
|
||||
|
||||
export let params
|
||||
|
||||
// Get the screen definition for the current route
|
||||
$: screenDefinition = screenStore.actions.getScreenByRoute($location)
|
||||
$: screenStore.actions
|
||||
$: screenDefinition = $screenStore.activeScreen
|
||||
|
||||
// Update route params
|
||||
$: routeStore.actions.setRouteParams(params)
|
||||
|
||||
// Redirect to home page if no matching route
|
||||
$: {
|
||||
if (screenDefinition == null) {
|
||||
routeStore.actions.navigate("/")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if screenDefinition}
|
||||
<Component
|
||||
component={screenDefinition.props._component}
|
||||
props={getValidProps(screenDefinition.props)}
|
||||
children={screenDefinition.props._children}
|
||||
styles={screenDefinition.props._styles.normal} />
|
||||
<Component definition={screenDefinition.props} />
|
||||
{/if}
|
||||
|
|
|
@ -2,6 +2,7 @@ import ClientApp from "./components/ClientApp.svelte"
|
|||
|
||||
// Initialise client app
|
||||
const loadBudibase = () => {
|
||||
window.document.body.innerHTML = ""
|
||||
new ClientApp({
|
||||
target: window.document.body,
|
||||
})
|
||||
|
|
|
@ -2,10 +2,8 @@ import { writable, get } from "svelte/store"
|
|||
import { getAppId } from "@budibase/component-sdk"
|
||||
import Router from "../components/Router.svelte"
|
||||
|
||||
const initialState = {}
|
||||
|
||||
export const createComponentStore = () => {
|
||||
const store = writable(initialState)
|
||||
const createComponentStore = () => {
|
||||
const store = writable({})
|
||||
|
||||
/**
|
||||
* Loads the component library from the server
|
||||
|
@ -25,14 +23,13 @@ export const createComponentStore = () => {
|
|||
if (!componentName) {
|
||||
return null
|
||||
}
|
||||
const split = componentName.split("/")
|
||||
const strippedName = split[split.length - 1]
|
||||
|
||||
// Edge case for screen slot
|
||||
if (strippedName === "screenslot") {
|
||||
if (componentName === "screenslot") {
|
||||
return Router
|
||||
}
|
||||
return get(store)[strippedName]
|
||||
|
||||
return get(store)[componentName]
|
||||
}
|
||||
|
||||
// Attach actions to the store
|
||||
|
@ -40,3 +37,5 @@ export const createComponentStore = () => {
|
|||
|
||||
return store
|
||||
}
|
||||
|
||||
export const componentStore = createComponentStore()
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
import { createComponentStore } from "./components"
|
||||
|
||||
export const componentStore = createComponentStore()
|
||||
export { componentStore } from "./components"
|
||||
|
|
|
@ -1,16 +1,3 @@
|
|||
/**
|
||||
* Builds a style string from a style object.
|
||||
*/
|
||||
export const buildStyle = styles => {
|
||||
let str = ""
|
||||
Object.entries(styles).forEach(([style, value]) => {
|
||||
if (style && value) {
|
||||
str += `${style}: ${value}; `
|
||||
}
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all valid props from a component definition that should be passed to
|
||||
* its actual component instance.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"rollup-plugin-svelte": "^6.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte": "^3.29.0",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,21 +14,19 @@ export const fetchRow = async ({ tableId, rowId }) => {
|
|||
/**
|
||||
* Creates a row in a table.
|
||||
*/
|
||||
export const saveRow = async (params, state) => {
|
||||
export const saveRow = async row => {
|
||||
return await api.post({
|
||||
url: `/api/${params.tableId}/rows`,
|
||||
body: makeRowRequestBody(params, state),
|
||||
url: `/api/${row.tableId}/rows`,
|
||||
body: row,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a row in a table.
|
||||
*/
|
||||
export const updateRow = async (params, state) => {
|
||||
const row = makeRowRequestBody(params, state)
|
||||
row._id = params._id
|
||||
export const updateRow = async row => {
|
||||
return await api.patch({
|
||||
url: `/api/${params.tableId}/rows/${params._id}`,
|
||||
url: `/api/${row.tableId}/rows/${row._id}`,
|
||||
body: row,
|
||||
})
|
||||
}
|
||||
|
@ -55,44 +53,6 @@ export const deleteRows = async ({ tableId, rows }) => {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitises and parses column types when saving and updating rows.
|
||||
*/
|
||||
const makeRowRequestBody = (parameters, state) => {
|
||||
// start with the row thats currently in context
|
||||
const body = { ...(state.data || {}) }
|
||||
|
||||
// dont send the table
|
||||
if (body._table) delete body._table
|
||||
|
||||
// then override with supplied parameters
|
||||
if (parameters.fields) {
|
||||
for (let fieldName of Object.keys(parameters.fields)) {
|
||||
const field = parameters.fields[fieldName]
|
||||
|
||||
// ensure fields sent are of the correct type
|
||||
if (field.type === "boolean") {
|
||||
if (field.value === "true") body[fieldName] = true
|
||||
if (field.value === "false") body[fieldName] = false
|
||||
} else if (field.type === "number") {
|
||||
const val = parseFloat(field.value)
|
||||
if (!isNaN(val)) {
|
||||
body[fieldName] = val
|
||||
}
|
||||
} else if (field.type === "datetime") {
|
||||
const date = new Date(field.value)
|
||||
if (!isNaN(date.getTime())) {
|
||||
body[fieldName] = date.toISOString()
|
||||
}
|
||||
} else {
|
||||
body[fieldName] = field.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches rows which contain certain field types so that they can
|
||||
* be properly displayed.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export const DataProvider = "bb-data-provider"
|
|
@ -0,0 +1,24 @@
|
|||
import { writable } from "svelte/store"
|
||||
|
||||
export const createDataProviderContext = () => {
|
||||
const store = writable({
|
||||
rows: [],
|
||||
table: null,
|
||||
})
|
||||
const setRows = rows => {
|
||||
store.update(state => {
|
||||
state.rows = rows
|
||||
return state
|
||||
})
|
||||
}
|
||||
const setTable = table => {
|
||||
store.update(state => {
|
||||
state.table = table
|
||||
return state
|
||||
})
|
||||
}
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { setRows, setTable },
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
export const RouterContext = "bb-router"
|
||||
export * from "./dataProvider"
|
||||
export * as ContextTypes from "./contextTypes"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export * from "./api"
|
||||
export * from "./store"
|
||||
export * as ContextTypes from "./context"
|
||||
export { getAppId } from "./utils"
|
||||
export { link } from "svelte-spa-router"
|
||||
export * from "./context"
|
||||
export * from "./utils"
|
||||
export { link as linkable } from "svelte-spa-router"
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { localStorageStore } from "../../../builder/src/builderStore/store/localStorage"
|
||||
import * as api from "../api"
|
||||
import { getAppId } from "../utils"
|
||||
|
||||
const initialState = ""
|
||||
import { writable } from "svelte/store"
|
||||
|
||||
export const createAuthStore = () => {
|
||||
const store = localStorageStore("budibase:token", initialState)
|
||||
const store = writable("")
|
||||
|
||||
/**
|
||||
* Logs a user in.
|
||||
|
@ -22,7 +20,7 @@ export const createAuthStore = () => {
|
|||
* Logs a user out.
|
||||
*/
|
||||
const logOut = () => {
|
||||
store.set(initialState)
|
||||
store.set("")
|
||||
|
||||
// Expire any cookies
|
||||
const appId = getAppId()
|
||||
|
@ -33,10 +31,14 @@ export const createAuthStore = () => {
|
|||
}
|
||||
}
|
||||
|
||||
store.actions = {
|
||||
logIn,
|
||||
logOut,
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { logIn, logOut },
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
if (!window.bbSDKAuthStore) {
|
||||
window.bbSDKAuthStore = createAuthStore()
|
||||
}
|
||||
|
||||
export const authStore = window.bbSDKAuthStore
|
||||
|
|
|
@ -37,11 +37,14 @@ export const createConfigStore = () => {
|
|||
handler && handler(error)
|
||||
}
|
||||
|
||||
store.actions = {
|
||||
initialise,
|
||||
reset,
|
||||
handleError,
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { initialise, reset, handleError },
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
if (!window.bbSDKConfigStore) {
|
||||
window.bbSDKConfigStore = createConfigStore()
|
||||
}
|
||||
|
||||
export const configStore = window.bbSDKConfigStore
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import { createConfigStore } from "./config"
|
||||
import { createAuthStore } from "./auth"
|
||||
import { createRouteStore } from "./routes"
|
||||
import { createScreenStore } from "./screens"
|
||||
|
||||
export const configStore = createConfigStore()
|
||||
export const authStore = createAuthStore()
|
||||
export const routeStore = createRouteStore()
|
||||
export const screenStore = createScreenStore()
|
||||
export { configStore } from "./config"
|
||||
export { authStore } from "./auth"
|
||||
export { routeStore } from "./routes"
|
||||
export { screenStore } from "./screens"
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { writable } from "svelte/store"
|
||||
import { push } from "svelte-spa-router"
|
||||
|
||||
const initialState = []
|
||||
|
||||
export const createRouteStore = () => {
|
||||
const initialState = {
|
||||
routes: [],
|
||||
routeParams: {},
|
||||
activeRoute: null,
|
||||
}
|
||||
const store = writable(initialState)
|
||||
|
||||
const fetchRoutes = () => {
|
||||
|
@ -12,10 +15,35 @@ export const createRouteStore = () => {
|
|||
path: screen.route,
|
||||
screenId: screen._id,
|
||||
}))
|
||||
store.set(routes)
|
||||
store.update(state => {
|
||||
state.routes = routes
|
||||
return state
|
||||
})
|
||||
}
|
||||
const setRouteParams = routeParams => {
|
||||
console.log("new route params: ")
|
||||
console.log(routeParams)
|
||||
store.update(state => {
|
||||
state.routeParams = routeParams
|
||||
return state
|
||||
})
|
||||
}
|
||||
const setActiveRoute = route => {
|
||||
store.update(state => {
|
||||
state.activeRoute = route
|
||||
return state
|
||||
})
|
||||
}
|
||||
const navigate = push
|
||||
store.actions = { fetchRoutes, navigate }
|
||||
|
||||
return store
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { fetchRoutes, navigate, setRouteParams, setActiveRoute },
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.bbSDKRouteStore) {
|
||||
window.bbSDKRouteStore = createRouteStore()
|
||||
}
|
||||
|
||||
export const routeStore = window.bbSDKRouteStore
|
||||
|
|
|
@ -1,25 +1,33 @@
|
|||
import { writable, derived, get } from "svelte/store"
|
||||
|
||||
const initialState = []
|
||||
import { writable, derived } from "svelte/store"
|
||||
import { routeStore } from "./routes"
|
||||
|
||||
export const createScreenStore = () => {
|
||||
const store = writable(initialState)
|
||||
const routeLookupMap = derived(store, $screens => {
|
||||
let map = {}
|
||||
$screens.forEach(screen => {
|
||||
map[screen.route] = screen
|
||||
})
|
||||
return map
|
||||
console.log("CREATE SCREEN STORE")
|
||||
|
||||
const screens = writable([])
|
||||
const store = derived([screens, routeStore], ([$screens, $routeStore]) => {
|
||||
const activeScreen = $screens.find(
|
||||
screen => screen.route === $routeStore.activeRoute
|
||||
)
|
||||
return {
|
||||
screens: $screens,
|
||||
activeScreen,
|
||||
}
|
||||
})
|
||||
|
||||
const fetchScreens = () => {
|
||||
const frontendDefinition = window["##BUDIBASE_FRONTEND_DEFINITION##"]
|
||||
store.set(frontendDefinition.screens)
|
||||
screens.set(frontendDefinition.screens)
|
||||
}
|
||||
const getScreenByRoute = path => {
|
||||
return get(routeLookupMap)[path]
|
||||
}
|
||||
store.actions = { fetchScreens, getScreenByRoute }
|
||||
|
||||
return store
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
actions: { fetchScreens },
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.bbSDKScreenStore) {
|
||||
window.bbSDKScreenStore = createScreenStore()
|
||||
}
|
||||
|
||||
export const screenStore = window.bbSDKScreenStore
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export { getAppId } from "./getAppId"
|
||||
export { styleable } from "./styleable"
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
const buildStyleString = styles => {
|
||||
let str = ""
|
||||
Object.entries(styles).forEach(([style, value]) => {
|
||||
if (style && value) {
|
||||
str += `${style}: ${value}; `
|
||||
}
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* Svelte action to apply correct component styles.
|
||||
*/
|
||||
export const styleable = (node, styles = {}) => {
|
||||
const normalStyles = styles.normal || {}
|
||||
const hoverStyles = {
|
||||
...normalStyles,
|
||||
...styles.hover,
|
||||
}
|
||||
|
||||
function applyNormalStyles() {
|
||||
node.style = buildStyleString(normalStyles)
|
||||
}
|
||||
|
||||
function applyHoverStyles() {
|
||||
node.style = buildStyleString(hoverStyles)
|
||||
}
|
||||
|
||||
// Add listeners to toggle hover styles
|
||||
node.addEventListener("mouseover", applyHoverStyles)
|
||||
node.addEventListener("mouseout", applyNormalStyles)
|
||||
|
||||
// Apply normal styles initially
|
||||
applyNormalStyles()
|
||||
|
||||
// Also apply data tags so we know how to reference each component
|
||||
node.setAttribute("data-bb-id", styles.id)
|
||||
|
||||
return {
|
||||
// Clean up event listeners when component is destroyed
|
||||
destroy: () => {
|
||||
node.removeEventListener("mouseover", applyHoverStyles)
|
||||
node.removeEventListener("mouseout", applyNormalStyles)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -828,6 +828,11 @@ svelte-spa-router@^3.0.5:
|
|||
dependencies:
|
||||
regexparam "1.3.0"
|
||||
|
||||
svelte@^3.29.0:
|
||||
version "3.29.7"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.29.7.tgz#e254eb2d0d609ce0fd60f052d444ac4a66d90f7d"
|
||||
integrity sha512-rx0g311kBODvEWUU01DFBUl3MJuJven04bvTVFUG/w0On/wuj0PajQY/QlXcJndFxG+W1s8iXKaB418tdHWc3A==
|
||||
|
||||
typedarray-to-buffer@~1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz#9bb8ba0e841fb3f4cf1fe7c245e9f3fa8a5fe99c"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/standard-components",
|
||||
"svelte": "src/index.svelte",
|
||||
"svelte": "src/index.js",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"scripts": {
|
||||
|
@ -15,13 +15,13 @@
|
|||
"devDependencies": {
|
||||
"@budibase/client": "^0.3.6",
|
||||
"@rollup/plugin-alias": "^3.1.1",
|
||||
"@rollup/plugin-commonjs": "^11.1.0",
|
||||
"@rollup/plugin-commonjs": "^16.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||
"@rollup/plugin-replace": "^2.3.4",
|
||||
"lodash": "^4.17.15",
|
||||
"rollup": "^2.11.2",
|
||||
"rollup-plugin-commonjs": "^10.0.2",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-livereload": "^1.0.1",
|
||||
"rollup-plugin-node-resolve": "^5.0.0",
|
||||
"rollup-plugin-postcss": "^3.1.5",
|
||||
"rollup-plugin-svelte": "^6.1.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import svelte from "rollup-plugin-svelte"
|
||||
import resolve from "rollup-plugin-node-resolve"
|
||||
import commonjs from "@rollup/plugin-commonjs"
|
||||
import postcss from "rollup-plugin-postcss"
|
||||
import alias from "@rollup/plugin-alias"
|
||||
import commonjs from "@rollup/plugin-commonjs"
|
||||
import resolve from "@rollup/plugin-node-resolve"
|
||||
import replace from "@rollup/plugin-replace"
|
||||
import svelte from "rollup-plugin-svelte"
|
||||
import postcss from "rollup-plugin-postcss"
|
||||
import { terser } from "rollup-plugin-terser"
|
||||
import path from "path"
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH
|
||||
const lodash_fp_exports = ["isEmpty"]
|
||||
const projectRootDir = path.resolve(__dirname)
|
||||
|
||||
export default {
|
||||
|
@ -17,7 +17,7 @@ export default {
|
|||
file: "dist/index.js",
|
||||
format: "esm",
|
||||
name: "budibaseStandardComponents",
|
||||
sourcemap: true,
|
||||
sourcemap: false,
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
|
@ -33,19 +33,18 @@ export default {
|
|||
],
|
||||
}),
|
||||
production && terser(),
|
||||
postcss({
|
||||
plugins: [],
|
||||
}),
|
||||
postcss(),
|
||||
svelte({
|
||||
hydratable: true,
|
||||
dev: !production,
|
||||
}),
|
||||
resolve({
|
||||
browser: true,
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"lodash/fp": lodash_fp_exports,
|
||||
},
|
||||
commonjs(),
|
||||
// Fix for https://github.com/sveltejs/svelte/issues/3165
|
||||
replace({
|
||||
"outros.c.push":
|
||||
"if (outros === undefined) { block.o(local); return }\noutros.c.push",
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
<script>
|
||||
import { styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let className = "default"
|
||||
export let disabled = false
|
||||
export let text
|
||||
export let styles
|
||||
|
||||
export let _bb
|
||||
let theButton
|
||||
|
||||
$: if (_bb.props._children && _bb.props._children.length > 0)
|
||||
theButton && _bb.attachChildren(theButton)
|
||||
|
||||
const clickHandler = () => {
|
||||
_bb.call("onClick")
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
bind:this={theButton}
|
||||
class="default"
|
||||
disabled={disabled || false}
|
||||
on:click|once={clickHandler}>
|
||||
{#if !_bb.props._children || _bb.props._children.length === 0}{text}{/if}
|
||||
use:styleable={styles}>
|
||||
{text}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
|
@ -37,28 +32,4 @@
|
|||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: var(--border);
|
||||
}
|
||||
|
||||
.color {
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.background {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.hoverBorder:hover {
|
||||
border: var(--hoverBorder);
|
||||
}
|
||||
|
||||
.hoverColor:hover {
|
||||
color: var(--hoverColor);
|
||||
}
|
||||
|
||||
.hoverBack:hover {
|
||||
background: var(--hoverBackground);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,58 +1,61 @@
|
|||
<script>
|
||||
import { styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let className = ""
|
||||
export let type = "div"
|
||||
|
||||
$: console.log(type)
|
||||
export let styles
|
||||
</script>
|
||||
|
||||
{#if type === 'div'}
|
||||
<div>
|
||||
<div use:styleable={styles}>
|
||||
<slot />
|
||||
</div>
|
||||
{:else if type === 'header'}
|
||||
<header>
|
||||
<header use:styleable={styles}>
|
||||
<slot />
|
||||
</header>
|
||||
{:else if type === 'main'}
|
||||
<main>
|
||||
<main use:styleable={styles}>
|
||||
<slot />
|
||||
</main>
|
||||
{:else if type === 'footer'}
|
||||
<footer>
|
||||
<footer use:styleable={styles}>
|
||||
<slot />
|
||||
</footer>
|
||||
{:else if type === 'aside'}
|
||||
<aside>
|
||||
<aside use:styleable={styles}>
|
||||
<slot />
|
||||
</aside>
|
||||
{:else if type === 'summary'}
|
||||
<summary>
|
||||
<summary use:styleable={styles}>
|
||||
<slot />
|
||||
</summary>
|
||||
{:else if type === 'details'}
|
||||
<details>
|
||||
<details use:styleable={styles}>
|
||||
<slot />
|
||||
</details>
|
||||
{:else if type === 'article'}
|
||||
<article>
|
||||
<article use:styleable={styles}>
|
||||
<slot />
|
||||
</article>
|
||||
{:else if type === 'nav'}
|
||||
<nav>
|
||||
<nav use:styleable={styles}>
|
||||
<slot />
|
||||
</nav>
|
||||
{:else if type === 'mark'}
|
||||
<mark><slot /></mark>
|
||||
<mark use:styleable={styles}>
|
||||
<slot />
|
||||
</mark>
|
||||
{:else if type === 'figure'}
|
||||
<figure>
|
||||
<figure use:styleable={styles}>
|
||||
<slot />
|
||||
</figure>
|
||||
{:else if type === 'figcaption'}
|
||||
<figcaption>
|
||||
<figcaption use:styleable={styles}>
|
||||
<slot />
|
||||
</figcaption>
|
||||
{:else if type === 'paragraph'}
|
||||
<p>
|
||||
<p use:styleable={styles}>
|
||||
<slot />
|
||||
</p>
|
||||
{/if}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import Form from "./Form.svelte"
|
||||
|
||||
export let _bb
|
||||
export let table
|
||||
export let title
|
||||
export let buttonText
|
||||
export let styles
|
||||
</script>
|
||||
|
||||
<Form {_bb} {table} {title} {buttonText} wide={true} />
|
||||
<Form {styles} {table} {title} {buttonText} wide />
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
<script>
|
||||
import { styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let embed
|
||||
export let styles
|
||||
</script>
|
||||
|
||||
{@html embed}
|
||||
<div use:styleable={styles}>
|
||||
{@html embed}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
}
|
||||
div :global(> *) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,54 +1,57 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { Label, DatePicker, Input, Select, Toggle } from "@budibase/bbui"
|
||||
import Dropzone from "./attachments/Dropzone.svelte"
|
||||
import LinkedRowSelector from "./LinkedRowSelector.svelte"
|
||||
import ErrorsBox from "./ErrorsBox.svelte"
|
||||
import { capitalise } from "./helpers"
|
||||
import { styleable, ContextTypes, screenStore } from "@budibase/component-sdk"
|
||||
|
||||
export let _bb
|
||||
export let table
|
||||
export let wide = false
|
||||
export let styles
|
||||
|
||||
let store = _bb.store
|
||||
let schema = {}
|
||||
let rowId
|
||||
let errors = {}
|
||||
|
||||
$: schema = $store.data && $store.data._table && $store.data._table.schema
|
||||
const dataProviderStore = getContext(ContextTypes.DataProvider)
|
||||
$: row = $dataProviderStore.rows[0]
|
||||
$: schema = $dataProviderStore.table && $dataProviderStore.table.schema
|
||||
$: fields = schema ? Object.keys(schema) : []
|
||||
</script>
|
||||
|
||||
<div class="form-content">
|
||||
<ErrorsBox errors={$store.saveRowErrors || {}} />
|
||||
<div class="form-content" use:styleable={styles}>
|
||||
<!-- <ErrorsBox errors={$store.saveRowErrors || {}} />-->
|
||||
{#each fields as field}
|
||||
<div class="form-field" class:wide>
|
||||
{#if !(schema[field].type === 'boolean' && !wide)}
|
||||
<Label extraSmall={!wide} grey>{capitalise(schema[field].name)}</Label>
|
||||
{/if}
|
||||
{#if schema[field].type === 'options'}
|
||||
<Select secondary bind:value={$store.data[field]}>
|
||||
<Select secondary bind:value={row[field]}>
|
||||
<option value="">Choose an option</option>
|
||||
{#each schema[field].constraints.inclusion as opt}
|
||||
<option>{opt}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
{:else if schema[field].type === 'datetime'}
|
||||
<DatePicker bind:value={$store.data[field]} />
|
||||
<DatePicker bind:value={row[field]} />
|
||||
{:else if schema[field].type === 'boolean'}
|
||||
<Toggle
|
||||
text={wide ? null : capitalise(schema[field].name)}
|
||||
bind:checked={$store.data[field]} />
|
||||
bind:checked={row[field]} />
|
||||
{:else if schema[field].type === 'number'}
|
||||
<Input type="number" bind:value={$store.data[field]} />
|
||||
<Input type="number" bind:value={row[field]} />
|
||||
{:else if schema[field].type === 'string'}
|
||||
<Input bind:value={$store.data[field]} />
|
||||
<Input bind:value={row[field]} />
|
||||
{:else if schema[field].type === 'attachment'}
|
||||
<Dropzone bind:files={$store.data[field]} />
|
||||
<Dropzone bind:files={row[field]} />
|
||||
{:else if schema[field].type === 'link'}
|
||||
<LinkedRowSelector
|
||||
secondary
|
||||
showLabel={false}
|
||||
bind:linkedRows={$store.data[field]}
|
||||
bind:linkedRows={row[field]}
|
||||
schema={schema[field]} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
<script>
|
||||
import { styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let className = ""
|
||||
export let type
|
||||
export let text = ""
|
||||
|
||||
export let _bb
|
||||
|
||||
let containerElement
|
||||
|
||||
$: containerElement &&
|
||||
!text &&
|
||||
_bb.props.children &&
|
||||
_bb.props.children.length &&
|
||||
_bb.attachChildren(containerElement)
|
||||
export let styles
|
||||
</script>
|
||||
|
||||
{#if type === 'h1'}
|
||||
<h1 class={className} bind:this={containerElement}>{text}</h1>
|
||||
<h1 class={className} use:styleable={styles}>{text}</h1>
|
||||
{:else if type === 'h2'}
|
||||
<h2 class={className} bind:this={containerElement}>{text}</h2>
|
||||
<h2 class={className} use:styleable={styles}>{text}</h2>
|
||||
{:else if type === 'h3'}
|
||||
<h3 class={className} bind:this={containerElement}>{text}</h3>
|
||||
<h3 class={className} use:styleable={styles}>{text}</h3>
|
||||
{:else if type === 'h4'}
|
||||
<h4 class={className} bind:this={containerElement}>{text}</h4>
|
||||
<h4 class={className} use:styleable={styles}>{text}</h4>
|
||||
{:else if type === 'h5'}
|
||||
<h5 class={className} bind:this={containerElement}>{text}</h5>
|
||||
<h5 class={className} use:styleable={styles}>{text}</h5>
|
||||
{:else if type === 'h6'}
|
||||
<h6 class={className} bind:this={containerElement}>{text}</h6>
|
||||
<h6 class={className} use:styleable={styles}>{text}</h6>
|
||||
{/if}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { link } from "@budibase/component-sdk"
|
||||
import { linkable } from "@budibase/component-sdk"
|
||||
|
||||
export let url = ""
|
||||
export let text = ""
|
||||
|
@ -8,7 +8,7 @@
|
|||
$: target = openInNewTab ? "_blank" : "_self"
|
||||
</script>
|
||||
|
||||
<a href={url} use:link {target}>
|
||||
<a href={url} use:linkable {target}>
|
||||
{text}
|
||||
<slot />
|
||||
</a>
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { fetchDatasource } from "@budibase/component-sdk"
|
||||
import { onMount, setContext } from "svelte"
|
||||
import {
|
||||
fetchDatasource,
|
||||
createDataProviderContext,
|
||||
fetchTableDefinition,
|
||||
ContextTypes,
|
||||
} from "@budibase/component-sdk"
|
||||
import { isEmpty } from "lodash/fp"
|
||||
|
||||
export let _bb
|
||||
export let datasource = []
|
||||
|
||||
let target
|
||||
let store = _bb.store
|
||||
|
||||
const dataProviderContext = createDataProviderContext()
|
||||
setContext(ContextTypes.DataProvider, dataProviderContext)
|
||||
|
||||
onMount(async () => {
|
||||
if (!isEmpty(datasource)) {
|
||||
const data = await fetchDatasource(datasource)
|
||||
_bb.attachChildren(target, {
|
||||
hydrate: false,
|
||||
context: data,
|
||||
})
|
||||
const rows = await fetchDatasource(datasource)
|
||||
dataProviderContext.actions.setRows(rows)
|
||||
if (datasource.tableId) {
|
||||
const tableDefinition = await fetchTableDefinition(datasource.tableId)
|
||||
dataProviderContext.actions.setTable(tableDefinition)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import { authStore } from "@budibase/component-sdk"
|
||||
import { authStore, styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let buttonText = "Log In"
|
||||
export let logo = ""
|
||||
export let title = ""
|
||||
export let buttonClass = ""
|
||||
export let inputClass = ""
|
||||
export let _bb
|
||||
export let styles
|
||||
|
||||
let username = ""
|
||||
let password = ""
|
||||
|
@ -33,7 +33,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="root" use:styleable={styles}>
|
||||
<div class="content">
|
||||
{#if logo}
|
||||
<div class="logo-container"><img src={logo} alt="logo" /></div>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<script>
|
||||
import { authStore, link } from "@budibase/component-sdk"
|
||||
import { authStore, linkable, styleable } from "@budibase/component-sdk"
|
||||
|
||||
export let logoUrl
|
||||
export let title
|
||||
export let styles
|
||||
|
||||
const logOut = () => {
|
||||
authStore.actions.logOut()
|
||||
|
@ -10,9 +11,9 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="nav">
|
||||
<div class="nav" use:styleable={styles}>
|
||||
<div class="nav__top">
|
||||
<a href="/" use:link>
|
||||
<a href="/" use:linkable>
|
||||
{#if logoUrl}
|
||||
<img class="logo" alt="logo" src={logoUrl} height="48" />
|
||||
{/if}
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { onMount, setContext } from "svelte"
|
||||
import {
|
||||
fetchTableDefinition,
|
||||
fetchTableData,
|
||||
fetchRow,
|
||||
screenStore,
|
||||
routeStore,
|
||||
createDataProviderContext,
|
||||
ContextTypes,
|
||||
} from "@budibase/component-sdk"
|
||||
|
||||
export let _bb
|
||||
export let table
|
||||
|
||||
let headers = []
|
||||
let store = _bb.store
|
||||
let target
|
||||
|
||||
// Expose data provider context for this row
|
||||
const dataProviderContext = createDataProviderContext()
|
||||
setContext(ContextTypes.DataProvider, dataProviderContext)
|
||||
|
||||
async function fetchFirstRow() {
|
||||
const rows = await fetchTableData(table)
|
||||
|
@ -24,8 +29,10 @@
|
|||
}
|
||||
|
||||
const pathParts = window.location.pathname.split("/")
|
||||
const routeParamId = _bb.routeParams().id
|
||||
const routeParamId = $routeStore.routeParams.id
|
||||
console.log(routeParamId)
|
||||
let row
|
||||
let tableDefinition
|
||||
|
||||
// if srcdoc, then we assume this is the builder preview
|
||||
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && table) {
|
||||
|
@ -37,18 +44,14 @@
|
|||
}
|
||||
|
||||
if (row) {
|
||||
row._table = await fetchTableDefinition(row.tableId)
|
||||
_bb.attachChildren(target, {
|
||||
context: row,
|
||||
})
|
||||
} else {
|
||||
_bb.attachChildren(target)
|
||||
tableDefinition = await fetchTableDefinition(row.tableId)
|
||||
}
|
||||
|
||||
dataProviderContext.actions.setRows([row])
|
||||
dataProviderContext.actions.setTable(tableDefinition)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await fetchData()
|
||||
})
|
||||
onMount(fetchData)
|
||||
</script>
|
||||
|
||||
<section bind:this={target} />
|
||||
<slot />
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
ModalContent,
|
||||
} from "@budibase/bbui"
|
||||
|
||||
export let _bb
|
||||
export let datasource = {}
|
||||
export let editable
|
||||
export let theme = "alpine"
|
||||
|
@ -32,7 +31,6 @@
|
|||
|
||||
let modal
|
||||
|
||||
let store = _bb.store
|
||||
let dataLoaded = false
|
||||
let data
|
||||
let columnDefs
|
||||
|
@ -52,7 +50,7 @@
|
|||
|
||||
onMount(async () => {
|
||||
if (!isEmpty(datasource)) {
|
||||
data = await SDK.fetchDatasource(datasource, $store)
|
||||
data = await SDK.fetchDatasource(datasource)
|
||||
let schema
|
||||
|
||||
// Get schema for datasource
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
let anchor
|
||||
let dropdown
|
||||
|
||||
export let _bb, table
|
||||
export let table
|
||||
</script>
|
||||
|
||||
<div bind:this={anchor}>
|
||||
|
@ -20,7 +20,6 @@
|
|||
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||
<h5>Add New Row</h5>
|
||||
<Modal
|
||||
{_bb}
|
||||
{table}
|
||||
onClosed={dropdown.hide}
|
||||
on:newRow={() => dispatch('newRow')} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { onMount, createEventDispatcher } from "svelte"
|
||||
import { fade } from "svelte/transition"
|
||||
import { fetchRow, saveRow, routeStore } from "@budibase/component-sdk"
|
||||
import { Button, Label, DatePicker } from "@budibase/bbui"
|
||||
import Dropzone from "../../attachments/Dropzone.svelte"
|
||||
import debounce from "lodash.debounce"
|
||||
|
@ -13,12 +13,10 @@
|
|||
link: [],
|
||||
}
|
||||
|
||||
export let _bb
|
||||
export let table
|
||||
export let onClosed
|
||||
|
||||
let row = { tableId: table._id }
|
||||
let store = _bb.store
|
||||
let schema = table.schema
|
||||
let saved = false
|
||||
let rowId
|
||||
|
@ -39,36 +37,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
const SAVE_ROW_URL = `/api/${table._id}/rows`
|
||||
const response = await _bb.api.post(SAVE_ROW_URL, row)
|
||||
const response = await saveRow(row)
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (response.status === 200) {
|
||||
store.update(state => {
|
||||
state[table._id] = state[table._id]
|
||||
? [...state[table._id], json]
|
||||
: [json]
|
||||
return state
|
||||
})
|
||||
if (!response.error) {
|
||||
// store.update(state => {
|
||||
// state[table._id] = state[table._id]
|
||||
// ? [...state[table._id], json]
|
||||
// : [json]
|
||||
// return state
|
||||
// })
|
||||
|
||||
errors = {}
|
||||
|
||||
// wipe form, if new row, otherwise update
|
||||
// table to get new _rev
|
||||
row = isNew ? { tableId: table._id } : json
|
||||
row = isNew ? { tableId: table._id } : response
|
||||
|
||||
onClosed()
|
||||
dispatch("newRow")
|
||||
}
|
||||
|
||||
if (response.status === 400) {
|
||||
errors = json.errors
|
||||
} else {
|
||||
errors = [response.error]
|
||||
}
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
const routeParams = _bb.routeParams()
|
||||
const routeParams = $routeStore.routeParams
|
||||
rowId =
|
||||
Object.keys(routeParams).length > 0 && (routeParams.id || routeParams[0])
|
||||
isNew = !rowId || rowId === "new"
|
||||
|
@ -78,10 +71,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
const GET_ROW_URL = `/api/${table._id}/rows/${rowId}`
|
||||
const response = await _bb.api.get(GET_ROW_URL)
|
||||
const json = await response.json()
|
||||
row = json
|
||||
row = await fetchRow({ tableId: table._id, rowId })
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script>
|
||||
import { linkable } from "@budibase/component-sdk"
|
||||
import { Button } from "@budibase/bbui"
|
||||
|
||||
export let url
|
||||
let link
|
||||
</script>
|
||||
|
||||
<a href={url} bind:this={link} />
|
||||
<a href={url} bind:this={link} use:linkable />
|
||||
<Button small translucent on:click={() => link.click()}>View</Button>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -147,6 +147,9 @@ function viewDetailsRenderer(options, constraints, editable) {
|
|||
if (options.detailUrl) {
|
||||
url = options.detailUrl.replace(":id", params.data._id)
|
||||
}
|
||||
if (!url.startsWith("/")) {
|
||||
url = `/${url}`
|
||||
}
|
||||
|
||||
new ViewDetails({
|
||||
target: container,
|
||||
|
|
Loading…
Reference in New Issue