commit
a608b225c5
|
@ -5,4 +5,5 @@ package-lock.json
|
|||
release/
|
||||
dist/
|
||||
cypress/screenshots
|
||||
cypress/videos
|
||||
routify
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
"d3-selection": "^1.4.1",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fast-sort": "^2.2.0",
|
||||
"feather-icons": "^4.21.0",
|
||||
"lodash": "^4.17.13",
|
||||
"mustache": "^4.0.1",
|
||||
"posthog-js": "1.3.1",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Array.flat needs polyfilled in < Node 11
|
||||
if (!Array.prototype.flat) {
|
||||
Object.defineProperty(Array.prototype, "flat", {
|
||||
configurable: true,
|
||||
|
|
|
@ -37,7 +37,9 @@ export default function({ componentInstanceId, screen, components, models }) {
|
|||
.filter(isInstanceInSharedContext(walkResult))
|
||||
.map(componentInstanceToBindable(walkResult)),
|
||||
|
||||
...walkResult.target._contexts.map(contextToBindables(walkResult)).flat(),
|
||||
...walkResult.target._contexts
|
||||
.map(contextToBindables(models, walkResult))
|
||||
.flat(),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -69,17 +71,31 @@ const componentInstanceToBindable = walkResult => i => {
|
|||
}
|
||||
}
|
||||
|
||||
const contextToBindables = walkResult => context => {
|
||||
const contextToBindables = (models, walkResult) => context => {
|
||||
const contextParentPath = getParentPath(walkResult, context)
|
||||
|
||||
return Object.keys(context.model.schema).map(k => ({
|
||||
const newBindable = key => ({
|
||||
type: "context",
|
||||
instance: context.instance,
|
||||
// how the binding expression persists, and is used in the app at runtime
|
||||
runtimeBinding: `${contextParentPath}data.${k}`,
|
||||
runtimeBinding: `${contextParentPath}data.${key}`,
|
||||
// how the binding exressions looks to the user of the builder
|
||||
readableBinding: `${context.instance._instanceName}.${context.model.name}.${k}`,
|
||||
}))
|
||||
readableBinding: `${context.instance._instanceName}.${context.model.label}.${key}`,
|
||||
})
|
||||
|
||||
// see ModelViewSelect.svelte for the format of context.model
|
||||
// ... this allows us to bind to Model scheams, or View schemas
|
||||
const model = models.find(m => m._id === context.model.modelId)
|
||||
const schema = context.model.isModel
|
||||
? model.schema
|
||||
: model.views[context.model.name].schema
|
||||
|
||||
return (
|
||||
Object.keys(schema)
|
||||
.map(newBindable)
|
||||
// add _id and _rev fields - not part of schema, but always valid
|
||||
.concat([newBindable("_id"), newBindable("_rev")])
|
||||
)
|
||||
}
|
||||
|
||||
const getParentPath = (walkResult, context) => {
|
||||
|
@ -135,7 +151,7 @@ const walk = ({ instance, targetId, components, models, result }) => {
|
|||
if (contextualInstance) {
|
||||
// add to currentContexts (ancestory of context)
|
||||
// before walking children
|
||||
const model = models.find(m => m._id === instance[component.context])
|
||||
const model = instance[component.context]
|
||||
result.currentContexts.push({ instance, model })
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
export const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g
|
||||
|
||||
export function readableToRuntimeBinding(bindableProperties, textWithBindings) {
|
||||
// Find all instances of mustasche
|
||||
const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE)
|
||||
|
||||
let result = textWithBindings
|
||||
// Replace readableBindings with runtimeBindings
|
||||
boundValues &&
|
||||
boundValues.forEach(boundValue => {
|
||||
const binding = bindableProperties.find(({ readableBinding }) => {
|
||||
return boundValue === `{{ ${readableBinding} }}`
|
||||
})
|
||||
if (binding) {
|
||||
result = textWithBindings.replace(
|
||||
boundValue,
|
||||
`{{ ${binding.runtimeBinding} }}`
|
||||
)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function runtimeToReadableBinding(bindableProperties, textWithBindings) {
|
||||
let temp = textWithBindings
|
||||
const boundValues =
|
||||
(typeof textWithBindings === "string" &&
|
||||
textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE)) ||
|
||||
[]
|
||||
|
||||
// Replace runtimeBindings with readableBindings:
|
||||
boundValues.forEach(v => {
|
||||
const binding = bindableProperties.find(({ runtimeBinding }) => {
|
||||
return v === `{{ ${runtimeBinding} }}`
|
||||
})
|
||||
if (binding) {
|
||||
temp = temp.replace(v, `{{ ${binding.readableBinding} }}`)
|
||||
}
|
||||
})
|
||||
|
||||
return temp
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10
|
||||
10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12
|
||||
13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z" />
|
||||
</svg>
|
After Width: | Height: | Size: 412 B |
|
@ -32,3 +32,4 @@ export { default as TwitterIcon } from "./Twitter.svelte"
|
|||
export { default as InfoIcon } from "./Info.svelte"
|
||||
export { default as CloseIcon } from "./Close.svelte"
|
||||
export { default as MoreIcon } from "./More.svelte"
|
||||
export { default as CloseCircleIcon } from "./CloseCircle.svelte"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import feather from "feather-icons"
|
||||
const getIcon = (icon, size) =>
|
||||
feather.icons[icon].toSvg({ height: size || "16", width: size || "16" })
|
||||
export default getIcon
|
|
@ -9,7 +9,6 @@
|
|||
CircleIndicator,
|
||||
EventsIcon,
|
||||
} from "components/common/Icons/"
|
||||
import EventsEditor from "./EventsEditor"
|
||||
import panelStructure from "./temporaryPanelStructure.js"
|
||||
import CategoryTab from "./CategoryTab.svelte"
|
||||
import DesignView from "./DesignView.svelte"
|
||||
|
@ -21,7 +20,6 @@
|
|||
let categories = [
|
||||
{ value: "settings", name: "Settings" },
|
||||
{ value: "design", name: "Design" },
|
||||
{ value: "events", name: "Events" },
|
||||
]
|
||||
let selectedCategory = categories[0]
|
||||
|
||||
|
@ -109,8 +107,6 @@
|
|||
displayNameField={displayName}
|
||||
onChange={onPropChanged}
|
||||
screenOrPageInstance={$store.currentView !== 'component' && $store.currentPreviewItem} />
|
||||
{:else if selectedCategory.value === 'events'}
|
||||
<EventsEditor component={componentInstance} />
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,168 +1,220 @@
|
|||
<script>
|
||||
import { store } from "builderStore"
|
||||
import { Button, Select } from "@budibase/bbui"
|
||||
import HandlerSelector from "./HandlerSelector.svelte"
|
||||
import ActionButton from "../../common/ActionButton.svelte"
|
||||
import getIcon from "../../common/icon"
|
||||
import { CloseIcon } from "components/common/Icons/"
|
||||
|
||||
import { TextButton, Button, Heading, DropdownMenu } from "@budibase/bbui"
|
||||
import { AddIcon, ArrowDownIcon } from "components/common/Icons/"
|
||||
import { EVENT_TYPE_MEMBER_NAME } from "../../common/eventHandlers"
|
||||
import actionTypes from "./actions"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let event
|
||||
export let eventOptions = []
|
||||
export let onClose
|
||||
|
||||
let eventType = ""
|
||||
let addActionButton
|
||||
let addActionDropdown
|
||||
let selectedAction
|
||||
|
||||
let draftEventHandler = { parameters: [] }
|
||||
|
||||
$: eventData = event || { handlers: [] }
|
||||
$: if (!eventOptions.includes(eventType) && eventOptions.length > 0)
|
||||
eventType = eventOptions[0].name
|
||||
$: actions = event || []
|
||||
$: selectedActionComponent =
|
||||
selectedAction &&
|
||||
actionTypes.find(t => t.name === selectedAction[EVENT_TYPE_MEMBER_NAME])
|
||||
.component
|
||||
|
||||
const closeModal = () => {
|
||||
onClose()
|
||||
dispatch("close")
|
||||
draftEventHandler = { parameters: [] }
|
||||
eventData = { handlers: [] }
|
||||
actions = []
|
||||
}
|
||||
|
||||
const updateEventHandler = (updatedHandler, index) => {
|
||||
eventData.handlers[index] = updatedHandler
|
||||
actions[index] = updatedHandler
|
||||
}
|
||||
|
||||
const updateDraftEventHandler = updatedHandler => {
|
||||
draftEventHandler = updatedHandler
|
||||
const deleteAction = index => {
|
||||
actions.splice(index, 1)
|
||||
actions = actions
|
||||
}
|
||||
|
||||
const deleteEventHandler = index => {
|
||||
eventData.handlers.splice(index, 1)
|
||||
eventData = eventData
|
||||
}
|
||||
|
||||
const createNewEventHandler = handler => {
|
||||
const newHandler = handler || {
|
||||
const addAction = actionType => () => {
|
||||
const newAction = {
|
||||
parameters: {},
|
||||
[EVENT_TYPE_MEMBER_NAME]: "",
|
||||
[EVENT_TYPE_MEMBER_NAME]: actionType.name,
|
||||
}
|
||||
eventData.handlers.push(newHandler)
|
||||
eventData = eventData
|
||||
actions.push(newAction)
|
||||
selectedAction = newAction
|
||||
actions = actions
|
||||
addActionDropdown.hide()
|
||||
}
|
||||
|
||||
const deleteEvent = () => {
|
||||
store.setComponentProp(eventType, [])
|
||||
closeModal()
|
||||
const selectAction = action => () => {
|
||||
selectedAction = action
|
||||
}
|
||||
|
||||
const saveEventData = () => {
|
||||
store.setComponentProp(eventType, eventData.handlers)
|
||||
dispatch("change", actions)
|
||||
closeModal()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="body">
|
||||
<div class="heading">
|
||||
<h3>
|
||||
{eventData.name ? `${eventData.name} Event` : 'Create a New Component Event'}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="event-options">
|
||||
<div class="section">
|
||||
<h4>Event Type</h4>
|
||||
<Select bind:value={eventType}>
|
||||
{#each eventOptions as option}
|
||||
<option value={option.name}>{option.name}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="root">
|
||||
|
||||
<div class="section">
|
||||
<h4>Event Action(s)</h4>
|
||||
<HandlerSelector
|
||||
newHandler
|
||||
onChanged={updateDraftEventHandler}
|
||||
onCreate={() => {
|
||||
createNewEventHandler(draftEventHandler)
|
||||
draftEventHandler = { parameters: [] }
|
||||
}}
|
||||
handler={draftEventHandler} />
|
||||
<div class="header">
|
||||
<Heading small dark>Actions</Heading>
|
||||
<div bind:this={addActionButton}>
|
||||
<TextButton text small blue on:click={addActionDropdown.show}>
|
||||
Add Action
|
||||
<div style="height: 20px; width: 20px;">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</TextButton>
|
||||
</div>
|
||||
{#if eventData}
|
||||
{#each eventData.handlers as handler, index}
|
||||
<HandlerSelector
|
||||
{index}
|
||||
onChanged={updateEventHandler}
|
||||
onRemoved={() => deleteEventHandler(index)}
|
||||
{handler} />
|
||||
<DropdownMenu
|
||||
bind:this={addActionDropdown}
|
||||
anchor={addActionButton}
|
||||
align="right">
|
||||
<div class="available-actions-container">
|
||||
{#each actionTypes as actionType}
|
||||
<div class="available-action" on:click={addAction(actionType)}>
|
||||
<span>{actionType.name}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div class="actions-container">
|
||||
{#if actions && actions.length > 0}
|
||||
{#each actions as action, index}
|
||||
<div class="action-container">
|
||||
<div class="action-header" on:click={selectAction(action)}>
|
||||
<p
|
||||
class="bb-body bb-body--small bb-body--color-dark"
|
||||
style="margin: var(--spacing-s) 0;">
|
||||
{index + 1}. {action[EVENT_TYPE_MEMBER_NAME]}
|
||||
</p>
|
||||
<div class="row-expander" class:rotate={action !== selectedAction}>
|
||||
<ArrowDownIcon />
|
||||
</div>
|
||||
</div>
|
||||
{#if action === selectedAction}
|
||||
<div class="selected-action-container">
|
||||
<svelte:component
|
||||
this={selectedActionComponent}
|
||||
parameters={selectedAction.parameters} />
|
||||
<div class="delete-action-button">
|
||||
<TextButton text medium on:click={() => deleteAction(index)}>
|
||||
Delete
|
||||
</TextButton>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="footer">
|
||||
{#if eventData.name}
|
||||
<Button
|
||||
outline
|
||||
on:click={deleteEvent}
|
||||
disabled={eventData.handlers.length === 0}>
|
||||
Delete
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="save">
|
||||
<Button
|
||||
primary
|
||||
on:click={saveEventData}
|
||||
disabled={eventData.handlers.length === 0}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="close-button" on:click={closeModal}>
|
||||
<CloseIcon />
|
||||
<a href="https://docs.budibase.com">Learn more about Actions</a>
|
||||
<Button secondary on:click={closeModal}>Cancel</Button>
|
||||
<Button primary on:click={saveEventData}>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
.heading {
|
||||
margin-bottom: 20px;
|
||||
.root {
|
||||
max-height: 50vh;
|
||||
width: 700px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-xl);
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.action-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-header > p {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.row-expander {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.available-action {
|
||||
padding: var(--spacing-s);
|
||||
font-size: var(--font-size-m);
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
.close-button :global(svg) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 10px;
|
||||
.available-action:hover {
|
||||
background: var(--grey-2);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
.actions-container {
|
||||
flex: 1;
|
||||
min-height: 0px;
|
||||
padding-bottom: var(--spacing-s);
|
||||
padding-top: 0;
|
||||
border: var(--border-light);
|
||||
border-width: 0 0 1px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.body {
|
||||
padding: 40px;
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
|
||||
.action-container {
|
||||
border: var(--border-light);
|
||||
border-width: 1px 0 0 0;
|
||||
padding-left: var(--spacing-xl);
|
||||
padding-right: var(--spacing-xl);
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.footer {
|
||||
|
||||
.selected-action-container {
|
||||
padding-bottom: var(--spacing-s);
|
||||
padding-top: var(--spacing-s);
|
||||
}
|
||||
|
||||
.delete-action-button {
|
||||
padding-top: var(--spacing-l);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 30px 40px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 50px;
|
||||
background-color: var(--grey-1);
|
||||
flex-direction: row;
|
||||
}
|
||||
.save {
|
||||
margin-left: 20px;
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-s);
|
||||
padding: var(--spacing-xl);
|
||||
padding-top: var(--spacing-m);
|
||||
}
|
||||
|
||||
.footer > a {
|
||||
flex: 1;
|
||||
color: var(--grey-5);
|
||||
font-size: var(--font-size-s);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer > a:hover {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.rotate :global(svg) {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
<script>
|
||||
import { Button, DropdownMenu } from "@budibase/bbui"
|
||||
import { Button, Modal } from "@budibase/bbui"
|
||||
import EventEditorModal from "./EventEditorModal.svelte"
|
||||
import { getContext } from "svelte"
|
||||
import { createEventDispatcher, onMount } from "svelte"
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let value
|
||||
export let name
|
||||
|
||||
let button
|
||||
let dropdown
|
||||
let eventsModal
|
||||
</script>
|
||||
|
||||
<div bind:this={button}>
|
||||
<Button secondary small on:click={dropdown.show}>Define Actions</Button>
|
||||
</div>
|
||||
<DropdownMenu bind:this={dropdown} align="right" anchor={button}>
|
||||
<Button secondary small on:click={eventsModal.show}>Define Actions</Button>
|
||||
|
||||
<Modal bind:this={eventsModal} maxWidth="100vw" hideCloseButton>
|
||||
<EventEditorModal
|
||||
event={value}
|
||||
eventType={name}
|
||||
on:change
|
||||
on:close={dropdown.hide} />
|
||||
</DropdownMenu>
|
||||
on:close={eventsModal.hide} />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<script>
|
||||
import { Select, Label } from "@budibase/bbui"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||
import SaveFields from "./SaveFields.svelte"
|
||||
|
||||
export let parameters
|
||||
|
||||
$: bindableProperties = fetchBindableProperties({
|
||||
componentInstanceId: $store.currentComponentInfo._id,
|
||||
components: $store.components,
|
||||
screen: $store.currentPreviewItem,
|
||||
models: $backendUiStore.models,
|
||||
})
|
||||
|
||||
// just wraps binding in {{ ... }}
|
||||
const toBindingExpression = bindingPath => `{{ ${bindingPath} }}`
|
||||
|
||||
const modelFields = modelId => {
|
||||
const model = $backendUiStore.models.find(m => m._id === modelId)
|
||||
|
||||
return Object.keys(model.schema).map(k => ({
|
||||
name: k,
|
||||
type: model.schema[k].type,
|
||||
}))
|
||||
}
|
||||
|
||||
$: schemaFields =
|
||||
parameters && parameters.modelId ? modelFields(parameters.modelId) : []
|
||||
|
||||
const onFieldsChanged = e => {
|
||||
parameters.fields = e.detail
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Label size="m" color="dark">Table</Label>
|
||||
<Select secondary bind:value={parameters.modelId}>
|
||||
<option value="" />
|
||||
{#each $backendUiStore.models as model}
|
||||
<option value={model._id}>{model.name}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
|
||||
{#if parameters.modelId}
|
||||
<SaveFields
|
||||
parameterFields={parameters.fields}
|
||||
{schemaFields}
|
||||
on:fieldschanged={onFieldsChanged} />
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: grid;
|
||||
column-gap: var(--spacing-s);
|
||||
row-gap: var(--spacing-s);
|
||||
grid-template-columns: auto 1fr auto 1fr auto;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.root :global(.relative:nth-child(2)) {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 6;
|
||||
}
|
||||
|
||||
.cannot-use {
|
||||
color: var(--red);
|
||||
font-size: var(--font-size-s);
|
||||
text-align: center;
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import { Select, Label } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
|
||||
export let parameters
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Label size="m" color="dark">Screen</Label>
|
||||
<Select secondary bind:value={parameters.url}>
|
||||
<option value="" />
|
||||
{#each $store.screens as screen}
|
||||
<option value={screen.route}>{screen.props._instanceName}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.root :global(.relative) {
|
||||
flex: 1;
|
||||
margin-left: var(--spacing-l);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,119 @@
|
|||
<script>
|
||||
// accepts an array of field names, and outputs an object of { FieldName: value }
|
||||
import { Select, Label, TextButton, Spacer } from "@budibase/bbui"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||
import { CloseCircleIcon, AddIcon } from "components/common/Icons"
|
||||
import {
|
||||
readableToRuntimeBinding,
|
||||
runtimeToReadableBinding,
|
||||
} from "builderStore/replaceBindings"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let parameterFields
|
||||
export let schemaFields
|
||||
|
||||
const emptyField = () => ({ name: "", value: "" })
|
||||
|
||||
// this statement initialises fields from parameters.fields
|
||||
$: fields =
|
||||
fields ||
|
||||
Object.keys(parameterFields || { "": "" }).map(name => ({
|
||||
name,
|
||||
value:
|
||||
(parameterFields &&
|
||||
runtimeToReadableBinding(
|
||||
bindableProperties,
|
||||
parameterFields[name].value
|
||||
)) ||
|
||||
"",
|
||||
}))
|
||||
|
||||
$: bindableProperties = fetchBindableProperties({
|
||||
componentInstanceId: $store.currentComponentInfo._id,
|
||||
components: $store.components,
|
||||
screen: $store.currentPreviewItem,
|
||||
models: $backendUiStore.models,
|
||||
})
|
||||
|
||||
const addField = () => {
|
||||
const newFields = fields.filter(f => f.name)
|
||||
newFields.push(emptyField())
|
||||
fields = newFields
|
||||
rebuildParameters()
|
||||
}
|
||||
|
||||
const removeField = field => () => {
|
||||
fields = fields.filter(f => f !== field)
|
||||
rebuildParameters()
|
||||
}
|
||||
|
||||
const rebuildParameters = () => {
|
||||
// rebuilds paramters.fields every time a field name or value is added
|
||||
// as UI below is bound to "fields" array, but we need to output a { key: value }
|
||||
const newParameterFields = {}
|
||||
for (let field of fields) {
|
||||
if (field.name) {
|
||||
// value and type is needed by the client, so it can parse
|
||||
// a string into a correct type
|
||||
newParameterFields[field.name] = {
|
||||
type: schemaFields.find(f => f.name === field.name).type,
|
||||
value: readableToRuntimeBinding(bindableProperties, field.value),
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatch("fieldschanged", newParameterFields)
|
||||
}
|
||||
|
||||
// just wraps binding in {{ ... }}
|
||||
const toBindingExpression = bindingPath => `{{ ${bindingPath} }}`
|
||||
</script>
|
||||
|
||||
{#if fields}
|
||||
{#each fields as field}
|
||||
<Label size="m" color="dark">Field</Label>
|
||||
<Select secondary bind:value={field.name} on:blur={rebuildParameters}>
|
||||
<option value="" />
|
||||
{#each schemaFields as schemaField}
|
||||
<option value={schemaField.name}>{schemaField.name}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
<Label size="m" color="dark">Value</Label>
|
||||
<Select
|
||||
editable
|
||||
secondary
|
||||
bind:value={field.value}
|
||||
on:blur={rebuildParameters}>
|
||||
<option value="" />
|
||||
{#each bindableProperties as bindableProp}
|
||||
<option value={toBindingExpression(bindableProp.readableBinding)}>
|
||||
{bindableProp.readableBinding}
|
||||
</option>
|
||||
{/each}
|
||||
</Select>
|
||||
<div class="remove-field-container">
|
||||
<TextButton text small on:click={removeField(field)}>
|
||||
<CloseCircleIcon />
|
||||
</TextButton>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<div>
|
||||
<Spacer small />
|
||||
|
||||
<TextButton text small blue on:click={addField}>
|
||||
Add Field
|
||||
<div style="height: 20px; width: 20px;">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</TextButton>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.remove-field-container :global(button) {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,134 @@
|
|||
<script>
|
||||
import { Select, Label } from "@budibase/bbui"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||
import SaveFields from "./SaveFields.svelte"
|
||||
import {
|
||||
readableToRuntimeBinding,
|
||||
runtimeToReadableBinding,
|
||||
} from "builderStore/replaceBindings"
|
||||
|
||||
export let parameters
|
||||
|
||||
$: bindableProperties = fetchBindableProperties({
|
||||
componentInstanceId: $store.currentComponentInfo._id,
|
||||
components: $store.components,
|
||||
screen: $store.currentPreviewItem,
|
||||
models: $backendUiStore.models,
|
||||
})
|
||||
|
||||
let idFields
|
||||
let recordId
|
||||
$: {
|
||||
idFields = bindableProperties.filter(
|
||||
bindable =>
|
||||
bindable.type === "context" && bindable.runtimeBinding.endsWith("._id")
|
||||
)
|
||||
// ensure recordId is always defaulted - there is usually only one option
|
||||
if (idFields.length > 0 && !parameters._id) {
|
||||
recordId = idFields[0].runtimeBinding
|
||||
parameters = parameters
|
||||
} else if (!recordId && parameters._id) {
|
||||
recordId = parameters._id
|
||||
.replace("{{", "")
|
||||
.replace("}}", "")
|
||||
.trim()
|
||||
}
|
||||
}
|
||||
|
||||
$: parameters._id = `{{ ${recordId} }}`
|
||||
|
||||
// just wraps binding in {{ ... }}
|
||||
const toBindingExpression = bindingPath => `{{ ${bindingPath} }}`
|
||||
|
||||
// finds the selected idBinding, then reads the table/view
|
||||
// from the component instance that it belongs to.
|
||||
// then returns the field names for that schema
|
||||
const schemaFromIdBinding = recordId => {
|
||||
if (!recordId) return []
|
||||
|
||||
const idBinding = bindableProperties.find(
|
||||
prop => prop.runtimeBinding === recordId
|
||||
)
|
||||
if (!idBinding) return []
|
||||
|
||||
const { instance } = idBinding
|
||||
|
||||
const component = $store.components[instance._component]
|
||||
|
||||
// component.context is the name of the prop that holds the modelId
|
||||
const modelInfo = instance[component.context]
|
||||
|
||||
if (!modelInfo) return []
|
||||
|
||||
const model = $backendUiStore.models.find(m => m._id === modelInfo.modelId)
|
||||
parameters.modelId = modelInfo.modelId
|
||||
return Object.keys(model.schema).map(k => ({
|
||||
name: k,
|
||||
type: model.schema[k].type,
|
||||
}))
|
||||
}
|
||||
|
||||
let schemaFields
|
||||
$: {
|
||||
if (parameters && recordId) {
|
||||
schemaFields = schemaFromIdBinding(recordId)
|
||||
} else {
|
||||
schemaFields = []
|
||||
}
|
||||
}
|
||||
|
||||
const onFieldsChanged = e => {
|
||||
parameters.fields = e.detail
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
{#if idFields.length === 0}
|
||||
<div class="cannot-use">
|
||||
Update record can only be used within a component that provides data, such
|
||||
as a List
|
||||
</div>
|
||||
{:else}
|
||||
<Label size="m" color="dark">Record Id</Label>
|
||||
<Select secondary bind:value={recordId}>
|
||||
<option value="" />
|
||||
{#each idFields as idField}
|
||||
<option value={idField.runtimeBinding}>
|
||||
{idField.readableBinding}
|
||||
</option>
|
||||
{/each}
|
||||
</Select>
|
||||
{/if}
|
||||
|
||||
{#if recordId}
|
||||
<SaveFields
|
||||
parameterFields={parameters.fields}
|
||||
{schemaFields}
|
||||
on:fieldschanged={onFieldsChanged} />
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
display: grid;
|
||||
column-gap: var(--spacing-s);
|
||||
row-gap: var(--spacing-s);
|
||||
grid-template-columns: auto 1fr auto 1fr auto;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.root :global(.relative:nth-child(2)) {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 6;
|
||||
}
|
||||
|
||||
.cannot-use {
|
||||
color: var(--red);
|
||||
font-size: var(--font-size-s);
|
||||
text-align: center;
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,23 @@
|
|||
import NavigateTo from "./NavigateTo.svelte"
|
||||
import UpdateRecord from "./UpdateRecord.svelte"
|
||||
import CreateRecord from "./CreateRecord.svelte"
|
||||
|
||||
// defines what actions are available, when adding a new one
|
||||
// the component is the setup panel for the action
|
||||
// NOTE that the "name" is used by the client library,
|
||||
// so if you want to change it, you must change it client lib too
|
||||
|
||||
export default [
|
||||
{
|
||||
name: "Create Record",
|
||||
component: CreateRecord,
|
||||
},
|
||||
{
|
||||
name: "Navigate To",
|
||||
component: NavigateTo,
|
||||
},
|
||||
{
|
||||
name: "Update Record",
|
||||
component: UpdateRecord,
|
||||
},
|
||||
]
|
|
@ -1 +0,0 @@
|
|||
export { default } from "./EventsEditor.svelte"
|
|
@ -3,6 +3,11 @@
|
|||
import Input from "./PropertyPanelControls/Input.svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||
import {
|
||||
readableToRuntimeBinding,
|
||||
runtimeToReadableBinding,
|
||||
CAPTURE_VAR_INSIDE_MUSTACHE,
|
||||
} from "builderStore/replaceBindings"
|
||||
import { DropdownMenu } from "@budibase/bbui"
|
||||
import BindingDropdown from "components/userInterface/BindingDropdown.svelte"
|
||||
import { onMount, getContext } from "svelte"
|
||||
|
@ -36,25 +41,12 @@
|
|||
})
|
||||
}
|
||||
|
||||
const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g
|
||||
function replaceBindings(textWithBindings) {
|
||||
getBindableProperties()
|
||||
// Find all instances of mustasche
|
||||
const boundValues = textWithBindings.match(CAPTURE_VAR_INSIDE_MUSTACHE)
|
||||
|
||||
// Replace with names:
|
||||
boundValues &&
|
||||
boundValues.forEach(boundValue => {
|
||||
const binding = bindableProperties.find(({ readableBinding }) => {
|
||||
return boundValue === `{{ ${readableBinding} }}`
|
||||
})
|
||||
if (binding) {
|
||||
textWithBindings = textWithBindings.replace(
|
||||
boundValue,
|
||||
`{{ ${binding.runtimeBinding} }}`
|
||||
)
|
||||
}
|
||||
})
|
||||
textWithBindings = readableToRuntimeBinding(
|
||||
bindableProperties,
|
||||
textWithBindings
|
||||
)
|
||||
onChange(key, textWithBindings)
|
||||
}
|
||||
|
||||
|
@ -76,21 +68,9 @@
|
|||
|
||||
const safeValue = () => {
|
||||
getBindableProperties()
|
||||
let temp = value
|
||||
const boundValues =
|
||||
(typeof value === "string" && value.match(CAPTURE_VAR_INSIDE_MUSTACHE)) ||
|
||||
[]
|
||||
|
||||
// Replace with names:
|
||||
boundValues.forEach(v => {
|
||||
const binding = bindableProperties.find(({ runtimeBinding }) => {
|
||||
return v === `{{ ${runtimeBinding} }}`
|
||||
})
|
||||
if (binding) {
|
||||
temp = temp.replace(v, `{{ ${binding.readableBinding} }}`)
|
||||
}
|
||||
})
|
||||
// console.log(temp)
|
||||
let temp = runtimeToReadableBinding(bindableProperties, value)
|
||||
|
||||
return value === undefined && props.defaultValue !== undefined
|
||||
? props.defaultValue
|
||||
: temp
|
||||
|
|
|
@ -28,7 +28,8 @@ describe("fetch bindable properties", () => {
|
|||
...testData()
|
||||
})
|
||||
const contextBindings = result.filter(r => r.instance._id === "list-id" && r.type==="context")
|
||||
expect(contextBindings.length).toBe(2)
|
||||
// 2 fields + _id + _rev
|
||||
expect(contextBindings.length).toBe(4)
|
||||
|
||||
const namebinding = contextBindings.find(b => b.runtimeBinding === "data.name")
|
||||
expect(namebinding).toBeDefined()
|
||||
|
@ -37,6 +38,10 @@ describe("fetch bindable properties", () => {
|
|||
const descriptionbinding = contextBindings.find(b => b.runtimeBinding === "data.description")
|
||||
expect(descriptionbinding).toBeDefined()
|
||||
expect(descriptionbinding.readableBinding).toBe("list-name.Test Model.description")
|
||||
|
||||
const idbinding = contextBindings.find(b => b.runtimeBinding === "data._id")
|
||||
expect(idbinding).toBeDefined()
|
||||
expect(idbinding.readableBinding).toBe("list-name.Test Model._id")
|
||||
})
|
||||
|
||||
it("should return model schema, for grantparent context", () => {
|
||||
|
@ -45,7 +50,8 @@ describe("fetch bindable properties", () => {
|
|||
...testData()
|
||||
})
|
||||
const contextBindings = result.filter(r => r.type==="context")
|
||||
expect(contextBindings.length).toBe(4)
|
||||
// 2 fields + _id + _rev ... x 2 models
|
||||
expect(contextBindings.length).toBe(8)
|
||||
|
||||
const namebinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.name")
|
||||
expect(namebinding_parent).toBeDefined()
|
||||
|
@ -120,7 +126,7 @@ const testData = () => {
|
|||
_id: "list-id",
|
||||
_component: "@budibase/standard-components/list",
|
||||
_instanceName: "list-name",
|
||||
model: "test-model-id",
|
||||
model: { isModel: true, modelId: "test-model-id", label: "Test Model", name: "all_test-model-id" },
|
||||
_children: [
|
||||
{
|
||||
_id: "list-item-heading-id",
|
||||
|
@ -138,7 +144,7 @@ const testData = () => {
|
|||
_id: "child-list-id",
|
||||
_component: "@budibase/standard-components/list",
|
||||
_instanceName: "child-list-name",
|
||||
model: "test-model-id",
|
||||
model: { isModel: true, modelId: "test-model-id", label: "Test Model", name: "all_test-model-id"},
|
||||
_children: [
|
||||
{
|
||||
_id: "child-list-item-heading-id",
|
||||
|
|
|
@ -695,6 +695,21 @@
|
|||
sirv-cli "^0.4.6"
|
||||
svelte-flatpickr "^2.4.0"
|
||||
|
||||
"@budibase/client@^0.1.19":
|
||||
version "0.1.19"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.19.tgz#3906781423ab4626118c981657ecf7a4578c547c"
|
||||
integrity sha512-crxnLgebsh6CR0aMleDahY/1vFPbveG6JuSS/EVZeoBmckzK8hwiUQYQhIlf68nZfzWsCE/M9TX7SJxsrKY3bQ==
|
||||
dependencies:
|
||||
"@nx-js/compiler-util" "^2.0.0"
|
||||
bcryptjs "^2.4.3"
|
||||
deep-equal "^2.0.1"
|
||||
lodash "^4.17.15"
|
||||
lunr "^2.3.5"
|
||||
mustache "^4.0.1"
|
||||
regexparam "^1.3.0"
|
||||
shortid "^2.2.8"
|
||||
svelte "^3.9.2"
|
||||
|
||||
"@budibase/colorpicker@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
||||
|
@ -944,6 +959,11 @@
|
|||
"@nodelib/fs.scandir" "2.1.3"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@nx-js/compiler-util@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297"
|
||||
integrity sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ==
|
||||
|
||||
"@polka/url@^0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31"
|
||||
|
@ -1370,6 +1390,11 @@ array-equal@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
||||
|
||||
array-filter@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
|
||||
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
|
||||
|
||||
array-union@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||
|
@ -1424,6 +1449,13 @@ atob@^2.1.2:
|
|||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
|
||||
available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
|
||||
integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
|
||||
dependencies:
|
||||
array-filter "^1.0.0"
|
||||
|
||||
aws-sign2@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||
|
@ -1498,6 +1530,11 @@ bcrypt-pbkdf@^1.0.0:
|
|||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
bcryptjs@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
|
||||
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
||||
|
@ -1795,10 +1832,6 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@^2.2.5:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
|
||||
cli-cursor@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
||||
|
@ -1946,10 +1979,6 @@ core-js-pure@^3.0.0:
|
|||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
|
||||
core-js@^3.1.3:
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||
|
||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
@ -2381,6 +2410,26 @@ decode-uri-component@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
|
||||
deep-equal@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0"
|
||||
integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==
|
||||
dependencies:
|
||||
es-abstract "^1.17.5"
|
||||
es-get-iterator "^1.1.0"
|
||||
is-arguments "^1.0.4"
|
||||
is-date-object "^1.0.2"
|
||||
is-regex "^1.0.5"
|
||||
isarray "^2.0.5"
|
||||
object-is "^1.1.2"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.0"
|
||||
regexp.prototype.flags "^1.3.0"
|
||||
side-channel "^1.0.2"
|
||||
which-boxed-primitive "^1.0.1"
|
||||
which-collection "^1.0.1"
|
||||
which-typed-array "^1.1.2"
|
||||
|
||||
deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
|
@ -2550,6 +2599,54 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
|
|||
string.prototype.trimleft "^2.1.1"
|
||||
string.prototype.trimright "^2.1.1"
|
||||
|
||||
es-abstract@^1.17.4:
|
||||
version "1.17.6"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
|
||||
integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.2.0"
|
||||
is-regex "^1.1.0"
|
||||
object-inspect "^1.7.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.0"
|
||||
string.prototype.trimend "^1.0.1"
|
||||
string.prototype.trimstart "^1.0.1"
|
||||
|
||||
es-abstract@^1.18.0-next.0:
|
||||
version "1.18.0-next.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc"
|
||||
integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.2.0"
|
||||
is-negative-zero "^2.0.0"
|
||||
is-regex "^1.1.1"
|
||||
object-inspect "^1.8.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.0"
|
||||
string.prototype.trimend "^1.0.1"
|
||||
string.prototype.trimstart "^1.0.1"
|
||||
|
||||
es-get-iterator@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
|
||||
integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==
|
||||
dependencies:
|
||||
es-abstract "^1.17.4"
|
||||
has-symbols "^1.0.1"
|
||||
is-arguments "^1.0.4"
|
||||
is-map "^2.0.1"
|
||||
is-set "^2.0.1"
|
||||
is-string "^1.0.5"
|
||||
isarray "^2.0.5"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||
|
@ -2800,13 +2897,6 @@ fd-slicer@~1.1.0:
|
|||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
feather-icons@^4.21.0:
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.28.0.tgz#e1892a401fe12c4559291770ff6e68b0168e760f"
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
core-js "^3.1.3"
|
||||
|
||||
figures@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||
|
@ -2863,9 +2953,10 @@ for-in@^1.0.2:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
|
||||
foreach@~2.0.1:
|
||||
foreach@^2.0.5, foreach@~2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
|
@ -3236,16 +3327,31 @@ is-accessor-descriptor@^1.0.0:
|
|||
dependencies:
|
||||
kind-of "^6.0.0"
|
||||
|
||||
is-arguments@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
|
||||
integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
|
||||
is-bigint@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4"
|
||||
integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-boolean-object@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
|
||||
integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
|
||||
|
||||
is-buffer@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
|
@ -3254,6 +3360,11 @@ is-callable@^1.1.4, is-callable@^1.1.5:
|
|||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
|
||||
|
||||
is-callable@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d"
|
||||
integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==
|
||||
|
||||
is-ci@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
||||
|
@ -3272,7 +3383,7 @@ is-data-descriptor@^1.0.0:
|
|||
dependencies:
|
||||
kind-of "^6.0.0"
|
||||
|
||||
is-date-object@^1.0.1:
|
||||
is-date-object@^1.0.1, is-date-object@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||
|
||||
|
@ -3337,10 +3448,25 @@ is-installed-globally@^0.3.2:
|
|||
global-dirs "^2.0.1"
|
||||
is-path-inside "^3.0.1"
|
||||
|
||||
is-map@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
|
||||
integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
|
||||
is-negative-zero@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
|
||||
integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
|
||||
|
||||
is-number-object@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
|
||||
integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
|
||||
|
||||
is-number@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||
|
@ -3397,6 +3523,18 @@ is-regex@^1.0.5:
|
|||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-regex@^1.1.0, is-regex@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
|
||||
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-set@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
|
||||
integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
|
@ -3405,16 +3543,41 @@ is-stream@^2.0.0:
|
|||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||
|
||||
is-string@^1.0.4, is-string@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
||||
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
||||
|
||||
is-symbol@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-typed-array@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d"
|
||||
integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.0"
|
||||
es-abstract "^1.17.4"
|
||||
foreach "^2.0.5"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
|
||||
is-weakmap@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
||||
integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
|
||||
|
||||
is-weakset@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
|
||||
integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==
|
||||
|
||||
is-windows@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||
|
@ -3435,6 +3598,11 @@ isarray@1.0.0, isarray@~1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
||||
isarray@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
||||
|
||||
isbuffer@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b"
|
||||
|
@ -4248,6 +4416,11 @@ ltgt@^2.1.2:
|
|||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
|
||||
|
||||
lunr@^2.3.5:
|
||||
version "2.3.9"
|
||||
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
|
||||
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
|
||||
|
||||
magic-string@^0.22.5:
|
||||
version "0.22.5"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
|
||||
|
@ -4545,6 +4718,19 @@ object-inspect@^1.7.0:
|
|||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
|
||||
|
||||
object-inspect@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
|
||||
integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
|
||||
|
||||
object-is@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6"
|
||||
integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.5"
|
||||
|
||||
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
|
@ -5055,6 +5241,19 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
|||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexp.prototype.flags@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
|
||||
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.0-next.1"
|
||||
|
||||
regexparam@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f"
|
||||
integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g==
|
||||
|
||||
regexpu-core@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
|
||||
|
@ -5452,12 +5651,21 @@ shellwords@^0.1.1:
|
|||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
|
||||
shortid@^2.2.15:
|
||||
shortid@^2.2.15, shortid@^2.2.8:
|
||||
version "2.2.15"
|
||||
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122"
|
||||
integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==
|
||||
dependencies:
|
||||
nanoid "^2.1.0"
|
||||
|
||||
side-channel@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
|
||||
integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==
|
||||
dependencies:
|
||||
es-abstract "^1.18.0-next.0"
|
||||
object-inspect "^1.8.0"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
|
@ -5684,7 +5892,7 @@ string-width@^4.2.0:
|
|||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
string.prototype.trimend@^1.0.0:
|
||||
string.prototype.trimend@^1.0.0, string.prototype.trimend@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
||||
dependencies:
|
||||
|
@ -5707,7 +5915,7 @@ string.prototype.trimright@^2.1.1:
|
|||
es-abstract "^1.17.5"
|
||||
string.prototype.trimend "^1.0.0"
|
||||
|
||||
string.prototype.trimstart@^1.0.0:
|
||||
string.prototype.trimstart@^1.0.0, string.prototype.trimstart@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
||||
dependencies:
|
||||
|
@ -5822,6 +6030,11 @@ svelte@3.23.x:
|
|||
version "3.23.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.23.0.tgz#bbcd6887cf588c24a975b14467455abfff9acd3f"
|
||||
|
||||
svelte@^3.9.2:
|
||||
version "3.25.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.25.0.tgz#e7f89d11a2223ac02d29f75f82b2a9cca7fd7a54"
|
||||
integrity sha512-CUfq+YTC5Q0tBg7Q9X/QxNutdEYp7ogdbcTZF3mrowOlp3+OF1ZexHwI0Io2VqFSDxht7TlA2lTIQC4RnhR1MQ==
|
||||
|
||||
symbol-observable@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
|
@ -6153,10 +6366,43 @@ whatwg-url@^8.0.0:
|
|||
tr46 "^2.0.2"
|
||||
webidl-conversions "^5.0.0"
|
||||
|
||||
which-boxed-primitive@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
|
||||
integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==
|
||||
dependencies:
|
||||
is-bigint "^1.0.0"
|
||||
is-boolean-object "^1.0.0"
|
||||
is-number-object "^1.0.3"
|
||||
is-string "^1.0.4"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
which-collection@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
|
||||
integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
|
||||
dependencies:
|
||||
is-map "^2.0.1"
|
||||
is-set "^2.0.1"
|
||||
is-weakmap "^2.0.1"
|
||||
is-weakset "^2.0.1"
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
|
||||
which-typed-array@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2"
|
||||
integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.2"
|
||||
es-abstract "^1.17.5"
|
||||
foreach "^2.0.5"
|
||||
function-bind "^1.1.1"
|
||||
has-symbols "^1.0.1"
|
||||
is-typed-array "^1.1.3"
|
||||
|
||||
which@^1.2.9, which@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
|
|
|
@ -53,7 +53,50 @@ const apiOpts = {
|
|||
delete: del,
|
||||
}
|
||||
|
||||
const createRecord = async params =>
|
||||
await post({
|
||||
url: `/api/${params.modelId}/records`,
|
||||
body: makeRecordRequestBody(params),
|
||||
})
|
||||
|
||||
const updateRecord = async params => {
|
||||
const record = makeRecordRequestBody(params)
|
||||
record._id = params._id
|
||||
await patch({
|
||||
url: `/api/${params.modelId}/records/${params._id}`,
|
||||
body: record,
|
||||
})
|
||||
}
|
||||
|
||||
const makeRecordRequestBody = parameters => {
|
||||
const body = {}
|
||||
for (let fieldName in parameters.fields) {
|
||||
const field = parameters.fields[fieldName]
|
||||
|
||||
// ensure fields sent are of the correct type
|
||||
if (field.type === "boolean") {
|
||||
if (field.value === "true") body[fieldName] = true
|
||||
if (field.value === "false") body[fieldName] = false
|
||||
} else if (field.type === "number") {
|
||||
const val = parseFloat(field.value)
|
||||
if (!isNaN(val)) {
|
||||
body[fieldName] = val
|
||||
}
|
||||
} else if (field.type === "datetime") {
|
||||
const date = new Date(field.value)
|
||||
if (!isNaN(date.getTime())) {
|
||||
body[fieldName] = date.toISOString()
|
||||
}
|
||||
} else {
|
||||
body[fieldName] = field.value
|
||||
}
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
export default {
|
||||
authenticate: authenticate(apiOpts),
|
||||
triggerWorkflow: triggerWorkflow(apiOpts),
|
||||
createRecord,
|
||||
updateRecord,
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ export const createApp = ({
|
|||
treeNode,
|
||||
onScreenSlotRendered,
|
||||
setupState: stateManager.setup,
|
||||
getCurrentState: stateManager.getCurrentState,
|
||||
})
|
||||
|
||||
return getInitialiseParams
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import setBindableComponentProp from "./setBindableComponentProp"
|
||||
import { attachChildren } from "../render/attachChildren"
|
||||
import store from "../state/store"
|
||||
|
||||
export const trimSlash = str => str.replace(/^\/+|\/+$/g, "")
|
||||
|
||||
export const bbFactory = ({
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
getCurrentState,
|
||||
runEventActions,
|
||||
}) => {
|
||||
const apiCall = method => (url, body) => {
|
||||
return fetch(url, {
|
||||
|
@ -26,13 +27,6 @@ export const bbFactory = ({
|
|||
delete: apiCall("DELETE"),
|
||||
}
|
||||
|
||||
const safeCallEvent = (event, context) => {
|
||||
const isFunction = obj =>
|
||||
!!(obj && obj.constructor && obj.call && obj.apply)
|
||||
|
||||
if (isFunction(event)) event(context)
|
||||
}
|
||||
|
||||
return (treeNode, setupState) => {
|
||||
const attachParams = {
|
||||
componentLibraries,
|
||||
|
@ -44,12 +38,17 @@ export const bbFactory = ({
|
|||
return {
|
||||
attachChildren: attachChildren(attachParams),
|
||||
props: treeNode.props,
|
||||
call: safeCallEvent,
|
||||
call: async eventName =>
|
||||
eventName &&
|
||||
(await runEventActions(
|
||||
treeNode.props[eventName],
|
||||
store.getState(treeNode.contextStoreKey)
|
||||
)),
|
||||
setBinding: setBindableComponentProp(treeNode),
|
||||
api,
|
||||
parent,
|
||||
// these parameters are populated by screenRouter
|
||||
routeParams: () => getCurrentState()["##routeParams"],
|
||||
routeParams: () => store.getState()["##routeParams"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,42 @@
|
|||
import api from "../api"
|
||||
import renderTemplateString from "./renderTemplateString"
|
||||
|
||||
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
|
||||
|
||||
export const eventHandlers = routeTo => {
|
||||
const handler = (parameters, execute) => ({
|
||||
execute,
|
||||
parameters,
|
||||
})
|
||||
|
||||
return {
|
||||
"Navigate To": handler(["url"], param => routeTo(param && param.url)),
|
||||
"Trigger Workflow": handler(["workflow"], api.triggerWorkflow),
|
||||
const handlers = {
|
||||
"Navigate To": param => routeTo(param && param.url),
|
||||
"Create Record": api.createRecord,
|
||||
"Update Record": api.updateRecord,
|
||||
"Trigger Workflow": api.triggerWorkflow,
|
||||
}
|
||||
|
||||
// when an event is called, this is what gets run
|
||||
const runEventActions = async (actions, state) => {
|
||||
if (!actions) return
|
||||
// calls event handlers sequentially
|
||||
for (let action of actions) {
|
||||
const handler = handlers[action[EVENT_TYPE_MEMBER_NAME]]
|
||||
const parameters = createParameters(action.parameters, state)
|
||||
if (handler) {
|
||||
await handler(parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return runEventActions
|
||||
}
|
||||
|
||||
export const isEventType = prop =>
|
||||
Array.isArray(prop) &&
|
||||
prop.length > 0 &&
|
||||
!prop[0][EVENT_TYPE_MEMBER_NAME] === undefined
|
||||
// this will take a parameters obj, iterate all keys, and do a mustache render
|
||||
// for every string. It will work recursively if it encounnters an {}
|
||||
const createParameters = (parameterTemplateObj, state) => {
|
||||
const parameters = {}
|
||||
for (let key in parameterTemplateObj) {
|
||||
if (typeof parameterTemplateObj[key] === "string") {
|
||||
parameters[key] = renderTemplateString(parameterTemplateObj[key], state)
|
||||
} else if (typeof parameterTemplateObj[key] === "object") {
|
||||
parameters[key] = createParameters(parameterTemplateObj[key], state)
|
||||
}
|
||||
}
|
||||
return parameters
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import {
|
||||
isEventType,
|
||||
eventHandlers,
|
||||
EVENT_TYPE_MEMBER_NAME,
|
||||
} from "./eventHandlers"
|
||||
import { eventHandlers } from "./eventHandlers"
|
||||
import { bbFactory } from "./bbComponentApi"
|
||||
import renderTemplateString from "./renderTemplateString"
|
||||
import appStore from "./store"
|
||||
|
@ -25,33 +21,23 @@ export const createStateManager = ({
|
|||
onScreenSlotRendered,
|
||||
routeTo,
|
||||
}) => {
|
||||
let handlerTypes = eventHandlers(routeTo)
|
||||
|
||||
// creating a reference to the current state
|
||||
// this avoids doing store.get() ... which is expensive on
|
||||
// hot paths, according to the svelte docs.
|
||||
// the state object reference never changes (although it's internals do)
|
||||
// so this should work fine for us
|
||||
let currentState
|
||||
appStore.subscribe(s => (currentState = s))
|
||||
const getCurrentState = () => currentState
|
||||
let runEventActions = eventHandlers(routeTo)
|
||||
|
||||
const bb = bbFactory({
|
||||
getCurrentState,
|
||||
componentLibraries,
|
||||
onScreenSlotRendered,
|
||||
runEventActions,
|
||||
})
|
||||
|
||||
const setup = _setup({ handlerTypes, getCurrentState, bb })
|
||||
const setup = _setup(bb)
|
||||
|
||||
return {
|
||||
setup,
|
||||
destroy: () => {},
|
||||
getCurrentState,
|
||||
}
|
||||
}
|
||||
|
||||
const _setup = ({ handlerTypes, getCurrentState, bb }) => node => {
|
||||
const _setup = bb => node => {
|
||||
const props = node.props
|
||||
const initialProps = { ...props }
|
||||
|
||||
|
@ -70,53 +56,10 @@ const _setup = ({ handlerTypes, getCurrentState, bb }) => node => {
|
|||
node.stateBound = true
|
||||
}
|
||||
}
|
||||
|
||||
if (isEventType(propValue)) {
|
||||
const state = appStore.getState(node.contextStoreKey)
|
||||
const handlersInfos = []
|
||||
for (let event of propValue) {
|
||||
const handlerInfo = {
|
||||
handlerType: event[EVENT_TYPE_MEMBER_NAME],
|
||||
parameters: event.parameters,
|
||||
}
|
||||
|
||||
const resolvedParams = {}
|
||||
for (let paramName in handlerInfo.parameters) {
|
||||
const paramValue = handlerInfo.parameters[paramName]
|
||||
resolvedParams[paramName] = () =>
|
||||
renderTemplateString(paramValue, state)
|
||||
}
|
||||
|
||||
handlerInfo.parameters = resolvedParams
|
||||
handlersInfos.push(handlerInfo)
|
||||
}
|
||||
|
||||
if (handlersInfos.length === 0) {
|
||||
initialProps[propName] = doNothing
|
||||
} else {
|
||||
initialProps[propName] = async context => {
|
||||
for (let handlerInfo of handlersInfos) {
|
||||
const handler = makeHandler(handlerTypes, handlerInfo)
|
||||
await handler(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setup = _setup({ handlerTypes, getCurrentState, bb })
|
||||
const setup = _setup(bb)
|
||||
initialProps._bb = bb(node, setup)
|
||||
|
||||
return initialProps
|
||||
}
|
||||
|
||||
const makeHandler = (handlerTypes, handlerInfo) => {
|
||||
const handlerType = handlerTypes[handlerInfo.handlerType]
|
||||
return async context => {
|
||||
const parameters = {}
|
||||
for (let paramName in handlerInfo.parameters) {
|
||||
parameters[paramName] = handlerInfo.parameters[paramName](context)
|
||||
}
|
||||
await handlerType.execute(parameters)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,8 +181,7 @@ const maketestlib = window => ({
|
|||
currentProps = Object.assign(currentProps, props)
|
||||
if (currentProps.onClick) {
|
||||
node.addEventListener("click", () => {
|
||||
const testText = currentProps.testText || "hello"
|
||||
currentProps._bb.call(props.onClick, { testText })
|
||||
currentProps._bb.call("onClick")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,40 @@ validateJs.extend(validateJs.validators.datetime, {
|
|||
},
|
||||
})
|
||||
|
||||
exports.patch = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const record = await db.get(ctx.params.id)
|
||||
const model = await db.get(record.modelId)
|
||||
const patchfields = ctx.request.body
|
||||
|
||||
for (let key in patchfields) {
|
||||
if (!model.schema[key]) continue
|
||||
record[key] = patchfields[key]
|
||||
}
|
||||
|
||||
const validateResult = await validate({
|
||||
record,
|
||||
model,
|
||||
})
|
||||
|
||||
if (!validateResult.valid) {
|
||||
ctx.status = 400
|
||||
ctx.body = {
|
||||
status: 400,
|
||||
errors: validateResult.errors,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const response = await db.put(record)
|
||||
record._rev = response.rev
|
||||
record.type = "record"
|
||||
ctx.body = record
|
||||
ctx.status = 200
|
||||
ctx.message = `${model.name} updated successfully.`
|
||||
return
|
||||
}
|
||||
|
||||
exports.save = async function(ctx) {
|
||||
const db = new CouchDB(ctx.user.instanceId)
|
||||
const record = ctx.request.body
|
||||
|
|
|
@ -22,6 +22,11 @@ router
|
|||
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
|
||||
recordController.save
|
||||
)
|
||||
.patch(
|
||||
"/api/:modelId/records/:id",
|
||||
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
|
||||
recordController.patch
|
||||
)
|
||||
.post(
|
||||
"/api/:modelId/records/validate",
|
||||
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
|
||||
|
|
|
@ -51,6 +51,12 @@ exports.createModel = async (request, appId, instanceId, model) => {
|
|||
type: "string",
|
||||
},
|
||||
},
|
||||
description: {
|
||||
type: "text",
|
||||
constraints: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -30,20 +30,30 @@ describe("/records", () => {
|
|||
model = await createModel(request, app._id, instance._id)
|
||||
record = {
|
||||
name: "Test Contact",
|
||||
description: "original description",
|
||||
status: "new",
|
||||
modelId: model._id
|
||||
}
|
||||
})
|
||||
|
||||
const createRecord = async r =>
|
||||
await request
|
||||
.post(`/api/${model._id}/records`)
|
||||
.send(r || record)
|
||||
.set(defaultHeaders(app._id, instance._id))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
|
||||
const loadRecord = async id =>
|
||||
await request
|
||||
.get(`/api/${model._id}/records/${id}`)
|
||||
.set(defaultHeaders(app._id, instance._id))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
|
||||
|
||||
describe("save, load, update, delete", () => {
|
||||
|
||||
const createRecord = async r =>
|
||||
await request
|
||||
.post(`/api/${model._id}/records`)
|
||||
.send(r || record)
|
||||
.set(defaultHeaders(app._id, instance._id))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
|
||||
it("returns a success message when the record is created", async () => {
|
||||
const res = await createRecord()
|
||||
|
@ -144,6 +154,35 @@ describe("/records", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("patch", () => {
|
||||
it("should update only the fields that are supplied", async () => {
|
||||
const rec = await createRecord()
|
||||
const existing = rec.body
|
||||
|
||||
const res = await request
|
||||
.patch(`/api/${model._id}/records/${existing._id}`)
|
||||
.send({
|
||||
_id: existing._id,
|
||||
_rev: existing._rev,
|
||||
modelId: model._id,
|
||||
name: "Updated Name",
|
||||
})
|
||||
.set(defaultHeaders(app._id, instance._id))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.res.statusMessage).toEqual(`${model.name} updated successfully.`)
|
||||
expect(res.body.name).toEqual("Updated Name")
|
||||
expect(res.body.description).toEqual(existing.description)
|
||||
|
||||
const savedRecord = await loadRecord(res.body._id)
|
||||
|
||||
expect(savedRecord.body.description).toEqual(existing.description)
|
||||
expect(savedRecord.body.name).toEqual("Updated Name")
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe("validate", () => {
|
||||
it("should return no errors on valid record", async () => {
|
||||
const result = await request
|
||||
|
|
|
@ -72,8 +72,14 @@ describe("/views", () => {
|
|||
type: "text",
|
||||
constraints: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
description: {
|
||||
type: "text",
|
||||
constraints: {
|
||||
type: "string"
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -252,7 +252,7 @@
|
|||
},
|
||||
"list": {
|
||||
"description": "A configurable data list that attaches to your backend models.",
|
||||
"context": "model",
|
||||
"context": "datasource",
|
||||
"children": true,
|
||||
"data": true,
|
||||
"props": {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,7 +2,6 @@
|
|||
export let className = "default"
|
||||
export let disabled = false
|
||||
export let text
|
||||
export let onClick
|
||||
|
||||
export let _bb
|
||||
let theButton
|
||||
|
@ -11,7 +10,7 @@
|
|||
theButton && _bb.attachChildren(theButton)
|
||||
|
||||
const clickHandler = () => {
|
||||
_bb.call(onClick)
|
||||
_bb.call("onClick")
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
if (containerElement) {
|
||||
_bb.attachChildren(containerElement)
|
||||
if (!hasLoaded) {
|
||||
_bb.call(onLoad)
|
||||
_bb.call("onLoad")
|
||||
hasLoaded = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
if (itemContainer) {
|
||||
_bb.attachChildren(itemContainer)
|
||||
if (!hasLoaded) {
|
||||
_bb.call(onLoad)
|
||||
_bb.call("onLoad")
|
||||
hasLoaded = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
export let _bb
|
||||
|
||||
const rowClickHandler = row => () => {
|
||||
_bb.call(onRowClick, row)
|
||||
// call currently only accepts one argument, so passing row does nothing
|
||||
// however, we do not expose this event anyway. I am leaving this
|
||||
// in for the future, as can and probably should hande this
|
||||
_bb.call("onRowClick", row)
|
||||
}
|
||||
|
||||
const cellValue = (colIndex, row) => {
|
||||
|
|
Loading…
Reference in New Issue