Merge pull request #1437 from Budibase/fix/views-and-prettier

Fix prettier and other misc fixes
This commit is contained in:
Andrew Kingston 2021-05-04 11:33:35 +01:00 committed by GitHub
commit 2942b4f6bc
380 changed files with 2127 additions and 1607 deletions

View File

@ -3,6 +3,8 @@
"semi": false, "semi": false,
"singleQuote": false, "singleQuote": false,
"trailingComma": "es5", "trailingComma": "es5",
"arrowParens": "avoid",
"jsxBracketSameLine": false,
"plugins": ["prettier-plugin-svelte"], "plugins": ["prettier-plugin-svelte"],
"svelteSortOrder" : "scripts-markup-styles" "svelteSortOrder" : "options-scripts-markup-styles"
} }

View File

@ -14,7 +14,7 @@
"prettier-plugin-svelte": "^2.2.0", "prettier-plugin-svelte": "^2.2.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup-plugin-replace": "^2.2.0", "rollup-plugin-replace": "^2.2.0",
"svelte": "^3.30.0" "svelte": "^3.37.0"
}, },
"scripts": { "scripts": {
"bootstrap": "lerna link && lerna bootstrap", "bootstrap": "lerna link && lerna bootstrap",

View File

@ -1,9 +1,9 @@
let Pouch let Pouch
module.exports.setDB = (pouch) => { module.exports.setDB = pouch => {
Pouch = pouch Pouch = pouch
} }
module.exports.getDB = (dbName) => { module.exports.getDB = dbName => {
return new Pouch(dbName) return new Pouch(dbName)
} }

View File

@ -48,7 +48,7 @@ exports.getGroupParams = (id = "", otherProps = {}) => {
* Generates a new global user ID. * Generates a new global user ID.
* @returns {string} The new user ID which the user doc can be stored under. * @returns {string} The new user ID which the user doc can be stored under.
*/ */
exports.generateGlobalUserID = (id) => { exports.generateGlobalUserID = id => {
return `${DocumentTypes.USER}${SEPARATOR}${id || newid()}` return `${DocumentTypes.USER}${SEPARATOR}${id || newid()}`
} }
@ -70,7 +70,7 @@ exports.getGlobalUserParams = (globalId, otherProps = {}) => {
* Generates a template ID. * Generates a template ID.
* @param ownerId The owner/user of the template, this could be global or a group level. * @param ownerId The owner/user of the template, this could be global or a group level.
*/ */
exports.generateTemplateID = (ownerId) => { exports.generateTemplateID = ownerId => {
return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${newid()}` return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${newid()}`
} }
@ -132,7 +132,7 @@ const determineScopedConfig = async function (db, { type, user, group }) {
} }
) )
) )
const configs = response.rows.map((row) => { const configs = response.rows.map(row => {
const config = row.doc const config = row.doc
// Config is specific to a user and a group // Config is specific to a user and a group

View File

@ -4,7 +4,7 @@ const { v4 } = require("uuid")
const SALT_ROUNDS = env.SALT_ROUNDS || 10 const SALT_ROUNDS = env.SALT_ROUNDS || 10
exports.hash = async (data) => { exports.hash = async data => {
const salt = await bcrypt.genSalt(SALT_ROUNDS) const salt = await bcrypt.genSalt(SALT_ROUNDS)
return bcrypt.hash(data, salt) return bcrypt.hash(data, salt)
} }

View File

@ -22,7 +22,7 @@ function confirmAppId(possibleAppId) {
* @param {object} ctx The main request body to look through. * @param {object} ctx The main request body to look through.
* @returns {string|undefined} If an appId was found it will be returned. * @returns {string|undefined} If an appId was found it will be returned.
*/ */
exports.getAppId = (ctx) => { exports.getAppId = ctx => {
const options = [ctx.headers["x-budibase-app-id"], ctx.params.appId] const options = [ctx.headers["x-budibase-app-id"], ctx.params.appId]
if (ctx.subdomains) { if (ctx.subdomains) {
options.push(ctx.subdomains[1]) options.push(ctx.subdomains[1])
@ -41,7 +41,7 @@ exports.getAppId = (ctx) => {
} }
let appPath = let appPath =
ctx.request.headers.referrer || ctx.request.headers.referrer ||
ctx.path.split("/").filter((subPath) => subPath.startsWith(APP_PREFIX)) ctx.path.split("/").filter(subPath => subPath.startsWith(APP_PREFIX))
if (!appId && appPath.length !== 0) { if (!appId && appPath.length !== 0) {
appId = confirmAppId(appPath[0]) appId = confirmAppId(appPath[0])
} }
@ -101,11 +101,11 @@ exports.clearCookie = (ctx, name) => {
* @param {object} ctx The koa context object to be tested. * @param {object} ctx The koa context object to be tested.
* @return {boolean} returns true if the call is from the client lib (a built app rather than the builder). * @return {boolean} returns true if the call is from the client lib (a built app rather than the builder).
*/ */
exports.isClient = (ctx) => { exports.isClient = ctx => {
return ctx.headers["x-budibase-type"] === "client" return ctx.headers["x-budibase-type"] === "client"
} }
exports.getGlobalUserByEmail = async (email) => { exports.getGlobalUserByEmail = async email => {
const db = getDB(StaticDatabases.GLOBAL.name) const db = getDB(StaticDatabases.GLOBAL.name)
try { try {
let users = ( let users = (
@ -114,7 +114,7 @@ exports.getGlobalUserByEmail = async (email) => {
include_docs: true, include_docs: true,
}) })
).rows ).rows
users = users.map((user) => user.doc) users = users.map(user => user.doc)
return users.length <= 1 ? users[0] : users return users.length <= 1 ? users[0] : users
} catch (err) { } catch (err) {
if (err != null && err.name === "not_found") { if (err != null && err.name === "not_found") {

View File

@ -42,12 +42,14 @@
class="spectrum-ActionButton spectrum-ActionButton--size{size}" class="spectrum-ActionButton spectrum-ActionButton--size{size}"
{disabled} {disabled}
on:longPress on:longPress
on:click|preventDefault> on:click|preventDefault
>
{#if longPressable} {#if longPressable}
<svg <svg
class="spectrum-Icon spectrum-UIIcon-CornerTriangle100 spectrum-ActionButton-hold" class="spectrum-Icon spectrum-UIIcon-CornerTriangle100 spectrum-ActionButton-hold"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-CornerTriangle100" /> <use xlink:href="#spectrum-css-icon-CornerTriangle100" />
</svg> </svg>
{/if} {/if}
@ -56,7 +58,8 @@
class="spectrum-Icon spectrum-Icon--size{size}" class="spectrum-Icon spectrum-Icon--size{size}"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label={icon}> aria-label={icon}
>
<use xlink:href="#spectrum-icon-18-{icon}" /> <use xlink:href="#spectrum-icon-18-{icon}" />
</svg> </svg>
{/if} {/if}

View File

@ -8,7 +8,7 @@
// Attaches a spectrum-ActionGroup-item class to buttons inside the div // Attaches a spectrum-ActionGroup-item class to buttons inside the div
function group(element) { function group(element) {
const buttons = Array.from(element.getElementsByTagName("button")) const buttons = Array.from(element.getElementsByTagName("button"))
buttons.forEach((button) => { buttons.forEach(button => {
button.classList.add("spectrum-ActionGroup-item") button.classList.add("spectrum-ActionGroup-item")
}) })
} }

View File

@ -47,7 +47,7 @@ export default function positionDropdown(element, { anchor, align }) {
element.style[positionSide] = `${dimensions[positionSide]}px` element.style[positionSide] = `${dimensions[positionSide]}px`
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px` element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver(entries => {
entries.forEach(() => { entries.forEach(() => {
dimensions = getDimensions() dimensions = getDimensions()
element.style[positionSide] = `${dimensions[positionSide]}px` element.style[positionSide] = `${dimensions[positionSide]}px`

View File

@ -23,13 +23,15 @@
class:active class:active
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}" class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
{disabled} {disabled}
on:click|preventDefault> on:click|preventDefault
>
{#if icon} {#if icon}
<svg <svg
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}" class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label={icon}> aria-label={icon}
>
<use xlink:href="#spectrum-icon-18-{icon}" /> <use xlink:href="#spectrum-icon-18-{icon}" />
</svg> </svg>
{/if} {/if}

View File

@ -1,15 +1,19 @@
<script> <script>
import "@spectrum-css/buttongroup/dist/index-vars.css" import "@spectrum-css/buttongroup/dist/index-vars.css"
export let vertical = false export let vertical = false
function group(element) { function group(element) {
const buttons = Array.from(element.getElementsByTagName('button')) const buttons = Array.from(element.getElementsByTagName("button"))
buttons.forEach(button => { buttons.forEach(button => {
button.classList.add('spectrum-ButtonGroup-item') button.classList.add("spectrum-ButtonGroup-item")
}) })
} }
</script> </script>
<div use:group class="spectrum-ButtonGroup" class:spectrum-ButtonGroup--vertical={vertical}> <div
<slot /> use:group
class="spectrum-ButtonGroup"
class:spectrum-ButtonGroup--vertical={vertical}
>
<slot />
</div> </div>

View File

@ -35,5 +35,6 @@
{placeholder} {placeholder}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
on:change={onChange} /> on:change={onChange}
/>
</Field> </Field>

View File

@ -17,27 +17,31 @@
<label <label
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized" class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
class:is-invalid={!!error}> class:is-invalid={!!error}
>
<input <input
checked={value} checked={value}
{disabled} {disabled}
on:change={onChange} on:change={onChange}
type="checkbox" type="checkbox"
class="spectrum-Checkbox-input" class="spectrum-Checkbox-input"
{id} /> {id}
/>
<span class="spectrum-Checkbox-box"> <span class="spectrum-Checkbox-box">
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark" class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" /> <use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg> </svg>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark" class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Dash100" /> <use xlink:href="#spectrum-css-icon-Dash100" />
</svg> </svg>
</span> </span>
<span class="spectrum-Checkbox-label">{text || ''}</span> <span class="spectrum-Checkbox-label">{text || ""}</span>
</label> </label>

View File

@ -49,7 +49,8 @@
<div <div
class="spectrum-Textfield spectrum-InputGroup-textfield" class="spectrum-Textfield spectrum-InputGroup-textfield"
class:is-disabled={!!error} class:is-disabled={!!error}
class:is-focused={open || focus}> class:is-focused={open || focus}
>
<input <input
type="text" type="text"
on:focus={() => (focus = true)} on:focus={() => (focus = true)}
@ -57,18 +58,21 @@
on:change={onChange} on:change={onChange}
{value} {value}
{placeholder} {placeholder}
class="spectrum-Textfield-input spectrum-InputGroup-input" /> class="spectrum-Textfield-input spectrum-InputGroup-input"
/>
</div> </div>
<button <button
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button" class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
tabindex="-1" tabindex="-1"
aria-haspopup="true" aria-haspopup="true"
disabled={!!error} disabled={!!error}
on:click={() => (open = true)}> on:click={() => (open = true)}
>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon spectrum-InputGroup-icon" class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon spectrum-InputGroup-icon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Chevron100" /> <use xlink:href="#spectrum-css-icon-Chevron100" />
</svg> </svg>
</button> </button>
@ -76,7 +80,8 @@
<div class="overlay" on:mousedown|self={() => (open = false)} /> <div class="overlay" on:mousedown|self={() => (open = false)} />
<div <div
transition:fly={{ y: -20, duration: 200 }} transition:fly={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom is-open"> class="spectrum-Popover spectrum-Popover--bottom is-open"
>
<ul class="spectrum-Menu" role="listbox"> <ul class="spectrum-Menu" role="listbox">
{#if options && Array.isArray(options)} {#if options && Array.isArray(options)}
{#each options as option} {#each options as option}
@ -86,13 +91,16 @@
role="option" role="option"
aria-selected="true" aria-selected="true"
tabindex="0" tabindex="0"
on:click={() => selectOption(getOptionValue(option))}> on:click={() => selectOption(getOptionValue(option))}
<span >
class="spectrum-Menu-itemLabel">{getOptionLabel(option)}</span> <span class="spectrum-Menu-itemLabel"
>{getOptionLabel(option)}</span
>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon" class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" /> <use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg> </svg>
</li> </li>

View File

@ -64,7 +64,8 @@
on:close={onClose} on:close={onClose}
options={flatpickrOptions} options={flatpickrOptions}
on:change={handleChange} on:change={handleChange}
element={`#${flatpickrId}`}> element={`#${flatpickrId}`}
>
<div <div
id={flatpickrId} id={flatpickrId}
class:is-disabled={disabled} class:is-disabled={disabled}
@ -73,17 +74,20 @@
class:is-focused={open} class:is-focused={open}
aria-readonly="false" aria-readonly="false"
aria-required="false" aria-required="false"
aria-haspopup="true"> aria-haspopup="true"
>
<div <div
on:click={flatpickr?.open} on:click={flatpickr?.open}
class="spectrum-Textfield spectrum-InputGroup-textfield" class="spectrum-Textfield spectrum-InputGroup-textfield"
class:is-disabled={disabled} class:is-disabled={disabled}
class:is-invalid={!!error}> class:is-invalid={!!error}
>
{#if !!error} {#if !!error}
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon" class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-Alert" /> <use xlink:href="#spectrum-icon-18-Alert" />
</svg> </svg>
{/if} {/if}
@ -94,7 +98,8 @@
class="spectrum-Textfield-input spectrum-InputGroup-input" class="spectrum-Textfield-input spectrum-InputGroup-input"
{placeholder} {placeholder}
{id} {id}
{value} /> {value}
/>
</div> </div>
<button <button
type="button" type="button"
@ -102,12 +107,14 @@
tabindex="-1" tabindex="-1"
{disabled} {disabled}
class:is-invalid={!!error} class:is-invalid={!!error}
on:click={flatpickr?.open}> on:click={flatpickr?.open}
>
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM" class="spectrum-Icon spectrum-Icon--sizeM"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label="Calendar"> aria-label="Calendar"
>
<use xlink:href="#spectrum-icon-18-Calendar" /> <use xlink:href="#spectrum-icon-18-Calendar" />
</svg> </svg>
</button> </button>

View File

@ -119,13 +119,15 @@
<div <div
class="nav left" class="nav left"
class:visible={selectedImageIdx > 0} class:visible={selectedImageIdx > 0}
on:click={navigateLeft}> on:click={navigateLeft}
>
<Icon name="ChevronLeft" /> <Icon name="ChevronLeft" />
</div> </div>
<div <div
class="nav right" class="nav right"
class:visible={selectedImageIdx < fileCount - 1} class:visible={selectedImageIdx < fileCount - 1}
on:click={navigateRight}> on:click={navigateRight}
>
<Icon name="ChevronRight" /> <Icon name="ChevronRight" />
</div> </div>
<div class="footer">File {selectedImageIdx + 1} of {fileCount}</div> <div class="footer">File {selectedImageIdx + 1} of {fileCount}</div>
@ -140,19 +142,22 @@
on:dragleave={handleDragLeave} on:dragleave={handleDragLeave}
on:dragenter={handleDragOver} on:dragenter={handleDragOver}
on:drop={handleDrop} on:drop={handleDrop}
class:is-dragged={fileDragged}> class:is-dragged={fileDragged}
>
<div class="spectrum-IllustratedMessage spectrum-IllustratedMessage--cta"> <div class="spectrum-IllustratedMessage spectrum-IllustratedMessage--cta">
<input <input
id={fieldId} id={fieldId}
{disabled} {disabled}
type="file" type="file"
multiple multiple
on:change={handleFile} /> on:change={handleFile}
/>
<svg <svg
class="spectrum-IllustratedMessage-illustration" class="spectrum-IllustratedMessage-illustration"
width="125" width="125"
height="60" height="60"
viewBox="0 0 199 97.7"><defs> viewBox="0 0 199 97.7"
><defs>
<style> <style>
.cls-1, .cls-1,
.cls-2 { .cls-2 {
@ -170,25 +175,31 @@
</defs> </defs>
<path <path
class="cls-1" class="cls-1"
d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66" /> d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66"
/>
<line class="cls-1" x1="99.5" y1="95.5" x2="99.5" y2="58.5" /> <line class="cls-1" x1="99.5" y1="95.5" x2="99.5" y2="58.5" />
<path class="cls-1" d="M105.5,73.5h19a2,2,0,0,0,2-2v-43" /> <path class="cls-1" d="M105.5,73.5h19a2,2,0,0,0,2-2v-43" />
<path <path
class="cls-1" class="cls-1"
d="M126.5,22.5h-19a2,2,0,0,1-2-2V1.5h-31a2,2,0,0,0-2,2v68a2,2,0,0,0,2,2h19" /> d="M126.5,22.5h-19a2,2,0,0,1-2-2V1.5h-31a2,2,0,0,0-2,2v68a2,2,0,0,0,2,2h19"
/>
<line class="cls-1" x1="105.5" y1="1.5" x2="126.5" y2="22.5" /> <line class="cls-1" x1="105.5" y1="1.5" x2="126.5" y2="22.5" />
<path <path
class="cls-2" class="cls-2"
d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z" /> d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z"
/>
<path <path
class="cls-2" class="cls-2"
d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2" /> d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2"
/>
<path <path
class="cls-2" class="cls-2"
d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7" /> d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7"
/>
<path <path
class="cls-1" class="cls-1"
d="M139.5,36.5H196A1.49,1.49,0,0,1,197.5,38V72A1.49,1.49,0,0,1,196,73.5H141A1.49,1.49,0,0,1,139.5,72V32A1.49,1.49,0,0,1,141,30.5H154a2.43,2.43,0,0,1,1.67.66l6,5.66" /> d="M139.5,36.5H196A1.49,1.49,0,0,1,197.5,38V72A1.49,1.49,0,0,1,196,73.5H141A1.49,1.49,0,0,1,139.5,72V32A1.49,1.49,0,0,1,141,30.5H154a2.43,2.43,0,0,1,1.67.66l6,5.66"
/>
<rect <rect
class="cls-1" class="cls-1"
x="1.5" x="1.5"
@ -196,16 +207,21 @@
width="58" width="58"
height="39" height="39"
rx="2" rx="2"
ry="2" /> ry="2"
/>
</svg> </svg>
<h2 <h2
class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading"> class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading"
>
Drag and drop your file Drag and drop your file
</h2> </h2>
{#if !disabled} {#if !disabled}
<p <p
class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description"> class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description"
<label for={fieldId} class="spectrum-Link">Select a file to upload</label> >
<label for={fieldId} class="spectrum-Link"
>Select a file to upload</label
>
<br /> <br />
from your computer from your computer
</p> </p>

View File

@ -78,4 +78,5 @@
{isOptionSelected} {isOptionSelected}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
onSelectOption={toggleOption} /> onSelectOption={toggleOption}
/>

View File

@ -37,7 +37,8 @@
class:is-invalid={!!error} class:is-invalid={!!error}
class:is-open={open} class:is-open={open}
aria-haspopup="listbox" aria-haspopup="listbox"
on:mousedown={onClick}> on:mousedown={onClick}
>
<span class="spectrum-Picker-label" class:is-placeholder={isPlaceholder}> <span class="spectrum-Picker-label" class:is-placeholder={isPlaceholder}>
{fieldText} {fieldText}
</span> </span>
@ -46,14 +47,16 @@
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon" class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label="Folder"> aria-label="Folder"
>
<use xlink:href="#spectrum-icon-18-Alert" /> <use xlink:href="#spectrum-icon-18-Alert" />
</svg> </svg>
{/if} {/if}
<svg <svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon" class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Chevron100" /> <use xlink:href="#spectrum-css-icon-Chevron100" />
</svg> </svg>
</button> </button>
@ -61,7 +64,8 @@
<div <div
use:clickOutside={() => (open = false)} use:clickOutside={() => (open = false)}
transition:fly={{ y: -20, duration: 200 }} transition:fly={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"> class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
>
<ul class="spectrum-Menu" role="listbox"> <ul class="spectrum-Menu" role="listbox">
{#if placeholderOption} {#if placeholderOption}
<li <li
@ -70,12 +74,14 @@
role="option" role="option"
aria-selected="true" aria-selected="true"
tabindex="0" tabindex="0"
on:click={() => onSelectOption(null)}> on:click={() => onSelectOption(null)}
>
<span class="spectrum-Menu-itemLabel">{placeholderOption}</span> <span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon" class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" /> <use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg> </svg>
</li> </li>
@ -88,13 +94,16 @@
role="option" role="option"
aria-selected="true" aria-selected="true"
tabindex="0" tabindex="0"
on:click={() => onSelectOption(getOptionValue(option, idx))}> on:click={() => onSelectOption(getOptionValue(option, idx))}
<span >
class="spectrum-Menu-itemLabel">{getOptionLabel(option, idx)}</span> <span class="spectrum-Menu-itemLabel"
>{getOptionLabel(option, idx)}</span
>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon" class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" /> <use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg> </svg>
</li> </li>

View File

@ -21,14 +21,16 @@
<div <div
title={getOptionLabel(option)} title={getOptionLabel(option)}
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized" class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
class:is-invalid={!!error}> class:is-invalid={!!error}
>
<input <input
on:change={onChange} on:change={onChange}
bind:group={value} bind:group={value}
value={getOptionValue(option)} value={getOptionValue(option)}
type="radio" type="radio"
class="spectrum-Radio-input" class="spectrum-Radio-input"
{disabled} /> {disabled}
/>
<span class="spectrum-Radio-button" /> <span class="spectrum-Radio-button" />
<label class="spectrum-Radio-label">{getOptionLabel(option)}</label> <label class="spectrum-Radio-label">{getOptionLabel(option)}</label>
</div> </div>

View File

@ -34,11 +34,13 @@
<div <div
class="spectrum-Textfield" class="spectrum-Textfield"
class:is-focused={focus} class:is-focused={focus}
class:is-disabled={disabled}> class:is-disabled={disabled}
>
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-icon" class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-icon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-Magnify" /> <use xlink:href="#spectrum-icon-18-Magnify" />
</svg> </svg>
<input <input
@ -46,23 +48,26 @@
on:keyup={updateValueOnEnter} on:keyup={updateValueOnEnter}
{disabled} {disabled}
{id} {id}
value={value || ''} value={value || ""}
placeholder={placeholder || ''} placeholder={placeholder || ""}
on:blur={onBlur} on:blur={onBlur}
on:focus={onFocus} on:focus={onFocus}
on:input on:input
type="search" type="search"
class="spectrum-Textfield-input spectrum-Search-input" class="spectrum-Textfield-input spectrum-Search-input"
autocomplete="off" /> autocomplete="off"
/>
</div> </div>
<button <button
on:click={() => updateValue('')} on:click={() => updateValue("")}
type="reset" type="reset"
class="spectrum-ClearButton spectrum-Search-clearButton"> class="spectrum-ClearButton spectrum-Search-clearButton"
>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Cross75" class="spectrum-Icon spectrum-UIIcon-Cross75"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Cross75" /> <use xlink:href="#spectrum-css-icon-Cross75" />
</svg> </svg>
</button> </button>

View File

@ -51,7 +51,8 @@
{options} {options}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
isPlaceholder={value == null || value === ''} isPlaceholder={value == null || value === ""}
placeholderOption={placeholder} placeholderOption={placeholder}
isOptionSelected={option => option === value} isOptionSelected={option => option === value}
onSelectOption={selectOption} /> onSelectOption={selectOption}
/>

View File

@ -21,7 +21,8 @@
on:change={onChange} on:change={onChange}
{id} {id}
type="checkbox" type="checkbox"
class="spectrum-Switch-input" /> class="spectrum-Switch-input"
/>
<span class="spectrum-Switch-switch" /> <span class="spectrum-Switch-switch" />
<label class="spectrum-Switch-label" for={id}>{text}</label> <label class="spectrum-Switch-label" for={id}>{text}</label>
</div> </div>

View File

@ -25,23 +25,28 @@
class="spectrum-Textfield spectrum-Textfield--multiline" class="spectrum-Textfield spectrum-Textfield--multiline"
class:is-invalid={!!error} class:is-invalid={!!error}
class:is-disabled={disabled} class:is-disabled={disabled}
class:is-focused={focus}> class:is-focused={focus}
>
{#if error} {#if error}
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon" class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-Alert" /> <use xlink:href="#spectrum-icon-18-Alert" />
</svg> </svg>
{/if} {/if}
<textarea <textarea
bind:this={textarea} bind:this={textarea}
placeholder={placeholder || ''} placeholder={placeholder || ""}
class="spectrum-Textfield-input" class="spectrum-Textfield-input"
{disabled} {disabled}
{id} {id}
on:focus={() => (focus = true)} on:focus={() => (focus = true)}
on:blur={onChange}>{value || ''}</textarea> on:blur={onChange}
>
{value || ""}
</textarea>
</div> </div>
<style> <style>

View File

@ -53,12 +53,14 @@
class="spectrum-Textfield" class="spectrum-Textfield"
class:is-invalid={!!error} class:is-invalid={!!error}
class:is-disabled={disabled} class:is-disabled={disabled}
class:is-focused={focus}> class:is-focused={focus}
>
{#if error} {#if error}
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon" class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-Alert" /> <use xlink:href="#spectrum-icon-18-Alert" />
</svg> </svg>
{/if} {/if}
@ -68,13 +70,14 @@
{disabled} {disabled}
{readonly} {readonly}
{id} {id}
value={value || ''} value={value || ""}
placeholder={placeholder || ''} placeholder={placeholder || ""}
on:blur={onBlur} on:blur={onBlur}
on:focus={onFocus} on:focus={onFocus}
on:input on:input
{type} {type}
class="spectrum-Textfield-input" /> class="spectrum-Textfield-input"
/>
</div> </div>
<style> <style>

View File

@ -25,5 +25,6 @@
{value} {value}
{placeholder} {placeholder}
{enableTime} {enableTime}
on:change={onChange} /> on:change={onChange}
/>
</Field> </Field>

View File

@ -27,5 +27,6 @@
{fileSizeLimit} {fileSizeLimit}
{processFiles} {processFiles}
{handleFileTooLarge} {handleFileTooLarge}
on:change={onChange} /> on:change={onChange}
/>
</Field> </Field>

View File

@ -9,7 +9,7 @@
export let error = null export let error = null
</script> </script>
<div class="spectrum-Form-item" class:above={labelPosition === 'above'}> <div class="spectrum-Form-item" class:above={labelPosition === "above"}>
{#if label} {#if label}
<FieldLabel forId={id} {label} position={labelPosition} /> <FieldLabel forId={id} {label} position={labelPosition} />
{/if} {/if}

View File

@ -10,8 +10,9 @@
<label <label
for={forId} for={forId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${className}`}> class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${className}`}
{label || ''} >
{label || ""}
</label> </label>
<style> <style>

View File

@ -29,5 +29,6 @@
{type} {type}
on:change={onChange} on:change={onChange}
on:click on:click
on:input /> on:input
/>
</Field> </Field>

View File

@ -31,5 +31,6 @@
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
on:change={onChange} on:change={onChange}
on:click /> on:click
/>
</Field> </Field>

View File

@ -33,5 +33,6 @@
{options} {options}
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
on:change={onChange} /> on:change={onChange}
/>
</Field> </Field>

View File

@ -23,5 +23,6 @@
{placeholder} {placeholder}
on:change={onChange} on:change={onChange}
on:click on:click
on:input /> on:input
/>
</Field> </Field>

View File

@ -38,5 +38,6 @@
{getOptionLabel} {getOptionLabel}
{getOptionValue} {getOptionValue}
on:change={onChange} on:change={onChange}
on:click /> on:click
/>
</Field> </Field>

View File

@ -25,5 +25,6 @@
{disabled} {disabled}
{value} {value}
{placeholder} {placeholder}
on:change={onChange} /> on:change={onChange}
/>
</Field> </Field>

View File

@ -17,4 +17,5 @@
class:spectrum-Link--secondary={secondary} class:spectrum-Link--secondary={secondary}
class:spectrum-Link--overBackground={overBackground} class:spectrum-Link--overBackground={overBackground}
class:spectrum-Link--quiet={quiet} class:spectrum-Link--quiet={quiet}
class="spectrum-Link spectrum-Link--size{size}"><slot /></a> class="spectrum-Link spectrum-Link--size{size}"><slot /></a
>

View File

@ -1,7 +1,7 @@
<script> <script>
import "@spectrum-css/menu/dist/index-vars.css" import "@spectrum-css/menu/dist/index-vars.css"
</script> </script>
<ul class="spectrum-Menu" role="menu"> <ul class="spectrum-Menu" role="menu">
<slot /> <slot />
</ul> </ul>

View File

@ -1,9 +1,10 @@
<script> <script>
export let heading export let heading
</script> </script>
<li role="presentation"> <li role="presentation">
<span class="spectrum-Menu-sectionHeading">{heading}</span> <span class="spectrum-Menu-sectionHeading">{heading}</span>
<ul class="spectrum-Menu" role="group"> <ul class="spectrum-Menu" role="group">
<slot /> <slot />
</ul> </ul>
</li> </li>

View File

@ -1 +1 @@
<li class="spectrum-Menu-divider" role="separator"></li> <li class="spectrum-Menu-divider" role="separator" />

View File

@ -17,7 +17,7 @@
<div on:click={increment}> <div on:click={increment}>
Click me Click me
{remaining} {remaining}
more time{remaining === 1 ? '' : 's'} more time{remaining === 1 ? "" : "s"}
to close this modal! to close this modal!
</div> </div>

View File

@ -50,13 +50,15 @@
<div <div
class="spectrum-Underlay is-open" class="spectrum-Underlay is-open"
transition:fade={{ duration: 200 }} transition:fade={{ duration: 200 }}
on:mousedown|self={hide}> on:mousedown|self={hide}
>
<div class="modal-wrapper" on:mousedown|self={hide}> <div class="modal-wrapper" on:mousedown|self={hide}>
<div class="modal-inner-wrapper" on:mousedown|self={hide}> <div class="modal-inner-wrapper" on:mousedown|self={hide}>
<div <div
use:focusFirstInput use:focusFirstInput
class="spectrum-Modal is-open" class="spectrum-Modal is-open"
transition:fly={{ y: 30, duration: 200 }}> transition:fly={{ y: 30, duration: 200 }}
>
<slot /> <slot />
</div> </div>
</div> </div>

View File

@ -7,7 +7,7 @@
import Context from "../context" import Context from "../context"
export let title = undefined export let title = undefined
export let size = "small" export let size = "S"
export let cancelText = "Cancel" export let cancelText = "Cancel"
export let confirmText = "Confirm" export let confirmText = "Confirm"
export let showCancelButton = true export let showCancelButton = true
@ -30,11 +30,16 @@
</script> </script>
<div <div
class="spectrum-Dialog spectrum-Dialog--{size}" class="spectrum-Dialog"
class:spectrum-Dialog--small={size === "S"}
class:spectrum-Dialog--medium={size === "M"}
class:spectrum-Dialog--large={size === "L"}
class:spectrum-Dialog--extraLarge={size === "XL"}
style="position: relative;" style="position: relative;"
role="dialog" role="dialog"
tabindex="-1" tabindex="-1"
aria-modal="true"> aria-modal="true"
>
<div class="spectrum-Dialog-grid"> <div class="spectrum-Dialog-grid">
<h1 class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader"> <h1 class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader">
{title} {title}
@ -47,7 +52,8 @@
</section> </section>
{#if showCancelButton || showConfirmButton} {#if showCancelButton || showConfirmButton}
<div <div
class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter"> class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter"
>
<slot name="footer" /> <slot name="footer" />
{#if showCancelButton} {#if showCancelButton}
<Button group secondary on:click={hide}>{cancelText}</Button> <Button group secondary on:click={hide}>{cancelText}</Button>
@ -58,7 +64,8 @@
cta cta
{...$$restProps} {...$$restProps}
disabled={confirmDisabled} disabled={confirmDisabled}
on:click={confirm}> on:click={confirm}
>
{confirmText} {confirmText}
</Button> </Button>
{/if} {/if}
@ -73,6 +80,9 @@
</div> </div>
<style> <style>
.spectrum-Dialog--extraLarge {
width: 1000px;
}
.content-grid { .content-grid {
display: grid; display: grid;
position: relative; position: relative;

View File

@ -31,7 +31,8 @@
bind:this={modal} bind:this={modal}
confirmText="Submit" confirmText="Submit"
onConfirm={answerQuiz} onConfirm={answerQuiz}
on:show={resetState}> on:show={resetState}
>
{#if error} {#if error}
<p class="error">Wrong answer! Try again.</p> <p class="error">Wrong answer! Try again.</p>
{/if} {/if}

View File

@ -1,17 +1,25 @@
<script> <script>
import "@spectrum-css/toast/dist/index-vars.css" import "@spectrum-css/toast/dist/index-vars.css"
import Portal from "svelte-portal" import Portal from "svelte-portal"
import { flip } from "svelte/animate" import { flip } from "svelte/animate"
import { fly } from "svelte/transition" import { fly } from "svelte/transition"
import { notifications } from '../Stores/notifications' import { notifications } from "../Stores/notifications"
</script> </script>
<Portal target=".modal-container"> <Portal target=".modal-container">
<div class="notifications"> <div class="notifications">
{#each $notifications as {type, icon, message, id} (id)} {#each $notifications as { type, icon, message, id } (id)}
<div animate:flip transition:fly={{ y: -30 }} class="spectrum-Toast spectrum-Toast--{type} notification-offset"> <div
animate:flip
transition:fly={{ y: -30 }}
class="spectrum-Toast spectrum-Toast--{type} notification-offset"
>
{#if icon} {#if icon}
<svg class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon" focusable="false" aria-hidden="true"> <svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Toast-typeIcon"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-{icon}" /> <use xlink:href="#spectrum-icon-18-{icon}" />
</svg> </svg>
{/if} {/if}
@ -21,24 +29,24 @@
</div> </div>
{/each} {/each}
</div> </div>
</Portal> </Portal>
<style>
.notifications {
position: fixed;
top: 10px;
left: 0;
right: 0;
margin: 0 auto;
padding: 0;
z-index: 9999;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
pointer-events: none;
}
.notification-offset {
margin-bottom: 10px;
}
</style>
<style>
.notifications {
position: fixed;
top: 10px;
left: 0;
right: 0;
margin: 0 auto;
padding: 0;
z-index: 9999;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
pointer-events: none;
}
.notification-offset {
margin-bottom: 10px;
}
</style>

View File

@ -37,7 +37,8 @@
use:clickOutside={hide} use:clickOutside={hide}
on:keydown={handleEscape} on:keydown={handleEscape}
class="spectrum-Popover is-open" class="spectrum-Popover is-open"
role="presentation"> role="presentation"
>
<slot /> <slot />
</div> </div>
</Portal> </Portal>

View File

@ -1,29 +1,33 @@
<script> <script>
// WIP! Does not yet work. // WIP! Does not yet work.
import "@spectrum-css/progresscircle/dist/index-vars.css" import "@spectrum-css/progresscircle/dist/index-vars.css"
import { tweened } from 'svelte/motion'; import { tweened } from "svelte/motion"
import { cubicOut } from 'svelte/easing'; import { cubicOut } from "svelte/easing"
export let value = false export let value = false
export let small; export let small
export let large; export let large
export let overBackground; export let overBackground
</script> </script>
<div class:spectrum-ProgressBar--indeterminate={!value} class:spectrum-ProgressCircle--small={small} class:spectrum-ProgressCircle--large={large} class="spectrum-ProgressCircle"> <div
<div class="spectrum-ProgressCircle-track"></div> class:spectrum-ProgressBar--indeterminate={!value}
<div class="spectrum-ProgressCircle-fills"> class:spectrum-ProgressCircle--small={small}
<div class="spectrum-ProgressCircle-fillMask1"> class:spectrum-ProgressCircle--large={large}
<div class="spectrum-ProgressCircle-fillSubMask1"> class="spectrum-ProgressCircle"
<div class="spectrum-ProgressCircle-fill"></div> >
</div> <div class="spectrum-ProgressCircle-track" />
<div class="spectrum-ProgressCircle-fills">
<div class="spectrum-ProgressCircle-fillMask1">
<div class="spectrum-ProgressCircle-fillSubMask1">
<div class="spectrum-ProgressCircle-fill" />
</div> </div>
<div class="spectrum-ProgressCircle-fillMask2"> </div>
<div class="spectrum-ProgressCircle-fillSubMask2"> <div class="spectrum-ProgressCircle-fillMask2">
<div class="spectrum-ProgressCircle-fill"></div> <div class="spectrum-ProgressCircle-fillSubMask2">
</div> <div class="spectrum-ProgressCircle-fill" />
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@ -1,30 +1,44 @@
<script> <script>
import { getContext } from 'svelte' import { getContext } from "svelte"
const multilevel = getContext('sidenav-type'); const multilevel = getContext("sidenav-type")
export let href = ""; export let href = ""
export let external = false; export let external = false
export let heading = "" export let heading = ""
export let icon = ""; export let icon = ""
export let selected = false; export let selected = false
export let disabled = false; export let disabled = false
</script> </script>
<li class="spectrum-SideNav-item" class:is-selected={selected} class:is-disabled={disabled}> <li
{#if heading} class="spectrum-SideNav-item"
<h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">{heading}</h2> class:is-selected={selected}
{/if} class:is-disabled={disabled}
<a target={external ? '_blank' : '_self'} {href} class="spectrum-SideNav-itemLink" aria-current="page"> >
{#if icon} {#if heading}
<svg class="spectrum-Icon spectrum-Icon--sizeM spectrum-SideNav-itemIcon" focusable="false" aria-hidden="true"> <h2 class="spectrum-SideNav-heading" id="nav-heading-{heading}">
<use xlink:href="#spectrum-icon-18-{icon}" /> {heading}
</svg> </h2>
{/if} {/if}
<slot /> <a
</a> target={external ? "_blank" : "_self"}
{#if multilevel && $$slots.subnav} {href}
<ul class="spectrum-SideNav"> class="spectrum-SideNav-itemLink"
<slot name="subnav" /> aria-current="page"
</ul> >
{#if icon}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-SideNav-itemIcon"
focusable="false"
aria-hidden="true"
>
<use xlink:href="#spectrum-icon-18-{icon}" />
</svg>
{/if} {/if}
<slot />
</a>
{#if multilevel && $$slots.subnav}
<ul class="spectrum-SideNav">
<slot name="subnav" />
</ul>
{/if}
</li> </li>

View File

@ -1,8 +1,8 @@
<script> <script>
import { setContext } from 'svelte' import { setContext } from "svelte"
import "@spectrum-css/sidenav/dist/index-vars.css" import "@spectrum-css/sidenav/dist/index-vars.css"
export let multilevel = false; export let multilevel = false
setContext('sidenav-type', multilevel) setContext("sidenav-type", multilevel)
</script> </script>
<nav> <nav>

View File

@ -7,7 +7,7 @@ export const createNotificationStore = () => {
const _notifications = writable([], () => { const _notifications = writable([], () => {
return () => { return () => {
// clear all the timers // clear all the timers
timeoutIds.forEach((timeoutId) => { timeoutIds.forEach(timeoutId => {
clearTimeout(timeoutId) clearTimeout(timeoutId)
}) })
_notifications.set([]) _notifications.set([])
@ -25,11 +25,11 @@ export const createNotificationStore = () => {
return return
} }
let _id = id() let _id = id()
_notifications.update((state) => { _notifications.update(state => {
return [...state, { id: _id, type, message, icon }] return [...state, { id: _id, type, message, icon }]
}) })
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
_notifications.update((state) => { _notifications.update(state => {
return state.filter(({ id }) => id !== _id) return state.filter(({ id }) => id !== _id)
}) })
}, NOTIFICATION_TIMEOUT) }, NOTIFICATION_TIMEOUT)
@ -41,10 +41,10 @@ export const createNotificationStore = () => {
return { return {
subscribe, subscribe,
send, send,
info: (msg) => send(msg, "info", "Info"), info: msg => send(msg, "info", "Info"),
error: (msg) => send(msg, "error", "Alert"), error: msg => send(msg, "error", "Alert"),
warning: (msg) => send(msg, "warning", "Alert"), warning: msg => send(msg, "warning", "Alert"),
success: (msg) => send(msg, "success", "CheckmarkCircle"), success: msg => send(msg, "success", "CheckmarkCircle"),
blockNotifications, blockNotifications,
} }
} }

View File

@ -5,24 +5,28 @@
</script> </script>
<label <label
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"> class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
>
<input <input
type="checkbox" type="checkbox"
class="spectrum-Checkbox-input" class="spectrum-Checkbox-input"
id="checkbox-1" id="checkbox-1"
disabled disabled
checked={!!value} /> checked={!!value}
/>
<span class="spectrum-Checkbox-box"> <span class="spectrum-Checkbox-box">
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark" class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Checkmark100" /> <use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg> </svg>
<svg <svg
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark" class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Dash100" /> <use xlink:href="#spectrum-css-icon-Dash100" />
</svg> </svg>
</span> </span>

View File

@ -26,7 +26,7 @@
$: renderer = customRenderer?.component ?? typeMap[type] $: renderer = customRenderer?.component ?? typeMap[type]
</script> </script>
{#if renderer && (customRenderer || (value != null && value !== ''))} {#if renderer && (customRenderer || (value != null && value !== ""))}
<svelte:component this={renderer} {row} {schema} {value} on:clickrelationship> <svelte:component this={renderer} {row} {schema} {value} on:clickrelationship>
<slot /> <slot />
</svelte:component> </svelte:component>

View File

@ -4,7 +4,7 @@
export let value export let value
</script> </script>
<div>{dayjs(value).format('MMMM D YYYY, HH:mm')}</div> <div>{dayjs(value).format("MMMM D YYYY, HH:mm")}</div>
<style> <style>
div { div {

View File

@ -31,6 +31,7 @@
// Table state // Table state
let height = 0 let height = 0
let loaded = false let loaded = false
$: schema = fixSchema(schema)
$: if (!loading) loaded = true $: if (!loading) loaded = true
$: rows = data ?? [] $: rows = data ?? []
$: visibleRowCount = getVisibleRowCount(loaded, height, rows.length, rowCount) $: visibleRowCount = getVisibleRowCount(loaded, height, rows.length, rowCount)
@ -50,7 +51,7 @@
rows.length rows.length
) )
// Reset state when data chanegs // Reset state when data changes
$: data.length, reset() $: data.length, reset()
const reset = () => { const reset = () => {
nextScrollTop = 0 nextScrollTop = 0
@ -59,6 +60,24 @@
timeout = null timeout = null
} }
const fixSchema = schema => {
let fixedSchema = {}
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
if (typeof fieldSchema === "string") {
fixedSchema[fieldName] = {
type: fieldSchema,
name: fieldName,
}
} else {
fixedSchema[fieldName] = {
...fieldSchema,
name: fieldName,
}
}
})
return fixedSchema
}
const getVisibleRowCount = (loaded, height, allRows, rowCount) => { const getVisibleRowCount = (loaded, height, allRows, rowCount) => {
if (!loaded) { if (!loaded) {
return rowCount || 0 return rowCount || 0
@ -118,7 +137,6 @@
if (!field || !fieldSchema) { if (!field || !fieldSchema) {
return return
} }
schema[field].name = field
if (!fieldSchema?.autocolumn) { if (!fieldSchema?.autocolumn) {
columns.push(fieldSchema) columns.push(fieldSchema)
} else if (showAutoColumns) { } else if (showAutoColumns) {
@ -192,7 +210,8 @@
on:scroll={onScroll} on:scroll={onScroll}
class:quiet class:quiet
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`} style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
class="container"> class="container"
>
<div style={contentStyle}> <div style={contentStyle}>
<table class="spectrum-Table" class:spectrum-Table--quiet={quiet}> <table class="spectrum-Table" class:spectrum-Table--quiet={quiet}>
{#if sortedRows?.length} {#if sortedRows?.length}
@ -201,7 +220,7 @@
{#if showEditColumn} {#if showEditColumn}
<th class="spectrum-Table-headCell"> <th class="spectrum-Table-headCell">
<div class="spectrum-Table-headCell-content"> <div class="spectrum-Table-headCell-content">
{editColumnTitle || ''} {editColumnTitle || ""}
</div> </div>
</th> </th>
{/if} {/if}
@ -209,15 +228,19 @@
<th <th
class="spectrum-Table-headCell" class="spectrum-Table-headCell"
class:is-sortable={schema[field].sortable !== false} class:is-sortable={schema[field].sortable !== false}
class:is-sorted-desc={sortColumn === field && sortOrder === 'Descending'} class:is-sorted-desc={sortColumn === field &&
class:is-sorted-asc={sortColumn === field && sortOrder === 'Ascending'} sortOrder === "Descending"}
on:click={() => sortBy(schema[field])}> class:is-sorted-asc={sortColumn === field &&
sortOrder === "Ascending"}
on:click={() => sortBy(schema[field])}
>
<div class="spectrum-Table-headCell-content"> <div class="spectrum-Table-headCell-content">
<div class="title">{getDisplayName(schema[field])}</div> <div class="title">{getDisplayName(schema[field])}</div>
{#if schema[field]?.autocolumn} {#if schema[field]?.autocolumn}
<svg <svg
class="spectrum-Icon spectrum-Table-autoIcon" class="spectrum-Icon spectrum-Table-autoIcon"
focusable="false"> focusable="false"
>
<use xlink:href="#spectrum-icon-18-MagicWand" /> <use xlink:href="#spectrum-icon-18-MagicWand" />
</svg> </svg>
{/if} {/if}
@ -225,7 +248,8 @@
<svg <svg
class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon" class="spectrum-Icon spectrum-UIIcon-ArrowDown100 spectrum-Table-sortedIcon"
focusable="false" focusable="false"
aria-hidden="true"> aria-hidden="true"
>
<use xlink:href="#spectrum-css-icon-Arrow100" /> <use xlink:href="#spectrum-css-icon-Arrow100" />
</svg> </svg>
{/if} {/if}
@ -233,7 +257,8 @@
<svg <svg
class="spectrum-Icon spectrum-Table-editIcon" class="spectrum-Icon spectrum-Table-editIcon"
focusable="false" focusable="false"
on:click={e => editColumn(e, field)}> on:click={e => editColumn(e, field)}
>
<use xlink:href="#spectrum-icon-18-Edit" /> <use xlink:href="#spectrum-icon-18-Edit" />
</svg> </svg>
{/if} {/if}
@ -249,11 +274,13 @@
<tr <tr
on:click={() => toggleSelectRow(row)} on:click={() => toggleSelectRow(row)}
class="spectrum-Table-row" class="spectrum-Table-row"
class:hidden={idx < firstVisibleRow || idx > lastVisibleRow}> class:hidden={idx < firstVisibleRow || idx > lastVisibleRow}
>
{#if idx >= firstVisibleRow && idx <= lastVisibleRow} {#if idx >= firstVisibleRow && idx <= lastVisibleRow}
{#if showEditColumn} {#if showEditColumn}
<td <td
class="spectrum-Table-cell spectrum-Table-cell--divider"> class="spectrum-Table-cell spectrum-Table-cell--divider"
>
<div class="spectrum-Table-cell-content"> <div class="spectrum-Table-cell-content">
<SelectEditRenderer <SelectEditRenderer
data={row} data={row}
@ -261,21 +288,25 @@
onToggleSelection={() => toggleSelectRow(row)} onToggleSelection={() => toggleSelectRow(row)}
onEdit={e => editRow(e, row)} onEdit={e => editRow(e, row)}
{allowSelectRows} {allowSelectRows}
{allowEditRows} /> {allowEditRows}
/>
</div> </div>
</td> </td>
{/if} {/if}
{#each fields as field} {#each fields as field}
<td <td
class="spectrum-Table-cell" class="spectrum-Table-cell"
class:spectrum-Table-cell--divider={!!schema[field].divider}> class:spectrum-Table-cell--divider={!!schema[field]
.divider}
>
<div class="spectrum-Table-cell-content"> <div class="spectrum-Table-cell-content">
<CellRenderer <CellRenderer
{customRenderers} {customRenderers}
{row} {row}
schema={schema[field]} schema={schema[field]}
value={row[field]} value={row[field]}
on:clickrelationship> on:clickrelationship
>
<slot /> <slot />
</CellRenderer> </CellRenderer>
</div> </div>
@ -288,7 +319,8 @@
<div class="placeholder"> <div class="placeholder">
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeXXL" class="spectrum-Icon spectrum-Icon--sizeXXL"
focusable="false"> focusable="false"
>
<use xlink:href="#spectrum-icon-18-Table" /> <use xlink:href="#spectrum-icon-18-Table" />
</svg> </svg>
<div>No rows found</div> <div>No rows found</div>

View File

@ -30,13 +30,15 @@
on:click={onClick} on:click={onClick}
class:is-selected={$selected.title === title} class:is-selected={$selected.title === title}
class="spectrum-Tabs-item" class="spectrum-Tabs-item"
tabindex="0"> tabindex="0"
>
{#if icon} {#if icon}
<svg <svg
class="spectrum-Icon spectrum-Icon--sizeM" class="spectrum-Icon spectrum-Icon--sizeM"
focusable="false" focusable="false"
aria-hidden="true" aria-hidden="true"
aria-label="Folder"> aria-label="Folder"
>
<use xlink:href="#spectrum-icon-18-{icon}" /> <use xlink:href="#spectrum-icon-18-{icon}" />
</svg> </svg>
{/if} {/if}

View File

@ -44,23 +44,22 @@
}) })
function id() { function id() {
return ( return "_" + Math.random().toString(36).substr(2, 9)
"_" +
Math.random()
.toString(36)
.substr(2, 9)
)
} }
</script> </script>
<div <div
bind:this={container} bind:this={container}
class="selected-border spectrum-Tabs spectrum-Tabs--{vertical ? 'vertical' : 'horizontal'}"> class="selected-border spectrum-Tabs spectrum-Tabs--{vertical
? 'vertical'
: 'horizontal'}"
>
<slot /> <slot />
{#if $tab.info} {#if $tab.info}
<div <div
class="spectrum-Tabs-selectionIndicator indicator-transition" class="spectrum-Tabs-selectionIndicator indicator-transition"
style="width: {width}; height: {height}; left: {left}; top: {top};" /> style="width: {width}; height: {height}; left: {left}; top: {top};"
/>
{/if} {/if}
</div> </div>

View File

@ -1,24 +1,34 @@
<script> <script>
import Avatar from '../Avatar/Avatar.svelte' import Avatar from "../Avatar/Avatar.svelte"
import ClearButton from '../ClearButton/ClearButton.svelte' import ClearButton from "../ClearButton/ClearButton.svelte"
export let icon = ""; export let icon = ""
export let avatar = ""; export let avatar = ""
export let invalid = false; export let invalid = false
export let disabled = false; export let disabled = false
export let closable = false; export let closable = false
</script> </script>
<div class:is-invalid={invalid} class:is-disabled={disabled} class="spectrum-Tags-item" role="listitem"> <div
class:is-invalid={invalid}
class:is-disabled={disabled}
class="spectrum-Tags-item"
role="listitem"
>
{#if avatar} {#if avatar}
<Avatar url={avatar} /> <Avatar url={avatar} />
{/if} {/if}
{#if icon} {#if icon}
<svg class="spectrum-Icon spectrum-Icon--sizeS" focusable="false" aria-hidden="true" aria-label="Tag"> <svg
class="spectrum-Icon spectrum-Icon--sizeS"
focusable="false"
aria-hidden="true"
aria-label="Tag"
>
<use xlink:href="#spectrum-icon-24-{icon}" /> <use xlink:href="#spectrum-icon-24-{icon}" />
</svg> </svg>
{/if} {/if}
<span class="spectrum-Tags-itemLabel"><slot /></span> <span class="spectrum-Tags-itemLabel"><slot /></span>
{#if closable} {#if closable}
<ClearButton on:click /> <ClearButton on:click />
{/if} {/if}
</div> </div>

View File

@ -1,7 +1,7 @@
<script> <script>
import "@spectrum-css/tags/dist/index-vars.css" import "@spectrum-css/tags/dist/index-vars.css"
</script> </script>
<div class="spectrum-Tags" role="list" aria-label="list"> <div class="spectrum-Tags" role="list" aria-label="list">
<slot /> <slot />
</div> </div>

View File

@ -1,28 +1,40 @@
<script> <script>
export let selected = false; export let selected = false
export let open = false; export let open = false
export let title; export let title
export let icon; export let icon
</script> </script>
<li <li
class:is-selected={selected} class:is-open={open} class="spectrum-TreeView-item"> class:is-selected={selected}
<a on:click class="spectrum-TreeView-itemLink" href="#"> class:is-open={open}
{#if $$slots.default} class="spectrum-TreeView-item"
<svg class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator" focusable="false" aria-hidden="true"> >
<use xlink:href="#spectrum-css-icon-Chevron100" /> <a on:click class="spectrum-TreeView-itemLink" href="#">
</svg> {#if $$slots.default}
{/if} <svg
{#if icon} class="spectrum-Icon spectrum-UIIcon-ChevronRight100 spectrum-TreeView-itemIndicator"
<svg class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM" focusable="false" aria-hidden="true" aria-label="Layers"> focusable="false"
<use xlink:href="#spectrum-icon-18-{icon}" /> aria-hidden="true"
</svg> >
{/if} <use xlink:href="#spectrum-css-icon-Chevron100" />
<span class="spectrum-TreeView-itemLabel">{title}</span> </svg>
</a> {/if}
{#if $$slots.default} {#if icon}
<ul class="spectrum-TreeView"> <svg
<slot /> class="spectrum-TreeView-itemIcon spectrum-Icon spectrum-Icon--sizeM"
</ul> focusable="false"
{/if} aria-hidden="true"
aria-label="Layers"
>
<use xlink:href="#spectrum-icon-18-{icon}" />
</svg>
{/if}
<span class="spectrum-TreeView-itemLabel">{title}</span>
</a>
{#if $$slots.default}
<ul class="spectrum-TreeView">
<slot />
</ul>
{/if}
</li> </li>

View File

@ -1,11 +1,16 @@
<script> <script>
import "@spectrum-css/treeview/dist/index-vars.css" import "@spectrum-css/treeview/dist/index-vars.css"
export let quiet = false export let quiet = false
export let standalone = true export let standalone = true
export let width = '250px'; export let width = "250px"
</script> </script>
<ul class:spectrum-TreeView--standalone={standalone} class:spectrum-TreeView--quiet={quiet} class="spectrum-TreeView" style="width: {width}"> <ul
<slot /> class:spectrum-TreeView--standalone={standalone}
class:spectrum-TreeView--quiet={quiet}
class="spectrum-TreeView"
style="width: {width}"
>
<slot />
</ul> </ul>

View File

@ -6,7 +6,7 @@
// //
Cypress.Commands.add("login", () => { Cypress.Commands.add("login", () => {
cy.getCookie("budibase:auth").then((cookie) => { cy.getCookie("budibase:auth").then(cookie => {
// Already logged in // Already logged in
if (cookie) return if (cookie) return
@ -20,13 +20,13 @@ Cypress.Commands.add("login", () => {
}) })
}) })
Cypress.Commands.add("createApp", (name) => { Cypress.Commands.add("createApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
// wait for init API calls on visit // wait for init API calls on visit
cy.wait(100) cy.wait(100)
cy.contains("Create New Web App").click() cy.contains("Create New Web App").click()
cy.get("body") cy.get("body")
.then(($body) => { .then($body => {
if ($body.find("input[name=apiKey]").length) { if ($body.find("input[name=apiKey]").length) {
// input was found, do something else here // input was found, do something else here
cy.get("input[name=apiKey]").type(name).should("have.value", name) cy.get("input[name=apiKey]").type(name).should("have.value", name)
@ -50,9 +50,9 @@ Cypress.Commands.add("createApp", (name) => {
}) })
}) })
Cypress.Commands.add("deleteApp", (name) => { Cypress.Commands.add("deleteApp", name => {
cy.visit(`localhost:${Cypress.env("PORT")}/builder`) cy.visit(`localhost:${Cypress.env("PORT")}/builder`)
cy.get(".apps").then(($apps) => { cy.get(".apps").then($apps => {
cy.wait(1000) cy.wait(1000)
if ($apps.find(`[data-cy="app-${name}"]`).length) { if ($apps.find(`[data-cy="app-${name}"]`).length) {
cy.get(`[data-cy="app-${name}"]`).contains("Open").click() cy.get(`[data-cy="app-${name}"]`).contains("Open").click()
@ -78,7 +78,7 @@ Cypress.Commands.add("createTestTableWithData", () => {
cy.addColumn("dog", "age", "Number") cy.addColumn("dog", "age", "Number")
}) })
Cypress.Commands.add("createTable", (tableName) => { Cypress.Commands.add("createTable", tableName => {
// Enter table name // Enter table name
cy.get("[data-cy=new-table]").click() cy.get("[data-cy=new-table]").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
@ -104,7 +104,7 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
}) })
}) })
Cypress.Commands.add("addRow", (values) => { Cypress.Commands.add("addRow", values => {
cy.contains("Create row").click() cy.contains("Create row").click()
cy.get(".spectrum-Modal").within(() => { cy.get(".spectrum-Modal").within(() => {
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
@ -134,7 +134,7 @@ Cypress.Commands.add("addComponent", (category, component) => {
} }
cy.get(`[data-cy="component-${component}"]`).click() cy.get(`[data-cy="component-${component}"]`).click()
cy.wait(1000) cy.wait(1000)
cy.location().then((loc) => { cy.location().then(loc => {
const params = loc.pathname.split("/") const params = loc.pathname.split("/")
const componentId = params[params.length - 1] const componentId = params[params.length - 1]
cy.getComponent(componentId).should("exist") cy.getComponent(componentId).should("exist")
@ -142,7 +142,7 @@ Cypress.Commands.add("addComponent", (category, component) => {
}) })
}) })
Cypress.Commands.add("getComponent", (componentId) => { Cypress.Commands.add("getComponent", componentId => {
return cy return cy
.get("iframe") .get("iframe")
.its("0.contentDocument") .its("0.contentDocument")

View File

@ -106,7 +106,7 @@
"rollup": "^2.44.0", "rollup": "^2.44.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"start-server-and-test": "^1.12.1", "start-server-and-test": "^1.12.1",
"svelte": "^3.36.0", "svelte": "^3.37.0",
"svelte-jester": "^1.3.2", "svelte-jester": "^1.3.2",
"vite": "^2.1.5" "vite": "^2.1.5"
}, },

View File

@ -32,7 +32,7 @@ function identify(id) {
if (!analyticsEnabled || !id) return if (!analyticsEnabled || !id) return
if (posthogConfigured) posthog.identify(id) if (posthogConfigured) posthog.identify(id)
if (sentryConfigured) if (sentryConfigured)
Sentry.configureScope((scope) => { Sentry.configureScope(scope => {
scope.setUser({ id: id }) scope.setUser({ id: id })
}) })
} }
@ -73,7 +73,7 @@ if (!localStorage.getItem(APP_FIRST_STARTED_KEY)) {
localStorage.setItem(APP_FIRST_STARTED_KEY, Date.now()) localStorage.setItem(APP_FIRST_STARTED_KEY, Date.now())
} }
const isFeedbackTimeElapsed = (sinceDateStr) => { const isFeedbackTimeElapsed = sinceDateStr => {
const sinceDate = parseFloat(sinceDateStr) const sinceDate = parseFloat(sinceDateStr)
const feedbackMilliseconds = feedbackHours * 60 * 60 * 1000 const feedbackMilliseconds = feedbackHours * 60 * 60 * 1000
return Date.now() > sinceDate + feedbackMilliseconds return Date.now() > sinceDate + feedbackMilliseconds
@ -107,7 +107,7 @@ function highlightFeedbackIcon() {
} }
// Opt In/Out // Opt In/Out
const ifAnalyticsEnabled = (func) => () => { const ifAnalyticsEnabled = func => () => {
if (analyticsEnabled && process.env.POSTHOG_TOKEN) { if (analyticsEnabled && process.env.POSTHOG_TOKEN) {
return func() return func()
} }

View File

@ -2,7 +2,7 @@ import { store } from "./index"
import { get as svelteGet } from "svelte/store" import { get as svelteGet } from "svelte/store"
import { removeCookie, Cookies } from "./cookies" import { removeCookie, Cookies } from "./cookies"
const apiCall = (method) => async ( const apiCall = method => async (
url, url,
body, body,
headers = { "Content-Type": "application/json" } headers = { "Content-Type": "application/json" }

View File

@ -4,7 +4,7 @@ export const Cookies = {
} }
export function getCookie(cookieName) { export function getCookie(cookieName) {
return document.cookie.split(";").some((cookie) => { return document.cookie.split(";").some(cookie => {
return cookie.trim().startsWith(`${cookieName}=`) return cookie.trim().startsWith(`${cookieName}=`)
}) })
} }

View File

@ -34,7 +34,7 @@ export const getDataProviderComponents = (asset, componentId) => {
path.pop() path.pop()
// Filter by only data provider components // Filter by only data provider components
return path.filter((component) => { return path.filter(component => {
const def = store.actions.components.getDefinition(component._component) const def = store.actions.components.getDefinition(component._component)
return def?.context != null return def?.context != null
}) })
@ -54,7 +54,7 @@ export const getActionProviderComponents = (asset, componentId, actionType) => {
path.pop() path.pop()
// Filter by only data provider components // Filter by only data provider components
return path.filter((component) => { return path.filter(component => {
const def = store.actions.components.getDefinition(component._component) const def = store.actions.components.getDefinition(component._component)
return def?.actions?.includes(actionType) return def?.actions?.includes(actionType)
}) })
@ -70,7 +70,7 @@ export const getDatasourceForProvider = (asset, component) => {
} }
// If this component has a dataProvider setting, go up the stack and use it // If this component has a dataProvider setting, go up the stack and use it
const dataProviderSetting = def.settings.find((setting) => { const dataProviderSetting = def.settings.find(setting => {
return setting.type === "dataProvider" return setting.type === "dataProvider"
}) })
if (dataProviderSetting) { if (dataProviderSetting) {
@ -82,7 +82,7 @@ export const getDatasourceForProvider = (asset, component) => {
// Extract datasource from component instance // Extract datasource from component instance
const validSettingTypes = ["dataSource", "table", "schema"] const validSettingTypes = ["dataSource", "table", "schema"]
const datasourceSetting = def.settings.find((setting) => { const datasourceSetting = def.settings.find(setting => {
return validSettingTypes.includes(setting.type) return validSettingTypes.includes(setting.type)
}) })
if (!datasourceSetting) { if (!datasourceSetting) {
@ -112,7 +112,7 @@ const getContextBindings = (asset, componentId) => {
let bindings = [] let bindings = []
// Create bindings for each data provider // Create bindings for each data provider
dataProviders.forEach((component) => { dataProviders.forEach(component => {
const def = store.actions.components.getDefinition(component._component) const def = store.actions.components.getDefinition(component._component)
const contextDefinition = def.context const contextDefinition = def.context
let schema let schema
@ -127,7 +127,7 @@ const getContextBindings = (asset, componentId) => {
// Static contexts are fully defined by the components // Static contexts are fully defined by the components
schema = {} schema = {}
const values = contextDefinition.values || [] const values = contextDefinition.values || []
values.forEach((value) => { values.forEach(value => {
schema[value.key] = { name: value.label, type: "string" } schema[value.key] = { name: value.label, type: "string" }
}) })
} else if (contextDefinition.type === "schema") { } else if (contextDefinition.type === "schema") {
@ -148,7 +148,7 @@ const getContextBindings = (asset, componentId) => {
// Create bindable properties for each schema field // Create bindable properties for each schema field
const safeComponentId = makePropSafe(component._id) const safeComponentId = makePropSafe(component._id)
keys.forEach((key) => { keys.forEach(key => {
const fieldSchema = schema[key] const fieldSchema = schema[key]
// Make safe runtime binding and replace certain bindings with a // Make safe runtime binding and replace certain bindings with a
@ -197,7 +197,7 @@ const getUserBindings = () => {
}) })
const keys = Object.keys(schema).sort() const keys = Object.keys(schema).sort()
const safeUser = makePropSafe("user") const safeUser = makePropSafe("user")
keys.forEach((key) => { keys.forEach(key => {
const fieldSchema = schema[key] const fieldSchema = schema[key]
// Replace certain bindings with a new property to help display components // Replace certain bindings with a new property to help display components
let runtimeBoundKey = key let runtimeBoundKey = key
@ -224,17 +224,17 @@ const getUserBindings = () => {
/** /**
* Gets all bindable properties from URL parameters. * Gets all bindable properties from URL parameters.
*/ */
const getUrlBindings = (asset) => { const getUrlBindings = asset => {
const url = asset?.routing?.route ?? "" const url = asset?.routing?.route ?? ""
const split = url.split("/") const split = url.split("/")
let params = [] let params = []
split.forEach((part) => { split.forEach(part => {
if (part.startsWith(":") && part.length > 1) { if (part.startsWith(":") && part.length > 1) {
params.push(part.replace(/:/g, "").replace(/\?/g, "")) params.push(part.replace(/:/g, "").replace(/\?/g, ""))
} }
}) })
const safeURL = makePropSafe("url") const safeURL = makePropSafe("url")
return params.map((param) => ({ return params.map(param => ({
type: "context", type: "context",
runtimeBinding: `${safeURL}.${makePropSafe(param)}`, runtimeBinding: `${safeURL}.${makePropSafe(param)}`,
readableBinding: `URL.${param}`, readableBinding: `URL.${param}`,
@ -250,10 +250,10 @@ export const getSchemaForDatasource = (datasource, isForm = false) => {
const { type } = datasource const { type } = datasource
if (type === "query") { if (type === "query") {
const queries = get(queriesStores).list const queries = get(queriesStores).list
table = queries.find((query) => query._id === datasource._id) table = queries.find(query => query._id === datasource._id)
} else { } else {
const tables = get(tablesStore).list const tables = get(tablesStore).list
table = tables.find((table) => table._id === datasource.tableId) table = tables.find(table => table._id === datasource.tableId)
} }
if (table) { if (table) {
if (type === "view") { if (type === "view") {
@ -261,7 +261,7 @@ export const getSchemaForDatasource = (datasource, isForm = false) => {
} else if (type === "query" && isForm) { } else if (type === "query" && isForm) {
schema = {} schema = {}
const params = table.parameters || [] const params = table.parameters || []
params.forEach((param) => { params.forEach(param => {
if (param?.name) { if (param?.name) {
schema[param.name] = { ...param, type: "string" } schema[param.name] = { ...param, type: "string" }
} }
@ -277,14 +277,23 @@ export const getSchemaForDatasource = (datasource, isForm = false) => {
schema["_rev"] = { type: "string" } schema["_rev"] = { type: "string" }
} }
// Ensure there are "name" properties for all fields // Ensure there are "name" properties for all fields and that field schema
if (schema) { // are objects
Object.keys(schema).forEach((field) => { let fixedSchema = {}
if (!schema[field].name) { Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
schema[field].name = field if (typeof fieldSchema === "string") {
fixedSchema[fieldName] = {
type: fieldSchema,
name: fieldName,
} }
}) } else {
} fixedSchema[fieldName] = {
...fieldSchema,
name: fieldName,
}
}
})
schema = fixedSchema
} }
return { schema, table } return { schema, table }
} }
@ -293,14 +302,14 @@ export const getSchemaForDatasource = (datasource, isForm = false) => {
* Builds a form schema given a form component. * Builds a form schema given a form component.
* A form schema is a schema of all the fields nested anywhere within a form. * A form schema is a schema of all the fields nested anywhere within a form.
*/ */
const buildFormSchema = (component) => { const buildFormSchema = component => {
let schema = {} let schema = {}
if (!component) { if (!component) {
return schema return schema
} }
const def = store.actions.components.getDefinition(component._component) const def = store.actions.components.getDefinition(component._component)
const fieldSetting = def?.settings?.find( const fieldSetting = def?.settings?.find(
(setting) => setting.key === "field" && setting.type.startsWith("field/") setting => setting.key === "field" && setting.type.startsWith("field/")
) )
if (fieldSetting && component.field) { if (fieldSetting && component.field) {
const type = fieldSetting.type.split("field/")[1] const type = fieldSetting.type.split("field/")[1]
@ -308,7 +317,7 @@ const buildFormSchema = (component) => {
schema[component.field] = { type } schema[component.field] = { type }
} }
} }
component._children?.forEach((child) => { component._children?.forEach(child => {
const childSchema = buildFormSchema(child) const childSchema = buildFormSchema(child)
schema = { ...schema, ...childSchema } schema = { ...schema, ...childSchema }
}) })
@ -339,7 +348,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) {
return textWithBindings return textWithBindings
} }
const convertFromProps = bindableProperties const convertFromProps = bindableProperties
.map((el) => el[convertFrom]) .map(el => el[convertFrom])
.sort((a, b) => { .sort((a, b) => {
return b.length - a.length return b.length - a.length
}) })
@ -349,9 +358,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) {
let newBoundValue = boundValue let newBoundValue = boundValue
for (let from of convertFromProps) { for (let from of convertFromProps) {
if (newBoundValue.includes(from)) { if (newBoundValue.includes(from)) {
const binding = bindableProperties.find( const binding = bindableProperties.find(el => el[convertFrom] === from)
(el) => el[convertFrom] === from
)
newBoundValue = newBoundValue.replace(from, binding[convertTo]) newBoundValue = newBoundValue.replace(from, binding[convertTo])
} }
} }

View File

@ -12,16 +12,12 @@ export const automationStore = getAutomationStore()
export const themeStore = getThemeStore() export const themeStore = getThemeStore()
export const hostingStore = getHostingStore() export const hostingStore = getHostingStore()
export const currentAsset = derived(store, ($store) => { export const currentAsset = derived(store, $store => {
const type = $store.currentFrontEndType const type = $store.currentFrontEndType
if (type === FrontendTypes.SCREEN) { if (type === FrontendTypes.SCREEN) {
return $store.screens.find( return $store.screens.find(screen => screen._id === $store.selectedScreenId)
(screen) => screen._id === $store.selectedScreenId
)
} else if (type === FrontendTypes.LAYOUT) { } else if (type === FrontendTypes.LAYOUT) {
return $store.layouts.find( return $store.layouts.find(layout => layout._id === $store.selectedLayoutId)
(layout) => layout._id === $store.selectedLayoutId
)
} }
return null return null
}) })
@ -36,24 +32,24 @@ export const selectedComponent = derived(
} }
) )
export const currentAssetId = derived(store, ($store) => { export const currentAssetId = derived(store, $store => {
return $store.currentFrontEndType === FrontendTypes.SCREEN return $store.currentFrontEndType === FrontendTypes.SCREEN
? $store.selectedScreenId ? $store.selectedScreenId
: $store.selectedLayoutId : $store.selectedLayoutId
}) })
export const currentAssetName = derived(currentAsset, ($currentAsset) => { export const currentAssetName = derived(currentAsset, $currentAsset => {
return $currentAsset?.name return $currentAsset?.name
}) })
// leave this as before for consistency // leave this as before for consistency
export const allScreens = derived(store, ($store) => { export const allScreens = derived(store, $store => {
return $store.screens return $store.screens
}) })
export const mainLayout = derived(store, ($store) => { export const mainLayout = derived(store, $store => {
return $store.layouts?.find( return $store.layouts?.find(
(layout) => layout._id === LAYOUT_NAMES.MASTER.PRIVATE layout => layout._id === LAYOUT_NAMES.MASTER.PRIVATE
) )
}) })

View File

@ -5,7 +5,7 @@ import { get } from "builderStore/api"
* their props and other metadata from components.json. * their props and other metadata from components.json.
* @param {string} appId - ID of the currently running app * @param {string} appId - ID of the currently running app
*/ */
export const fetchComponentLibDefinitions = async (appId) => { export const fetchComponentLibDefinitions = async appId => {
const LIB_DEFINITION_URL = `/api/${appId}/components/definitions` const LIB_DEFINITION_URL = `/api/${appId}/components/definitions`
try { try {
const libDefinitionResponse = await get(LIB_DEFINITION_URL) const libDefinitionResponse = await get(LIB_DEFINITION_URL)

View File

@ -37,7 +37,7 @@ export default class Automation {
return return
} }
const stepIdx = steps.findIndex((step) => step.id === id) const stepIdx = steps.findIndex(step => step.id === id)
if (stepIdx < 0) throw new Error("Block not found.") if (stepIdx < 0) throw new Error("Block not found.")
steps.splice(stepIdx, 1, updatedBlock) steps.splice(stepIdx, 1, updatedBlock)
this.automation.definition.steps = steps this.automation.definition.steps = steps
@ -51,7 +51,7 @@ export default class Automation {
return return
} }
const stepIdx = steps.findIndex((step) => step.id === id) const stepIdx = steps.findIndex(step => step.id === id)
if (stepIdx < 0) throw new Error("Block not found.") if (stepIdx < 0) throw new Error("Block not found.")
steps.splice(stepIdx, 1) steps.splice(stepIdx, 1)
this.automation.definition.steps = steps this.automation.definition.steps = steps

View File

@ -4,14 +4,14 @@ import Automation from "./Automation"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import analytics from "analytics" import analytics from "analytics"
const automationActions = (store) => ({ const automationActions = store => ({
fetch: async () => { fetch: async () => {
const responses = await Promise.all([ const responses = await Promise.all([
api.get(`/api/automations`), api.get(`/api/automations`),
api.get(`/api/automations/definitions/list`), api.get(`/api/automations/definitions/list`),
]) ])
const jsonResponses = await Promise.all(responses.map((x) => x.json())) const jsonResponses = await Promise.all(responses.map(x => x.json()))
store.update((state) => { store.update(state => {
let selected = state.selectedAutomation?.automation let selected = state.selectedAutomation?.automation
state.automations = jsonResponses[0] state.automations = jsonResponses[0]
state.blockDefinitions = { state.blockDefinitions = {
@ -22,7 +22,7 @@ const automationActions = (store) => ({
// if previously selected find the new obj and select it // if previously selected find the new obj and select it
if (selected) { if (selected) {
selected = jsonResponses[0].filter( selected = jsonResponses[0].filter(
(automation) => automation._id === selected._id automation => automation._id === selected._id
) )
state.selectedAutomation = new Automation(selected[0]) state.selectedAutomation = new Automation(selected[0])
} }
@ -40,7 +40,7 @@ const automationActions = (store) => ({
const CREATE_AUTOMATION_URL = `/api/automations` const CREATE_AUTOMATION_URL = `/api/automations`
const response = await api.post(CREATE_AUTOMATION_URL, automation) const response = await api.post(CREATE_AUTOMATION_URL, automation)
const json = await response.json() const json = await response.json()
store.update((state) => { store.update(state => {
state.automations = [...state.automations, json.automation] state.automations = [...state.automations, json.automation]
store.actions.select(json.automation) store.actions.select(json.automation)
return state return state
@ -50,9 +50,9 @@ const automationActions = (store) => ({
const UPDATE_AUTOMATION_URL = `/api/automations` const UPDATE_AUTOMATION_URL = `/api/automations`
const response = await api.put(UPDATE_AUTOMATION_URL, automation) const response = await api.put(UPDATE_AUTOMATION_URL, automation)
const json = await response.json() const json = await response.json()
store.update((state) => { store.update(state => {
const existingIdx = state.automations.findIndex( const existingIdx = state.automations.findIndex(
(existing) => existing._id === automation._id existing => existing._id === automation._id
) )
state.automations.splice(existingIdx, 1, json.automation) state.automations.splice(existingIdx, 1, json.automation)
state.automations = state.automations state.automations = state.automations
@ -65,9 +65,9 @@ const automationActions = (store) => ({
const DELETE_AUTOMATION_URL = `/api/automations/${_id}/${_rev}` const DELETE_AUTOMATION_URL = `/api/automations/${_id}/${_rev}`
await api.delete(DELETE_AUTOMATION_URL) await api.delete(DELETE_AUTOMATION_URL)
store.update((state) => { store.update(state => {
const existingIdx = state.automations.findIndex( const existingIdx = state.automations.findIndex(
(existing) => existing._id === _id existing => existing._id === _id
) )
state.automations.splice(existingIdx, 1) state.automations.splice(existingIdx, 1)
state.automations = state.automations state.automations = state.automations
@ -81,15 +81,15 @@ const automationActions = (store) => ({
const TRIGGER_AUTOMATION_URL = `/api/automations/${_id}/trigger` const TRIGGER_AUTOMATION_URL = `/api/automations/${_id}/trigger`
return await api.post(TRIGGER_AUTOMATION_URL) return await api.post(TRIGGER_AUTOMATION_URL)
}, },
select: (automation) => { select: automation => {
store.update((state) => { store.update(state => {
state.selectedAutomation = new Automation(cloneDeep(automation)) state.selectedAutomation = new Automation(cloneDeep(automation))
state.selectedBlock = null state.selectedBlock = null
return state return state
}) })
}, },
addBlockToAutomation: (block) => { addBlockToAutomation: block => {
store.update((state) => { store.update(state => {
const newBlock = state.selectedAutomation.addBlock(cloneDeep(block)) const newBlock = state.selectedAutomation.addBlock(cloneDeep(block))
state.selectedBlock = newBlock state.selectedBlock = newBlock
return state return state
@ -98,10 +98,10 @@ const automationActions = (store) => ({
name: block.name, name: block.name,
}) })
}, },
deleteAutomationBlock: (block) => { deleteAutomationBlock: block => {
store.update((state) => { store.update(state => {
const idx = state.selectedAutomation.automation.definition.steps.findIndex( const idx = state.selectedAutomation.automation.definition.steps.findIndex(
(x) => x.id === block.id x => x.id === block.id
) )
state.selectedAutomation.deleteBlock(block.id) state.selectedAutomation.deleteBlock(block.id)

View File

@ -49,10 +49,10 @@ export const getFrontendStore = () => {
const store = writable({ ...INITIAL_FRONTEND_STATE }) const store = writable({ ...INITIAL_FRONTEND_STATE })
store.actions = { store.actions = {
initialise: async (pkg) => { initialise: async pkg => {
const { layouts, screens, application, clientLibPath } = pkg const { layouts, screens, application, clientLibPath } = pkg
const components = await fetchComponentLibDefinitions(application._id) const components = await fetchComponentLibDefinitions(application._id)
store.update((state) => ({ store.update(state => ({
...state, ...state,
libraries: application.componentLibraries, libraries: application.componentLibraries,
components, components,
@ -70,7 +70,7 @@ export const getFrontendStore = () => {
// Initialise backend stores // Initialise backend stores
const [_integrations] = await Promise.all([ const [_integrations] = await Promise.all([
api.get("/api/integrations").then((r) => r.json()), api.get("/api/integrations").then(r => r.json()),
]) ])
datasources.init() datasources.init()
integrations.set(_integrations) integrations.set(_integrations)
@ -82,18 +82,18 @@ export const getFrontendStore = () => {
fetch: async () => { fetch: async () => {
const response = await api.get("/api/routing") const response = await api.get("/api/routing")
const json = await response.json() const json = await response.json()
store.update((state) => { store.update(state => {
state.routes = json.routes state.routes = json.routes
return state return state
}) })
}, },
}, },
screens: { screens: {
select: (screenId) => { select: screenId => {
store.update((state) => { store.update(state => {
let screens = get(allScreens) let screens = get(allScreens)
let screen = let screen =
screens.find((screen) => screen._id === screenId) || screens[0] screens.find(screen => screen._id === screenId) || screens[0]
if (!screen) return state if (!screen) return state
// Update role to the screen's role setting so that it will always // Update role to the screen's role setting so that it will always
@ -107,9 +107,9 @@ export const getFrontendStore = () => {
return state return state
}) })
}, },
create: async (screen) => { create: async screen => {
screen = await store.actions.screens.save(screen) screen = await store.actions.screens.save(screen)
store.update((state) => { store.update(state => {
state.selectedScreenId = screen._id state.selectedScreenId = screen._id
state.selectedComponentId = screen.props._id state.selectedComponentId = screen.props._id
state.currentFrontEndType = FrontendTypes.SCREEN state.currentFrontEndType = FrontendTypes.SCREEN
@ -118,15 +118,15 @@ export const getFrontendStore = () => {
}) })
return screen return screen
}, },
save: async (screen) => { save: async screen => {
const creatingNewScreen = screen._id === undefined const creatingNewScreen = screen._id === undefined
const response = await api.post(`/api/screens`, screen) const response = await api.post(`/api/screens`, screen)
screen = await response.json() screen = await response.json()
await store.actions.routing.fetch() await store.actions.routing.fetch()
store.update((state) => { store.update(state => {
const foundScreen = state.screens.findIndex( const foundScreen = state.screens.findIndex(
(el) => el._id === screen._id el => el._id === screen._id
) )
if (foundScreen !== -1) { if (foundScreen !== -1) {
state.screens.splice(foundScreen, 1) state.screens.splice(foundScreen, 1)
@ -141,14 +141,14 @@ export const getFrontendStore = () => {
return screen return screen
}, },
delete: async (screens) => { delete: async screens => {
const screensToDelete = Array.isArray(screens) ? screens : [screens] const screensToDelete = Array.isArray(screens) ? screens : [screens]
const screenDeletePromises = [] const screenDeletePromises = []
store.update((state) => { store.update(state => {
for (let screenToDelete of screensToDelete) { for (let screenToDelete of screensToDelete) {
state.screens = state.screens.filter( state.screens = state.screens.filter(
(screen) => screen._id !== screenToDelete._id screen => screen._id !== screenToDelete._id
) )
screenDeletePromises.push( screenDeletePromises.push(
api.delete( api.delete(
@ -177,8 +177,8 @@ export const getFrontendStore = () => {
}, },
}, },
layouts: { layouts: {
select: (layoutId) => { select: layoutId => {
store.update((state) => { store.update(state => {
const layout = const layout =
store.actions.layouts.find(layoutId) || get(store).layouts[0] store.actions.layouts.find(layoutId) || get(store).layouts[0]
if (!layout) return if (!layout) return
@ -189,15 +189,15 @@ export const getFrontendStore = () => {
return state return state
}) })
}, },
save: async (layout) => { save: async layout => {
const layoutToSave = cloneDeep(layout) const layoutToSave = cloneDeep(layout)
const creatingNewLayout = layoutToSave._id === undefined const creatingNewLayout = layoutToSave._id === undefined
const response = await api.post(`/api/layouts`, layoutToSave) const response = await api.post(`/api/layouts`, layoutToSave)
const savedLayout = await response.json() const savedLayout = await response.json()
store.update((state) => { store.update(state => {
const layoutIdx = state.layouts.findIndex( const layoutIdx = state.layouts.findIndex(
(stateLayout) => stateLayout._id === savedLayout._id stateLayout => stateLayout._id === savedLayout._id
) )
if (layoutIdx >= 0) { if (layoutIdx >= 0) {
// update existing layout // update existing layout
@ -216,14 +216,14 @@ export const getFrontendStore = () => {
return savedLayout return savedLayout
}, },
find: (layoutId) => { find: layoutId => {
if (!layoutId) { if (!layoutId) {
return get(mainLayout) return get(mainLayout)
} }
const storeContents = get(store) const storeContents = get(store)
return storeContents.layouts.find((layout) => layout._id === layoutId) return storeContents.layouts.find(layout => layout._id === layoutId)
}, },
delete: async (layoutToDelete) => { delete: async layoutToDelete => {
const response = await api.delete( const response = await api.delete(
`/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}` `/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}`
) )
@ -231,9 +231,9 @@ export const getFrontendStore = () => {
const json = await response.json() const json = await response.json()
throw new Error(json.message) throw new Error(json.message)
} }
store.update((state) => { store.update(state => {
state.layouts = state.layouts.filter( state.layouts = state.layouts.filter(
(layout) => layout._id !== layoutToDelete._id layout => layout._id !== layoutToDelete._id
) )
if (layoutToDelete._id === state.selectedLayoutId) { if (layoutToDelete._id === state.selectedLayoutId) {
state.selectedLayoutId = get(mainLayout)._id state.selectedLayoutId = get(mainLayout)._id
@ -243,7 +243,7 @@ export const getFrontendStore = () => {
}, },
}, },
components: { components: {
select: (component) => { select: component => {
if (!component) { if (!component) {
return return
} }
@ -263,13 +263,13 @@ export const getFrontendStore = () => {
} }
// Otherwise select the component // Otherwise select the component
store.update((state) => { store.update(state => {
state.selectedComponentId = component._id state.selectedComponentId = component._id
state.currentView = "component" state.currentView = "component"
return state return state
}) })
}, },
getDefinition: (componentName) => { getDefinition: componentName => {
if (!componentName) { if (!componentName) {
return null return null
} }
@ -287,7 +287,7 @@ export const getFrontendStore = () => {
// Generate default props // Generate default props
let props = { ...presetProps } let props = { ...presetProps }
if (definition.settings) { if (definition.settings) {
definition.settings.forEach((setting) => { definition.settings.forEach(setting => {
if (setting.defaultValue !== undefined) { if (setting.defaultValue !== undefined) {
props[setting.key] = setting.defaultValue props[setting.key] = setting.defaultValue
} }
@ -367,7 +367,7 @@ export const getFrontendStore = () => {
// Save components and update UI // Save components and update UI
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
store.update((state) => { store.update(state => {
state.currentView = "component" state.currentView = "component"
state.selectedComponentId = componentInstance._id state.selectedComponentId = componentInstance._id
return state return state
@ -380,7 +380,7 @@ export const getFrontendStore = () => {
return componentInstance return componentInstance
}, },
delete: async (component) => { delete: async component => {
if (!component) { if (!component) {
return return
} }
@ -391,7 +391,7 @@ export const getFrontendStore = () => {
const parent = findComponentParent(asset.props, component._id) const parent = findComponentParent(asset.props, component._id)
if (parent) { if (parent) {
parent._children = parent._children.filter( parent._children = parent._children.filter(
(child) => child._id !== component._id child => child._id !== component._id
) )
store.actions.components.select(parent) store.actions.components.select(parent)
} }
@ -404,7 +404,7 @@ export const getFrontendStore = () => {
} }
// Update store with copied component // Update store with copied component
store.update((state) => { store.update(state => {
state.componentToPaste = cloneDeep(component) state.componentToPaste = cloneDeep(component)
state.componentToPaste.isCut = cut state.componentToPaste.isCut = cut
return state return state
@ -415,7 +415,7 @@ export const getFrontendStore = () => {
const parent = findComponentParent(selectedAsset.props, component._id) const parent = findComponentParent(selectedAsset.props, component._id)
if (parent) { if (parent) {
parent._children = parent._children.filter( parent._children = parent._children.filter(
(child) => child._id !== component._id child => child._id !== component._id
) )
store.actions.components.select(parent) store.actions.components.select(parent)
} }
@ -423,7 +423,7 @@ export const getFrontendStore = () => {
}, },
paste: async (targetComponent, mode) => { paste: async (targetComponent, mode) => {
let promises = [] let promises = []
store.update((state) => { store.update(state => {
// Stop if we have nothing to paste // Stop if we have nothing to paste
if (!state.componentToPaste) { if (!state.componentToPaste) {
return state return state
@ -444,7 +444,7 @@ export const getFrontendStore = () => {
if (cut) { if (cut) {
state.componentToPaste = null state.componentToPaste = null
} else { } else {
const randomizeIds = (component) => { const randomizeIds = component => {
if (!component) { if (!component) {
return return
} }
@ -497,7 +497,7 @@ export const getFrontendStore = () => {
} }
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
}, },
updateCustomStyle: async (style) => { updateCustomStyle: async style => {
const selected = get(selectedComponent) const selected = get(selectedComponent)
selected._styles.custom = style selected._styles.custom = style
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
@ -507,7 +507,7 @@ export const getFrontendStore = () => {
selected._styles = { normal: {}, hover: {}, active: {} } selected._styles = { normal: {}, hover: {}, active: {} }
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
}, },
updateTransition: async (transition) => { updateTransition: async transition => {
const selected = get(selectedComponent) const selected = get(selectedComponent)
if (transition == null || transition === "") { if (transition == null || transition === "") {
selected._transition = "" selected._transition = ""
@ -522,7 +522,7 @@ export const getFrontendStore = () => {
return return
} }
component[name] = value component[name] = value
store.update((state) => { store.update(state => {
state.selectedComponentId = component._id state.selectedComponentId = component._id
return state return state
}) })

View File

@ -17,20 +17,18 @@ export const getHostingStore = () => {
api.get("/api/hosting/"), api.get("/api/hosting/"),
api.get("/api/hosting/urls"), api.get("/api/hosting/urls"),
]) ])
const [info, urls] = await Promise.all( const [info, urls] = await Promise.all(responses.map(resp => resp.json()))
responses.map((resp) => resp.json()) store.update(state => {
)
store.update((state) => {
state.hostingInfo = info state.hostingInfo = info
state.appUrl = urls.app state.appUrl = urls.app
return state return state
}) })
return info return info
}, },
save: async (hostingInfo) => { save: async hostingInfo => {
const response = await api.post("/api/hosting", hostingInfo) const response = await api.post("/api/hosting", hostingInfo)
const revision = (await response.json()).rev const revision = (await response.json()).rev
store.update((state) => { store.update(state => {
state.hostingInfo = { state.hostingInfo = {
...hostingInfo, ...hostingInfo,
_rev: revision, _rev: revision,
@ -40,12 +38,10 @@ export const getHostingStore = () => {
}, },
fetchDeployedApps: async () => { fetchDeployedApps: async () => {
let deployments = await (await get("/api/hosting/apps")).json() let deployments = await (await get("/api/hosting/apps")).json()
store.update((state) => { store.update(state => {
state.deployedApps = deployments state.deployedApps = deployments
state.deployedAppNames = Object.values(deployments).map( state.deployedAppNames = Object.values(deployments).map(app => app.name)
(app) => app.name state.deployedAppUrls = Object.values(deployments).map(app => app.url)
)
state.deployedAppUrls = Object.values(deployments).map((app) => app.url)
return state return state
}) })
return deployments return deployments

View File

@ -12,13 +12,13 @@ export const localStorageStore = (localStorageKey, initialValue) => {
}) })
// New store setter which updates the store and localstorage // New store setter which updates the store and localstorage
const set = (value) => { const set = value => {
store.set(value) store.set(value)
localStorage.setItem(localStorageKey, JSON.stringify(value)) localStorage.setItem(localStorageKey, JSON.stringify(value))
} }
// New store updater which updates the store and localstorage // New store updater which updates the store and localstorage
const update = (updaterFn) => set(updaterFn(get(store))) const update = updaterFn => set(updaterFn(get(store)))
// Hydrates the store from localstorage // Hydrates the store from localstorage
const hydrate = () => { const hydrate = () => {

View File

@ -6,7 +6,7 @@ export const notificationStore = writable({
}) })
export function send(message, type = "default") { export function send(message, type = "default") {
notificationStore.update((state) => { notificationStore.update(state => {
state.notifications = [ state.notifications = [
...state.notifications, ...state.notifications,
{ id: generate(), type, message }, { id: generate(), type, message },
@ -16,8 +16,8 @@ export function send(message, type = "default") {
} }
export const notifier = { export const notifier = {
danger: (msg) => send(msg, "danger"), danger: msg => send(msg, "danger"),
warning: (msg) => send(msg, "warning"), warning: msg => send(msg, "warning"),
info: (msg) => send(msg, "info"), info: msg => send(msg, "info"),
success: (msg) => send(msg, "success"), success: msg => send(msg, "success"),
} }

View File

@ -3,7 +3,7 @@ import rowDetailScreen from "./rowDetailScreen"
import rowListScreen from "./rowListScreen" import rowListScreen from "./rowListScreen"
import createFromScratchScreen from "./createFromScratchScreen" import createFromScratchScreen from "./createFromScratchScreen"
const allTemplates = (tables) => [ const allTemplates = tables => [
...newRowScreen(tables), ...newRowScreen(tables),
...rowDetailScreen(tables), ...rowDetailScreen(tables),
...rowListScreen(tables), ...rowListScreen(tables),
@ -18,7 +18,7 @@ const createTemplateOverride = (frontendState, create) => () => {
} }
export default (frontendState, tables) => { export default (frontendState, tables) => {
const enrichTemplate = (template) => ({ const enrichTemplate = template => ({
...template, ...template,
create: createTemplateOverride(frontendState, template.create), create: createTemplateOverride(frontendState, template.create),
}) })

View File

@ -10,7 +10,7 @@ import {
} from "./utils/commonComponents" } from "./utils/commonComponents"
export default function (tables) { export default function (tables) {
return tables.map((table) => { return tables.map(table => {
return { return {
name: `${table.name} - New`, name: `${table.name} - New`,
create: () => createScreen(table), create: () => createScreen(table),
@ -19,14 +19,14 @@ export default function (tables) {
}) })
} }
export const newRowUrl = (table) => sanitizeUrl(`/${table.name}/new/row`) export const newRowUrl = table => sanitizeUrl(`/${table.name}/new/row`)
export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE" export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE"
function generateTitleContainer(table, formId) { function generateTitleContainer(table, formId) {
return makeTitleContainer("New Row").addChild(makeSaveButton(table, formId)) return makeTitleContainer("New Row").addChild(makeSaveButton(table, formId))
} }
const createScreen = (table) => { const createScreen = table => {
const screen = new Screen() const screen = new Screen()
.component("@budibase/standard-components/container") .component("@budibase/standard-components/container")
.instanceName(`${table.name} - New`) .instanceName(`${table.name} - New`)
@ -52,7 +52,7 @@ const createScreen = (table) => {
// Add all form fields from this schema to the field group // Add all form fields from this schema to the field group
const datasource = { type: "table", tableId: table._id } const datasource = { type: "table", tableId: table._id }
makeDatasourceFormComponents(datasource).forEach((component) => { makeDatasourceFormComponents(datasource).forEach(component => {
fieldGroup.addChild(component) fieldGroup.addChild(component)
}) })

View File

@ -13,7 +13,7 @@ import {
} from "./utils/commonComponents" } from "./utils/commonComponents"
export default function (tables) { export default function (tables) {
return tables.map((table) => { return tables.map(table => {
return { return {
name: `${table.name} - Detail`, name: `${table.name} - Detail`,
create: () => createScreen(table), create: () => createScreen(table),
@ -23,7 +23,7 @@ export default function (tables) {
} }
export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE" export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE"
export const rowDetailUrl = (table) => sanitizeUrl(`/${table.name}/:id`) export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`)
function generateTitleContainer(table, title, formId, repeaterId) { function generateTitleContainer(table, title, formId, repeaterId) {
// have to override style for this, its missing margin // have to override style for this, its missing margin
@ -80,7 +80,7 @@ function generateTitleContainer(table, title, formId, repeaterId) {
return makeTitleContainer(title).addChild(deleteButton).addChild(saveButton) return makeTitleContainer(title).addChild(deleteButton).addChild(saveButton)
} }
const createScreen = (table) => { const createScreen = table => {
const provider = new Component("@budibase/standard-components/dataprovider") const provider = new Component("@budibase/standard-components/dataprovider")
.instanceName(`Data Provider`) .instanceName(`Data Provider`)
.customProps({ .customProps({
@ -122,7 +122,7 @@ const createScreen = (table) => {
// Add all form fields from this schema to the field group // Add all form fields from this schema to the field group
const datasource = { type: "table", tableId: table._id } const datasource = { type: "table", tableId: table._id }
makeDatasourceFormComponents(datasource).forEach((component) => { makeDatasourceFormComponents(datasource).forEach(component => {
fieldGroup.addChild(component) fieldGroup.addChild(component)
}) })

View File

@ -5,7 +5,7 @@ import { Component } from "./utils/Component"
import { makePropSafe } from "@budibase/string-templates" import { makePropSafe } from "@budibase/string-templates"
export default function (tables) { export default function (tables) {
return tables.map((table) => { return tables.map(table => {
return { return {
name: `${table.name} - List`, name: `${table.name} - List`,
create: () => createScreen(table), create: () => createScreen(table),
@ -15,7 +15,7 @@ export default function (tables) {
} }
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE" export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
export const rowListUrl = (table) => sanitizeUrl(`/${table.name}`) export const rowListUrl = table => sanitizeUrl(`/${table.name}`)
function generateTitleContainer(table) { function generateTitleContainer(table) {
const newButton = new Component("@budibase/standard-components/button") const newButton = new Component("@budibase/standard-components/button")
@ -70,7 +70,7 @@ function generateTitleContainer(table) {
.addChild(newButton) .addChild(newButton)
} }
const createScreen = (table) => { const createScreen = table => {
const provider = new Component("@budibase/standard-components/dataprovider") const provider = new Component("@budibase/standard-components/dataprovider")
.instanceName(`Data Provider`) .instanceName(`Data Provider`)
.customProps({ .customProps({

View File

@ -178,7 +178,7 @@ export function makeDatasourceFormComponents(datasource) {
const { schema } = getSchemaForDatasource(datasource, true) const { schema } = getSchemaForDatasource(datasource, true)
let components = [] let components = []
let fields = Object.keys(schema || {}) let fields = Object.keys(schema || {})
fields.forEach((field) => { fields.forEach(field => {
const fieldSchema = schema[field] const fieldSchema = schema[field]
// skip autocolumns // skip autocolumns
if (fieldSchema.autocolumn) { if (fieldSchema.autocolumn) {

View File

@ -1,7 +1,7 @@
export default function (url) { export default function (url) {
return url return url
.split("/") .split("/")
.map((part) => { .map(part => {
// if parameter, then use as is // if parameter, then use as is
if (part.startsWith(":")) return part if (part.startsWith(":")) return part
return encodeURIComponent(part.replace(/ /g, "-")) return encodeURIComponent(part.replace(/ /g, "-"))

View File

@ -9,14 +9,14 @@ export const getThemeStore = () => {
const store = localStorageStore("bb-theme", initialValue) const store = localStorageStore("bb-theme", initialValue)
// Update theme class when store changes // Update theme class when store changes
store.subscribe((state) => { store.subscribe(state => {
// Handle any old local storage values - this can be removed after the update // Handle any old local storage values - this can be removed after the update
if (state.darkMode !== undefined) { if (state.darkMode !== undefined) {
store.set(initialValue) store.set(initialValue)
return return
} }
state.options.forEach((option) => { state.options.forEach(option => {
themeElement.classList.toggle( themeElement.classList.toggle(
`spectrum--${option}`, `spectrum--${option}`,
option === state.theme option === state.theme

View File

@ -2,14 +2,14 @@
* Recursively searches for a specific component ID * Recursively searches for a specific component ID
*/ */
export const findComponent = (rootComponent, id) => { export const findComponent = (rootComponent, id) => {
return searchComponentTree(rootComponent, (comp) => comp._id === id) return searchComponentTree(rootComponent, comp => comp._id === id)
} }
/** /**
* Recursively searches for a specific component type * Recursively searches for a specific component type
*/ */
export const findComponentType = (rootComponent, type) => { export const findComponentType = (rootComponent, type) => {
return searchComponentTree(rootComponent, (comp) => comp._component === type) return searchComponentTree(rootComponent, comp => comp._component === type)
} }
/** /**
@ -68,7 +68,7 @@ export const findAllMatchingComponents = (rootComponent, selector) => {
} }
let components = [] let components = []
if (rootComponent._children) { if (rootComponent._children) {
rootComponent._children.forEach((child) => { rootComponent._children.forEach(child => {
components = [ components = [
...components, ...components,
...findAllMatchingComponents(child, selector), ...findAllMatchingComponents(child, selector),

View File

@ -1,7 +1,7 @@
export function uuid() { export function uuid() {
// always want to make this start with a letter, as this makes it // always want to make this start with a letter, as this makes it
// easier to use with template string bindings in the client // easier to use with template string bindings in the client
return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, (c) => { return "cxxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, c => {
const r = (Math.random() * 16) | 0, const r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8 v = c == "x" ? r : (r & 0x3) | 0x8
return v.toString(16) return v.toString(16)

View File

@ -63,13 +63,14 @@
<div class="tab-container"> <div class="tab-container">
{#each tabs as tab, idx} {#each tabs as tab, idx}
<div bind:this={anchors[idx]}> <div bind:this={anchors[idx]}>
<ActionButton <ActionButton
quiet quiet
size="S" size="S"
icon={tab.icon} icon={tab.icon}
disabled={tab.disabled} disabled={tab.disabled}
on:click={tab.disabled ? null : () => onChangeTab(idx)}> on:click={tab.disabled ? null : () => onChangeTab(idx)}
>
{tab.label} {tab.label}
</ActionButton> </ActionButton>
</div> </div>
@ -79,14 +80,16 @@
on:close={() => (selectedIndex = null)} on:close={() => (selectedIndex = null)}
bind:this={popover} bind:this={popover}
{anchor} {anchor}
align="left"> align="left"
>
<DropdownContainer> <DropdownContainer>
{#each blocks as [stepId, blockDefinition]} {#each blocks as [stepId, blockDefinition]}
<DropdownItem <DropdownItem
icon={blockDefinition.icon} icon={blockDefinition.icon}
title={blockDefinition.name} title={blockDefinition.name}
subtitle={blockDefinition.description} subtitle={blockDefinition.description}
on:click={() => addBlockToAutomation(stepId, blockDefinition)} /> on:click={() => addBlockToAutomation(stepId, blockDefinition)}
/>
{/each} {/each}
</DropdownContainer> </DropdownContainer>
</Popover> </Popover>

View File

@ -3,10 +3,12 @@
height="75" height="75"
viewBox="0 0 9 75" viewBox="0 0 9 75"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M5.0625 70H9L4.5 75L0 70H3.9375V65H5.0625V70Z" d="M5.0625 70H9L4.5 75L0 70H3.9375V65H5.0625V70Z"
fill="var(--grey-5)" /> fill="var(--grey-5)"
/>
<rect x="4" width="1" height="65" fill="var(--grey-5)" /> <rect x="4" width="1" height="65" fill="var(--grey-5)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 326 B

View File

@ -26,7 +26,8 @@
class="block" class="block"
animate:flip={{ duration: 600 }} animate:flip={{ duration: 600 }}
in:fade|local in:fade|local
out:fly|local={{ x: 100 }}> out:fly|local={{ x: 100 }}
>
<FlowItem {onSelect} {block} /> <FlowItem {onSelect} {block} />
{#if idx !== blocks.length - 1} {#if idx !== blocks.length - 1}
<Arrow /> <Arrow />

View File

@ -14,7 +14,7 @@
$: allowDeleteTrigger = !steps.length $: allowDeleteTrigger = !steps.length
function deleteStep() { function deleteStep() {
console.log('Running') console.log("Running")
automationStore.actions.deleteAutomationBlock(block) automationStore.actions.deleteAutomationBlock(block)
} }
</script> </script>
@ -22,23 +22,24 @@
<div <div
class={`block ${block.type} hoverable`} class={`block ${block.type} hoverable`}
class:selected class:selected
on:click={() => onSelect(block)}> on:click={() => onSelect(block)}
>
<header> <header>
{#if block.type === 'TRIGGER'} {#if block.type === "TRIGGER"}
<Icon name="Light" /> <Icon name="Light" />
<span>When this happens...</span> <span>When this happens...</span>
{:else if block.type === 'ACTION'} {:else if block.type === "ACTION"}
<Icon name="FlashOn" /> <Icon name="FlashOn" />
<span>Do this...</span> <span>Do this...</span>
{:else if block.type === 'LOGIC'} {:else if block.type === "LOGIC"}
<Icon name="Branch2" /> <Icon name="Branch2" />
<span>Only continue if...</span> <span>Only continue if...</span>
{/if} {/if}
<div class="label"> <div class="label">
{#if block.type === 'TRIGGER'}Trigger{:else}Step {blockIdx + 1}{/if} {#if block.type === "TRIGGER"}Trigger{:else}Step {blockIdx + 1}{/if}
</div> </div>
{#if block.type !== 'TRIGGER' || allowDeleteTrigger} {#if block.type !== "TRIGGER" || allowDeleteTrigger}
<div on:click|stopPropagation={deleteStep}><Icon name="Close"/></div> <div on:click|stopPropagation={deleteStep}><Icon name="Close" /></div>
{/if} {/if}
</header> </header>
<hr /> <hr />

View File

@ -27,12 +27,14 @@
confirmText="Create" confirmText="Create"
size="L" size="L"
onConfirm={createAutomation} onConfirm={createAutomation}
disabled={!valid}> disabled={!valid}
>
<Input bind:value={name} label="Name" /> <Input bind:value={name} label="Name" />
<a <a
slot="footer" slot="footer"
target="_blank" target="_blank"
href="https://docs.budibase.com/automate/introduction-to-automate"> href="https://docs.budibase.com/automate/introduction-to-automate"
>
<Icon name="InfoOutline" /> <Icon name="InfoOutline" />
<span>Learn about automations</span> <span>Learn about automations</span>
</a> </a>

View File

@ -60,7 +60,7 @@
<div class="section"> <div class="section">
{#each categories as [categoryName, bindings]} {#each categories as [categoryName, bindings]}
<Heading size="XS">{categoryName}</Heading> <Heading size="XS">{categoryName}</Heading>
{#each bindableProperties.filter((binding) => {#each bindableProperties.filter(binding =>
binding.label.match(searchRgx) binding.label.match(searchRgx)
) as binding} ) as binding}
<div class="binding" on:click={() => addToText(binding)}> <div class="binding" on:click={() => addToText(binding)}>
@ -76,7 +76,7 @@
</div> </div>
<div class="section"> <div class="section">
<Heading size="XS">Helpers</Heading> <Heading size="XS">Helpers</Heading>
{#each helpers.filter((helper) => helper.label.match(searchRgx) || helper.description.match(searchRgx)) as helper} {#each helpers.filter(helper => helper.label.match(searchRgx) || helper.description.match(searchRgx)) as helper}
<div class="binding" on:click={() => addToText(helper)}> <div class="binding" on:click={() => addToText(helper)}>
<span class="binding__label">{helper.label}</span> <span class="binding__label">{helper.label}</span>
<br /> <br />

View File

@ -54,40 +54,43 @@
{#each inputs as [key, value]} {#each inputs as [key, value]}
<div class="block-field"> <div class="block-field">
<Label>{value.title}</Label> <Label>{value.title}</Label>
{#if value.type === 'string' && value.enum} {#if value.type === "string" && value.enum}
<Select <Select
bind:value={block.inputs[key]} bind:value={block.inputs[key]}
options={value.enum} options={value.enum}
getOptionLabel={(x, idx) => (value.pretty ? value.pretty[idx] : x)} /> getOptionLabel={(x, idx) => (value.pretty ? value.pretty[idx] : x)}
{:else if value.customType === 'password'} />
{:else if value.customType === "password"}
<Input type="password" bind:value={block.inputs[key]} /> <Input type="password" bind:value={block.inputs[key]} />
{:else if value.customType === 'email'} {:else if value.customType === "email"}
<DrawerBindableInput <DrawerBindableInput
panel={AutomationBindingPanel} panel={AutomationBindingPanel}
type={'email'} type={"email"}
value={block.inputs[key]} value={block.inputs[key]}
on:change={e => (block.inputs[key] = e.detail)} on:change={e => (block.inputs[key] = e.detail)}
{bindings} /> {bindings}
{:else if value.customType === 'table'} />
{:else if value.customType === "table"}
<TableSelector bind:value={block.inputs[key]} /> <TableSelector bind:value={block.inputs[key]} />
{:else if value.customType === 'row'} {:else if value.customType === "row"}
<RowSelector bind:value={block.inputs[key]} {bindings} /> <RowSelector bind:value={block.inputs[key]} {bindings} />
{:else if value.customType === 'webhookUrl'} {:else if value.customType === "webhookUrl"}
<WebhookDisplay value={block.inputs[key]} /> <WebhookDisplay value={block.inputs[key]} />
{:else if value.customType === 'triggerSchema'} {:else if value.customType === "triggerSchema"}
<SchemaSetup bind:value={block.inputs[key]} /> <SchemaSetup bind:value={block.inputs[key]} />
{:else if value.type === 'string' || value.type === 'number'} {:else if value.type === "string" || value.type === "number"}
<DrawerBindableInput <DrawerBindableInput
panel={AutomationBindingPanel} panel={AutomationBindingPanel}
type={value.customType} type={value.customType}
value={block.inputs[key]} value={block.inputs[key]}
on:change={e => (block.inputs[key] = e.detail)} on:change={e => (block.inputs[key] = e.detail)}
{bindings} /> {bindings}
/>
{/if} {/if}
</div> </div>
{/each} {/each}
</div> </div>
{#if stepId === 'WEBHOOK'} {#if stepId === "WEBHOOK"}
<Button secondary on:click={() => webhookModal.show()}>Set Up Webhook</Button> <Button secondary on:click={() => webhookModal.show()}>Set Up Webhook</Button>
{/if} {/if}

View File

@ -23,7 +23,8 @@
bind:value={value.tableId} bind:value={value.tableId}
options={$tables.list} options={$tables.list}
getOptionLabel={table => table.name} getOptionLabel={table => table.name}
getOptionValue={table => table._id} /> getOptionValue={table => table._id}
/>
{#if schemaFields.length} {#if schemaFields.length}
<div class="schema-fields"> <div class="schema-fields">
@ -33,15 +34,17 @@
<Select <Select
label={field} label={field}
bind:value={value[field]} bind:value={value[field]}
options={schema.constraints.inclusion} /> options={schema.constraints.inclusion}
{:else if schema.type === 'string' || schema.type === 'number'} />
{:else if schema.type === "string" || schema.type === "number"}
<DrawerBindableInput <DrawerBindableInput
panel={AutomationBindingPanel} panel={AutomationBindingPanel}
value={value[field]} value={value[field]}
on:change={e => (value[field] = e.detail)} on:change={e => (value[field] = e.detail)}
label={field} label={field}
type="string" type="string"
{bindings} /> {bindings}
/>
{/if} {/if}
{/if} {/if}
{/each} {/each}

View File

@ -62,14 +62,17 @@
value={field.name} value={field.name}
secondary secondary
placeholder="Enter field name" placeholder="Enter field name"
on:change={fieldNameChanged(field.name)} /> on:change={fieldNameChanged(field.name)}
/>
<Select <Select
value={field.type} value={field.type}
on:change={e => (value[field.name] = e.target.value)} on:change={e => (value[field.name] = e.target.value)}
options={typeOptions} /> options={typeOptions}
/>
<i <i
class="remove-field ri-delete-bin-line" class="remove-field ri-delete-bin-line"
on:click={() => removeField(field.name)} /> on:click={() => removeField(field.name)}
/>
</div> </div>
{/each} {/each}
</div> </div>

View File

@ -9,4 +9,5 @@
bind:value bind:value
options={$tables.list} options={$tables.list}
getOptionLabel={table => table.name} getOptionLabel={table => table.name}
getOptionValue={table => table._id} /> getOptionValue={table => table._id}
/>

View File

@ -53,12 +53,14 @@
{data} {data}
allowEditing={true} allowEditing={true}
bind:hideAutocolumns bind:hideAutocolumns
{loading}> {loading}
>
<CreateColumnButton /> <CreateColumnButton />
{#if schema && Object.keys(schema).length > 0} {#if schema && Object.keys(schema).length > 0}
<CreateRowButton <CreateRowButton
title={isUsersTable ? 'Create user' : 'Create row'} title={isUsersTable ? "Create user" : "Create row"}
modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} /> modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow}
/>
<CreateViewButton /> <CreateViewButton />
<ManageAccessButton resourceId={$tables.selected?._id} /> <ManageAccessButton resourceId={$tables.selected?._id} />
{#if isUsersTable} {#if isUsersTable}

View File

@ -13,21 +13,22 @@
$: label = capitalise(meta.name) $: label = capitalise(meta.name)
</script> </script>
{#if type === 'options'} {#if type === "options"}
<Select <Select
{label} {label}
data-cy="{meta.name}-select" data-cy="{meta.name}-select"
bind:value bind:value
options={meta.constraints.inclusion} /> options={meta.constraints.inclusion}
{:else if type === 'datetime'} />
{:else if type === "datetime"}
<DatePicker {label} bind:value /> <DatePicker {label} bind:value />
{:else if type === 'attachment'} {:else if type === "attachment"}
<Dropzone {label} bind:value /> <Dropzone {label} bind:value />
{:else if type === 'boolean'} {:else if type === "boolean"}
<Toggle text={label} bind:checked={value} data-cy="{meta.name}-input" /> <Toggle text={label} bind:checked={value} data-cy="{meta.name}-input" />
{:else if type === 'link'} {:else if type === "link"}
<LinkedRowSelector bind:linkedRows={value} schema={meta} /> <LinkedRowSelector bind:linkedRows={value} schema={meta} />
{:else if type === 'longform'} {:else if type === "longform"}
<TextArea {label} bind:value /> <TextArea {label} bind:value />
{:else} {:else}
<Input <Input
@ -35,5 +36,6 @@
data-cy="{meta.name}-input" data-cy="{meta.name}-input"
{type} {type}
bind:value bind:value
disabled={readonly} /> disabled={readonly}
/>
{/if} {/if}

View File

@ -40,7 +40,7 @@
component: RoleCell, component: RoleCell,
}, },
] ]
UNEDITABLE_USER_FIELDS.forEach((field) => { UNEDITABLE_USER_FIELDS.forEach(field => {
if (schema[field]) { if (schema[field]) {
schema[field].editable = false schema[field].editable = false
} }
@ -68,19 +68,19 @@
rows: selectedRows, rows: selectedRows,
type: "delete", type: "delete",
}) })
data = data.filter((row) => !selectedRows.includes(row)) data = data.filter(row => !selectedRows.includes(row))
notifications.success(`Successfully deleted ${selectedRows.length} rows`) notifications.success(`Successfully deleted ${selectedRows.length} rows`)
selectedRows = [] selectedRows = []
} }
const editRow = (row) => { const editRow = row => {
editableRow = row editableRow = row
if (row) { if (row) {
editRowModal.show() editRowModal.show()
} }
} }
const editColumn = (field) => { const editColumn = field => {
editableColumn = schema?.[field] editableColumn = schema?.[field]
if (editableColumn) { if (editableColumn) {
editColumnModal.show() editColumnModal.show()
@ -118,9 +118,9 @@
allowEditRows={allowEditing} allowEditRows={allowEditing}
allowEditColumns={allowEditing} allowEditColumns={allowEditing}
showAutoColumns={!hideAutocolumns} showAutoColumns={!hideAutocolumns}
on:editcolumn={(e) => editColumn(e.detail)} on:editcolumn={e => editColumn(e.detail)}
on:editrow={(e) => editRow(e.detail)} on:editrow={e => editRow(e.detail)}
on:clickrelationship={(e) => selectRelationship(e.detail)} on:clickrelationship={e => selectRelationship(e.detail)}
/> />
{/key} {/key}

View File

@ -58,7 +58,8 @@
{data} {data}
{loading} {loading}
allowEditing={!view?.calculation} allowEditing={!view?.calculation}
bind:hideAutocolumns> bind:hideAutocolumns
>
<FilterButton {view} /> <FilterButton {view} />
<CalculateButton {view} /> <CalculateButton {view} />
{#if view.calculation} {#if view.calculation}

View File

@ -12,7 +12,8 @@
size="S" size="S"
quiet quiet
on:click={modal.show} on:click={modal.show}
active={view.field && view.calculation}> active={view.field && view.calculation}
>
Calculate Calculate
</ActionButton> </ActionButton>
<Modal bind:this={modal}> <Modal bind:this={modal}>

View File

@ -22,8 +22,9 @@
bind:this={modal} bind:this={modal}
okText="Delete" okText="Delete"
onOk={confirmDeletion} onOk={confirmDeletion}
title="Confirm Deletion"> title="Confirm Deletion"
>
Are you sure you want to delete Are you sure you want to delete
{selectedRows.length} {selectedRows.length}
row{selectedRows.length > 1 ? 's' : ''}? row{selectedRows.length > 1 ? "s" : ""}?
</ConfirmDialog> </ConfirmDialog>

View File

@ -12,7 +12,8 @@
size="S" size="S"
quiet quiet
on:click={modal.show} on:click={modal.show}
active={view.filters?.length}> active={view.filters?.length}
>
Filter Filter
</ActionButton> </ActionButton>
<Modal bind:this={modal}> <Modal bind:this={modal}>

View File

@ -13,7 +13,8 @@
size="S" size="S"
quiet quiet
active={!!view.groupBy} active={!!view.groupBy}
on:click={modal.show}> on:click={modal.show}
>
Group By Group By
</Button> </Button>
<Modal bind:this={modal}> <Modal bind:this={modal}>

View File

@ -21,5 +21,6 @@
<ManageAccessModal <ManageAccessModal
{resourceId} {resourceId}
levels={$permissions} levels={$permissions}
permissions={resourcePermissions} /> permissions={resourcePermissions}
/>
</Modal> </Modal>

Some files were not shown because too many files have changed in this diff Show More