Merge branch 'master' of github.com:Budibase/budibase into feature/opinionated-sql

This commit is contained in:
mike12345567 2021-06-15 20:00:52 +01:00
commit 113486ef48
61 changed files with 1616 additions and 533 deletions

View File

@ -172,7 +172,7 @@ For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase
## 📝 License
Budibase is open-source. The builder is licensed [AGPL v3](https://www.gnu.org/licenses/agpl-3.0.en.html), the server is licensed [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html), and the client is licensed [MPL](https://directory.fsf.org/wiki/License:MPL-2.0).
Budibase is open-source, licensed as [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). The client and component libraries are licensed as [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - so the apps that you build can be licensed however you like.
---

View File

@ -1,5 +1,5 @@
{
"version": "0.9.45",
"version": "0.9.48",
"npmClient": "yarn",
"packages": [
"packages/*"

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/auth",
"version": "0.9.45",
"version": "0.9.48",
"description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js",
"author": "Budibase",

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "0.9.45",
"version": "0.9.48",
"license": "AGPL-3.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",

View File

@ -5,6 +5,7 @@
export let disabled = false
export let align = "left"
export let portalTarget
let anchor
let dropdown
@ -32,7 +33,7 @@
<div use:getAnchor on:click={openMenu}>
<slot name="control" />
</div>
<Popover bind:this={dropdown} {anchor} {align}>
<Popover bind:this={dropdown} {anchor} {align} {portalTarget}>
<Menu>
<slot />
</Menu>

View File

@ -9,6 +9,7 @@
export let anchor
export let align = "right"
export let portalTarget
export const show = () => {
dispatch("open")
@ -30,7 +31,7 @@
</script>
{#if open}
<Portal>
<Portal target={portalTarget}>
<div
tabindex="0"
use:positionDropdown={{ anchor, align }}

View File

@ -29,7 +29,7 @@ context("Create Bindings", () => {
// The builder preview pages don't have a real URL, so all we can do
// is check that we were able to bind to the property, and that the
// component exists on the page
cy.getComponent(componentId).should("have.text", "")
cy.getComponent(componentId).should("have.text", "Placeholder text")
})
})

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "0.9.45",
"version": "0.9.48",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@ -65,10 +65,10 @@
}
},
"dependencies": {
"@budibase/bbui": "^0.9.45",
"@budibase/client": "^0.9.45",
"@budibase/bbui": "^0.9.48",
"@budibase/client": "^0.9.48",
"@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.45",
"@budibase/string-templates": "^0.9.48",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",

View File

@ -6,9 +6,5 @@ export default {
}
const createScreen = () => {
return new Screen()
.mainType("div")
.component("@budibase/standard-components/container")
.instanceName("New Screen")
.json()
return new Screen().instanceName("New Screen").json()
}

View File

@ -28,8 +28,10 @@ function generateTitleContainer(table, formId) {
const createScreen = table => {
const screen = new Screen()
.component("@budibase/standard-components/container")
.instanceName(`${table.name} - New`)
.customProps({
hAlign: "center",
})
.route(newRowUrl(table))
const form = makeMainForm()

View File

@ -149,9 +149,11 @@ const createScreen = table => {
provider.addChild(repeater)
return new Screen()
.component("@budibase/standard-components/container")
.instanceName(`${table.name} - Detail`)
.route(rowDetailUrl(table))
.customProps({
hAlign: "center",
})
.addChild(provider)
.json()
}

View File

@ -56,14 +56,15 @@ function generateTitleContainer(table) {
.text(table.name)
return new Component("@budibase/standard-components/container")
.type("div")
.normalStyle({
display: "flex",
"flex-direction": "row",
"justify-content": "space-between",
"align-items": "center",
"margin-bottom": "32px",
})
.customProps({
direction: "row",
hAlign: "stretch",
vAlign: "middle",
size: "shrink",
})
.instanceName("Title Container")
.addChild(heading)
.addChild(newButton)
@ -79,7 +80,8 @@ const createScreen = table => {
tableId: table._id,
type: "table",
},
paginate: false,
paginate: true,
limit: 8,
})
const spectrumTable = new Component("@budibase/standard-components/table")
@ -126,7 +128,6 @@ const createScreen = table => {
background: "white",
"border-radius": "0.5rem",
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
margin: "auto",
"margin-top": "20px",
"border-width": "2px",
"border-color": "rgba(0, 0, 0, 0.1)",
@ -137,13 +138,17 @@ const createScreen = table => {
"padding-left": "48px",
"margin-bottom": "20px",
})
.type("div")
.customProps({
direction: "column",
hAlign: "stretch",
vAlign: "top",
size: "shrink",
})
.instanceName("Container")
.addChild(generateTitleContainer(table))
.addChild(provider)
return new Screen()
.component("@budibase/standard-components/container")
.route(rowListUrl(table))
.instanceName(`${table.name} - List`)
.addChild(mainContainer)

View File

@ -8,7 +8,7 @@ export class Screen extends BaseStructure {
layoutId: "layout_private_master",
props: {
_id: uuid(),
_component: "",
_component: "@budibase/standard-components/container",
_styles: {
normal: {},
hover: {},
@ -17,6 +17,10 @@ export class Screen extends BaseStructure {
},
_children: [],
_instanceName: "",
direction: "column",
hAlign: "stretch",
vAlign: "top",
size: "grow",
},
routing: {
route: "",
@ -41,11 +45,6 @@ export class Screen extends BaseStructure {
return this
}
mainType(type) {
this._json.type = type
return this
}
route(route) {
this._json.routing.route = route
return this
@ -60,4 +59,11 @@ export class Screen extends BaseStructure {
this._json.props._instanceName = name
return this
}
customProps(props) {
for (let key of Object.keys(props)) {
this._json.props[key] = props[key]
}
return this
}
}

View File

@ -35,13 +35,11 @@ export function makeLinkComponent(tableName) {
export function makeMainForm() {
return new Component("@budibase/standard-components/form")
.type("div")
.normalStyle({
width: "700px",
padding: "0px",
"border-radius": "0.5rem",
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
margin: "auto",
"margin-top": "20px",
"padding-top": "48px",
"padding-bottom": "48px",
@ -79,11 +77,16 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) {
.instanceName("Identifier")
return new Component("@budibase/standard-components/container")
.type("div")
.normalStyle({
"font-size": "14px",
color: "#757575",
})
.customProps({
direction: "row",
hAlign: "left",
vAlign: "middle",
size: "shrink",
})
.instanceName("Breadcrumbs")
.addChild(link)
.addChild(arrowText)
@ -149,15 +152,16 @@ export function makeTitleContainer(title) {
.text(title)
return new Component("@budibase/standard-components/container")
.type("div")
.normalStyle({
display: "flex",
"flex-direction": "row",
"justify-content": "space-between",
"align-items": "center",
"margin-top": "32px",
"margin-bottom": "32px",
})
.customProps({
direction: "row",
hAlign: "stretch",
vAlign: "middle",
size: "shrink",
})
.instanceName("Title Container")
.addChild(heading)
}

View File

@ -104,6 +104,7 @@
options={$roles}
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
disabled={!creating}
/>
{#each customSchemaKeys as [key, meta]}
{#if !meta.autocolumn}

View File

@ -70,9 +70,16 @@
{ once: true }
)
// Add listener to select components
iframe.contentWindow.addEventListener("bb-select-component", data => {
store.actions.components.select({ _id: data.detail })
// Add listener for events sent by cliebt library in preview
iframe.contentWindow.addEventListener("bb-event", event => {
const { type, data } = event.detail
if (type === "select-component") {
store.actions.components.select({ _id: data.id })
} else if (type === "update-prop") {
store.actions.components.updateProp(data.prop, data.value)
} else {
console.log(data)
}
})
})
</script>
@ -94,12 +101,12 @@
overflow: hidden;
margin: auto;
height: 100%;
background-color: white;
}
.component-container iframe {
border: 0;
left: 0;
top: 0;
width: 100%;
background-color: transparent;
}
</style>

View File

@ -14,10 +14,14 @@ export default `
<style>
html,
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
body {
padding: 2px;
}
*,

View File

@ -194,7 +194,6 @@
padding: var(--spacing-l) 40px var(--spacing-xl) 40px;
}
.preview-content {
background: var(--background);
box-shadow: 0 0 12px rgba(0, 0, 0, 0.05);
flex: 1 1 auto;
}

View File

@ -33,12 +33,16 @@
role: {},
}
$: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : ""
// Merge the Apps list and the roles response to get something that makes sense for the table
$: appList = Object.keys($apps?.data).map(id => ({
...$apps?.data?.[id],
_id: id,
role: [$userFetch?.data?.roles?.[id]],
}))
$: appList = Object.keys($apps?.data).map(id => {
const role = $userFetch?.data?.roles?.[id] || defaultRoleId
return {
...$apps?.data?.[id],
_id: id,
role: [role],
}
})
let selectedApp
const userFetch = fetchData(`/api/admin/users/${userId}`)

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "0.9.45",
"version": "0.9.48",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "0.9.45",
"version": "0.9.48",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -18,13 +18,14 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/string-templates": "^0.9.45",
"@budibase/bbui": "^0.9.48",
"@budibase/standard-components": "^0.9.48",
"@budibase/string-templates": "^0.9.48",
"regexparam": "^1.3.0",
"shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5"
},
"devDependencies": {
"@budibase/standard-components": "^0.9.45",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"fs-extra": "^8.1.0",

View File

@ -14,6 +14,9 @@
builderStore,
} from "../store"
import { TableNames, ActionTypes } from "../constants"
import SettingsBar from "./preview/SettingsBar.svelte"
import SelectionIndicator from "./preview/SelectionIndicator.svelte"
import HoverIndicator from "./preview/HoverIndicator.svelte"
// Provide contexts
setContext("sdk", SDK)
@ -54,18 +57,46 @@
</script>
{#if dataLoaded && $screenStore.activeLayout}
<div lang="en" dir="ltr" class="spectrum spectrum--medium spectrum--light">
<div
id="spectrum-root"
lang="en"
dir="ltr"
class="spectrum spectrum--medium spectrum--light"
>
<Provider key="user" data={$authStore} {actions}>
<Component definition={$screenStore.activeLayout.props} />
<div id="app-root">
<Component instance={$screenStore.activeLayout.props} />
</div>
<NotificationDisplay />
<!-- Key block needs to be outside the if statement or it breaks -->
{#key $builderStore.selectedComponentId}
{#if $builderStore.inBuilder}
<SettingsBar />
{/if}
{/key}
<!--
We don't want to key these by componentID as they control their own
re-mounting to avoid flashes.
-->
{#if $builderStore.inBuilder}
<SelectionIndicator />
<HoverIndicator />
{/if}
</Provider>
</div>
{/if}
<style>
div {
background: transparent;
#spectrum-root {
height: 100%;
width: 100%;
overflow: hidden;
}
#app-root {
height: 100%;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
position: relative;
}
</style>

View File

@ -6,8 +6,10 @@
import { enrichProps, propsAreSame } from "../utils/componentProps"
import { builderStore } from "../store"
import { hashString } from "../utils/hash"
import Manifest from "@budibase/standard-components/manifest.json"
import { Placeholder } from "@budibase/standard-components"
export let definition = {}
export let instance = {}
// Props that will be passed to the component instance
let componentProps
@ -23,25 +25,39 @@
// Get contexts
const context = getContext("context")
const insideScreenslot = !!getContext("screenslot")
// Create component context
const componentStore = writable({})
setContext("component", componentStore)
// Extract component definition info
$: constructor = getComponentConstructor(definition._component)
$: children = definition._children || []
$: id = definition._id
$: updateComponentProps(definition, $context)
$: styles = definition._styles
$: transition = definition._transition
// Extract component instance info
$: constructor = getComponentConstructor(instance._component)
$: definition = getComponentDefinition(instance._component)
$: children = instance._children || []
$: id = instance._id
$: name = instance._instanceName
$: empty =
!children.length &&
definition?.hasChildren &&
definition?.showEmptyState !== false &&
$builderStore.inBuilder
$: updateComponentProps(instance, $context)
$: selected =
$builderStore.inBuilder &&
$builderStore.selectedComponentId === instance._id
$: interactive = $builderStore.previewType === "layout" || insideScreenslot
// Update component context
$: componentStore.set({
id,
children: children.length,
styles: { ...styles, id },
transition,
styles: { ...instance._styles, id, empty, interactive },
empty,
transition: instance._transition,
selected,
props: componentProps,
name,
})
// Gets the component constructor for the specified component
@ -54,14 +70,20 @@
return ComponentLibrary[name]
}
const getComponentDefinition = component => {
const prefix = "@budibase/standard-components/"
const type = component?.replace(prefix, "")
return type ? Manifest[type] : null
}
// Enriches any string component props using handlebars
const updateComponentProps = (definition, context) => {
const updateComponentProps = (instance, context) => {
// Record the timestamp so we can reference it after enrichment
latestUpdateTime = Date.now()
const enrichmentTime = latestUpdateTime
// Enrich props with context
const enrichedProps = enrichProps(definition, context)
const enrichedProps = enrichProps(instance, context)
// Abandon this update if a newer update has started
if (enrichmentTime !== latestUpdateTime) {
@ -94,14 +116,29 @@
}
</script>
{#if constructor && componentProps}
<div
class={`component ${id}`}
data-type={interactive ? "component" : ""}
data-id={id}
data-name={name}
>
{#key propsHash}
<svelte:component this={constructor} {...componentProps}>
{#if children.length}
{#each children as child (child._id)}
<svelte:self definition={child} />
{/each}
{/if}
</svelte:component>
{#if constructor && componentProps}
<svelte:component this={constructor} {...componentProps}>
{#if children.length}
{#each children as child (child._id)}
<svelte:self instance={child} />
{/each}
{:else if empty}
<Placeholder />
{/if}
</svelte:component>
{/if}
{/key}
{/if}
</div>
<style>
.component {
display: contents;
}
</style>

View File

@ -1,12 +1,12 @@
<script>
import { getContext } from "svelte"
import { setContext, getContext } from "svelte"
import Router from "svelte-spa-router"
import { routeStore } from "../store"
import Screen from "./Screen.svelte"
import { onMount } from "svelte"
const { styleable } = getContext("sdk")
const component = getContext("component")
setContext("screenslot", true)
// Only wrap this as an array to take advantage of svelte keying,
// to ensure the svelte-spa-router is fully remounted when route config
@ -41,5 +41,6 @@
<style>
div {
position: relative;
overflow-x: auto;
}
</style>

View File

@ -22,6 +22,6 @@
<!-- Ensure to fully remount when screen changes -->
{#key screenDefinition?._id}
<Provider key="url" data={params}>
<Component definition={screenDefinition} />
<Component instance={screenDefinition} />
</Provider>
{/key}

View File

@ -0,0 +1,32 @@
<script>
import { onMount, onDestroy } from "svelte"
import IndicatorSet from "./IndicatorSet.svelte"
import { builderStore } from "../../store"
let componentId
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
const onMouseOver = e => {
const element = e.target.closest("[data-type='component']")
const newId = element?.dataset?.id
if (newId !== componentId) {
componentId = newId
}
}
const onMouseLeave = () => {
componentId = null
}
onMount(() => {
document.addEventListener("mouseover", onMouseOver)
document.body.addEventListener("mouseleave", onMouseLeave)
})
onDestroy(() => {
document.removeEventListener("mouseover", onMouseOver)
document.body.removeEventListener("mouseleave", onMouseLeave)
})
</script>
<IndicatorSet {componentId} color="rgb(120, 170, 244)" transition {zIndex} />

View File

@ -0,0 +1,61 @@
<script>
import { fade } from "svelte/transition"
export let top
export let left
export let width
export let height
export let text
export let color
export let zIndex
export let transition = false
</script>
<div
in:fade={{
delay: transition ? 50 : 0,
duration: transition ? 130 : 0,
}}
out:fade={{ duration: transition ? 130 : 0 }}
class="indicator"
style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color}; --zIndex: {zIndex};"
>
{#if text}
<div class="text" class:flipped={top < 22}>
{text}
</div>
{/if}
</div>
<style>
.indicator {
position: fixed;
z-index: var(--zIndex);
border: 2px solid var(--color);
pointer-events: none;
border-radius: 4px;
}
.text {
background-color: var(--color);
color: white;
position: absolute;
top: 0;
left: -2px;
height: 20px;
padding: 0 8px 2px 8px;
transform: translateY(-100%);
font-size: 11px;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
white-space: nowrap;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.flipped {
transform: translateY(0%);
top: -2px;
}
</style>

View File

@ -0,0 +1,118 @@
<script>
import { onMount, onDestroy } from "svelte"
import Indicator from "./Indicator.svelte"
import { domDebounce } from "../../utils/domDebounce"
export let componentId
export let color
export let transition
export let zIndex
let indicators = []
let interval
let text
$: visibleIndicators = indicators.filter(x => x.visible)
let updating = false
let observers = []
let callbackCount = 0
let nextIndicators = []
const createIntersectionCallback = idx => entries => {
if (callbackCount >= observers.length) {
return
}
nextIndicators[idx].visible = entries[0].isIntersecting
if (++callbackCount === observers.length) {
indicators = nextIndicators
updating = false
}
}
const updatePosition = () => {
if (updating) {
return
}
// Sanity check
if (!componentId) {
indicators = []
return
}
// Reset state
updating = true
callbackCount = 0
observers.forEach(o => o.disconnect())
observers = []
nextIndicators = []
// Determine next set of indicators
const parents = document.getElementsByClassName(componentId)
if (parents.length) {
text = parents[0].dataset.name
}
// Batch reads to minimize reflow
const scrollX = window.scrollX
const scrollY = window.scrollY
// Extract valid children
// Sanity limit of 100 active indicators
const children = Array.from(parents)
.map(parent => parent?.childNodes?.[0])
.filter(child => child != null)
.slice(0, 100)
// If there aren't any nodes then reset
if (!children.length) {
indicators = []
updating = false
}
children.forEach((child, idx) => {
const callback = createIntersectionCallback(idx)
const threshold = children.length > 1 ? 1 : 0
const observer = new IntersectionObserver(callback, { threshold })
observer.observe(child)
observers.push(observer)
const elBounds = child.getBoundingClientRect()
nextIndicators.push({
top: elBounds.top + scrollY - 2,
left: elBounds.left + scrollX - 2,
width: elBounds.width + 4,
height: elBounds.height + 4,
visible: false,
})
})
}
const debouncedUpdate = domDebounce(updatePosition)
onMount(() => {
debouncedUpdate()
interval = setInterval(debouncedUpdate, 100)
document.addEventListener("scroll", debouncedUpdate, true)
})
onDestroy(() => {
clearInterval(interval)
document.removeEventListener("scroll", debouncedUpdate, true)
observers.forEach(o => o.disconnect())
})
</script>
{#key componentId}
{#each visibleIndicators as indicator, idx}
<Indicator
top={indicator.top}
left={indicator.left}
width={indicator.width}
height={indicator.height}
text={idx === 0 ? text : null}
{transition}
{zIndex}
{color}
/>
{/each}
{/key}

View File

@ -0,0 +1,11 @@
<script>
import { builderStore } from "../../store"
import IndicatorSet from "./IndicatorSet.svelte"
</script>
<IndicatorSet
componentId={$builderStore.selectedComponentId}
color="rgb(66, 133, 244)"
zIndex="910"
transition
/>

View File

@ -0,0 +1,131 @@
<script>
import { onMount, onDestroy } from "svelte"
import SettingsButton from "./SettingsButton.svelte"
import { builderStore } from "../../store"
import { domDebounce } from "../../utils/domDebounce"
const verticalOffset = 28
const horizontalOffset = 2
let top = 0
let left = 0
let interval
let self
let measured = false
$: definition = $builderStore.selectedComponentDefinition
$: showBar = definition?.showSettingsBar
$: settings = definition?.settings?.filter(setting => setting.showInBar) ?? []
const updatePosition = () => {
if (!showBar) {
return
}
const id = $builderStore.selectedComponentId
const parent = document.getElementsByClassName(id)?.[0]
const element = parent?.childNodes?.[0]
if (element && self) {
// Batch reads to minimize reflow
const elBounds = element.getBoundingClientRect()
const width = self.offsetWidth
const height = self.offsetHeight
const { scrollX, scrollY, innerWidth } = window
// Vertically, always render above unless no room, then render inside
let newTop = elBounds.top + scrollY - verticalOffset - height
if (newTop < 0) {
newTop = elBounds.top + scrollY + verticalOffset
}
// Horizontally, try to center first.
// Failing that, render to left edge of component.
// Failing that, render to right edge of component,
// Failing that, render to window left edge and accept defeat.
let elCenter = elBounds.left + scrollX + elBounds.width / 2
let newLeft = elCenter - width / 2
if (newLeft < 0 || newLeft + width > innerWidth) {
newLeft = elBounds.left + scrollX - horizontalOffset
if (newLeft < 0 || newLeft + width > innerWidth) {
newLeft = elBounds.right + scrollX - width + horizontalOffset
if (newLeft < 0 || newLeft + width > innerWidth) {
newLeft = horizontalOffset
}
}
}
// Only update state when things changes to minimize renders
if (Math.round(newTop) !== Math.round(top)) {
top = newTop
}
if (Math.round(newLeft) !== Math.round(left)) {
left = newLeft
}
measured = true
}
}
const debouncedUpdate = domDebounce(updatePosition)
onMount(() => {
debouncedUpdate()
interval = setInterval(debouncedUpdate, 100)
document.addEventListener("scroll", debouncedUpdate, true)
})
onDestroy(() => {
clearInterval(interval)
document.removeEventListener("scroll", debouncedUpdate, true)
})
</script>
{#if showBar}
<div
class="bar"
style="top: {top}px; left: {left}px;"
bind:this={self}
class:visible={measured}
>
{#each settings as setting, idx}
{#if setting.type === "select"}
{#each setting.options as option}
<SettingsButton
prop={setting.key}
value={option.value}
icon={option.barIcon}
title={option.barTitle}
/>
{/each}
{/if}
{#if idx < settings.length - 1}
<div class="divider" />
{/if}
{/each}
</div>
{/if}
<style>
.bar {
display: flex;
position: absolute;
z-index: 930;
padding: 6px 8px;
opacity: 0;
flex-direction: row;
background: var(--background);
justify-content: center;
align-items: center;
border-radius: 4px;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.2);
gap: 2px;
transition: opacity 0.13s ease-in-out;
}
.visible {
opacity: 1;
}
.divider {
flex: 0 0 1px;
align-self: stretch;
margin: 0 4px;
background-color: var(--spectrum-global-color-gray-300);
}
</style>

View File

@ -0,0 +1,50 @@
<script>
import { Icon } from "@budibase/bbui"
import { builderStore } from "../../store"
export let prop
export let value
export let icon
export let title
export let rotate = false
export let bool = false
$: currentValue = $builderStore.selectedComponent?.[prop]
$: active = prop && (bool ? !!currentValue : currentValue === value)
</script>
<div
{title}
class:rotate
class:active
on:click={() => {
if (prop) {
const newValue = bool ? !currentValue : value
builderStore.actions.updateProp(prop, newValue)
}
}}
>
<Icon name={icon} size="S" />
</div>
<style>
div {
padding: 6px;
border-radius: 2px;
color: var(--spectrum-global-color-gray-700);
display: flex;
transition: color 0.13s ease-in-out, background-color 0.13s ease-in-out;
}
div:hover {
background-color: var(--spectrum-global-color-gray-200);
cursor: pointer;
}
.active,
.active:hover {
background-color: rgba(13, 102, 208, 0.1);
color: var(--spectrum-global-color-blue-600);
}
.rotate {
transform: rotate(90deg);
}
</style>

View File

@ -1,4 +1,32 @@
import { writable } from "svelte/store"
import { writable, derived } from "svelte/store"
import Manifest from "@budibase/standard-components/manifest.json"
const dispatchEvent = (type, data) => {
window.dispatchEvent(
new CustomEvent("bb-event", {
detail: { type, data },
})
)
}
const findComponentById = (component, componentId) => {
if (!component || !componentId) {
return null
}
if (component._id === componentId) {
return component
}
if (!component._children?.length) {
return null
}
for (let child of component._children) {
const result = findComponentById(child, componentId)
if (result) {
return result
}
}
return null
}
const createBuilderStore = () => {
const initialState = {
@ -10,18 +38,35 @@ const createBuilderStore = () => {
previewId: null,
previewType: null,
}
const store = writable(initialState)
const writableStore = writable(initialState)
const derivedStore = derived(writableStore, $state => {
// Derive the selected component instance and definition
const { layout, screen, previewType, selectedComponentId } = $state
const asset = previewType === "layout" ? layout : screen
const component = findComponentById(asset?.props, selectedComponentId)
const prefix = "@budibase/standard-components/"
const type = component?._component?.replace(prefix, "")
const definition = type ? Manifest[type] : null
return {
...$state,
selectedComponent: component,
selectedComponentDefinition: definition,
}
})
const actions = {
selectComponent: id => {
if (id) {
window.dispatchEvent(
new CustomEvent("bb-select-component", { detail: id })
)
dispatchEvent("select-component", { id })
}
},
updateProp: (prop, value) => {
dispatchEvent("update-prop", { prop, value })
},
}
return {
...store,
...writableStore,
subscribe: derivedStore.subscribe,
actions,
}
}

View File

@ -0,0 +1,12 @@
export const domDebounce = callback => {
let active = false
return e => {
if (!active) {
window.requestAnimationFrame(() => {
callback(e)
active = false
})
active = true
}
}
}

View File

@ -14,25 +14,6 @@ const buildStyleString = (styleObject, customStyles) => {
return str + (customStyles || "")
}
/**
* Applies styles to enrich the builder preview.
* Applies styles to highlight the selected component, and allows pointer
* events for any selectable components (overriding the blanket ban on pointer
* events in the iframe HTML).
*/
const addBuilderPreviewStyles = (node, styleString, componentId) => {
if (componentId === get(builderStore).selectedComponentId) {
const style = window.getComputedStyle(node)
const property = style?.display === "table-row" ? "outline" : "border"
return (
styleString +
`;${property}: 2px solid #4285f4 !important; border-radius: 4px !important;`
)
} else {
return styleString
}
}
/**
* Svelte action to apply correct component styles.
* This also applies handlers for selecting components from the builder preview.
@ -44,9 +25,17 @@ export const styleable = (node, styles = {}) => {
// Creates event listeners and applies initial styles
const setupStyles = (newStyles = {}) => {
// Use empty state styles as base styles if required, but let them, get
// overridden by any user specified styles
let baseStyles = {}
if (newStyles.empty) {
baseStyles.border = "2px dashed var(--grey-5)"
baseStyles.padding = "var(--spacing-l)"
}
const componentId = newStyles.id
const customStyles = newStyles.custom || ""
const normalStyles = newStyles.normal || {}
const normalStyles = { ...baseStyles, ...newStyles.normal }
const hoverStyles = {
...normalStyles,
...(newStyles.hover || {}),
@ -54,7 +43,7 @@ export const styleable = (node, styles = {}) => {
// Applies a style string to a DOM node
const applyStyles = styleString => {
node.style = addBuilderPreviewStyles(node, styleString, componentId)
node.style = styleString
node.dataset.componentId = componentId
}
@ -71,7 +60,9 @@ export const styleable = (node, styles = {}) => {
// Handler to select a component in the builder when clicking it in the
// builder preview
selectComponent = event => {
builderStore.actions.selectComponent(componentId)
if (newStyles.interactive) {
builderStore.actions.selectComponent(componentId)
}
event.preventDefault()
event.stopPropagation()
return false

View File

@ -23,6 +23,113 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
<<<<<<< HEAD
=======
"@budibase/bbui@^0.9.46":
version "0.9.46"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.46.tgz#3109666b618daa65b29d1c7c45549420c62e6489"
integrity sha512-PRW8kR9+QrMiom6hVzisMYd268dj03ojC0ruzEkDhKMONg2I021ST62hzKXdb7zh5LgoYXtapmM9qsKwoHfkPg==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
"@spectrum-css/actiongroup" "^1.0.1"
"@spectrum-css/avatar" "^3.0.2"
"@spectrum-css/button" "^3.0.1"
"@spectrum-css/buttongroup" "^3.0.2"
"@spectrum-css/checkbox" "^3.0.2"
"@spectrum-css/dialog" "^3.0.1"
"@spectrum-css/divider" "^1.0.1"
"@spectrum-css/dropzone" "^3.0.2"
"@spectrum-css/fieldgroup" "^3.0.2"
"@spectrum-css/fieldlabel" "^3.0.1"
"@spectrum-css/icon" "^3.0.1"
"@spectrum-css/illustratedmessage" "^3.0.2"
"@spectrum-css/inputgroup" "^3.0.2"
"@spectrum-css/label" "^2.0.10"
"@spectrum-css/link" "^3.1.1"
"@spectrum-css/menu" "^3.0.1"
"@spectrum-css/modal" "^3.0.1"
"@spectrum-css/pagination" "^3.0.3"
"@spectrum-css/picker" "^1.0.1"
"@spectrum-css/popover" "^3.0.1"
"@spectrum-css/progressbar" "^1.0.2"
"@spectrum-css/progresscircle" "^1.0.2"
"@spectrum-css/radio" "^3.0.2"
"@spectrum-css/search" "^3.0.2"
"@spectrum-css/sidenav" "^3.0.2"
"@spectrum-css/statuslight" "^3.0.2"
"@spectrum-css/switch" "^1.0.2"
"@spectrum-css/table" "^3.0.1"
"@spectrum-css/tabs" "^3.0.1"
"@spectrum-css/tags" "^3.0.2"
"@spectrum-css/textfield" "^3.0.1"
"@spectrum-css/toast" "^3.0.1"
"@spectrum-css/tooltip" "^3.0.3"
"@spectrum-css/treeview" "^3.0.2"
"@spectrum-css/typography" "^3.0.1"
"@spectrum-css/underlay" "^2.0.9"
"@spectrum-css/vars" "^3.0.1"
dayjs "^1.10.4"
svelte-flatpickr "^3.1.0"
svelte-portal "^1.0.0"
"@budibase/handlebars-helpers@^0.11.4":
version "0.11.4"
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.4.tgz#8acfa2ee84134f7be4b2906e710fce6d25c5d000"
integrity sha512-AJNJYlJnxZmn9QJ8tBl8nrm4YxbwHP4AR0pbiVGK+EoOylkNBlUnZ/QDL1VyqM5fTkAE/Z2IZVLKrrG3kxuWLA==
dependencies:
arr-flatten "^1.1.0"
array-sort "^0.1.4"
define-property "^1.0.0"
extend-shallow "^3.0.2"
"falsey" "^0.3.2"
for-in "^1.0.2"
for-own "^1.0.0"
get-object "^0.2.0"
get-value "^2.0.6"
handlebars "^4.0.11"
handlebars-utils "^1.0.6"
has-value "^1.0.0"
helper-date "^1.0.1"
helper-markdown "^1.0.0"
helper-md "^0.2.2"
html-tag "^2.0.0"
is-even "^1.0.0"
is-glob "^4.0.0"
is-number "^4.0.0"
kind-of "^6.0.0"
logging-helpers "^1.0.0"
micromatch "^3.1.4"
relative "^3.0.2"
striptags "^3.1.0"
to-gfm-code-block "^0.1.1"
year "^0.2.1"
"@budibase/standard-components@^0.9.46":
version "0.9.46"
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.46.tgz#a31a253ca51a2029c3aaf5d8aca5c953358e1d67"
integrity sha512-QjW4tukMw4Xa477wGTle2UPz85ygodQ3KG+WEdPAWKq7j0IDv0Fad0oDmWtzLvGxxB+AiRbEnM6T1QV6X1ItCA==
dependencies:
"@budibase/bbui" "^0.9.46"
"@spectrum-css/page" "^3.0.1"
"@spectrum-css/vars" "^3.0.1"
apexcharts "^3.22.1"
dayjs "^1.10.5"
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
"@budibase/string-templates@^0.9.46":
version "0.9.46"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.46.tgz#e43f87513977879a892ae52f3941d3320cb9ff88"
integrity sha512-yOVS7Y/QLATj31QuBu8KP78Oyzhs60V09JEQKa7n4vRP8TBemcev/LFShln8Iiv70YZSpdJviguQuJ6Ow0aUNA==
dependencies:
"@budibase/handlebars-helpers" "^0.11.4"
dayjs "^1.10.4"
handlebars "^4.7.6"
handlebars-utils "^1.0.6"
lodash "^4.17.20"
>>>>>>> 81e794065761192883767a80679d7d94d67afcc2
"@rollup/plugin-commonjs@^18.0.0":
version "18.0.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.0.0.tgz#50dc7518b5aa9e66a270e529ea85115d269825c4"

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "0.9.45",
"version": "0.9.48",
"description": "Budibase Web Server",
"main": "src/electron.js",
"repository": {
@ -55,9 +55,9 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/auth": "^0.9.45",
"@budibase/client": "^0.9.45",
"@budibase/string-templates": "^0.9.45",
"@budibase/auth": "^0.9.48",
"@budibase/client": "^0.9.48",
"@budibase/string-templates": "^0.9.48",
"@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1",
@ -110,7 +110,7 @@
"devDependencies": {
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.45",
"@budibase/standard-components": "^0.9.48",
"@jest/test-sequencer": "^24.8.0",
"babel-jest": "^27.0.2",
"docker-compose": "^0.23.6",

View File

@ -4,7 +4,6 @@ const {
getUserMetadataParams,
} = require("../../db/utils")
const { InternalTables } = require("../../db/utils")
const { addAppRoleToUser } = require("../../utilities/workerRequests")
const { getGlobalUsers } = require("../../utilities/global")
const { getFullUser } = require("../../utilities/users")
@ -53,9 +52,6 @@ exports.updateMetadata = async function (ctx) {
const appId = ctx.appId
const db = new CouchDB(appId)
const user = removeGlobalProps(ctx.request.body)
if (user.roleId) {
await addAppRoleToUser(ctx, appId, user.roleId, user._id)
}
const metadata = {
tableId: InternalTables.USER_METADATA,
...user,

View File

@ -23,9 +23,8 @@ const EMPTY_LAYOUT = {
"justify-content": "flex-start",
"align-items": "stretch",
"max-width": "100%",
"margin-left": "20px",
"margin-right": "20px",
width: "1400px",
padding: "20px",
},
hover: {},
active: {},
@ -34,24 +33,19 @@ const EMPTY_LAYOUT = {
_children: [],
},
],
type: "div",
_styles: {
active: {},
hover: {},
normal: {
display: "flex",
"flex-direction": "column",
"align-items": "center",
"justify-content": "flex-start",
"margin-right": "auto",
"margin-left": "auto",
"min-height": "100%",
"background-image": "#f5f5f5",
},
selected: {},
},
className: "",
onLoad: [],
direction: "column",
hAlign: "center",
vAlign: "top",
size: "grow",
},
}
@ -72,10 +66,6 @@ const BASE_LAYOUTS = [
_component: "@budibase/standard-components/container",
_styles: {
normal: {
display: "flex",
"flex-direction": "row",
"justify-content": "flex-start",
"align-items": "flex-start",
background: "#fff",
width: "100%",
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
@ -84,9 +74,6 @@ const BASE_LAYOUTS = [
active: {},
selected: {},
},
className: "",
onLoad: [],
type: "div",
_instanceName: "Header",
_children: [
{
@ -95,8 +82,6 @@ const BASE_LAYOUTS = [
_styles: {
normal: {
"max-width": "1400px",
"margin-left": "auto",
"margin-right": "auto",
padding: "20px",
"font-weight": "400",
"font-size": "16px",
@ -143,6 +128,10 @@ const BASE_LAYOUTS = [
],
},
],
direction: "row",
hAlign: "center",
vAlign: "middle",
size: "shrink",
},
{
_id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967",
@ -155,8 +144,6 @@ const BASE_LAYOUTS = [
"justify-content": "flex-start",
"align-items": "stretch",
"max-width": "100%",
"margin-left": "20px",
"margin-right": "20px",
width: "1400px",
padding: "20px",
},
@ -167,24 +154,19 @@ const BASE_LAYOUTS = [
_children: [],
},
],
type: "div",
_styles: {
active: {},
hover: {},
normal: {
display: "flex",
"flex-direction": "column",
"align-items": "center",
"justify-content": "flex-start",
"margin-right": "auto",
"margin-left": "auto",
"min-height": "100%",
background: "#f5f5f5",
},
selected: {},
},
className: "",
onLoad: [],
direction: "column",
hAlign: "center",
vAlign: "top",
size: "grow",
},
},
{
@ -209,8 +191,6 @@ const BASE_LAYOUTS = [
"justify-content": "flex-start",
"align-items": "stretch",
"max-width": "100%",
"margin-left": "20px",
"margin-right": "20px",
width: "1400px",
padding: "20px",
},
@ -221,24 +201,19 @@ const BASE_LAYOUTS = [
_children: [],
},
],
type: "div",
_styles: {
active: {},
hover: {},
normal: {
display: "flex",
"flex-direction": "column",
"align-items": "center",
"justify-content": "center",
"margin-right": "auto",
"margin-left": "auto",
"min-height": "100%",
background: "#f5f5f5",
},
selected: {},
},
className: "",
onLoad: [],
direction: "column",
hAlign: "center",
vAlign: "top",
size: "grow",
},
},
]

View File

@ -9,19 +9,12 @@ exports.createHomeScreen = () => ({
_id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
_component: "@budibase/standard-components/container",
_styles: {
normal: {
flex: "1 1 auto",
display: "flex",
"flex-direction": "column",
"justify-content": "flex-start",
"align-items": "stretch",
},
normal: {},
hover: {},
active: {},
selected: {},
},
_transition: "fade",
type: "div",
_children: [
{
_id: "ef60083f-4a02-4df3-80f3-a0d3d16847e7",
@ -41,6 +34,10 @@ exports.createHomeScreen = () => ({
},
],
_instanceName: "Home",
direction: "column",
hAlign: "stretch",
vAlign: "top",
size: "grow",
},
routing: {
route: "/",

View File

@ -12,14 +12,14 @@ exports.updateAppRole = (appId, user) => {
if (!user.roles) {
return user
}
if (user.builder && user.builder.global) {
// always use the deployed app
user.roleId = user.roles[getDeployedAppID(appId)]
// if a role wasn't found then either set as admin (builder) or public (everyone else)
if (!user.roleId && user.builder && user.builder.global) {
user.roleId = BUILTIN_ROLE_IDS.ADMIN
} else {
// always use the deployed app
user.roleId = user.roles[getDeployedAppID(appId)]
if (!user.roleId) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
}
} else if (!user.roleId) {
user.roleId = BUILTIN_ROLE_IDS.PUBLIC
}
delete user.roles
return user

View File

@ -6,7 +6,117 @@
"hasChildren": true,
"styleable": true,
"transitionable": true,
"settings": []
"showSettingsBar": true,
"settings": [
{
"type": "select",
"label": "Direction",
"key": "direction",
"showInBar": true,
"options": [
{
"label": "Column",
"value": "column",
"barIcon": "ViewRow",
"barTitle": "Column layout"
},
{
"label": "Row",
"value": "row",
"barIcon": "ViewColumn",
"barTitle": "Row layout"
}
],
"defaultValue": "column"
},
{
"type": "select",
"label": "Horiz. Align",
"key": "hAlign",
"showInBar": true,
"options": [
{
"label": "Left",
"value": "left",
"barIcon": "AlignLeft",
"barTitle": "Align left"
},
{
"label": "Center",
"value": "center",
"barIcon": "AlignCenter",
"barTitle": "Align center"
},
{
"label": "Right",
"value": "right",
"barIcon": "AlignRight",
"barTitle": "Align right"
},
{
"label": "Stretch",
"value": "stretch",
"barIcon": "MoveLeftRight",
"barTitle": "Align stretched horizontally"
}
],
"defaultValue": "stretch"
},
{
"type": "select",
"label": "Vert. Align",
"key": "vAlign",
"showInBar": "true",
"options": [
{
"label": "Top",
"value": "top",
"barIcon": "AlignTop",
"barTitle": "Align top"
},
{
"label": "Middle",
"value": "middle",
"barIcon": "AlignMiddle",
"barTitle": "Align middle"
},
{
"label": "Bottom",
"value": "bottom",
"barIcon": "AlignBottom",
"barTitle": "Align bottom"
},
{
"label": "Stretch",
"value": "stretch",
"barIcon": "MoveUpDown",
"barTitle": "Align stretched vertically"
}
],
"defaultValue": "top"
},
{
"type": "select",
"label": "Size",
"key": "size",
"showInBar": true,
"options": [
{
"label": "Shrink",
"value": "shrink",
"barIcon": "Minimize",
"barTitle": "Shrink container"
},
{
"label": "Grow",
"value": "grow",
"barIcon": "Maximize",
"barTitle": "Grow container"
}
],
"defaultValue": "shrink"
}
]
},
"screenslot": {
"name": "Screenslot",
@ -53,7 +163,7 @@
"type": "text",
"label": "Empty Text",
"key": "noRowsMessage",
"defaultValue": "No rows found."
"defaultValue": "No rows found"
},
{
"type": "filter",
@ -1438,6 +1548,7 @@
"icon": "Table",
"styleable": true,
"hasChildren": true,
"showEmptyState": false,
"settings": [
{
"type": "dataProvider",

View File

@ -29,11 +29,11 @@
"keywords": [
"svelte"
],
"version": "0.9.45",
"version": "0.9.48",
"license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": {
"@budibase/bbui": "^0.9.45",
"@budibase/bbui": "^0.9.48",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1",

View File

@ -1,13 +1,87 @@
<script>
import { getContext } from "svelte"
const { styleable, transition } = getContext("sdk")
const { styleable, transition, builderStore } = getContext("sdk")
const component = getContext("component")
export let direction
export let hAlign
export let vAlign
export let size
$: directionClass = direction ? `valid-container direction-${direction}` : ""
$: hAlignClass = hAlign ? `hAlign-${hAlign}` : ""
$: vAlignClass = vAlign ? `vAlign-${vAlign}` : ""
$: sizeClass = size ? `size-${size}` : ""
</script>
<div
class={[directionClass, hAlignClass, vAlignClass, sizeClass].join(" ")}
in:transition={{ type: $component.transition }}
use:styleable={$component.styles}
>
<slot />
</div>
<style>
.valid-container {
display: flex;
max-width: 100%;
}
.valid-container :global([data-type="component"] > *) {
max-width: 100%;
}
.direction-row {
flex-direction: row;
}
.direction-column {
flex-direction: column;
}
/* Grow containers inside a row need 0 width 0 so that they ignore content */
/* The nested selector for data-type is the wrapper around all components */
.direction-row :global(> [data-type="component"] > .size-grow) {
width: 0;
}
.size-shrink {
flex: 0 1 auto;
}
.size-grow {
flex: 1 1 auto;
}
.direction-row.hAlign-left,
.direction-column.vAlign-top {
justify-content: flex-start;
}
.direction-row.hAlign-center,
.direction-column.vAlign-middle {
justify-content: center;
}
.direction-row.hAlign-right,
.direction-column.vAlign-bottom {
justify-content: flex-end;
}
.direction-row.hAlign-stretch,
.direction-column.vAlign-stretch {
justify-content: space-between;
}
.direction-row.vAlign-top,
.direction-column.hAlign-left {
align-items: flex-start;
}
.direction-row.vAlign-middle,
.direction-column.hAlign-center {
align-items: center;
}
.direction-row.vAlign-bottom,
.direction-column.hAlign-right {
align-items: flex-end;
}
.direction-row.vAlign-stretch,
.direction-column.hAlign-stretch {
align-items: stretch;
}
</style>

View File

@ -7,6 +7,7 @@
luceneSort,
luceneLimit,
} from "./lucene"
import Placeholder from "./Placeholder.svelte"
export let dataSource
export let filter
@ -22,7 +23,8 @@
let loading = false
// Loading flag for the initial load
let loaded = false
// Mark as loaded if we have no datasource so we don't stall forever
let loaded = !dataSource
let schemaLoaded = false
// Provider state
@ -87,6 +89,7 @@
// bindings, but are used internally by other components
id: $component?.id,
state: { query },
loaded,
}
const getSortType = (schema, sortColumn) => {
@ -231,7 +234,11 @@
<ProgressCircle />
</div>
{:else}
<slot />
{#if !$component.children}
<Placeholder />
{:else}
<slot />
{/if}
{#if paginate && internalTable}
<div class="pagination">
<Pagination

View File

@ -1,25 +1,30 @@
<script>
import { getContext } from "svelte"
const { styleable } = getContext("sdk")
const { styleable, builderStore } = getContext("sdk")
const component = getContext("component")
export let type
export let text = ""
export let text
$: placeholder = $builderStore.inBuilder && !text
$: componentText = $builderStore.inBuilder
? text || "Placeholder text"
: text || ""
</script>
{#if type === "h1"}
<h1 use:styleable={$component.styles}>{text}</h1>
<h1 class:placeholder use:styleable={$component.styles}>{componentText}</h1>
{:else if type === "h2"}
<h2 use:styleable={$component.styles}>{text}</h2>
<h2 class:placeholder use:styleable={$component.styles}>{componentText}</h2>
{:else if type === "h3"}
<h3 use:styleable={$component.styles}>{text}</h3>
<h3 class:placeholder use:styleable={$component.styles}>{componentText}</h3>
{:else if type === "h4"}
<h4 use:styleable={$component.styles}>{text}</h4>
<h4 class:placeholder use:styleable={$component.styles}>{componentText}</h4>
{:else if type === "h5"}
<h5 use:styleable={$component.styles}>{text}</h5>
<h5 class:placeholder use:styleable={$component.styles}>{componentText}</h5>
{:else if type === "h6"}
<h6 use:styleable={$component.styles}>{text}</h6>
<h6 class:placeholder use:styleable={$component.styles}>{componentText}</h6>
{/if}
<style>
@ -31,4 +36,9 @@
h6 {
white-space: pre-wrap;
}
.placeholder {
font-style: italic;
color: var(--grey-6);
}
</style>

View File

@ -65,15 +65,11 @@
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.nav__menu > * {
margin-right: 16px;
gap: 16px;
}
:global(.nav__menu > a) {
font-size: 1.5em;
text-decoration: none;
margin-right: 16px;
}
</style>

View File

@ -0,0 +1,21 @@
<script>
import { getContext } from "svelte"
const { builderStore } = getContext("sdk")
const component = getContext("component")
export let text = $component.name || "Placeholder"
</script>
{#if $builderStore.inBuilder}
<div>
{text}
</div>
{/if}
<style>
div {
color: var(--grey-6);
font-size: var(--font-size-s);
}
</style>

View File

@ -1,5 +1,6 @@
<script>
import { getContext } from "svelte"
import Placeholder from "./Placeholder.svelte"
export let dataProvider
export let noRowsMessage
@ -9,37 +10,32 @@
const context = getContext("context")
$: rows = dataProvider?.rows ?? []
$: loaded = dataProvider?.loaded ?? false
$: loaded = dataProvider?.loaded ?? true
</script>
<div use:styleable={$component.styles}>
{#if rows.length > 0}
{#if $component.children === 0 && $builderStore.inBuilder}
<p><i class="ri-image-line" />Add some components to display.</p>
{:else}
{#each rows as row}
<Provider data={row}>
<slot />
</Provider>
{/each}
{/if}
{#if $component.empty}
<Placeholder />
{:else if rows.length > 0}
{#each rows as row}
<Provider data={row}>
<slot />
</Provider>
{/each}
{:else if loaded && noRowsMessage}
<p><i class="ri-list-check-2" />{noRowsMessage}</p>
<div class="noRows"><i class="ri-list-check-2" />{noRowsMessage}</div>
{/if}
</div>
<style>
p {
margin: 0 var(--spacing-m);
background-color: var(--grey-2);
.noRows {
color: var(--grey-6);
font-size: var(--font-size-s);
padding: var(--spacing-l);
border-radius: var(--border-radius-s);
display: grid;
place-items: center;
}
p i {
.noRows i {
margin-bottom: var(--spacing-m);
font-size: 1.5rem;
color: var(--grey-5);

View File

@ -7,7 +7,7 @@
export let imageUrl = ""
export let heading = ""
export let subheading = ""
export let destinationUrl = ""
export let destinationUrl = "/"
$: showImage = !!imageUrl
</script>

View File

@ -1,17 +1,29 @@
<script>
import { getContext } from "svelte"
const { styleable } = getContext("sdk")
const { styleable, builderStore } = getContext("sdk")
const component = getContext("component")
export let text = ""
export let text
$: placeholder = $builderStore.inBuilder && !text
$: componentText = $builderStore.inBuilder
? text || "Placeholder text"
: text || ""
</script>
<p use:styleable={$component.styles}>{text}</p>
<p use:styleable={$component.styles} class:placeholder>
{componentText}
</p>
<style>
p {
display: inline-block;
white-space: pre-wrap;
}
.placeholder {
font-style: italic;
color: var(--grey-6);
}
</style>

View File

@ -1,6 +1,7 @@
<script>
import { getContext } from "svelte"
import { chart } from "svelte-apexcharts"
import Placeholder from "../Placeholder.svelte"
const { styleable, builderStore } = getContext("sdk")
const component = getContext("component")
@ -11,8 +12,8 @@
{#if options}
<div use:chart={options} use:styleable={$component.styles} />
{:else if $builderStore.inBuilder}
<div class="placeholder" use:styleable={$component.styles}>
Use the settings panel to build your chart.
<div use:styleable={$component.styles}>
<Placeholder text="Use the settings panel to build your chart" />
</div>
{/if}
@ -24,7 +25,4 @@
div :global(.apexcharts-yaxis-label, .apexcharts-xaxis-label) {
fill: #aaa;
}
div.placeholder {
padding: 10px;
}
</style>

View File

@ -1,5 +1,5 @@
<script>
import Placeholder from "./Placeholder.svelte"
import Placeholder from "../Placeholder.svelte"
import FieldGroupFallback from "./FieldGroupFallback.svelte"
import { getContext } from "svelte"
@ -36,6 +36,7 @@
<FieldGroupFallback>
<div class="spectrum-Form-item" use:styleable={$component.styles}>
<label
class:hidden={!label}
for={$fieldState?.fieldId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${labelPositionClass}`}
>
@ -43,15 +44,15 @@
</label>
<div class="spectrum-Form-itemField">
{#if !formContext}
<Placeholder>Form components need to be wrapped in a Form</Placeholder>
<Placeholder text="Form components need to be wrapped in a form" />
{:else if !fieldState}
<Placeholder>
Add the Field setting to start using your component
</Placeholder>
<Placeholder
text="Add the Field setting to start using your component"
/>
{:else if fieldSchema?.type && fieldSchema?.type !== type}
<Placeholder>
This Field setting is the wrong data type for this component
</Placeholder>
<Placeholder
text="This Field setting is the wrong data type for this component"
/>
{:else}
<slot />
{#if $fieldState.error}
@ -66,6 +67,9 @@
label {
white-space: nowrap;
}
label.hidden {
padding: 0;
}
.spectrum-Form-itemField {
position: relative;

View File

@ -1,8 +1,6 @@
<script>
import { setContext, getContext, onMount } from "svelte"
import { writable, get } from "svelte/store"
import { createValidatorFromConstraints } from "./validation"
import { generateID } from "../helpers"
import { getContext } from "svelte"
import InnerForm from "./InnerForm.svelte"
export let dataSource
export let theme
@ -10,14 +8,7 @@
export let disabled = false
export let actionType = "Create"
const component = getContext("component")
const context = getContext("context")
const { styleable, API, Provider, ActionTypes } = getContext("sdk")
let loaded = false
let schema
let table
let fieldMap = {}
// Returns the closes data context which isn't a built in context
const getInitialValues = (type, dataSource, context) => {
@ -41,167 +32,19 @@
return closestContext
}
// Use the closest data context as the initial form values
const initialValues = getInitialValues(actionType, dataSource, $context)
// Form state contains observable data about the form
const formState = writable({ values: initialValues, errors: {}, valid: true })
// Form API contains functions to control the form
const formApi = {
registerField: (field, defaultValue = null, fieldDisabled = false) => {
if (!field) {
return
}
// Auto columns are always disabled
const isAutoColumn = !!schema?.[field]?.autocolumn
// Create validation function based on field schema
const constraints = schema?.[field]?.constraints
const validate = createValidatorFromConstraints(constraints, field, table)
// Construct field object
fieldMap[field] = {
fieldState: makeFieldState(
field,
defaultValue,
disabled || fieldDisabled || isAutoColumn
),
fieldApi: makeFieldApi(field, defaultValue, validate),
fieldSchema: schema?.[field] ?? {},
}
// Set initial value
const initialValue = get(fieldMap[field].fieldState).value
formState.update(state => ({
...state,
values: {
...state.values,
[field]: initialValue,
},
}))
return fieldMap[field]
},
validate: () => {
const fields = Object.keys(fieldMap)
fields.forEach(field => {
const { fieldApi } = fieldMap[field]
fieldApi.validate()
})
return get(formState).valid
},
}
// Provide both form API and state to children
setContext("form", { formApi, formState, dataSource })
// Action context to pass to children
$: actions = [{ type: ActionTypes.ValidateForm, callback: formApi.validate }]
// Creates an API for a specific field
const makeFieldApi = (field, defaultValue, validate) => {
const setValue = (value, skipCheck = false) => {
const { fieldState } = fieldMap[field]
// Skip if the value is the same
if (!skipCheck && get(fieldState).value === value) {
return
}
const newValue = value == null ? defaultValue : value
const newError = validate ? validate(newValue) : null
// Update field state
fieldState.update(state => {
state.value = newValue
state.error = newError
return state
})
// Update form state
formState.update(state => {
state.values = { ...state.values, [field]: newValue }
if (newError) {
state.errors = { ...state.errors, [field]: newError }
} else {
delete state.errors[field]
}
state.valid = Object.keys(state.errors).length === 0
return state
})
return !newError
}
return {
setValue,
validate: () => {
const { fieldState } = fieldMap[field]
setValue(get(fieldState).value, true)
},
}
}
// Creates observable state data about a specific field
const makeFieldState = (field, defaultValue, fieldDisabled) => {
return writable({
field,
fieldId: `id-${generateID()}`,
value: initialValues[field] ?? defaultValue,
error: null,
disabled: fieldDisabled,
})
}
// Fetches the form schema from this form's dataSource, if one exists
const fetchSchema = async () => {
if (!dataSource?.tableId) {
schema = {}
table = null
} else {
table = await API.fetchTableDefinition(dataSource?.tableId)
if (table) {
if (dataSource?.type === "query") {
schema = {}
const params = table.parameters || []
params.forEach(param => {
schema[param.name] = { ...param, type: "string" }
})
} else {
schema = table.schema || {}
}
}
}
loaded = true
}
// Load the form schema on mount
onMount(fetchSchema)
$: initialValues = getInitialValues(actionType, dataSource, $context)
$: resetKey = JSON.stringify(initialValues)
</script>
<Provider
{actions}
data={{ ...$formState.values, tableId: dataSource?.tableId }}
>
<div
lang="en"
dir="ltr"
use:styleable={$component.styles}
class={`spectrum ${size || "spectrum--medium"} ${
theme || "spectrum--light"
}`}
{#key resetKey}
<InnerForm
{dataSource}
{theme}
{size}
{disabled}
{actionType}
{initialValues}
>
{#if loaded}
<slot />
{/if}
</div>
</Provider>
<style>
div {
padding: 20px;
position: relative;
background-color: var(--spectrum-alias-background-color-secondary);
}
</style>
<slot />
</InnerForm>
{/key}

View File

@ -0,0 +1,185 @@
<script>
import { setContext, getContext, onMount } from "svelte"
import { writable, get } from "svelte/store"
import { createValidatorFromConstraints } from "./validation"
import { generateID } from "../helpers"
export let dataSource
export let theme
export let size
export let disabled = false
export let actionType = "Create"
export let initialValues
const component = getContext("component")
const context = getContext("context")
const { styleable, API, Provider, ActionTypes } = getContext("sdk")
let loaded = false
let schema
let table
let fieldMap = {}
// Form state contains observable data about the form
const formState = writable({ values: initialValues, errors: {}, valid: true })
// Form API contains functions to control the form
const formApi = {
registerField: (field, defaultValue = null, fieldDisabled = false) => {
if (!field) {
return
}
// Auto columns are always disabled
const isAutoColumn = !!schema?.[field]?.autocolumn
// Create validation function based on field schema
const constraints = schema?.[field]?.constraints
const validate = createValidatorFromConstraints(constraints, field, table)
// Construct field object
fieldMap[field] = {
fieldState: makeFieldState(
field,
defaultValue,
disabled || fieldDisabled || isAutoColumn
),
fieldApi: makeFieldApi(field, defaultValue, validate),
fieldSchema: schema?.[field] ?? {},
}
// Set initial value
const initialValue = get(fieldMap[field].fieldState).value
formState.update(state => ({
...state,
values: {
...state.values,
[field]: initialValue,
},
}))
return fieldMap[field]
},
validate: () => {
const fields = Object.keys(fieldMap)
fields.forEach(field => {
const { fieldApi } = fieldMap[field]
fieldApi.validate()
})
return get(formState).valid
},
}
// Provide both form API and state to children
setContext("form", { formApi, formState, dataSource })
// Action context to pass to children
const actions = [
{ type: ActionTypes.ValidateForm, callback: formApi.validate },
]
// Creates an API for a specific field
const makeFieldApi = (field, defaultValue, validate) => {
const setValue = (value, skipCheck = false) => {
const { fieldState } = fieldMap[field]
// Skip if the value is the same
if (!skipCheck && get(fieldState).value === value) {
return
}
const newValue = value == null ? defaultValue : value
const newError = validate ? validate(newValue) : null
// Update field state
fieldState.update(state => {
state.value = newValue
state.error = newError
return state
})
// Update form state
formState.update(state => {
state.values = { ...state.values, [field]: newValue }
if (newError) {
state.errors = { ...state.errors, [field]: newError }
} else {
delete state.errors[field]
}
state.valid = Object.keys(state.errors).length === 0
return state
})
return !newError
}
return {
setValue,
validate: () => {
const { fieldState } = fieldMap[field]
setValue(get(fieldState).value, true)
},
}
}
// Creates observable state data about a specific field
const makeFieldState = (field, defaultValue, fieldDisabled) => {
return writable({
field,
fieldId: `id-${generateID()}`,
value: initialValues[field] ?? defaultValue,
error: null,
disabled: fieldDisabled,
})
}
// Fetches the form schema from this form's dataSource, if one exists
const fetchSchema = async () => {
if (!dataSource?.tableId) {
schema = {}
table = null
} else {
table = await API.fetchTableDefinition(dataSource?.tableId)
if (table) {
if (dataSource?.type === "query") {
schema = {}
const params = table.parameters || []
params.forEach(param => {
schema[param.name] = { ...param, type: "string" }
})
} else {
schema = table.schema || {}
}
}
}
loaded = true
}
// Load the form schema on mount
onMount(fetchSchema)
</script>
<Provider
{actions}
data={{ ...$formState.values, tableId: dataSource?.tableId }}
>
<div
lang="en"
dir="ltr"
use:styleable={$component.styles}
class={`spectrum ${size || "spectrum--medium"} ${
theme || "spectrum--light"
}`}
>
{#if loaded}
<slot />
{/if}
</div>
</Provider>
<style>
div {
padding: 20px;
position: relative;
background-color: var(--spectrum-alias-background-color-secondary);
}
</style>

View File

@ -1,30 +0,0 @@
<script>
import { getContext } from "svelte"
const { builderStore } = getContext("sdk")
</script>
{#if $builderStore.inBuilder}
<div>
<slot />
</div>
{/if}
<style>
div {
height: var(
--spectrum-alias-item-height-m,
var(--spectrum-global-dimension-size-400)
);
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
color: var(--spectrum-global-color-gray-600);
font-size: var(
--spectrum-alias-item-text-size-m,
var(--spectrum-global-dimension-font-size-100)
);
font-style: var(--spectrum-global-font-style-italic, italic);
font-weight: var(--spectrum-global-font-weight-regular, 400);
}
</style>

View File

@ -8,30 +8,6 @@ export const capitalise = string => {
return string.substring(0, 1).toUpperCase() + string.substring(1)
}
/**
* Svelte action to set CSS variables on a DOM node.
*
* @param node
* @param props
*/
export const cssVars = (node, props) => {
Object.entries(props).forEach(([key, value]) => {
node.style.setProperty(`--${key}`, value)
})
return {
update(new_props) {
Object.entries(new_props).forEach(([key, value]) => {
node.style.setProperty(`--${key}`, value)
delete props[key]
})
Object.keys(props).forEach(name => node.style.removeProperty(`--${name}`))
props = new_props
},
}
}
/**
* Generates a short random ID.
* This is "nanoid" but rollup was derping attempting to bundle it, so the

View File

@ -10,6 +10,10 @@ import "@spectrum-css/page/dist/index-vars.css"
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js"
loadSpectrumIcons()
// Non user-facing components
export { default as Placeholder } from "./Placeholder.svelte"
// User facing components
export { default as container } from "./Container.svelte"
export { default as dataprovider } from "./DataProvider.svelte"
export { default as screenslot } from "./ScreenSlot.svelte"

View File

@ -2,6 +2,59 @@
# yarn lockfile v1
"@adobe/spectrum-css-workflow-icons@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
"@budibase/bbui@^0.9.41":
version "0.9.45"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.45.tgz#c37e84ffbf99daf0ebaa5c19b9109e7222463591"
integrity sha512-+wojKap0kieYoeCqhah1V9EswVbR+Pg1k9Nlw1n/zTsuGtjA0kUH652yKjz7zNzZgFm+s5oYS1CoPieIMehhSw==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
"@spectrum-css/actiongroup" "^1.0.1"
"@spectrum-css/avatar" "^3.0.2"
"@spectrum-css/button" "^3.0.1"
"@spectrum-css/buttongroup" "^3.0.2"
"@spectrum-css/checkbox" "^3.0.2"
"@spectrum-css/dialog" "^3.0.1"
"@spectrum-css/divider" "^1.0.1"
"@spectrum-css/dropzone" "^3.0.2"
"@spectrum-css/fieldgroup" "^3.0.2"
"@spectrum-css/fieldlabel" "^3.0.1"
"@spectrum-css/icon" "^3.0.1"
"@spectrum-css/illustratedmessage" "^3.0.2"
"@spectrum-css/inputgroup" "^3.0.2"
"@spectrum-css/label" "^2.0.10"
"@spectrum-css/link" "^3.1.1"
"@spectrum-css/menu" "^3.0.1"
"@spectrum-css/modal" "^3.0.1"
"@spectrum-css/pagination" "^3.0.3"
"@spectrum-css/picker" "^1.0.1"
"@spectrum-css/popover" "^3.0.1"
"@spectrum-css/progressbar" "^1.0.2"
"@spectrum-css/progresscircle" "^1.0.2"
"@spectrum-css/radio" "^3.0.2"
"@spectrum-css/search" "^3.0.2"
"@spectrum-css/sidenav" "^3.0.2"
"@spectrum-css/statuslight" "^3.0.2"
"@spectrum-css/switch" "^1.0.2"
"@spectrum-css/table" "^3.0.1"
"@spectrum-css/tabs" "^3.0.1"
"@spectrum-css/tags" "^3.0.2"
"@spectrum-css/textfield" "^3.0.1"
"@spectrum-css/toast" "^3.0.1"
"@spectrum-css/tooltip" "^3.0.3"
"@spectrum-css/treeview" "^3.0.2"
"@spectrum-css/typography" "^3.0.1"
"@spectrum-css/underlay" "^2.0.9"
"@spectrum-css/vars" "^3.0.1"
dayjs "^1.10.4"
svelte-flatpickr "^3.1.0"
svelte-portal "^1.0.0"
"@rollup/pluginutils@^4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838"
@ -10,31 +63,218 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@spectrum-css/page@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846"
integrity sha512-LAlKF8km5BlsGPpZ2SNtwKOQIHn1lz0X93aczGZVZceOg73O4gyeoT5cx4vi1z+KtBRY5VMDWx3XgGtUwwjqwA==
dependencies:
"@spectrum-css/vars" "^3.0.1"
"@spectrum-css/actionbutton@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.3.tgz#8f7342a69b303c5acdcfa0a59f5e9267b9f3cb30"
integrity sha512-P9qoCPSiZ1SB6ZYqK5hub0vGty00YYqonQE0KTjtb1i+T1nYR/87vWqVPERx9j63uhgZncjwFYaThTvRqye7eQ==
"@spectrum-css/vars@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e"
integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w==
"@spectrum-css/actiongroup@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/actiongroup/-/actiongroup-1.0.3.tgz#4713ce65e6f5c72c404a7b638fbc3b4fd7e3874f"
integrity sha512-NlB9Q4ZlWixXxymoPIYG6g2hkwAGKFodHWPFfxHD8ddkjXWRB9G2akUP7cfsJ4DcYn4VisUORCOYQwqDiSmboQ==
"@spectrum-css/avatar@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/avatar/-/avatar-3.0.2.tgz#4f1826223eae330e64b6d3cc899e9bc2e98dac95"
integrity sha512-wEczvSqxttTWSiL3cOvXV/RmGRwSkw2w6+slcHhnf0kb7ovymMM+9oz8vvEpEsSeo5u598bc+7ktrKFpAd6soQ==
"@spectrum-css/button@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.3.tgz#2df1efaab6c7e0b3b06cb4b59e1eae59c7f1fc84"
integrity sha512-6CnLPqqtaU/PcSSIGeGRi0iFIIxIUByYLKFO6zn5NEUc12KQ28dJ4PLwB6WBa0L8vRoAGlnWWH2ZZweTijbXgg==
"@spectrum-css/buttongroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/buttongroup/-/buttongroup-3.0.3.tgz#719d868845ac9d2c4f939c1b9f6044507902d5aa"
integrity sha512-eXl8U4QWMWXqyTu654FdQdEGnmczgOYlpIFSHyCMVjhtPqZp2xwnLFiGh6LKw+bLio6eeOZ0L+vpk1GcoYqgkw==
"@spectrum-css/checkbox@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.3.tgz#8577067fc8b97e4609f92bd242364937a533a7bb"
integrity sha512-QVG9uMHq+lh70Dh6mDNnY+vEvNz2p7VC6xgLfDYfijp2qeiqYPq72fQK6p/SiyqPk96ZACzNRwgeROU6Xf6Wgg==
"@spectrum-css/dialog@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dialog/-/dialog-3.0.3.tgz#7715a4ea435e753afb623d99ca5917ed1bcd6f34"
integrity sha512-AhmKgfRIVyTe3ABiJ8lLUQL34VB/H6fN16na2LlbDRJvyRMzkdN1Xf2i6U3f4OMd3qQ8Gm5xat4BvMxIQPBAUQ==
"@spectrum-css/divider@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/divider/-/divider-1.0.3.tgz#639e2ebaa0834efa40f42397668bbd5c153ea385"
integrity sha512-Zy4Rn40w8UtzMh3wx/U9+CepSCpm1aOCGftHgWDub0XZuVTzh0c1WwyzTuLCx2Hf21z5VRGNiDh8bGEEzSbtNA==
dependencies:
"@spectrum-css/vars" "^3.0.2"
"@spectrum-css/dropzone@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/dropzone/-/dropzone-3.0.3.tgz#aee71697a2c195947599d7541b858c3c198741dc"
integrity sha512-ujrswdtB6bHigklyHsm6zomFd6rUnKJ3xVVRjroVF4+ouK4DxK5tX0iVd0EW6AOfOjx4Cc28uyFot3fpxp+MQw==
"@spectrum-css/fieldgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldgroup/-/fieldgroup-3.0.3.tgz#85d85da048d08200f25ceab378026dd2b11e0bb2"
integrity sha512-wXUXTXN1CPnR7M4Ltd+3uh7BfcNGUV1+Xe0/h0tCpq/j+S2Sd4xo7/pUMdU19sIDrAAtpEFp1tt+nouHcU5HGQ==
"@spectrum-css/fieldlabel@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.3.tgz#f73c04d20734d4718ffb620dc46458904685b449"
integrity sha512-nEvIkEXCD5n4fW67Unq6Iu7VXoauEd/JGpfTY02VsC5p4FJLnwKfPDbJUuUsqClAxqw7nAsmXVKtn4zQFf5yPQ==
"@spectrum-css/icon@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.3.tgz#5c612822380927087aebd526855d82ed2c3e2cba"
integrity sha512-hyloKOejPCXhP3MBNsm3SjR9j8xT1R1S19p32KW/0Qhj+VMUtfyEPmevyFptpn7wcovQccdl/vZVIVDuML/imw==
"@spectrum-css/illustratedmessage@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.2.tgz#6a480be98b027e050b086e7899e40d87adb0a8c0"
integrity sha512-dqnE8X27bGcO0HN8+dYx8O4o0dNNIAqeivOzDHhe2El+V4dTzMrNIerF6G0NLm3GjVf6XliwmitsZK+K6FmbtA==
"@spectrum-css/inputgroup@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.3.tgz#00c9a370ddc2c55cf0f37dd6069faa9501fd7eb5"
integrity sha512-FqRJTiLL7jiGfzDVXWUGVLqHryJjCcqQIrqAi+Tq0oenapzsBe2qc/zIrKgh2wtMI+NTIBLXTECvij3L1HlqAg==
"@spectrum-css/label@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001"
integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ==
"@spectrum-css/link@^3.1.1":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==
"@spectrum-css/menu@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.3.tgz#46a9b221bb5f470a2f8a934bdfd512d84d2fdc4d"
integrity sha512-qKA9J/MrikNKIpCEHsAazG2vY3im5tjGCmo6p9Pdnu8/aQMsiuZDHZayukeCttJUZkrb9guDVL9OIHlK5RZvcQ==
"@spectrum-css/modal@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/modal/-/modal-3.0.2.tgz#58b6621cab65f90788d310374f40df1f7090473f"
integrity sha512-YnIivJhoaao7Otu+HV7sgebPyFbO6sd/oMvTN/Rb2wwgnaMnIIuIRdGandSrcgotN2uNgs+P0knG6mv/xA1/dg==
"@spectrum-css/page@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.2.tgz#8f0c03d25f5565fb13115541a8fcaf0e1d3a8ee0"
integrity sha512-lCXWjonLwYBg8FHUEkiFX0Mmfk+9Uivgvxq0DTulPlWrJcULTwjaOiY28/YBz7Fy1wuv/0KORbkPRALpYldBZg==
dependencies:
"@spectrum-css/vars" "^3.0.2"
"@spectrum-css/pagination@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/pagination/-/pagination-3.0.3.tgz#b204c3ada384c4af751a354bc428346d82eeea65"
integrity sha512-OJ/v9GeNXJOZ9Yr9LDBYPrR2NCiLOWP9wANT/a5sqFuugRnQbn/HYMnRp9TBxwpDY6ihaPo0T/wi7kLiAJFdDw==
"@spectrum-css/picker@^1.0.1":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.3.tgz#21379bcf8ae94277deeb6ad65dcd9e2bbfacb487"
integrity sha512-oHLGxBx5BwVCSGo7/T1C9PTHX1+/5AmVjyLiTJ4UoIdSJmOERw9YcRZbcGZgBJNWbxcjr4TyGtwj1EcSjEy97w==
"@spectrum-css/popover@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.3.tgz#6fb69873474fb968afb738eacb9e121f93e83a09"
integrity sha512-KvmXv4TV19FBx39KfmgVlDYtvtBqv/8RRK7RRLDDHGViTxZtShjVsVpwIgfkfgn4iJztCnXpWzFqRXWUu2XCpQ==
"@spectrum-css/progressbar@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/progressbar/-/progressbar-1.0.3.tgz#f70bcc38a2a21cff2f422ec825724ebbb9455e67"
integrity sha512-vJHplefUuy8+NjCw1X7fLbqHVGNVBpvGFXNAeaIj4SFf4ygxiUq/5c9iRhhsCQixEsJlfD/b7BnGXU7BUDkr6Q==
"@spectrum-css/progresscircle@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/progresscircle/-/progresscircle-1.0.2.tgz#258ea9170fb70f795edda03e38a61d93bef4487c"
integrity sha512-JLULpyzjIY95lzlWR1yE1gv4l1K6p+scQ+edmuZZUHBzwM3pUtkvHJmUlA9TYdResUYW6Uka60VRdY6lZ8gnFQ==
"@spectrum-css/radio@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/radio/-/radio-3.0.3.tgz#25c3bc5e9c30a8a8ae728717b7c7fb736cdae640"
integrity sha512-LaLGfz/eGNR2iyqouXYILIA+pKRqF769iPdwM0REm5RpWvMQDD7rPZ/kWlg18owjaFsyllEp25gEjmhRJIIVOw==
"@spectrum-css/search@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/search/-/search-3.0.3.tgz#3415dc106aca0d5dd996e87084a1b47c2b95a882"
integrity sha512-kdLpKTt0obljuhS1q1tukujRlvSs8sBwij76D4Qp8KpMzwePfZyvv1kYzuWPNZfTeISxWsmyZ6Wxd1uvzjn+UA==
"@spectrum-css/sidenav@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/sidenav/-/sidenav-3.0.3.tgz#132141fbd2500a927c312fa4e1d712c438b3d597"
integrity sha512-cQ+CgwjxGftrcc79i1XnGd66QTl7H7zopSU0UTV4Qq7hvqfrjjWxfZ6b+3tezrrhNlDope1ff9o8sm67PsPXtg==
"@spectrum-css/statuslight@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5"
integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w==
"@spectrum-css/switch@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44"
integrity sha512-zqmHpgWPNg1gEwdUNFYV3CBX5JaeALfIqcJIxE0FLZqr9d1C4+oLE0ItIFzt1bwr4bFAOmkEpvtiY+amluzGxQ==
"@spectrum-css/table@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.3.tgz#7f7f19905ef3275cbf907ce3a5818e63c30b2caf"
integrity sha512-nxwzVjLPsXoY/v4sdxOVYLcC+cEbGgJyLcLclT5LT9MGSbngFeUMJzzVR4EvehzuN4dH7hrATG7Mbuq29Mf0Hg==
"@spectrum-css/tabs@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.0.3.tgz#51dd6f168c897b0fdc3a7e9f901df7bd2288b4fc"
integrity sha512-iLP1I72bJWz9APdQB1jiw+pOv5a7N+hYOCJvRoc56Us/hJKVzowkyGRe3uH+8v36nCG9bHxiAQNLoU8eXisVrg==
"@spectrum-css/tags@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.3.tgz#fc76d2735cdc442de91b7eb3bee49a928c0767ac"
integrity sha512-SL8vPxVDfWcY5VdIuyl0TImEXcOU1I7yCyXkk7MudMwfnYs81FaIyY32hFV9OHj0Tz/36UzRzc7AVMSuRQ53pw==
"@spectrum-css/textfield@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.2.tgz#907f62d2dc82852dd6236a820be99e252b531631"
integrity sha512-nkFgAb0cP4jUodkUBErMNfyF78jJLtgL1Mrr9/rvGpGobo10IAbb8zZY4CkZ64o8XmMy/85+wZTKcx+KHatqpg==
"@spectrum-css/toast@^3.0.1":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/toast/-/toast-3.0.3.tgz#97c1527384707600832ecda35643ed304615250f"
integrity sha512-CjLeaMs+cjUXojCCRtbj0YkD2BoZW16kjj2o5omkEpUTjA34IJ8xJ1a+CCtDILWekhXvN0MBN4sbumcnwcnx8w==
"@spectrum-css/tooltip@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/tooltip/-/tooltip-3.0.3.tgz#26b8ca3b3d30e29630244d85eb4fc11d0c841281"
integrity sha512-ztRF7WW1FzyNavXBRc+80z67UoOrY9wl3cMYsVD3MpDnyxdzP8cjza1pCcolKBaFqRTcQKkxKw3GWtGICRKR5A==
"@spectrum-css/treeview@^3.0.2":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/treeview/-/treeview-3.0.3.tgz#aeda5175158b9f8d7529cb2b394428eb2a428046"
integrity sha512-D5gGzZC/KtRArdx86Mesc9+99W9nTbUOeyYGqoJoAfJSOttoT6Tk5CrDvlCmAqjKf5rajemAkGri1ChqvUIwkw==
"@spectrum-css/typography@^3.0.1":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/typography/-/typography-3.0.2.tgz#ea3ca0a60e18064527819d48c8c4364cab4fcd38"
integrity sha512-5ZOLmQe0edzsDMyhghUd4hBb5uxGsFrxzf+WasfcUw9klSfTsRZ09n1BsaaWbgrLjlMQ+EEHS46v5VNo0Ms2CA==
"@spectrum-css/underlay@^2.0.9":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@spectrum-css/underlay/-/underlay-2.0.10.tgz#8b75b646605a311850f6620caa18d4996cd64ed7"
integrity sha512-PmsmkzeGD/rY4pp3ILXHt9w8BW7uaEqXe08hQRS7rGki7wqCpG4mE0/8N3yEcA3QxWQclmG9gdkg5uz6wMmYzA==
"@spectrum-css/vars@^3.0.1", "@spectrum-css/vars@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.2.tgz#ea9062c3c98dfc6ba59e5df14a03025ad8969999"
integrity sha512-vzS9KqYXot4J3AEER/u618MXWAS+IoMvYMNrOoscKiLLKYQWenaueakUWulFonToPd/9vIpqtdbwxznqrK5qDw==
"@sveltejs/vite-plugin-svelte@^1.0.0-next.5":
version "1.0.0-next.5"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.5.tgz#8cf608f7a3c33dfa5b648397aae1ba90e6a4883f"
integrity sha512-RVjafsqziWwnQm8VEy2y0qNaugNDvRd8tTaCt9rjgQkqaS/BDiyDCluXxA28PRC+ddZjvwUeq9k+0EfbLVObfg==
version "1.0.0-next.10"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.10.tgz#15526067ea2edd334420b1e14602e29b370be25e"
integrity sha512-ImvxbhPePm2hWNTKBSA3LHAYGwiEjHjvvgfPLXm4R87sfZ+BMXql9jBmDpzUC/URBLT4BB3Jxos/i523qkJBHg==
dependencies:
"@rollup/pluginutils" "^4.1.0"
chalk "^4.1.0"
chalk "^4.1.1"
debug "^4.3.2"
hash-sum "^2.0.0"
require-relative "^0.8.7"
slash "^3.0.0"
slash "^4.0.0"
source-map "^0.7.3"
svelte-hmr "^0.13.3"
svelte-hmr "^0.14.2"
ansi-styles@^4.1.0:
version "4.3.0"
@ -44,9 +284,9 @@ ansi-styles@^4.1.0:
color-convert "^2.0.1"
apexcharts@^3.19.2, apexcharts@^3.22.1:
version "3.26.0"
resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.26.0.tgz#a78abc108b2e1b3086a738f0ec7c98e292f4a14b"
integrity sha512-zdYHs3k3tgmCn1BpYLj7rhGEndBYF33Pq1+g0ora37xAr+3act5CJrpdXM2jx2boVUyXgavoSp6sa8WpK7RkSA==
version "3.27.1"
resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.27.1.tgz#b0e6dd3b3ace028f29b32fcd88e19a2420a18089"
integrity sha512-2pfw3pxeWhI0ap5lfxyfGNGoGScfEwfc8XnTpbnzgRdr1AOH5JJN9hh3MvfwrC9TQQfJYC2TZc8P/q9qXUj1bQ==
dependencies:
svg.draggable.js "^2.2.2"
svg.easing.js "^2.0.0"
@ -55,10 +295,10 @@ apexcharts@^3.19.2, apexcharts@^3.22.1:
svg.resize.js "^1.4.3"
svg.select.js "^3.0.1"
chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
chalk@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
@ -80,7 +320,7 @@ colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
dayjs@^1.10.5:
dayjs@^1.10.4, dayjs@^1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986"
integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==
@ -92,10 +332,10 @@ debug@^4.3.2:
dependencies:
ms "2.1.2"
esbuild@^0.9.3:
version "0.9.7"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.9.7.tgz#ea0d639cbe4b88ec25fbed4d6ff00c8d788ef70b"
integrity sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==
esbuild@^0.12.5:
version "0.12.8"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.8.tgz#ac90da77cb3bfbf49ab815200bcef7ffe1a3348f"
integrity sha512-sx/LwlP/SWTGsd9G4RlOPrXnIihAJ2xwBUmzoqe2nWwbXORMQWtAGNJNYLBJJqa3e9PWvVzxdrtyFZJcr7D87g==
estree-walker@^2.0.1:
version "2.0.2"
@ -135,9 +375,9 @@ hash-sum@^2.0.0:
integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
is-core-module@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==
version "2.4.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
dependencies:
has "^1.0.3"
@ -146,29 +386,29 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.1.22:
version "3.1.22"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
nanoid@^3.1.23:
version "3.1.23"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picomatch@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
postcss@^8.2.1:
version "8.2.9"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.9.tgz#fd95ff37b5cee55c409b3fdd237296ab4096fba3"
integrity sha512-b+TmuIL4jGtCHtoLi+G/PisuIl9avxs8IZMSmlABRwNz5RLUUACrC+ws81dcomz1nRezm5YPdXiMEzBEKgYn+Q==
postcss@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f"
integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==
dependencies:
colorette "^1.2.2"
nanoid "^3.1.22"
source-map "^0.6.1"
nanoid "^3.1.23"
source-map-js "^0.6.2"
require-relative@^0.8.7:
version "0.8.7"
@ -184,21 +424,21 @@ resolve@^1.19.0:
path-parse "^1.0.6"
rollup@^2.38.5:
version "2.44.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.44.0.tgz#8da324d1c4fd12beef9ae6e12f4068265b6d95eb"
integrity sha512-rGSF4pLwvuaH/x4nAS+zP6UNn5YUDWf/TeEU5IoXSZKBbKRNTCI3qMnYXKZgrC0D2KzS2baiOZt1OlqhMu5rnQ==
version "2.51.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.51.1.tgz#87bcd4095fe79b14c9bec0edc7ffa44e4827f793"
integrity sha512-8xfDbAtBleXotb6qKEHWuo/jkn94a9dVqGc7Rwl3sqspCVlnCfbRek7ldhCARSi7h32H0xR4QThm1t9zHN+3uw==
optionalDependencies:
fsevents "~2.3.1"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
slash@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
source-map@^0.7.3:
version "0.7.3"
@ -220,16 +460,21 @@ svelte-apexcharts@^1.0.2:
apexcharts "^3.19.2"
svelte-flatpickr@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.1.0.tgz#ad83588430dbd55196a1a258b8ba27e7f9c1ee37"
integrity sha512-zKyV+ukeVuJ8CW0Ing3T19VSekc4bPkou/5Riutt1yATrLvSsanNqcgqi7Q5IePvIoOF9GJ5OtHvn1qK9Wx9BQ==
version "3.1.1"
resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.1.1.tgz#c79da72a4acab252ed39bac8168f30f79db3ff80"
integrity sha512-z/sV/AMTqyybeWPCjoWYUOyuMmu9qX2c2f2cRqYos9K9sM5L3CqagtvfVy7GZQrnEYe+K7l/gGDCAyxJNp2+gA==
dependencies:
flatpickr "^4.5.2"
svelte-hmr@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.13.3.tgz#fba5739b477ea44caf70e542a24a4352bee2b897"
integrity sha512-gagW62pLQ2lULmvNA3pIZu9pBCYOaGu3rQikUOv6Nokz5VxUgT9/mQLfMxj9phDEKHCg/lgr3i6PkqZDbO9P2Q==
svelte-hmr@^0.14.2:
version "0.14.4"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.4.tgz#b7ef2bfeef23916e0e912828c50645ca572ac355"
integrity sha512-kItFF7vqzStckSigoFmMnxJpTOdB9TWnQAW6Js+yAB4277tLbJIIE5KBlGHNmJNpA7MguqidsPB27Uw5UzQPCA==
svelte-portal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3"
integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q==
svelte@^3.38.2:
version "3.38.2"
@ -292,12 +537,12 @@ svg.select.js@^3.0.1:
svg.js "^2.6.5"
vite@^2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.1.5.tgz#4857da441c62f7982c83cbd5f42a00330f20c9c1"
integrity sha512-tYU5iaYeUgQYvK/CNNz3tiJ8vYqPWfCE9IQ7K0iuzYovWw7lzty7KRYGWwV3CQPh0NKxWjOczAqiJsCL0Xb+Og==
version "2.3.7"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.7.tgz#3023892419367465e1af1739578f8663d04243b2"
integrity sha512-Y0xRz11MPYu/EAvzN94+FsOZHbSvO6FUvHv127CyG7mV6oDoay2bw+g5y9wW3Blf8OY3chaz3nc/DcRe1IQ3Nw==
dependencies:
esbuild "^0.9.3"
postcss "^8.2.1"
esbuild "^0.12.5"
postcss "^8.3.0"
resolve "^1.19.0"
rollup "^2.38.5"
optionalDependencies:

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
"version": "0.9.45",
"version": "0.9.48",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "0.9.45",
"version": "0.9.48",
"description": "Budibase background service",
"main": "src/index.js",
"repository": {
@ -21,8 +21,8 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@budibase/auth": "^0.9.45",
"@budibase/string-templates": "^0.9.45",
"@budibase/auth": "^0.9.48",
"@budibase/string-templates": "^0.9.48",
"@koa/router": "^8.0.0",
"aws-sdk": "^2.811.0",
"bcryptjs": "^2.4.3",

View File

@ -3,6 +3,9 @@ const { EmailTemplatePurpose } = require("../../../constants")
const nodemailer = require("nodemailer")
const fetch = require("node-fetch")
// need a longer timeout for getting these
jest.setTimeout(30000)
describe("/api/admin/email", () => {
let request = setup.getRequest()
let config = setup.getConfig()