Add lots of new work on client library rewrite

This commit is contained in:
Andrew Kingston 2020-11-17 12:08:24 +00:00
parent 3a981ac256
commit e11656fa24
42 changed files with 397 additions and 345 deletions

View File

@ -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"
}

View File

@ -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,

View File

@ -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}

View File

@ -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 constructor}
<svelte:component this={constructor} {...componentProps} {styles}>
{#if children && children.length}
{#each children as child}
<svelte:self
props={getValidProps(child)}
component={child._component}
children={child._children}
styles={child._styles.normal} />
<svelte:self definition={child} />
{/each}
{/if}
</svelte:component>
</div>
{/if}

View File

@ -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}

View File

@ -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}

View File

@ -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,
})

View File

@ -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()

View File

@ -1,3 +1 @@
import { createComponentStore } from "./components"
export const componentStore = createComponentStore()
export { componentStore } from "./components"

View File

@ -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.

View File

@ -21,6 +21,7 @@
"rollup-plugin-svelte": "^6.1.1"
},
"dependencies": {
"svelte": "^3.29.0",
"svelte-spa-router": "^3.0.5"
}
}

View File

@ -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.

View File

@ -0,0 +1 @@
export const DataProvider = "bb-data-provider"

View File

@ -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 },
}
}

View File

@ -1 +1,2 @@
export const RouterContext = "bb-router"
export * from "./dataProvider"
export * as ContextTypes from "./contextTypes"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -1 +1,2 @@
export { getAppId } from "./getAppId"
export { styleable } from "./styleable"

View File

@ -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)
},
}
}

View File

@ -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"

View File

@ -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",

View File

@ -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",
}),
],
}

View File

@ -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>

View File

@ -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}

View File

@ -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 />

View File

@ -1,5 +1,23 @@
<script>
import { styleable } from "@budibase/component-sdk"
export let embed
export let styles
</script>
<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>

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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)
}
onMount(async () => {
await fetchData()
})
dataProviderContext.actions.setRows([row])
dataProviderContext.actions.setTable(tableDefinition)
}
onMount(fetchData)
</script>
<section bind:this={target} />
<slot />

View File

@ -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

View File

@ -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')} />

View File

@ -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>

View File

@ -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>

View File

@ -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,