Merge branch 'master' of github.com:Budibase/budibase into deployment
This commit is contained in:
commit
993eb71019
Binary file not shown.
After Width: | Height: | Size: 5.4 MiB |
Binary file not shown.
Before Width: | Height: | Size: 201 KiB |
Binary file not shown.
Before Width: | Height: | Size: 105 KiB |
|
@ -55,7 +55,7 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.15.0",
|
||||
"@budibase/bbui": "^1.15.4",
|
||||
"@budibase/client": "^0.0.32",
|
||||
"@nx-js/compiler-util": "^2.0.0",
|
||||
"codemirror": "^5.51.0",
|
||||
|
@ -87,6 +87,7 @@
|
|||
"babel-jest": "^24.8.0",
|
||||
"browser-sync": "^2.26.7",
|
||||
"cypress": "^4.8.0",
|
||||
"eslint-plugin-cypress": "^2.11.1",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"jest": "^24.8.0",
|
||||
"ncp": "^2.0.0",
|
||||
|
@ -109,4 +110,4 @@
|
|||
"svelte-jester": "^1.0.6"
|
||||
},
|
||||
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
/* Budibase Component Styles */
|
||||
.header {
|
||||
font-size: 0.75rem;
|
||||
color: var(--ink);
|
||||
text-transform: uppercase;
|
||||
margin-top: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
.budibase__title {
|
||||
font-weight: 900;
|
||||
|
|
|
@ -29,7 +29,8 @@ import {
|
|||
export const getStore = () => {
|
||||
const initial = {
|
||||
apps: [],
|
||||
appname: "",
|
||||
name: "",
|
||||
description: "",
|
||||
pages: DEFAULT_PAGES_OBJECT,
|
||||
mainUi: {},
|
||||
unauthenticatedUi: {},
|
||||
|
@ -101,7 +102,8 @@ const setPackage = (store, initial) => async pkg => {
|
|||
|
||||
initial.libraries = pkg.application.componentLibraries
|
||||
initial.components = await fetchComponentLibDefinitions(pkg.application._id)
|
||||
initial.appname = pkg.application.name
|
||||
initial.name = pkg.application.name
|
||||
initial.description = pkg.application.description
|
||||
initial.appId = pkg.application._id
|
||||
initial.pages = pkg.pages
|
||||
initial.hasAppPackage = true
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
$: currentAppInfo = {
|
||||
appname: $store.appname,
|
||||
name: $store.name,
|
||||
}
|
||||
|
||||
async function fetchUsers() {
|
||||
|
|
|
@ -1,15 +1,39 @@
|
|||
<script>
|
||||
import { Input, TextArea, Button } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import api from "builderStore/api"
|
||||
import Title from "../TabTitle.svelte"
|
||||
|
||||
async function updateApplication(data) {
|
||||
const response = await api.put(`/api/${$store.appId}`, data)
|
||||
const app = await response.json()
|
||||
store.update(state => {
|
||||
state = {
|
||||
...state,
|
||||
...data,
|
||||
}
|
||||
return state
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Title>General</Title>
|
||||
<div class="container">
|
||||
<div class="background">
|
||||
<Input thin edit placeholder="Enter your name" label="Name" />
|
||||
<Input
|
||||
on:save={e => updateApplication({ name: e.detail })}
|
||||
thin
|
||||
edit
|
||||
value={$store.name}
|
||||
label="Name" />
|
||||
</div>
|
||||
<div class="background">
|
||||
<TextArea thin edit placeholder="Enter your name" label="Name" />
|
||||
<TextArea
|
||||
on:save={e => updateApplication({ description: e.detail })}
|
||||
thin
|
||||
edit
|
||||
value={$store.description}
|
||||
label="Name" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<style>
|
||||
.apps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 380px);
|
||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
||||
grid-gap: 20px 40px;
|
||||
justify-content: start;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
<span class="icon">
|
||||
<AppsIcon />
|
||||
</span>
|
||||
<h3>Create new web app</h3>
|
||||
<h3 class="header">Create new web app</h3>
|
||||
</div>
|
||||
<Input
|
||||
name="name"
|
||||
|
@ -90,7 +90,7 @@
|
|||
<InfoIcon />
|
||||
How to get started
|
||||
</a>
|
||||
<Button outline thin on:click={_onCancel}>Cancel</Button>
|
||||
<Button secondary thin on:click={_onCancel}>Cancel</Button>
|
||||
<Button primary thin on:click={_onOkay}>Save</Button>
|
||||
</div>
|
||||
<div class="close-button" on:click={_onCancel}>
|
||||
|
@ -125,10 +125,11 @@
|
|||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h3 {
|
||||
.header {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
font-family: inter;
|
||||
}
|
||||
.icon {
|
||||
display: grid;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { fade } from "svelte/transition"
|
||||
import Swatch from "./Swatch.svelte"
|
||||
import CheckedBackground from "./CheckedBackground.svelte"
|
||||
import {buildStyle} from "../helpers.js"
|
||||
import { buildStyle } from "../helpers.js"
|
||||
import {
|
||||
getColorFormat,
|
||||
convertToHSVA,
|
||||
|
@ -14,11 +14,12 @@
|
|||
import ButtonGroup from "./ButtonGroup.svelte"
|
||||
import Input from "./Input.svelte"
|
||||
import Portal from "./Portal.svelte"
|
||||
import {keyevents} from "../actions"
|
||||
import { keyevents } from "../actions"
|
||||
|
||||
export let value = "#3ec1d3ff"
|
||||
export let open = false;
|
||||
export let swatches = [] //TODO: Safe swatches - limit to 12. warn in console
|
||||
export let open = false
|
||||
export let swatches = []
|
||||
|
||||
export let disableSwatches = false
|
||||
export let format = "hexa"
|
||||
export let style = ""
|
||||
|
@ -27,6 +28,7 @@
|
|||
|
||||
let colorPicker = null
|
||||
let adder = null
|
||||
let swatchesSetFromLocalStore = false
|
||||
|
||||
let h = 0
|
||||
let s = 0
|
||||
|
@ -38,10 +40,17 @@
|
|||
onMount(() => {
|
||||
if (!swatches.length > 0) {
|
||||
//Don't use locally stored recent colors if swatches have been passed as props
|
||||
getRecentColors()
|
||||
swatchesSetFromLocalStore = true
|
||||
swatches = getRecentColors() || []
|
||||
}
|
||||
|
||||
if(colorPicker) {
|
||||
if (swatches.length > 12) {
|
||||
console.warn(
|
||||
`Colorpicker - ${swatches.length} swatches were provided. Only the first 12 swatches will be displayed.`
|
||||
)
|
||||
}
|
||||
|
||||
if (colorPicker) {
|
||||
colorPicker.focus()
|
||||
}
|
||||
|
||||
|
@ -53,24 +62,21 @@
|
|||
function getRecentColors() {
|
||||
let colorStore = localStorage.getItem("cp:recent-colors")
|
||||
if (colorStore) {
|
||||
swatches = JSON.parse(colorStore)
|
||||
return JSON.parse(colorStore)
|
||||
}
|
||||
}
|
||||
|
||||
function handleEscape() {
|
||||
if(open) {
|
||||
open = false;
|
||||
if (open) {
|
||||
open = false
|
||||
}
|
||||
}
|
||||
|
||||
function setRecentColor(color) {
|
||||
if (swatches.length === 12) {
|
||||
swatches.splice(0, 1)
|
||||
}
|
||||
if (!swatches.includes(color)) {
|
||||
swatches = [...swatches, color]
|
||||
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches))
|
||||
}
|
||||
function setRecentColors(color) {
|
||||
const s = swatchesSetFromLocalStore
|
||||
? swatches
|
||||
: [...getRecentColors(), color]
|
||||
localStorage.setItem("cp:recent-colors", JSON.stringify(s))
|
||||
}
|
||||
|
||||
function convertAndSetHSVA() {
|
||||
|
@ -134,8 +140,16 @@
|
|||
|
||||
function addSwatch() {
|
||||
if (format) {
|
||||
if (swatches.length === 12) {
|
||||
swatches.splice(0, 1)
|
||||
}
|
||||
|
||||
if (!swatches.includes(value)) {
|
||||
swatches = [...swatches, value]
|
||||
setRecentColors(value)
|
||||
}
|
||||
|
||||
dispatch("addswatch", value)
|
||||
setRecentColor(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +157,10 @@
|
|||
let removedSwatch = swatches.splice(idx, 1)
|
||||
swatches = swatches
|
||||
dispatch("removeswatch", removedSwatch)
|
||||
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches))
|
||||
if (swatchesSetFromLocalStore) {
|
||||
//as could be a swatch not present in local storage
|
||||
setRecentColors()
|
||||
}
|
||||
}
|
||||
|
||||
function applySwatch(color) {
|
||||
|
@ -159,14 +176,14 @@
|
|||
|
||||
$: border = v > 90 && s < 5 ? "1px dashed #dedada" : ""
|
||||
$: selectedColorStyle = buildStyle({ background: value, border })
|
||||
$: shrink = swatches.length > 0
|
||||
|
||||
$: hasSwatches = swatches.length > 0
|
||||
|
||||
</script>
|
||||
|
||||
<Portal>
|
||||
<div
|
||||
class="colorpicker-container"
|
||||
use:keyevents={{"Escape": handleEscape}}
|
||||
use:keyevents={{ Escape: handleEscape }}
|
||||
transition:fade
|
||||
bind:this={colorPicker}
|
||||
{style}
|
||||
|
@ -182,7 +199,10 @@
|
|||
<div class="alpha-hue-panel">
|
||||
<div>
|
||||
<CheckedBackground borderRadius="50%" backgroundSize="8px">
|
||||
<div class="selected-color" style={selectedColorStyle} />
|
||||
<div
|
||||
class="selected-color"
|
||||
title={value}
|
||||
style={selectedColorStyle} />
|
||||
</CheckedBackground>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -205,22 +225,24 @@
|
|||
|
||||
{#if !disableSwatches}
|
||||
<div transition:fade class="swatch-panel">
|
||||
{#if swatches.length > 0}
|
||||
{#if hasSwatches}
|
||||
{#each swatches as color, idx}
|
||||
<Swatch
|
||||
{color}
|
||||
on:click={() => applySwatch(color)}
|
||||
on:removeswatch={() => removeSwatch(idx)} />
|
||||
{#if idx < 12}
|
||||
<Swatch
|
||||
{color}
|
||||
on:click={() => applySwatch(color)}
|
||||
on:removeswatch={() => removeSwatch(idx)} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{#if swatches.length !== 12}
|
||||
{#if swatches.length < 12}
|
||||
<div
|
||||
tabindex="0"
|
||||
use:keyevents={{"Enter": addSwatch}}
|
||||
use:keyevents={{ Enter: addSwatch }}
|
||||
bind:this={adder}
|
||||
transition:fade
|
||||
class="adder"
|
||||
class:shrink
|
||||
class:shrink={hasSwatches}
|
||||
on:click={addSwatch}>
|
||||
<span>+</span>
|
||||
</div>
|
||||
|
@ -310,7 +332,7 @@
|
|||
margin-left: 5px;
|
||||
margin-top: 3px;
|
||||
font-weight: 500;
|
||||
outline-color: #003cb0;
|
||||
outline-color: #003cb0;
|
||||
outline-width: thin;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import CheckedBackground from "./CheckedBackground.svelte"
|
||||
import { createEventDispatcher, beforeUpdate, onMount } from "svelte"
|
||||
|
||||
import {buildStyle, debounce} from "../helpers.js"
|
||||
import { buildStyle, debounce } from "../helpers.js"
|
||||
import { fade } from "svelte/transition"
|
||||
import { getColorFormat } from "../utils.js"
|
||||
|
||||
|
@ -38,7 +38,6 @@
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
function openColorpicker(event) {
|
||||
if (colorPreview) {
|
||||
open = true
|
||||
|
@ -51,7 +50,7 @@
|
|||
}
|
||||
|
||||
function calculateDimensions() {
|
||||
const {
|
||||
const {
|
||||
top: spaceAbove,
|
||||
width,
|
||||
bottom,
|
||||
|
@ -62,22 +61,22 @@
|
|||
const spaceBelow = window.innerHeight - bottom
|
||||
const previewCenter = previewWidth / 2
|
||||
|
||||
let y, x;
|
||||
let y, x
|
||||
|
||||
if(spaceAbove > spaceBelow) {
|
||||
if (spaceAbove > spaceBelow) {
|
||||
positionSide = "bottom"
|
||||
y = (window.innerHeight - spaceAbove)
|
||||
}else{
|
||||
y = window.innerHeight - spaceAbove
|
||||
} else {
|
||||
positionSide = "top"
|
||||
y = bottom
|
||||
}
|
||||
|
||||
x = (left + previewCenter) - (220 / 2)
|
||||
x = left + previewCenter - 220 / 2
|
||||
|
||||
dimensions = { [positionSide]: y.toFixed(1), left: x.toFixed(1) }
|
||||
dimensions = { [positionSide]: y.toFixed(1), left: x.toFixed(1) }
|
||||
}
|
||||
|
||||
$: if(open && colorPreview) {
|
||||
$: if (open && colorPreview) {
|
||||
calculateDimensions()
|
||||
}
|
||||
|
||||
|
@ -87,7 +86,6 @@
|
|||
[positionSide]: `${dimensions[positionSide]}px`,
|
||||
left: `${dimensions.left}px`,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={debounce(calculateDimensions, 200)} />
|
||||
|
@ -99,29 +97,32 @@
|
|||
bind:this={colorPreview}
|
||||
bind:clientHeight={previewHeight}
|
||||
bind:clientWidth={previewWidth}
|
||||
title={value}
|
||||
class="color-preview"
|
||||
style={previewStyle}
|
||||
on:click={openColorpicker} />
|
||||
</CheckedBackground>
|
||||
|
||||
{#if open}
|
||||
<Colorpicker
|
||||
style={pickerStyle}
|
||||
on:change={onColorChange}
|
||||
on:addswatch
|
||||
on:removeswatch
|
||||
bind:format
|
||||
bind:value
|
||||
bind:pickerHeight
|
||||
bind:pickerWidth
|
||||
bind:open
|
||||
{swatches}
|
||||
{disableSwatches}
|
||||
/>
|
||||
<Colorpicker
|
||||
style={pickerStyle}
|
||||
on:change={onColorChange}
|
||||
on:addswatch
|
||||
on:removeswatch
|
||||
bind:format
|
||||
bind:value
|
||||
bind:pickerHeight
|
||||
bind:pickerWidth
|
||||
bind:open
|
||||
{swatches}
|
||||
{disableSwatches} />
|
||||
<div on:click|self={() => (open = false)} class="overlay" />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="color-preview preview-error" style={errorPreviewStyle}>
|
||||
<div
|
||||
class="color-preview preview-error"
|
||||
title="Invalid Color"
|
||||
style={errorPreviewStyle}>
|
||||
<span>×</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let target = document.body;
|
||||
export let target = document.body
|
||||
|
||||
let targetEl;
|
||||
let portal;
|
||||
let componentInstance;
|
||||
let targetEl
|
||||
let portal
|
||||
let componentInstance
|
||||
|
||||
onMount(() => {
|
||||
if (typeof target === "string") {
|
||||
targetEl = document.querySelector(target);
|
||||
targetEl = document.querySelector(target)
|
||||
// Force exit
|
||||
if (targetEl === null) {
|
||||
return () => {};
|
||||
return () => {}
|
||||
}
|
||||
} else if (target instanceof HTMLElement) {
|
||||
targetEl = target;
|
||||
targetEl = target
|
||||
} else {
|
||||
throw new TypeError(
|
||||
`Unknown target type: ${typeof target}. Allowed types: String (CSS selector), HTMLElement.`
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
portal = document.createElement("div");
|
||||
targetEl.appendChild(portal);
|
||||
portal.appendChild(componentInstance);
|
||||
portal = document.createElement("div")
|
||||
targetEl.appendChild(portal)
|
||||
portal.appendChild(componentInstance)
|
||||
|
||||
return () => {
|
||||
targetEl.removeChild(portal);
|
||||
};
|
||||
});
|
||||
targetEl.removeChild(portal)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={componentInstance}>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { createEventDispatcher } from "svelte"
|
||||
import { fade } from "svelte/transition"
|
||||
import CheckedBackground from "./CheckedBackground.svelte"
|
||||
import {keyevents} from "../actions"
|
||||
import { keyevents } from "../actions"
|
||||
|
||||
export let hovered = false
|
||||
export let color = "#fff"
|
||||
|
@ -14,8 +14,9 @@
|
|||
<CheckedBackground borderRadius="6px">
|
||||
<div
|
||||
tabindex="0"
|
||||
use:keyevents={{"Enter": () => dispatch("click")}}
|
||||
use:keyevents={{ Enter: () => dispatch('click') }}
|
||||
in:fade
|
||||
title={color}
|
||||
class="swatch"
|
||||
style={`background: ${color};`}
|
||||
on:click|self
|
||||
|
@ -41,7 +42,7 @@
|
|||
border: 1px solid #dedada;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
outline-color: #003cb0;
|
||||
outline-color: #003cb0;
|
||||
outline-width: thin;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<script>
|
||||
import {onMount} from "svelte"
|
||||
import { onMount } from "svelte"
|
||||
import PropertyGroup from "./PropertyGroup.svelte"
|
||||
import FlatButtonGroup from "./FlatButtonGroup.svelte"
|
||||
|
||||
|
||||
export let panelDefinition = {}
|
||||
export let componentInstance = {}
|
||||
export let componentDefinition = {}
|
||||
|
@ -34,7 +33,6 @@
|
|||
]
|
||||
|
||||
$: propertyGroupNames = Object.keys(panelDefinition)
|
||||
|
||||
</script>
|
||||
|
||||
<div class="design-view-container">
|
||||
|
@ -55,7 +53,7 @@
|
|||
{componentDefinition}
|
||||
{componentInstance}
|
||||
open={currentGroup === groupName}
|
||||
on:open={() => currentGroup = groupName} />
|
||||
on:open={() => (currentGroup = groupName)} />
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="no-design">
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
let selectMenu
|
||||
let icon
|
||||
|
||||
let selectAnchor = null;
|
||||
let dimensions = {top: 0, bottom: 0, left: 0}
|
||||
let selectAnchor = null
|
||||
let dimensions = { top: 0, bottom: 0, left: 0 }
|
||||
|
||||
let positionSide = "top"
|
||||
let maxHeight = 0
|
||||
let scrollTop = 0;
|
||||
let containerEl = null;
|
||||
let scrollTop = 0
|
||||
let containerEl = null
|
||||
|
||||
const handleStyleBind = value =>
|
||||
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {}
|
||||
|
@ -32,61 +32,64 @@
|
|||
return () => {
|
||||
select.removeEventListener("keydown", handleEnter)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
function handleEscape(e) {
|
||||
if(open && e.key === "Escape") {
|
||||
toggleSelect(false)
|
||||
if (open && e.key === "Escape") {
|
||||
toggleSelect(false)
|
||||
}
|
||||
}
|
||||
|
||||
function getDimensions() {
|
||||
const { bottom, top: spaceAbove, left } = selectAnchor.getBoundingClientRect()
|
||||
const {
|
||||
bottom,
|
||||
top: spaceAbove,
|
||||
left,
|
||||
} = selectAnchor.getBoundingClientRect()
|
||||
const spaceBelow = window.innerHeight - bottom
|
||||
|
||||
let y;
|
||||
let y
|
||||
|
||||
if (spaceAbove > spaceBelow) {
|
||||
positionSide = "bottom"
|
||||
maxHeight = spaceAbove - 20
|
||||
y = (window.innerHeight - spaceAbove)
|
||||
maxHeight = spaceAbove - 20
|
||||
y = window.innerHeight - spaceAbove
|
||||
} else {
|
||||
positionSide = "top"
|
||||
y = bottom
|
||||
maxHeight = spaceBelow - 20
|
||||
}
|
||||
|
||||
dimensions = {[positionSide]: y, left}
|
||||
dimensions = { [positionSide]: y, left }
|
||||
}
|
||||
|
||||
function handleEnter(e) {
|
||||
if (!open && e.key === "Enter") {
|
||||
toggleSelect(true)
|
||||
}
|
||||
toggleSelect(true)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelect(isOpen) {
|
||||
getDimensions()
|
||||
if (isOpen) {
|
||||
icon.style.transform = "rotate(180deg)"
|
||||
icon.style.transform = "rotate(180deg)"
|
||||
} else {
|
||||
icon.style.transform = "rotate(0deg)"
|
||||
}
|
||||
open = isOpen
|
||||
}
|
||||
|
||||
|
||||
function handleClick(val) {
|
||||
value = val
|
||||
onChange(value)
|
||||
toggleSelect(false)
|
||||
}
|
||||
|
||||
$: menuStyle = buildStyle({
|
||||
"max-height": `${maxHeight.toFixed(0)}px`,
|
||||
"transform-origin": `center ${positionSide}`,
|
||||
[positionSide]: `${dimensions[positionSide]}px`,
|
||||
"left": `${dimensions.left.toFixed(0)}px`,
|
||||
left: `${dimensions.left.toFixed(0)}px`,
|
||||
})
|
||||
|
||||
$: isOptionsObject = options.every(o => typeof o === "object")
|
||||
|
@ -95,7 +98,7 @@
|
|||
? options.find(o => o.value === value)
|
||||
: {}
|
||||
|
||||
$: if(open && selectMenu) {
|
||||
$: if (open && selectMenu) {
|
||||
selectMenu.focus()
|
||||
}
|
||||
|
||||
|
@ -108,43 +111,43 @@
|
|||
bind:this={select}
|
||||
class="bb-select-container"
|
||||
on:click={() => toggleSelect(!open)}>
|
||||
<div bind:this={selectAnchor} class="bb-select-anchor selected">
|
||||
<div bind:this={selectAnchor} title={value} class="bb-select-anchor selected">
|
||||
<span>{displayLabel}</span>
|
||||
<i bind:this={icon} class="ri-arrow-down-s-fill" />
|
||||
</div>
|
||||
{#if open}
|
||||
<Portal>
|
||||
<div
|
||||
tabindex="0"
|
||||
class:open
|
||||
bind:this={selectMenu}
|
||||
style={menuStyle}
|
||||
on:keydown={handleEscape}
|
||||
class="bb-select-menu">
|
||||
<ul>
|
||||
{#if isOptionsObject}
|
||||
{#each options as { value: v, label }}
|
||||
<li
|
||||
{...handleStyleBind(v)}
|
||||
on:click|self={handleClick(v)}
|
||||
class:selected={value === v}>
|
||||
{label}
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each options as v}
|
||||
<li
|
||||
{...handleStyleBind(v)}
|
||||
on:click|self={handleClick(v)}
|
||||
class:selected={value === v}>
|
||||
{v}
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
<div on:click|self={() => toggleSelect(false)} class="overlay" />
|
||||
</Portal>
|
||||
<Portal>
|
||||
<div
|
||||
tabindex="0"
|
||||
class:open
|
||||
bind:this={selectMenu}
|
||||
style={menuStyle}
|
||||
on:keydown={handleEscape}
|
||||
class="bb-select-menu">
|
||||
<ul>
|
||||
{#if isOptionsObject}
|
||||
{#each options as { value: v, label }}
|
||||
<li
|
||||
{...handleStyleBind(v)}
|
||||
on:click|self={handleClick(v)}
|
||||
class:selected={value === v}>
|
||||
{label}
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each options as v}
|
||||
<li
|
||||
{...handleStyleBind(v)}
|
||||
on:click|self={handleClick(v)}
|
||||
class:selected={value === v}>
|
||||
{v}
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
<div on:click|self={() => toggleSelect(false)} class="overlay" />
|
||||
</Portal>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
@ -174,6 +177,7 @@
|
|||
background-color: var(--grey-2);
|
||||
border-radius: 5px;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bb-select-anchor > span {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
$: style = componentInstance["_styles"][styleCategory] || {}
|
||||
</script>
|
||||
|
||||
<DetailSummary {name} on:open show={open} >
|
||||
<DetailSummary {name} on:open show={open}>
|
||||
{#each properties as props}
|
||||
<PropertyControl
|
||||
label={props.label}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</script>
|
||||
|
||||
<section>
|
||||
<Button purple wide on:click{newWorkflow}>Create New Workflow</Button>
|
||||
<Button purple wide on:click={newWorkflow}>Create New Workflow</Button>
|
||||
<ul>
|
||||
{#each $workflowStore.workflows as workflow}
|
||||
<li
|
||||
|
|
|
@ -38,7 +38,9 @@
|
|||
if (!activeTopNav) return
|
||||
store.update(state => {
|
||||
if (!state.previousTopNavPath) state.previousTopNavPath = {}
|
||||
state.previousTopNavPath[activeTopNav.path] = window.location.pathname.replace("/_builder", "")
|
||||
state.previousTopNavPath[
|
||||
activeTopNav.path
|
||||
] = window.location.pathname.replace("/_builder", "")
|
||||
$goto(state.previousTopNavPath[path] || path)
|
||||
return state
|
||||
})
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<script>
|
||||
import { params } from "@sveltech/routify"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { params } from "@sveltech/routify"
|
||||
import { backendUiStore } from "builderStore"
|
||||
|
||||
if ($params.selectedModel) {
|
||||
const model = $backendUiStore.models.find(m => m._id === $params.selectedModel)
|
||||
if (model) {
|
||||
backendUiStore.actions.models.select(model)
|
||||
if ($params.selectedModel) {
|
||||
const model = $backendUiStore.models.find(
|
||||
m => m._id === $params.selectedModel
|
||||
)
|
||||
if (model) {
|
||||
backendUiStore.actions.models.select(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
onMount(async () => {
|
||||
// navigate to first model in list, if not already selected
|
||||
// and this is the final url (i.e. no selectedModel)
|
||||
if (!$leftover && $backendUiStore.models.length > 0 && (!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)) {
|
||||
if (
|
||||
!$leftover &&
|
||||
$backendUiStore.models.length > 0 &&
|
||||
(!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)
|
||||
) {
|
||||
$goto(`./${$backendUiStore.models[0]._id}`)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,9 +8,13 @@
|
|||
}
|
||||
|
||||
onMount(async () => {
|
||||
// navigate to first model in list, if not already selected
|
||||
// navigate to first model in list, if not already selected
|
||||
// and this is the final url (i.e. no selectedModel)
|
||||
if (!$leftover && $backendUiStore.models.length > 0 && (!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)) {
|
||||
if (
|
||||
!$leftover &&
|
||||
$backendUiStore.models.length > 0 &&
|
||||
(!$backendUiStore.selectedModel || !$backendUiStore.selectedModel._id)
|
||||
) {
|
||||
// this file routes as .../models/index, so, go up one.
|
||||
$goto(`../${$backendUiStore.models[0]._id}`)
|
||||
}
|
||||
|
@ -18,7 +22,5 @@
|
|||
</script>
|
||||
|
||||
{#if $backendUiStore.models.length === 0}
|
||||
Please create a model
|
||||
{:else}
|
||||
Please select a model
|
||||
{/if}
|
||||
Please create a model
|
||||
{:else}Please select a model{/if}
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
if ($leftover) {
|
||||
// Get the correct screen children.
|
||||
const screenChildren = $store.pages[$params.page]._screens.find(
|
||||
screen => screen.props._instanceName === $params.screen
|
||||
screen =>
|
||||
(screen.props._instanceName === $params.screen
|
||||
|| screen.props._instanceName === decodeURIComponent($params.screen))
|
||||
).props._children
|
||||
findComponent(componentIds, screenChildren)
|
||||
}
|
||||
|
|
|
@ -40,10 +40,7 @@
|
|||
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">Contact</div>
|
||||
<Link
|
||||
icon={ContributionIcon}
|
||||
title="Contribute to our product"
|
||||
href="/" />
|
||||
<Link icon={ContributionIcon} title="Contribute" href="/" />
|
||||
<Link icon={BugIcon} title="Report bug" href="/" />
|
||||
<Link icon={EmailIcon} title="Email" href="/" />
|
||||
<Link icon={TwitterIcon} title="Twitter" href="/" />
|
||||
|
@ -59,7 +56,7 @@
|
|||
<style>
|
||||
.root {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
grid-template-columns: 260px 1fr;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: var(--grey-1);
|
||||
|
@ -67,6 +64,7 @@
|
|||
|
||||
.main {
|
||||
grid-column: 2;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ui-nav {
|
||||
|
@ -75,7 +73,6 @@
|
|||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--grey-4);
|
||||
}
|
||||
|
||||
.home-logo {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { get } from "builderStore/api"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import CreateAppModal from "components/start/CreateAppModal.svelte"
|
||||
import { Button } from "@budibase/bbui"
|
||||
|
||||
let promise = getApps()
|
||||
|
||||
|
@ -43,21 +44,17 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="welcome">Welcome to Budibase</div>
|
||||
<div class="header">
|
||||
<div class="welcome">Welcome to the Budibase Beta</div>
|
||||
<Button purple large on:click={showCreateAppModal}>Create New Web App</Button>
|
||||
</div>
|
||||
|
||||
<div class="banner">
|
||||
<img src="/_builder/assets/rocket.jpg" alt="rocket" />
|
||||
<img src="/_builder/assets/orange-landscape.png" alt="rocket" />
|
||||
<div class="banner-content">
|
||||
Every accomplishment starts with a decision to try.
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-section-header">
|
||||
<div class="app-section-title">Your Web Apps</div>
|
||||
<button class="banner-button" type="button" on:click={showCreateAppModal}>
|
||||
<i class="ri-add-circle-fill" />
|
||||
Create New Web App
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#await promise}
|
||||
<div class="spinner-container">
|
||||
|
@ -70,11 +67,17 @@
|
|||
{/await}
|
||||
|
||||
<style>
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 40px 80px 0px 80px;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
font-size: 42px;
|
||||
color: var(--ink);
|
||||
font-weight: 700;
|
||||
margin: 40px 0px 0px 80px;
|
||||
}
|
||||
|
||||
.banner {
|
||||
|
@ -108,48 +111,4 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.banner-button {
|
||||
background-color: var(--ink);
|
||||
color: var(--white);
|
||||
padding: 12px 24px;
|
||||
border-radius: 5px;
|
||||
border: var(--ink) 1px solid;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease 0s;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ri-add-circle-fill {
|
||||
margin-right: 4px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.banner-button:hover {
|
||||
background-color: var(--white);
|
||||
color: var(--ink);
|
||||
border: var(--grey-4) 1px solid;
|
||||
}
|
||||
|
||||
.app-section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 40px 80px 0px 80px;
|
||||
}
|
||||
|
||||
.app-section-title {
|
||||
font-size: 20px;
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.4 MiB |
|
@ -90,6 +90,22 @@ exports.create = async function(ctx) {
|
|||
ctx.message = `Application ${ctx.request.body.name} created successfully`
|
||||
}
|
||||
|
||||
exports.update = async function(ctx) {
|
||||
const clientId = await lookupClientId(ctx.params.applicationId)
|
||||
const db = new CouchDB(ClientDb.name(clientId))
|
||||
const application = await db.get(ctx.params.applicationId)
|
||||
|
||||
const data = ctx.request.body
|
||||
const newData = { ...application, ...data }
|
||||
|
||||
const response = await db.put(newData)
|
||||
data._rev = response.rev
|
||||
|
||||
ctx.status = 200
|
||||
ctx.message = `Application ${application.name} updated successfully.`
|
||||
ctx.body = response
|
||||
}
|
||||
|
||||
const createEmptyAppPackage = async (ctx, app) => {
|
||||
const templateFolder = resolve(
|
||||
__dirname,
|
||||
|
|
|
@ -12,6 +12,7 @@ router
|
|||
authorized(BUILDER),
|
||||
controller.fetchAppPackage
|
||||
)
|
||||
.put("/api/:applicationId", authorized(BUILDER), controller.update)
|
||||
.post("/api/applications", authorized(BUILDER), controller.create)
|
||||
|
||||
module.exports = router
|
||||
|
|
Loading…
Reference in New Issue