Merging with master and fixing conflicts
This commit is contained in:
commit
89e3ddcab8
|
@ -0,0 +1,18 @@
|
||||||
|
xcontext('Create a Binding', () => {
|
||||||
|
before(() => {
|
||||||
|
cy.visit('localhost:4001/_builder')
|
||||||
|
cy.createApp('Binding App', 'Binding App Description')
|
||||||
|
cy.navigateToFrontend()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('add an input binding', () => {
|
||||||
|
cy.get(".nav-items-container").contains('Home').click()
|
||||||
|
cy.contains("Add").click()
|
||||||
|
cy.get("[data-cy=Input]").click()
|
||||||
|
cy.get("[data-cy=Textfield]").click()
|
||||||
|
cy.contains("Heading").click()
|
||||||
|
cy.get("[data-cy=text-binding-button]").click()
|
||||||
|
cy.get("[data-cy=binding-dropdown-modal]").contains('Input 1').click()
|
||||||
|
cy.get("[data-cy=binding-dropdown-modal] textarea").should('have.value', 'Home{{ Input 1 }}')
|
||||||
|
})
|
||||||
|
})
|
|
@ -9,7 +9,7 @@ context('Create a User', () => {
|
||||||
|
|
||||||
// https://on.cypress.io/interacting-with-elements
|
// https://on.cypress.io/interacting-with-elements
|
||||||
it('should create a user', () => {
|
it('should create a user', () => {
|
||||||
cy.createUser('bbuser', 'test', 'ADMIN')
|
cy.createUser('bbuser', 'test', 'POWER_USER')
|
||||||
|
|
||||||
// Check to make sure user was created!
|
// Check to make sure user was created!
|
||||||
cy.get("input[disabled]").should('have.value', 'bbuser')
|
cy.get("input[disabled]").should('have.value', 'bbuser')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
xcontext('Create a workflow', () => {
|
context('Create a workflow', () => {
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.server()
|
cy.server()
|
||||||
|
@ -9,7 +9,7 @@ xcontext('Create a workflow', () => {
|
||||||
|
|
||||||
// https://on.cypress.io/interacting-with-elements
|
// https://on.cypress.io/interacting-with-elements
|
||||||
it('should create a workflow', () => {
|
it('should create a workflow', () => {
|
||||||
cy.createTable('dog', 'name', 'age')
|
cy.createTestTableWithData()
|
||||||
|
|
||||||
cy.contains('workflow').click()
|
cy.contains('workflow').click()
|
||||||
cy.contains('Create New Workflow').click()
|
cy.contains('Create New Workflow').click()
|
||||||
|
@ -23,21 +23,23 @@ xcontext('Create a workflow', () => {
|
||||||
|
|
||||||
// Create action
|
// Create action
|
||||||
cy.get('[data-cy=SAVE_RECORD]').click()
|
cy.get('[data-cy=SAVE_RECORD]').click()
|
||||||
cy.get(':nth-child(2) > .budibase__input').type('goodboy')
|
cy.get('.container input').first().type('goodboy')
|
||||||
cy.get(':nth-child(3) > .budibase__input').type('11')
|
cy.get('.container input').eq(1).type('11')
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.contains('Save Workflow').click()
|
cy.contains('Save Workflow').click()
|
||||||
|
|
||||||
// Activate Workflow
|
// Activate Workflow
|
||||||
cy.get('[data-cy=activate-workflow]').click()
|
cy.get('[data-cy=activate-workflow]').click()
|
||||||
|
cy.contains("Add Record").should("be.visible")
|
||||||
|
cy.get(".stop-button.highlighted").should("be.visible")
|
||||||
})
|
})
|
||||||
xit('should add record when a new record is added', () => {
|
|
||||||
|
it('should add record when a new record is added', () => {
|
||||||
cy.contains('backend').click()
|
cy.contains('backend').click()
|
||||||
|
|
||||||
cy.addRecord('bob', '15')
|
cy.addRecord(["Rover", 15])
|
||||||
|
cy.reload()
|
||||||
cy.contains('goodboy').should('have.text', 'goodboy')
|
cy.contains('goodboy').should('have.text', 'goodboy')
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -57,12 +57,17 @@ Cypress.Commands.add("createApp", name => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("createTestTableWithData", () => {
|
||||||
|
cy.createTable("dog")
|
||||||
|
cy.addColumn("dog", "name", "Plain Text")
|
||||||
|
cy.addColumn("dog", "age", "Number")
|
||||||
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createTable", tableName => {
|
Cypress.Commands.add("createTable", tableName => {
|
||||||
// Enter model name
|
// Enter model name
|
||||||
cy.contains("Create New Table").click()
|
cy.contains("Create New Table").click()
|
||||||
cy.get("[placeholder='Table Name']").type(tableName)
|
cy.get("[placeholder='Table Name']").type(tableName)
|
||||||
|
|
||||||
// Add 'name' field
|
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
cy.contains(tableName).should("be.visible")
|
cy.contains(tableName).should("be.visible")
|
||||||
})
|
})
|
||||||
|
@ -84,7 +89,7 @@ Cypress.Commands.add("addRecord", values => {
|
||||||
cy.contains("Create New Row").click()
|
cy.contains("Create New Row").click()
|
||||||
|
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
cy.get("input")
|
cy.get(".actions input")
|
||||||
.eq(i)
|
.eq(i)
|
||||||
.type(values[i])
|
.type(values[i])
|
||||||
}
|
}
|
||||||
|
@ -93,7 +98,7 @@ Cypress.Commands.add("addRecord", values => {
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createUser", (username, password) => {
|
Cypress.Commands.add("createUser", (username, password, accessLevel) => {
|
||||||
// Create User
|
// Create User
|
||||||
cy.get(".toprightnav > .settings").click()
|
cy.get(".toprightnav > .settings").click()
|
||||||
cy.contains("Users").click()
|
cy.contains("Users").click()
|
||||||
|
@ -104,9 +109,12 @@ Cypress.Commands.add("createUser", (username, password) => {
|
||||||
cy.get("[name=Password]")
|
cy.get("[name=Password]")
|
||||||
.first()
|
.first()
|
||||||
.type(password)
|
.type(password)
|
||||||
|
cy.get("select")
|
||||||
|
.first()
|
||||||
|
.select(accessLevel)
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.get(".create-button").click()
|
cy.get(".create-button > button").click()
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("addHeadlineComponent", text => {
|
Cypress.Commands.add("addHeadlineComponent", text => {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
Cypress.Cookies.defaults({
|
Cypress.Cookies.defaults({
|
||||||
whitelist: "builder:token",
|
preserve: "builder:token",
|
||||||
})
|
})
|
||||||
|
|
|
@ -49,15 +49,21 @@
|
||||||
],
|
],
|
||||||
"setupFilesAfterEnv": [
|
"setupFilesAfterEnv": [
|
||||||
"@testing-library/jest-dom/extend-expect"
|
"@testing-library/jest-dom/extend-expect"
|
||||||
|
],
|
||||||
|
"setupFiles": [
|
||||||
|
"./scripts/jestSetup.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:cypress/recommended"
|
"plugin:cypress/recommended"
|
||||||
]
|
],
|
||||||
|
"rules": {
|
||||||
|
"cypress/no-unnecessary-waiting": "off"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.27.1",
|
"@budibase/bbui": "^1.29.1",
|
||||||
"@budibase/client": "^0.1.19",
|
"@budibase/client": "^0.1.19",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
|
@ -88,7 +94,7 @@
|
||||||
"@testing-library/jest-dom": "^5.11.0",
|
"@testing-library/jest-dom": "^5.11.0",
|
||||||
"@testing-library/svelte": "^3.0.0",
|
"@testing-library/svelte": "^3.0.0",
|
||||||
"babel-jest": "^24.8.0",
|
"babel-jest": "^24.8.0",
|
||||||
"cypress": "^4.8.0",
|
"cypress": "^5.1.0",
|
||||||
"cypress-terminal-report": "^1.4.1",
|
"cypress-terminal-report": "^1.4.1",
|
||||||
"eslint-plugin-cypress": "^2.11.1",
|
"eslint-plugin-cypress": "^2.11.1",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
if (!Array.prototype.flat) {
|
||||||
|
Object.defineProperty(Array.prototype, "flat", {
|
||||||
|
configurable: true,
|
||||||
|
value: function flat() {
|
||||||
|
var depth = isNaN(arguments[0]) ? 1 : Number(arguments[0])
|
||||||
|
|
||||||
|
return depth
|
||||||
|
? Array.prototype.reduce.call(
|
||||||
|
this,
|
||||||
|
function(acc, cur) {
|
||||||
|
if (Array.isArray(cur)) {
|
||||||
|
acc.push.apply(acc, flat.call(cur, depth - 1))
|
||||||
|
} else {
|
||||||
|
acc.push(cur)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
: Array.prototype.slice.call(this)
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
})
|
||||||
|
}
|
|
@ -127,4 +127,30 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table styles */
|
||||||
|
.bb-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
* + .bb-table {
|
||||||
|
margin-top: var(--spacing-xl)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb-table th {
|
||||||
|
padding: var(--spacing-l) var(--spacing-m);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb-table td {
|
||||||
|
padding: var(--spacing-l) var(--spacing-m);
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bb-table td > :last-child { margin-bottom: 0; }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
import { cloneDeep, difference } from "lodash/fp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parameter for fetchBindableProperties function
|
||||||
|
* @typedef {Object} fetchBindablePropertiesParameter
|
||||||
|
* @property {string} componentInstanceId - an _id of a component that has been added to a screen, which you want to fetch bindable props for
|
||||||
|
* @propperty {Object} screen - current screen - where componentInstanceId lives
|
||||||
|
* @property {Object} components - dictionary of component definitions
|
||||||
|
* @property {Array} models - array of all models
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @typedef {Object} BindableProperty
|
||||||
|
* @property {string} type - either "instance" (binding to a component instance) or "context" (binding to data in context e.g. List Item)
|
||||||
|
* @property {Object} instance - relevant component instance. If "context" type, this instance is the component that provides the context... e.g. the List
|
||||||
|
* @property {string} runtimeBinding - a binding string that is a) saved against the string, and b) used at runtime to read/write the value
|
||||||
|
* @property {string} readableBinding - a binding string that is displayed to the user, in the builder
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates all allowed bindings from within any particular component instance
|
||||||
|
* @param {fetchBindablePropertiesParameter} param
|
||||||
|
* @returns {Array.<BindableProperty>}
|
||||||
|
*/
|
||||||
|
export default function({ componentInstanceId, screen, components, models }) {
|
||||||
|
const walkResult = walk({
|
||||||
|
// cloning so we are free to mutate props (e.g. by adding _contexts)
|
||||||
|
instance: cloneDeep(screen.props),
|
||||||
|
targetId: componentInstanceId,
|
||||||
|
components,
|
||||||
|
models,
|
||||||
|
})
|
||||||
|
|
||||||
|
return [
|
||||||
|
...walkResult.bindableInstances
|
||||||
|
.filter(isInstanceInSharedContext(walkResult))
|
||||||
|
.map(componentInstanceToBindable(walkResult)),
|
||||||
|
|
||||||
|
...walkResult.target._contexts.map(contextToBindables(walkResult)).flat(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInstanceInSharedContext = walkResult => i =>
|
||||||
|
// should cover
|
||||||
|
// - neither are in any context
|
||||||
|
// - both in same context
|
||||||
|
// - instance is in ancestor context of target
|
||||||
|
i.instance._contexts.length <= walkResult.target._contexts.length &&
|
||||||
|
difference(i.instance._contexts, walkResult.target._contexts).length === 0
|
||||||
|
|
||||||
|
// turns a component instance prop into binding expressions
|
||||||
|
// used by the UI
|
||||||
|
const componentInstanceToBindable = walkResult => i => {
|
||||||
|
const lastContext =
|
||||||
|
i.instance._contexts.length &&
|
||||||
|
i.instance._contexts[i.instance._contexts.length - 1]
|
||||||
|
const contextParentPath = lastContext
|
||||||
|
? getParentPath(walkResult, lastContext)
|
||||||
|
: ""
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "instance",
|
||||||
|
instance: i.instance,
|
||||||
|
// how the binding expression persists, and is used in the app at runtime
|
||||||
|
runtimeBinding: `${contextParentPath}${i.instance._id}.${i.prop}`,
|
||||||
|
// how the binding exressions looks to the user of the builder
|
||||||
|
readableBinding: `${i.instance._instanceName}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextToBindables = walkResult => context => {
|
||||||
|
const contextParentPath = getParentPath(walkResult, context)
|
||||||
|
|
||||||
|
return Object.keys(context.model.schema).map(k => ({
|
||||||
|
type: "context",
|
||||||
|
instance: context.instance,
|
||||||
|
// how the binding expression persists, and is used in the app at runtime
|
||||||
|
runtimeBinding: `${contextParentPath}data.${k}`,
|
||||||
|
// how the binding exressions looks to the user of the builder
|
||||||
|
readableBinding: `${context.instance._instanceName}.${context.model.name}.${k}`,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const getParentPath = (walkResult, context) => {
|
||||||
|
// describes the number of "parent" in the path
|
||||||
|
// clone array first so original array is not mtated
|
||||||
|
const contextParentNumber = [...walkResult.target._contexts]
|
||||||
|
.reverse()
|
||||||
|
.indexOf(context)
|
||||||
|
|
||||||
|
return (
|
||||||
|
new Array(contextParentNumber).fill("parent").join(".") +
|
||||||
|
// trailing . if has parents
|
||||||
|
(contextParentNumber ? "." : "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const walk = ({ instance, targetId, components, models, result }) => {
|
||||||
|
if (!result) {
|
||||||
|
result = {
|
||||||
|
target: null,
|
||||||
|
bindableInstances: [],
|
||||||
|
allContexts: [],
|
||||||
|
currentContexts: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!instance._contexts) instance._contexts = []
|
||||||
|
|
||||||
|
// "component" is the component definition (object in component.json)
|
||||||
|
const component = components[instance._component]
|
||||||
|
|
||||||
|
if (instance._id === targetId) {
|
||||||
|
// found it
|
||||||
|
result.target = instance
|
||||||
|
} else {
|
||||||
|
if (component && component.bindable) {
|
||||||
|
// pushing all components in here initially
|
||||||
|
// but this will not be correct, as some of
|
||||||
|
// these components will be in another context
|
||||||
|
// but we dont know this until the end of the walk
|
||||||
|
// so we will filter in another method
|
||||||
|
result.bindableInstances.push({
|
||||||
|
instance,
|
||||||
|
prop: component.bindable,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a component that provides context to it's children
|
||||||
|
const contextualInstance =
|
||||||
|
component && component.context && instance[component.context]
|
||||||
|
|
||||||
|
if (contextualInstance) {
|
||||||
|
// add to currentContexts (ancestory of context)
|
||||||
|
// before walking children
|
||||||
|
const model = models.find(m => m._id === instance[component.context])
|
||||||
|
result.currentContexts.push({ instance, model })
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentContexts = [...result.currentContexts]
|
||||||
|
for (let child of instance._children || []) {
|
||||||
|
// attaching _contexts of components, for eas comparison later
|
||||||
|
// these have been deep cloned above, so shouln't modify the
|
||||||
|
// original component instances
|
||||||
|
child._contexts = currentContexts
|
||||||
|
walk({ instance: child, targetId, components, models, result })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contextualInstance) {
|
||||||
|
// child walk done, remove from currentContexts
|
||||||
|
result.currentContexts.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { walkProps } from "./storeUtils"
|
||||||
|
import { get_capitalised_name } from "../helpers"
|
||||||
|
|
||||||
|
export default function(component, state) {
|
||||||
|
const capitalised = get_capitalised_name(component)
|
||||||
|
|
||||||
|
const matchingComponents = []
|
||||||
|
|
||||||
|
const findMatches = props => {
|
||||||
|
walkProps(props, c => {
|
||||||
|
if ((c._instanceName || "").startsWith(capitalised)) {
|
||||||
|
matchingComponents.push(c._instanceName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// check page first
|
||||||
|
findMatches(state.pages[state.currentPageName].props)
|
||||||
|
|
||||||
|
// if viewing screen, check current screen for duplicate
|
||||||
|
if (state.currentFrontEndType === "screen") {
|
||||||
|
findMatches(state.currentPreviewItem.props)
|
||||||
|
} else {
|
||||||
|
// viewing master page - need to find against all screens
|
||||||
|
for (let screen of state.screens) {
|
||||||
|
findMatches(screen.props)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = 1
|
||||||
|
let name
|
||||||
|
while (!name) {
|
||||||
|
const tryName = `${capitalised} ${index}`
|
||||||
|
if (!matchingComponents.includes(tryName)) name = tryName
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { values, cloneDeep } from "lodash/fp"
|
import { values, cloneDeep } from "lodash/fp"
|
||||||
import { get_capitalised_name } from "../../helpers"
|
import getNewComponentName from "../getNewComponentName"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import api from "../api"
|
import api from "../api"
|
||||||
|
@ -24,6 +24,7 @@ import {
|
||||||
saveScreenApi as _saveScreenApi,
|
saveScreenApi as _saveScreenApi,
|
||||||
regenerateCssForCurrentScreen,
|
regenerateCssForCurrentScreen,
|
||||||
generateNewIdsForComponent,
|
generateNewIdsForComponent,
|
||||||
|
getComponentDefinition,
|
||||||
} from "../storeUtils"
|
} from "../storeUtils"
|
||||||
export const getStore = () => {
|
export const getStore = () => {
|
||||||
const initial = {
|
const initial = {
|
||||||
|
@ -74,9 +75,6 @@ export const getStore = () => {
|
||||||
|
|
||||||
export default getStore
|
export default getStore
|
||||||
|
|
||||||
export const getComponentDefinition = (state, name) =>
|
|
||||||
name.startsWith("##") ? getBuiltin(name) : state.components[name]
|
|
||||||
|
|
||||||
const setPackage = (store, initial) => async pkg => {
|
const setPackage = (store, initial) => async pkg => {
|
||||||
const [main_screens, unauth_screens] = await Promise.all([
|
const [main_screens, unauth_screens] = await Promise.all([
|
||||||
api
|
api
|
||||||
|
@ -278,7 +276,7 @@ const addChildComponent = store => (componentToAdd, presetProps = {}) => {
|
||||||
const component = getComponentDefinition(state, componentToAdd)
|
const component = getComponentDefinition(state, componentToAdd)
|
||||||
|
|
||||||
const instanceId = get(backendUiStore).selectedDatabase._id
|
const instanceId = get(backendUiStore).selectedDatabase._id
|
||||||
const instanceName = get_capitalised_name(componentToAdd)
|
const instanceName = getNewComponentName(componentToAdd, state)
|
||||||
|
|
||||||
const newComponent = createProps(
|
const newComponent = createProps(
|
||||||
component,
|
component,
|
||||||
|
@ -484,7 +482,7 @@ const pasteComponent = store => (targetComponent, mode) => {
|
||||||
// in case we paste a second time
|
// in case we paste a second time
|
||||||
s.componentToPaste.isCut = false
|
s.componentToPaste.isCut = false
|
||||||
} else {
|
} else {
|
||||||
generateNewIdsForComponent(componentToPaste)
|
generateNewIdsForComponent(componentToPaste, s)
|
||||||
}
|
}
|
||||||
delete componentToPaste.isCut
|
delete componentToPaste.isCut
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { makePropsSafe } from "components/userInterface/pagesParsing/createProps"
|
import {
|
||||||
|
makePropsSafe,
|
||||||
|
getBuiltin,
|
||||||
|
} from "components/userInterface/pagesParsing/createProps"
|
||||||
import api from "./api"
|
import api from "./api"
|
||||||
import { generate_screen_css } from "./generate_css"
|
import { generate_screen_css } from "./generate_css"
|
||||||
import { uuid } from "./uuid"
|
import { uuid } from "./uuid"
|
||||||
|
import getNewComponentName from "./getNewComponentName"
|
||||||
|
|
||||||
export const selectComponent = (state, component) => {
|
export const selectComponent = (state, component) => {
|
||||||
const componentDef = component._component.startsWith("##")
|
const componentDef = component._component.startsWith("##")
|
||||||
|
@ -81,7 +85,11 @@ export const regenerateCssForCurrentScreen = state => {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateNewIdsForComponent = c =>
|
export const generateNewIdsForComponent = (c, state) =>
|
||||||
walkProps(c, p => {
|
walkProps(c, p => {
|
||||||
p._id = uuid()
|
p._id = uuid()
|
||||||
|
p._instanceName = getNewComponentName(p._component, state)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const getComponentDefinition = (state, name) =>
|
||||||
|
name.startsWith("##") ? getBuiltin(name) : state.components[name]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
export function uuid() {
|
export function uuid() {
|
||||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
|
// always want to make this start with a letter, as this makes it
|
||||||
|
// easier to use with mustache bindings in the client
|
||||||
|
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)
|
||||||
|
|
|
@ -26,9 +26,7 @@
|
||||||
|
|
||||||
<div class="bb-margin-m">
|
<div class="bb-margin-m">
|
||||||
<label class="uk-form-label">{label}</label>
|
<label class="uk-form-label">{label}</label>
|
||||||
<div class="uk-form-controls">
|
<input
|
||||||
<input
|
class="uk-input uk-form-width-{width} uk-form-{size}"
|
||||||
class="uk-input uk-form-width-{width} uk-form-{size}"
|
bind:this={input} />
|
||||||
bind:this={input} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
<div class="margin">
|
<div class="margin">
|
||||||
<label class="label">{label}</label>
|
<label class="label">{label}</label>
|
||||||
<div class="uk-form-controls">
|
|
||||||
<textarea value={valuesText} on:change={inputChanged} />
|
<textarea value={valuesText} on:change={inputChanged} />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="uk-table">
|
<table class="bb-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="edit-header">
|
<th class="edit-header">
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="uk-table">
|
<table class="bb-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{#each columns as header}
|
{#each columns as header}
|
||||||
|
|
|
@ -45,8 +45,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Table title={decodeURI(view.name)} schema={view.schema} {data}>
|
<Table title={decodeURI(name)} schema={view.schema} {data}>
|
||||||
<FilterPopover {view} />
|
<FilterPopover {view} />
|
||||||
<CalculationPopover {view} />
|
<CalculationPopover {view} />
|
||||||
<GroupByPopover {view} />
|
{#if view.calculation}
|
||||||
|
<GroupByPopover {view} />
|
||||||
|
{/if}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
|
@ -53,7 +53,6 @@
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let dropdown
|
let dropdown
|
||||||
let filters = view.filters || []
|
|
||||||
|
|
||||||
$: viewModel = $backendUiStore.models.find(
|
$: viewModel = $backendUiStore.models.find(
|
||||||
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
||||||
|
@ -61,19 +60,18 @@
|
||||||
$: fields = viewModel && Object.keys(viewModel.schema)
|
$: fields = viewModel && Object.keys(viewModel.schema)
|
||||||
|
|
||||||
function saveView() {
|
function saveView() {
|
||||||
view.filters = filters
|
|
||||||
backendUiStore.actions.views.save(view)
|
backendUiStore.actions.views.save(view)
|
||||||
notifier.success(`View ${view.name} saved.`)
|
notifier.success(`View ${view.name} saved.`)
|
||||||
dropdown.hide()
|
dropdown.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFilter(idx) {
|
function removeFilter(idx) {
|
||||||
filters.splice(idx, 1)
|
view.filters.splice(idx, 1)
|
||||||
filters = filters
|
view.filters = view.filters
|
||||||
}
|
}
|
||||||
|
|
||||||
function addFilter() {
|
function addFilter() {
|
||||||
filters = [...filters, {}]
|
view.filters = [...view.filters, {}]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@
|
||||||
text
|
text
|
||||||
small
|
small
|
||||||
on:click={dropdown.show}
|
on:click={dropdown.show}
|
||||||
active={filters && filters.length}>
|
active={view.filters && view.filters.length}>
|
||||||
<Icon name="filter" />
|
<Icon name="filter" />
|
||||||
Filter
|
Filter
|
||||||
</TextButton>
|
</TextButton>
|
||||||
|
@ -90,7 +88,7 @@
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
<h5>Filter</h5>
|
<h5>Filter</h5>
|
||||||
<div class="input-group-row">
|
<div class="input-group-row">
|
||||||
{#each filters as filter, idx}
|
{#each view.filters as filter, idx}
|
||||||
{#if idx === 0}
|
{#if idx === 0}
|
||||||
<p>Where</p>
|
<p>Where</p>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
<script>
|
||||||
|
import groupBy from "lodash/fp/groupBy"
|
||||||
|
import { Button, TextArea, Label, Body } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
export let bindableProperties
|
||||||
|
console.log("Bindable Props: ", bindableProperties)
|
||||||
|
export let value = ""
|
||||||
|
export let close
|
||||||
|
|
||||||
|
function addToText(readableBinding) {
|
||||||
|
value = value + `{{ ${readableBinding} }}`
|
||||||
|
}
|
||||||
|
let originalValue = value
|
||||||
|
|
||||||
|
$: dispatch("update", value)
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
dispatch("update", originalValue)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
$: ({ instance, context } = groupBy("type", bindableProperties))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container" data-cy="binding-dropdown-modal">
|
||||||
|
<div class="list">
|
||||||
|
<Label size="l" color="dark">Objects</Label>
|
||||||
|
{#if context}
|
||||||
|
<Label size="s" color="dark">Table</Label>
|
||||||
|
<ul>
|
||||||
|
{#each context as { readableBinding }}
|
||||||
|
<li on:click={() => addToText(readableBinding)}>{readableBinding}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
{#if instance}
|
||||||
|
<Label size="s" color="dark">Components</Label>
|
||||||
|
<ul>
|
||||||
|
{#each instance as { readableBinding }}
|
||||||
|
<li on:click={() => addToText(readableBinding)}>{readableBinding}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="text">
|
||||||
|
<Label size="l" color="dark">Data binding</Label>
|
||||||
|
<Body size="s" color="dark">
|
||||||
|
Binding connects one piece of data to another and makes it dynamic. Click
|
||||||
|
the objects on the left, to add them to the textbox.
|
||||||
|
</Body>
|
||||||
|
<TextArea bind:value placeholder="" />
|
||||||
|
<div class="controls">
|
||||||
|
<a href="#">
|
||||||
|
<Body size="s" color="light">Learn more about binding</Body>
|
||||||
|
</a>
|
||||||
|
<Button on:click={cancel} secondary>Cancel</Button>
|
||||||
|
<Button on:click={close} primary>Done</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
}
|
||||||
|
.list,
|
||||||
|
.text {
|
||||||
|
padding: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
margin-top: var(--spacing-m);
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
grid-template-columns: 1fr auto auto;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
width: 150px;
|
||||||
|
border-right: 1.5px solid var(--grey-4);
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
width: 600px;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
.text :global(p) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: var(--spacing-s) var(--spacing-m);
|
||||||
|
margin: auto 0px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li:active {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { MoreIcon } from "components/common/Icons"
|
import { MoreIcon } from "components/common/Icons"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { getComponentDefinition } from "builderStore/store"
|
import { getComponentDefinition } from "builderStore/storeUtils"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { last, cloneDeep } from "lodash/fp"
|
import { last, cloneDeep } from "lodash/fp"
|
||||||
import UIkit from "uikit"
|
import UIkit from "uikit"
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
|
|
||||||
export let screens = []
|
export let screens = []
|
||||||
|
|
||||||
|
/*
|
||||||
|
Using a store here seems odd....
|
||||||
|
have a look in the <ComponentsHierarchyChildren /> code file to find out why.
|
||||||
|
I have commented the dragDropStore parameter
|
||||||
|
*/
|
||||||
const dragDropStore = writable({})
|
const dragDropStore = writable({})
|
||||||
|
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
|
|
|
@ -10,11 +10,29 @@
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
} from "../common/Icons"
|
} from "../common/Icons"
|
||||||
|
import { getComponentDefinition } from "builderStore/storeUtils"
|
||||||
|
|
||||||
export let components = []
|
export let components = []
|
||||||
export let currentComponent
|
export let currentComponent
|
||||||
export let onSelect = () => {}
|
export let onSelect = () => {}
|
||||||
export let level = 0
|
export let level = 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
"dragDropStore" is a svelte store.
|
||||||
|
This component is recursive... a tree.
|
||||||
|
Using a single, shared store, all the nodes in the tree can subscribe to state that is changed by other nodes, in the same tree.
|
||||||
|
|
||||||
|
e.g. Say i have the structure
|
||||||
|
- Heading 1
|
||||||
|
- Container
|
||||||
|
- Heading 2
|
||||||
|
- Heading 3
|
||||||
|
- Heading 4
|
||||||
|
|
||||||
|
1. When I dragover "Heading 1", a placeholder drop-slot appears below it
|
||||||
|
2. I drag down a bit so the cursor is INSIDE the container (i.e. now in a child <ComponentsHierarchyChildren />)
|
||||||
|
3. Using store subscribes... the original drop-slot now knows that it should disappear, and a new one is created inside the container.
|
||||||
|
*/
|
||||||
export let dragDropStore
|
export let dragDropStore
|
||||||
|
|
||||||
let dropUnderComponent
|
let dropUnderComponent
|
||||||
|
@ -47,7 +65,7 @@
|
||||||
|
|
||||||
const dragover = (component, index) => e => {
|
const dragover = (component, index) => e => {
|
||||||
const canHaveChildrenButIsEmpty =
|
const canHaveChildrenButIsEmpty =
|
||||||
$store.components[component._component].children &&
|
getComponentDefinition($store, component._component).children &&
|
||||||
component._children.length === 0
|
component._children.length === 0
|
||||||
|
|
||||||
e.dataTransfer.dropEffect = "copy"
|
e.dataTransfer.dropEffect = "copy"
|
||||||
|
@ -97,6 +115,15 @@
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dragend = () => {
|
||||||
|
dragDropStore.update(s => {
|
||||||
|
s.dropPosition = ""
|
||||||
|
s.targetComponent = null
|
||||||
|
s.componentToDrop = null
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -117,6 +144,7 @@
|
||||||
class:selected={currentComponent === component}
|
class:selected={currentComponent === component}
|
||||||
style="padding-left: {level * 20 + 40}px"
|
style="padding-left: {level * 20 + 40}px"
|
||||||
draggable={true}
|
draggable={true}
|
||||||
|
on:dragend={dragend}
|
||||||
on:dragstart={dragstart(component)}
|
on:dragstart={dragstart(component)}
|
||||||
on:dragover={dragover(component, index)}
|
on:dragover={dragover(component, index)}
|
||||||
on:drop={drop}
|
on:drop={drop}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { Button, DropdownMenu } from "@budibase/bbui"
|
||||||
|
import EventEditorModal from "./EventEditorModal.svelte"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
export let name
|
||||||
|
|
||||||
|
let button
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={button}>
|
||||||
|
<Button secondary small on:click={dropdown.show}>Define Actions</Button>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} align="right" anchor={button}>
|
||||||
|
<EventEditorModal
|
||||||
|
event={value}
|
||||||
|
eventType={name}
|
||||||
|
on:change
|
||||||
|
on:close={dropdown.hide} />
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,4 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { Icon } from "@budibase/bbui"
|
||||||
|
import Input from "./PropertyPanelControls/Input.svelte"
|
||||||
|
import { store, backendUiStore } from "builderStore"
|
||||||
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
|
import { DropdownMenu } from "@budibase/bbui"
|
||||||
|
import BindingDropdown from "components/userInterface/BindingDropdown.svelte"
|
||||||
import { onMount, getContext } from "svelte"
|
import { onMount, getContext } from "svelte"
|
||||||
|
|
||||||
export let label = ""
|
export let label = ""
|
||||||
|
@ -9,6 +15,49 @@
|
||||||
export let props = {}
|
export let props = {}
|
||||||
export let onChange = () => {}
|
export let onChange = () => {}
|
||||||
|
|
||||||
|
let temporaryBindableValue = value
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
handleChange(key, temporaryBindableValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
let bindableProperties = []
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
|
||||||
|
function getBindableProperties() {
|
||||||
|
// Get all bindableProperties
|
||||||
|
bindableProperties = fetchBindableProperties({
|
||||||
|
componentInstanceId: $store.currentComponentInfo._id,
|
||||||
|
components: $store.components,
|
||||||
|
screen: $store.currentPreviewItem,
|
||||||
|
models: $backendUiStore.models,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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} }}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onChange(key, textWithBindings)
|
||||||
|
}
|
||||||
|
|
||||||
function handleChange(key, v) {
|
function handleChange(key, v) {
|
||||||
let innerVal = v
|
let innerVal = v
|
||||||
if (typeof v === "object") {
|
if (typeof v === "object") {
|
||||||
|
@ -18,13 +67,29 @@
|
||||||
innerVal = props.valueKey ? v.target[props.valueKey] : v.target.value
|
innerVal = props.valueKey ? v.target[props.valueKey] : v.target.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onChange(key, innerVal)
|
replaceBindings(innerVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeValue = () => {
|
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)
|
||||||
return value === undefined && props.defaultValue !== undefined
|
return value === undefined && props.defaultValue !== undefined
|
||||||
? props.defaultValue
|
? props.defaultValue
|
||||||
: value
|
: temp
|
||||||
}
|
}
|
||||||
|
|
||||||
//Incase the component has a different value key name
|
//Incase the component has a different value key name
|
||||||
|
@ -32,7 +97,7 @@
|
||||||
props.valueKey ? { [props.valueKey]: safeValue() } : { value: safeValue() }
|
props.valueKey ? { [props.valueKey]: safeValue() } : { value: safeValue() }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="property-control">
|
<div class="property-control" bind:this={anchor}>
|
||||||
<div class="label">{label}</div>
|
<div class="label">{label}</div>
|
||||||
<div data-cy={`${key}-prop-control`} class="control">
|
<div data-cy={`${key}-prop-control`} class="control">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
|
@ -44,10 +109,29 @@
|
||||||
{...props}
|
{...props}
|
||||||
name={key} />
|
name={key} />
|
||||||
</div>
|
</div>
|
||||||
|
{#if control == Input}
|
||||||
|
<button data-cy={`${key}-binding-button`} on:click={dropdown.show}>
|
||||||
|
<Icon name="edit" />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{#if control == Input}
|
||||||
|
<DropdownMenu
|
||||||
|
on:close={handleClose}
|
||||||
|
bind:this={dropdown}
|
||||||
|
{anchor}
|
||||||
|
align="right">
|
||||||
|
<BindingDropdown
|
||||||
|
{...handlevalueKey(value)}
|
||||||
|
close={dropdown.hide}
|
||||||
|
on:update={e => (temporaryBindableValue = e.detail)}
|
||||||
|
{bindableProperties} />
|
||||||
|
</DropdownMenu>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.property-control {
|
.property-control {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
|
@ -73,4 +157,15 @@
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
max-width: 164px;
|
max-width: 164px;
|
||||||
}
|
}
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
background: rgb(224, 224, 224);
|
||||||
|
right: 5px;
|
||||||
|
--spacing-s: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
export let onStyleChanged = () => {}
|
export let onStyleChanged = () => {}
|
||||||
export let open = false
|
export let open = false
|
||||||
|
|
||||||
|
$: console.log(properties)
|
||||||
|
|
||||||
$: style = componentInstance["_styles"][styleCategory] || {}
|
$: style = componentInstance["_styles"][styleCategory] || {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import Input from "./PropertyPanelControls/Input.svelte"
|
import Input from "./PropertyPanelControls/Input.svelte"
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { excludeProps } from "./propertyCategories.js"
|
import { excludeProps } from "./propertyCategories.js"
|
||||||
|
import { store } from "builderStore"
|
||||||
|
import { walkProps } from "builderStore/storeUtils"
|
||||||
|
|
||||||
export let panelDefinition = []
|
export let panelDefinition = []
|
||||||
export let componentDefinition = {}
|
export let componentDefinition = {}
|
||||||
|
@ -13,6 +15,7 @@
|
||||||
export let screenOrPageInstance
|
export let screenOrPageInstance
|
||||||
|
|
||||||
let pageScreenProps = ["title", "favicon", "description", "route"]
|
let pageScreenProps = ["title", "favicon", "description", "route"]
|
||||||
|
let duplicateName = false
|
||||||
|
|
||||||
const propExistsOnComponentDef = prop =>
|
const propExistsOnComponentDef = prop =>
|
||||||
pageScreenProps.includes(prop) || prop in componentDefinition.props
|
pageScreenProps.includes(prop) || prop in componentDefinition.props
|
||||||
|
@ -42,6 +45,43 @@
|
||||||
|
|
||||||
$: isPage = screenOrPageInstance && screenOrPageInstance.favicon
|
$: isPage = screenOrPageInstance && screenOrPageInstance.favicon
|
||||||
$: screenOrPageDefinition = isPage ? pageDefinition : screenDefinition
|
$: screenOrPageDefinition = isPage ? pageDefinition : screenDefinition
|
||||||
|
|
||||||
|
const isDuplicateName = name => {
|
||||||
|
let duplicate = false
|
||||||
|
|
||||||
|
const lookForDuplicate = rootPops => {
|
||||||
|
walkProps(rootPops, (inst, cancel) => {
|
||||||
|
if (inst._instanceName === name && inst._id !== componentInstance._id) {
|
||||||
|
duplicate = true
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// check page first
|
||||||
|
lookForDuplicate($store.pages[$store.currentPageName].props)
|
||||||
|
if (duplicate) return true
|
||||||
|
|
||||||
|
// if viwing screen, check current screen for duplicate
|
||||||
|
if ($store.currentFrontEndType === "screen") {
|
||||||
|
lookForDuplicate($store.currentPreviewItem.props)
|
||||||
|
} else {
|
||||||
|
// viewing master page - need to dedupe against all screens
|
||||||
|
for (let screen of $store.screens) {
|
||||||
|
lookForDuplicate(screen.props)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
const onInstanceNameChange = (_, name) => {
|
||||||
|
if (isDuplicateName(name)) {
|
||||||
|
duplicateName = true
|
||||||
|
} else {
|
||||||
|
duplicateName = false
|
||||||
|
onChange("_instanceName", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if screenOrPageInstance}
|
{#if screenOrPageInstance}
|
||||||
|
@ -63,7 +103,10 @@
|
||||||
label="Name"
|
label="Name"
|
||||||
key="_instanceName"
|
key="_instanceName"
|
||||||
value={componentInstance._instanceName}
|
value={componentInstance._instanceName}
|
||||||
{onChange} />
|
onChange={onInstanceNameChange} />
|
||||||
|
{#if duplicateName}
|
||||||
|
<span class="duplicate-name">Name must be unique</span>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if panelDefinition && panelDefinition.length > 0}
|
{#if panelDefinition && panelDefinition.length > 0}
|
||||||
|
@ -89,4 +132,11 @@
|
||||||
div {
|
div {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.duplicate-name {
|
||||||
|
color: var(--red);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
position: relative;
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Checkbox from "../common/Checkbox.svelte"
|
||||||
import ModelSelect from "components/userInterface/ModelSelect.svelte"
|
import ModelSelect from "components/userInterface/ModelSelect.svelte"
|
||||||
import ModelViewSelect from "components/userInterface/ModelViewSelect.svelte"
|
import ModelViewSelect from "components/userInterface/ModelViewSelect.svelte"
|
||||||
import ModelViewFieldSelect from "components/userInterface/ModelViewFieldSelect.svelte"
|
import ModelViewFieldSelect from "components/userInterface/ModelViewFieldSelect.svelte"
|
||||||
|
import Event from "components/userInterface/EventsEditor/EventPropertyControl.svelte"
|
||||||
|
|
||||||
import { all } from "./propertyCategories.js"
|
import { all } from "./propertyCategories.js"
|
||||||
/*
|
/*
|
||||||
|
@ -203,6 +204,7 @@ export default {
|
||||||
valueKey: "checked",
|
valueKey: "checked",
|
||||||
control: Checkbox,
|
control: Checkbox,
|
||||||
},
|
},
|
||||||
|
{ label: "onClick", key: "onClick", control: Event },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,21 +25,19 @@
|
||||||
|
|
||||||
<div class="bb-margin-xl block-field">
|
<div class="bb-margin-xl block-field">
|
||||||
<label class="uk-form-label">Page</label>
|
<label class="uk-form-label">Page</label>
|
||||||
<div class="uk-form-controls">
|
|
||||||
<select class="budibase__input" bind:value={pageName}>
|
<select class="budibase__input" bind:value={pageName}>
|
||||||
{#each Object.keys(pages) as page}
|
{#each Object.keys(pages) as page}
|
||||||
<option value={page}>{page}</option>
|
<option value={page}>{page}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
{#if components.length > 0}
|
{#if components.length > 0}
|
||||||
<label class="uk-form-label">Component</label>
|
<label class="uk-form-label">Component</label>
|
||||||
<div class="uk-form-controls">
|
<select class="budibase__input" bind:value>
|
||||||
<select class="budibase__input" bind:value>
|
{#each components as component}
|
||||||
{#each components as component}
|
<option value={component._id}>{component._id}</option>
|
||||||
<option value={component._id}>{component._id}</option>
|
{/each}
|
||||||
{/each}
|
</select>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,12 +5,10 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bb-margin-xl block-field">
|
<div class="bb-margin-xl block-field">
|
||||||
<div class="uk-form-controls">
|
<select class="budibase__input" bind:value>
|
||||||
<select class="budibase__input" bind:value>
|
<option value="" />
|
||||||
<option value="" />
|
{#each $backendUiStore.models as model}
|
||||||
{#each $backendUiStore.models as model}
|
<option value={model}>{model.name}</option>
|
||||||
<option value={model}>{model.name}</option>
|
{/each}
|
||||||
{/each}
|
</select>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,24 +3,36 @@
|
||||||
import { Input } from "@budibase/bbui"
|
import { Input } from "@budibase/bbui"
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
|
function setParsedValue(evt, field) {
|
||||||
|
const fieldSchema = value.model.schema[field]
|
||||||
|
if (fieldSchema.type === "number") {
|
||||||
|
value[field] = parseInt(evt.target.value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value[field] = evt.target.value
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="bb-margin-xl block-field">
|
<div class="bb-margin-xl block-field">
|
||||||
<div class="uk-form-controls">
|
<select class="budibase__input" bind:value={value.model}>
|
||||||
<select class="budibase__input" bind:value={value.model}>
|
{#each $backendUiStore.models as model}
|
||||||
{#each $backendUiStore.models as model}
|
<option value={model}>{model.name}</option>
|
||||||
<option value={model}>{model.name}</option>
|
{/each}
|
||||||
{/each}
|
</select>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if value.model}
|
{#if value.model}
|
||||||
<div class="bb-margin-xl block-field">
|
<div class="bb-margin-xl block-field">
|
||||||
<label class="uk-form-label fields">Fields</label>
|
<label class="uk-form-label fields">Fields</label>
|
||||||
{#each Object.keys(value.model.schema) as field}
|
{#each Object.keys(value.model.schema) as field}
|
||||||
<div class="uk-form-controls bb-margin-xl">
|
<div class="bb-margin-xl">
|
||||||
<Input bind:value={value[field]} label={field} />
|
<Input
|
||||||
|
thin
|
||||||
|
value={value[field]}
|
||||||
|
label={field}
|
||||||
|
on:change={e => setParsedValue(e, field)} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
{:else if type === 'model'}
|
{:else if type === 'model'}
|
||||||
<ModelSelector bind:value={workflowBlock.args[parameter]} />
|
<ModelSelector bind:value={workflowBlock.args[parameter]} />
|
||||||
{:else if type === 'record'}
|
{:else if type === 'record'}
|
||||||
<RecordSelector bind:value={workflowBlock.args[parameter]} />
|
<RecordSelector value={workflowBlock.args[parameter]} />
|
||||||
{:else if type === 'string'}
|
{:else if type === 'string'}
|
||||||
<Input type="text" thin bind:value={workflowBlock.args[parameter]} />
|
<Input type="text" thin bind:value={workflowBlock.args[parameter]} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -96,6 +96,8 @@ textarea {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Top bottom spacing */
|
||||||
|
|
||||||
.bb-margin-m {
|
.bb-margin-m {
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
import fetchBindableProperties from "../src/builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
|
describe("fetch bindable properties", () => {
|
||||||
|
|
||||||
|
it("should return bindable properties from screen components", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "heading-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const componentBinding = result.find(r => r.instance._id === "search-input-id" && r.type === "instance")
|
||||||
|
expect(componentBinding).toBeDefined()
|
||||||
|
expect(componentBinding.type).toBe("instance")
|
||||||
|
expect(componentBinding.runtimeBinding).toBe("search-input-id.value")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not return bindable components when not in their context", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "heading-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const componentBinding = result.find(r => r.instance._id === "list-item-input-id")
|
||||||
|
expect(componentBinding).not.toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return model schema, when inside a context", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "list-item-input-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const contextBindings = result.filter(r => r.instance._id === "list-id" && r.type==="context")
|
||||||
|
expect(contextBindings.length).toBe(2)
|
||||||
|
|
||||||
|
const namebinding = contextBindings.find(b => b.runtimeBinding === "data.name")
|
||||||
|
expect(namebinding).toBeDefined()
|
||||||
|
expect(namebinding.readableBinding).toBe("list-name.Test Model.name")
|
||||||
|
|
||||||
|
const descriptionbinding = contextBindings.find(b => b.runtimeBinding === "data.description")
|
||||||
|
expect(descriptionbinding).toBeDefined()
|
||||||
|
expect(descriptionbinding.readableBinding).toBe("list-name.Test Model.description")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return model schema, for grantparent context", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "child-list-item-input-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const contextBindings = result.filter(r => r.type==="context")
|
||||||
|
expect(contextBindings.length).toBe(4)
|
||||||
|
|
||||||
|
const namebinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.name")
|
||||||
|
expect(namebinding_parent).toBeDefined()
|
||||||
|
expect(namebinding_parent.readableBinding).toBe("list-name.Test Model.name")
|
||||||
|
|
||||||
|
const descriptionbinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.description")
|
||||||
|
expect(descriptionbinding_parent).toBeDefined()
|
||||||
|
expect(descriptionbinding_parent.readableBinding).toBe("list-name.Test Model.description")
|
||||||
|
|
||||||
|
const namebinding_own = contextBindings.find(b => b.runtimeBinding === "data.name")
|
||||||
|
expect(namebinding_own).toBeDefined()
|
||||||
|
expect(namebinding_own.readableBinding).toBe("child-list-name.Test Model.name")
|
||||||
|
|
||||||
|
const descriptionbinding_own = contextBindings.find(b => b.runtimeBinding === "data.description")
|
||||||
|
expect(descriptionbinding_own).toBeDefined()
|
||||||
|
expect(descriptionbinding_own.readableBinding).toBe("child-list-name.Test Model.description")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return bindable component props, from components in same context", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "list-item-heading-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const componentBinding = result.find(r => r.instance._id === "list-item-input-id" && r.type === "instance")
|
||||||
|
expect(componentBinding).toBeDefined()
|
||||||
|
expect(componentBinding.runtimeBinding).toBe("list-item-input-id.value")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not return components from child context", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "list-item-heading-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
|
||||||
|
expect(componentBinding).not.toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return bindable component props, from components in same context (when nested context)", () => {
|
||||||
|
const result = fetchBindableProperties({
|
||||||
|
componentInstanceId: "child-list-item-heading-id",
|
||||||
|
...testData()
|
||||||
|
})
|
||||||
|
const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
|
||||||
|
expect(componentBinding).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const testData = () => {
|
||||||
|
|
||||||
|
const screen = {
|
||||||
|
instanceName: "test screen",
|
||||||
|
name: "screen-id",
|
||||||
|
route: "/",
|
||||||
|
props: {
|
||||||
|
_id:"screent-root-id",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "heading-id",
|
||||||
|
_instanceName: "list item heading",
|
||||||
|
_component: "@budibase/standard-components/heading",
|
||||||
|
text: "Screen Title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "search-input-id",
|
||||||
|
_instanceName: "Search Input",
|
||||||
|
_component: "@budibase/standard-components/input",
|
||||||
|
value: "search phrase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "list-id",
|
||||||
|
_component: "@budibase/standard-components/list",
|
||||||
|
_instanceName: "list-name",
|
||||||
|
model: "test-model-id",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "list-item-heading-id",
|
||||||
|
_instanceName: "list item heading",
|
||||||
|
_component: "@budibase/standard-components/heading",
|
||||||
|
text: "hello"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "list-item-input-id",
|
||||||
|
_instanceName: "List Item Input",
|
||||||
|
_component: "@budibase/standard-components/input",
|
||||||
|
value: "list item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "child-list-id",
|
||||||
|
_component: "@budibase/standard-components/list",
|
||||||
|
_instanceName: "child-list-name",
|
||||||
|
model: "test-model-id",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "child-list-item-heading-id",
|
||||||
|
_instanceName: "child list item heading",
|
||||||
|
_component: "@budibase/standard-components/heading",
|
||||||
|
text: "hello"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "child-list-item-input-id",
|
||||||
|
_instanceName: "Child List Item Input",
|
||||||
|
_component: "@budibase/standard-components/input",
|
||||||
|
value: "child list item"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const models = [{
|
||||||
|
_id: "test-model-id",
|
||||||
|
name: "Test Model",
|
||||||
|
schema: {
|
||||||
|
name: {
|
||||||
|
type: "string"
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
"@budibase/standard-components/container" : {
|
||||||
|
props: {},
|
||||||
|
},
|
||||||
|
"@budibase/standard-components/list" : {
|
||||||
|
context: "model",
|
||||||
|
props: {
|
||||||
|
model: "string"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"@budibase/standard-components/input" : {
|
||||||
|
bindable: "value",
|
||||||
|
props: {
|
||||||
|
value: "string"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"@budibase/standard-components/heading" : {
|
||||||
|
props: {
|
||||||
|
text: "string"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return { screen, models, components }
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -21,28 +21,24 @@
|
||||||
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
|
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
|
||||||
},
|
},
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
"js"
|
"js",
|
||||||
|
"svelte"
|
||||||
],
|
],
|
||||||
"moduleDirectories": [
|
"moduleDirectories": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
],
|
],
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+js$": "babel-jest"
|
"^.+js$": "babel-jest",
|
||||||
|
"^.+.svelte$": "svelte-jester"
|
||||||
},
|
},
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
"/node_modules/(?!svelte).+\\.js$"
|
"/node_modules/(?!svelte).+\\.js$"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
|
||||||
"deep-equal": "^2.0.1",
|
"deep-equal": "^2.0.1",
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"lunr": "^2.3.5",
|
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.0.1",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0"
|
||||||
"shortid": "^2.2.8",
|
|
||||||
"svelte": "^3.9.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.5",
|
"@babel/core": "^7.5.5",
|
||||||
|
@ -58,7 +54,9 @@
|
||||||
"rollup-plugin-node-builtins": "^2.1.2",
|
"rollup-plugin-node-builtins": "^2.1.2",
|
||||||
"rollup-plugin-node-globals": "^1.4.0",
|
"rollup-plugin-node-globals": "^1.4.0",
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"rollup-plugin-terser": "^4.0.4"
|
"rollup-plugin-terser": "^4.0.4",
|
||||||
|
"svelte": "3.23.x",
|
||||||
|
"svelte-jester": "^1.0.6"
|
||||||
},
|
},
|
||||||
"gitHead": "e4e053cb6ff9a0ddc7115b44ccaa24b8ec41fb9a"
|
"gitHead": "e4e053cb6ff9a0ddc7115b44ccaa24b8ec41fb9a"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,74 +3,6 @@ import commonjs from "rollup-plugin-commonjs"
|
||||||
import builtins from "rollup-plugin-node-builtins"
|
import builtins from "rollup-plugin-node-builtins"
|
||||||
import nodeglobals from "rollup-plugin-node-globals"
|
import nodeglobals from "rollup-plugin-node-globals"
|
||||||
|
|
||||||
const lodash_fp_exports = [
|
|
||||||
"find",
|
|
||||||
"compose",
|
|
||||||
"isUndefined",
|
|
||||||
"split",
|
|
||||||
"max",
|
|
||||||
"last",
|
|
||||||
"union",
|
|
||||||
"reduce",
|
|
||||||
"isObject",
|
|
||||||
"cloneDeep",
|
|
||||||
"some",
|
|
||||||
"isArray",
|
|
||||||
"map",
|
|
||||||
"filter",
|
|
||||||
"keys",
|
|
||||||
"isFunction",
|
|
||||||
"isEmpty",
|
|
||||||
"countBy",
|
|
||||||
"join",
|
|
||||||
"includes",
|
|
||||||
"flatten",
|
|
||||||
"constant",
|
|
||||||
"first",
|
|
||||||
"intersection",
|
|
||||||
"take",
|
|
||||||
"has",
|
|
||||||
"mapValues",
|
|
||||||
"isString",
|
|
||||||
"isBoolean",
|
|
||||||
"isNull",
|
|
||||||
"isNumber",
|
|
||||||
"isObjectLike",
|
|
||||||
"isDate",
|
|
||||||
"clone",
|
|
||||||
"values",
|
|
||||||
"keyBy",
|
|
||||||
"isNaN",
|
|
||||||
"isInteger",
|
|
||||||
"toNumber",
|
|
||||||
]
|
|
||||||
|
|
||||||
const lodash_exports = [
|
|
||||||
"flow",
|
|
||||||
"head",
|
|
||||||
"find",
|
|
||||||
"each",
|
|
||||||
"tail",
|
|
||||||
"findIndex",
|
|
||||||
"startsWith",
|
|
||||||
"dropRight",
|
|
||||||
"takeRight",
|
|
||||||
"trim",
|
|
||||||
"split",
|
|
||||||
"replace",
|
|
||||||
"merge",
|
|
||||||
"assign",
|
|
||||||
]
|
|
||||||
|
|
||||||
const coreExternal = [
|
|
||||||
"lodash",
|
|
||||||
"lodash/fp",
|
|
||||||
"lunr",
|
|
||||||
"safe-buffer",
|
|
||||||
"shortid",
|
|
||||||
"@nx-js/compiler-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: "src/index.js",
|
input: "src/index.js",
|
||||||
output: [
|
output: [
|
||||||
|
@ -90,17 +22,8 @@ export default {
|
||||||
resolve({
|
resolve({
|
||||||
preferBuiltins: true,
|
preferBuiltins: true,
|
||||||
browser: true,
|
browser: true,
|
||||||
dedupe: importee => {
|
|
||||||
return coreExternal.includes(importee)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
commonjs({
|
|
||||||
namedExports: {
|
|
||||||
"lodash/fp": lodash_fp_exports,
|
|
||||||
lodash: lodash_exports,
|
|
||||||
shortid: ["generate"],
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
commonjs(),
|
||||||
builtins(),
|
builtins(),
|
||||||
nodeglobals(),
|
nodeglobals(),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import appStore from "../state/store"
|
||||||
|
|
||||||
export const USER_STATE_PATH = "_bbuser"
|
export const USER_STATE_PATH = "_bbuser"
|
||||||
|
|
||||||
export const authenticate = api => async ({ username, password }) => {
|
export const authenticate = api => async ({ username, password }) => {
|
||||||
|
@ -17,6 +19,10 @@ export const authenticate = api => async ({ username, password }) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// set user even if error - so it is defined at least
|
// set user even if error - so it is defined at least
|
||||||
api.setState(USER_STATE_PATH, user)
|
appStore.update(s => {
|
||||||
|
s[USER_STATE_PATH] = user
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
|
||||||
localStorage.setItem("budibase:user", JSON.stringify(user))
|
localStorage.setItem("budibase:user", JSON.stringify(user))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,59 @@
|
||||||
import { authenticate } from "./authenticate"
|
import { authenticate } from "./authenticate"
|
||||||
import { triggerWorkflow } from "./workflow"
|
import { triggerWorkflow } from "./workflow"
|
||||||
|
import appStore from "../state/store"
|
||||||
|
|
||||||
export const createApi = ({ setState, getState }) => {
|
const apiCall = method => async ({ url, body }) => {
|
||||||
const apiCall = method => async ({ url, body }) => {
|
const response = await fetch(url, {
|
||||||
const response = await fetch(url, {
|
method: method,
|
||||||
method: method,
|
headers: {
|
||||||
headers: {
|
"Content-Type": "application/json",
|
||||||
"Content-Type": "application/json",
|
},
|
||||||
},
|
body: body && JSON.stringify(body),
|
||||||
body: body && JSON.stringify(body),
|
credentials: "same-origin",
|
||||||
credentials: "same-origin",
|
})
|
||||||
})
|
|
||||||
|
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 200:
|
case 200:
|
||||||
|
return response.json()
|
||||||
|
case 404:
|
||||||
|
return error(`${url} Not found`)
|
||||||
|
case 400:
|
||||||
|
return error(`${url} Bad Request`)
|
||||||
|
case 403:
|
||||||
|
return error(`${url} Forbidden`)
|
||||||
|
default:
|
||||||
|
if (response.status >= 200 && response.status < 400) {
|
||||||
return response.json()
|
return response.json()
|
||||||
case 404:
|
}
|
||||||
return error(`${url} Not found`)
|
|
||||||
case 400:
|
|
||||||
return error(`${url} Bad Request`)
|
|
||||||
case 403:
|
|
||||||
return error(`${url} Forbidden`)
|
|
||||||
default:
|
|
||||||
if (response.status >= 200 && response.status < 400) {
|
|
||||||
return response.json()
|
|
||||||
}
|
|
||||||
|
|
||||||
return error(`${url} - ${response.statusText}`)
|
return error(`${url} - ${response.statusText}`)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const post = apiCall("POST")
|
|
||||||
const get = apiCall("GET")
|
|
||||||
const patch = apiCall("PATCH")
|
|
||||||
const del = apiCall("DELETE")
|
|
||||||
|
|
||||||
const ERROR_MEMBER = "##error"
|
|
||||||
const error = message => {
|
|
||||||
const err = { [ERROR_MEMBER]: message }
|
|
||||||
setState("##error_message", message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSuccess = obj => !obj || !obj[ERROR_MEMBER]
|
|
||||||
|
|
||||||
const apiOpts = {
|
|
||||||
setState,
|
|
||||||
getState,
|
|
||||||
isSuccess,
|
|
||||||
error,
|
|
||||||
post,
|
|
||||||
get,
|
|
||||||
patch,
|
|
||||||
delete: del,
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
authenticate: authenticate(apiOpts),
|
|
||||||
triggerWorkflow: triggerWorkflow(apiOpts),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const post = apiCall("POST")
|
||||||
|
const get = apiCall("GET")
|
||||||
|
const patch = apiCall("PATCH")
|
||||||
|
const del = apiCall("DELETE")
|
||||||
|
|
||||||
|
const ERROR_MEMBER = "##error"
|
||||||
|
const error = message => {
|
||||||
|
const err = { [ERROR_MEMBER]: message }
|
||||||
|
appStore.update(s => s["##error_message"], message)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSuccess = obj => !obj || !obj[ERROR_MEMBER]
|
||||||
|
|
||||||
|
const apiOpts = {
|
||||||
|
isSuccess,
|
||||||
|
error,
|
||||||
|
post,
|
||||||
|
get,
|
||||||
|
patch,
|
||||||
|
delete: del,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
authenticate: authenticate(apiOpts),
|
||||||
|
triggerWorkflow: triggerWorkflow(apiOpts),
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
import { setState } from "../../state/setState"
|
|
||||||
|
|
||||||
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
SET_STATE: ({ context, args, id }) => {
|
|
||||||
setState(...Object.values(args))
|
|
||||||
context = {
|
|
||||||
...context,
|
|
||||||
[id]: args,
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
},
|
|
||||||
NAVIGATE: () => {
|
NAVIGATE: () => {
|
||||||
// TODO client navigation
|
// TODO client navigation
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { get } from "svelte/store"
|
import renderTemplateString from "../../state/renderTemplateString"
|
||||||
import mustache from "mustache"
|
import appStore from "../../state/store"
|
||||||
import { appStore } from "../../state/store"
|
|
||||||
import Orchestrator from "./orchestrator"
|
import Orchestrator from "./orchestrator"
|
||||||
import clientActions from "./actions"
|
import clientActions from "./actions"
|
||||||
|
|
||||||
|
@ -18,9 +17,9 @@ export const clientStrategy = ({ api }) => ({
|
||||||
if (typeof argValue !== "string") continue
|
if (typeof argValue !== "string") continue
|
||||||
|
|
||||||
// Render the string with values from the workflow context and state
|
// Render the string with values from the workflow context and state
|
||||||
mappedArgs[arg] = mustache.render(argValue, {
|
mappedArgs[arg] = renderTemplateString(argValue, {
|
||||||
context: this.context,
|
context: this.context,
|
||||||
state: get(appStore),
|
state: appStore.get(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { split, last, compose } from "lodash/fp"
|
|
||||||
import { prepareRenderComponent } from "./prepareRenderComponent"
|
import { prepareRenderComponent } from "./prepareRenderComponent"
|
||||||
import { isScreenSlot } from "./builtinComponents"
|
import { isScreenSlot } from "./builtinComponents"
|
||||||
import deepEqual from "deep-equal"
|
import deepEqual from "deep-equal"
|
||||||
|
import appStore from "../state/store"
|
||||||
|
|
||||||
export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
||||||
const {
|
const {
|
||||||
|
@ -30,11 +30,28 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextArray = Array.isArray(context) ? context : [context]
|
const contextStoreKeys = []
|
||||||
|
|
||||||
|
// create new context if supplied
|
||||||
|
if (context) {
|
||||||
|
let childIndex = 0
|
||||||
|
// if context is an array, map to new structure
|
||||||
|
const contextArray = Array.isArray(context) ? context : [context]
|
||||||
|
for (let ctx of contextArray) {
|
||||||
|
const key = appStore.create(
|
||||||
|
ctx,
|
||||||
|
treeNode.props._id,
|
||||||
|
childIndex,
|
||||||
|
treeNode.contextStoreKey
|
||||||
|
)
|
||||||
|
contextStoreKeys.push(key)
|
||||||
|
childIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const childNodes = []
|
const childNodes = []
|
||||||
|
|
||||||
for (let context of contextArray) {
|
const createChildNodes = contextStoreKey => {
|
||||||
for (let childProps of treeNode.props._children) {
|
for (let childProps of treeNode.props._children) {
|
||||||
const { componentName, libName } = splitName(childProps._component)
|
const { componentName, libName } = splitName(childProps._component)
|
||||||
|
|
||||||
|
@ -42,25 +59,33 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
||||||
|
|
||||||
const ComponentConstructor = componentLibraries[libName][componentName]
|
const ComponentConstructor = componentLibraries[libName][componentName]
|
||||||
|
|
||||||
const prepareNodes = ctx => {
|
const childNode = prepareRenderComponent({
|
||||||
const childNodesThisIteration = prepareRenderComponent({
|
props: childProps,
|
||||||
props: childProps,
|
parentNode: treeNode,
|
||||||
parentNode: treeNode,
|
ComponentConstructor,
|
||||||
ComponentConstructor,
|
htmlElement,
|
||||||
htmlElement,
|
anchor,
|
||||||
anchor,
|
// in same context as parent, unless a new one was supplied
|
||||||
context: ctx,
|
contextStoreKey,
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let childNode of childNodesThisIteration) {
|
childNodes.push(childNode)
|
||||||
childNodes.push(childNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareNodes(context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
// if new context(s) is supplied, then create nodes
|
||||||
|
// with keys to new context stores
|
||||||
|
for (let contextStoreKey of contextStoreKeys) {
|
||||||
|
createChildNodes(contextStoreKey)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// otherwise, use same context store as parent
|
||||||
|
// which maybe undefined (therfor using the root state)
|
||||||
|
createChildNodes(treeNode.contextStoreKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if everything is equal, then don't re-render
|
||||||
if (areTreeNodesEqual(treeNode.children, childNodes)) return treeNode.children
|
if (areTreeNodesEqual(treeNode.children, childNodes)) return treeNode.children
|
||||||
|
|
||||||
for (let node of childNodes) {
|
for (let node of childNodes) {
|
||||||
|
@ -81,9 +106,9 @@ export const attachChildren = initialiseOpts => (htmlElement, options) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const splitName = fullname => {
|
const splitName = fullname => {
|
||||||
const getComponentName = compose(last, split("/"))
|
const nameParts = fullname.split("/")
|
||||||
|
|
||||||
const componentName = getComponentName(fullname)
|
const componentName = nameParts[nameParts.length - 1]
|
||||||
|
|
||||||
const libName = fullname.substring(
|
const libName = fullname.substring(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { appStore } from "../state/store"
|
import renderTemplateString from "../state/renderTemplateString"
|
||||||
import mustache from "mustache"
|
import appStore from "../state/store"
|
||||||
|
import hasBinding from "../state/hasBinding"
|
||||||
|
|
||||||
export const prepareRenderComponent = ({
|
export const prepareRenderComponent = ({
|
||||||
ComponentConstructor,
|
ComponentConstructor,
|
||||||
|
@ -7,62 +8,54 @@ export const prepareRenderComponent = ({
|
||||||
anchor,
|
anchor,
|
||||||
props,
|
props,
|
||||||
parentNode,
|
parentNode,
|
||||||
context,
|
contextStoreKey,
|
||||||
}) => {
|
}) => {
|
||||||
const parentContext = (parentNode && parentNode.context) || {}
|
const thisNode = createTreeNode()
|
||||||
|
thisNode.parentNode = parentNode
|
||||||
|
thisNode.props = props
|
||||||
|
thisNode.contextStoreKey = contextStoreKey
|
||||||
|
|
||||||
let nodesToRender = []
|
// the treeNode is first created (above), and then this
|
||||||
const createNodeAndRender = () => {
|
// render method is add. The treeNode is returned, and
|
||||||
let componentContext = parentContext
|
// render is called later (in attachChildren)
|
||||||
if (context) {
|
thisNode.render = initialProps => {
|
||||||
componentContext = { ...context }
|
thisNode.component = new ComponentConstructor({
|
||||||
componentContext.$parent = parentContext
|
target: htmlElement,
|
||||||
|
props: initialProps,
|
||||||
|
hydrate: false,
|
||||||
|
anchor,
|
||||||
|
})
|
||||||
|
|
||||||
|
// finds the root element of the component, which was created by the contructor above
|
||||||
|
// we use this later to attach a className to. This is how styles
|
||||||
|
// are applied by the builder
|
||||||
|
thisNode.rootElement = htmlElement.children[htmlElement.children.length - 1]
|
||||||
|
|
||||||
|
let [componentName] = props._component.match(/[a-z]*$/)
|
||||||
|
if (props._id && thisNode.rootElement) {
|
||||||
|
thisNode.rootElement.classList.add(`${componentName}-${props._id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisNode = createTreeNode()
|
// make this node listen to the store
|
||||||
thisNode.context = componentContext
|
if (thisNode.stateBound) {
|
||||||
thisNode.parentNode = parentNode
|
const unsubscribe = appStore.subscribe(state => {
|
||||||
thisNode.props = props
|
const storeBoundProps = Object.keys(initialProps._bb.props).filter(p =>
|
||||||
nodesToRender.push(thisNode)
|
hasBinding(initialProps._bb.props[p])
|
||||||
|
)
|
||||||
thisNode.render = initialProps => {
|
if (storeBoundProps.length > 0) {
|
||||||
thisNode.component = new ComponentConstructor({
|
const toSet = {}
|
||||||
target: htmlElement,
|
for (let prop of storeBoundProps) {
|
||||||
props: initialProps,
|
const propValue = initialProps._bb.props[prop]
|
||||||
hydrate: false,
|
toSet[prop] = renderTemplateString(propValue, state)
|
||||||
anchor,
|
|
||||||
})
|
|
||||||
thisNode.rootElement =
|
|
||||||
htmlElement.children[htmlElement.children.length - 1]
|
|
||||||
|
|
||||||
let [componentName] = props._component.match(/[a-z]*$/)
|
|
||||||
if (props._id && thisNode.rootElement) {
|
|
||||||
thisNode.rootElement.classList.add(`${componentName}-${props._id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make this node listen to the store
|
|
||||||
if (thisNode.stateBound) {
|
|
||||||
const unsubscribe = appStore.subscribe(state => {
|
|
||||||
const storeBoundProps = { ...initialProps._bb.props }
|
|
||||||
for (let prop in storeBoundProps) {
|
|
||||||
const propValue = storeBoundProps[prop]
|
|
||||||
if (typeof propValue === "string") {
|
|
||||||
storeBoundProps[prop] = mustache.render(propValue, {
|
|
||||||
state,
|
|
||||||
context: componentContext,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
thisNode.component.$set(storeBoundProps)
|
thisNode.component.$set(toSet)
|
||||||
})
|
}
|
||||||
thisNode.unsubscribe = unsubscribe
|
}, thisNode.contextStoreKey)
|
||||||
}
|
thisNode.unsubscribe = unsubscribe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNodeAndRender()
|
return thisNode
|
||||||
|
|
||||||
return nodesToRender
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createTreeNode = () => ({
|
export const createTreeNode = () => ({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import regexparam from "regexparam"
|
import regexparam from "regexparam"
|
||||||
import { appStore } from "../state/store"
|
import appStore from "../state/store"
|
||||||
import { parseAppIdFromCookie } from "./getAppId"
|
import { parseAppIdFromCookie } from "./getAppId"
|
||||||
|
|
||||||
export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { setState } from "./setState"
|
import setBindableComponentProp from "./setBindableComponentProp"
|
||||||
import { attachChildren } from "../render/attachChildren"
|
import { attachChildren } from "../render/attachChildren"
|
||||||
import { getContext, setContext } from "./getSetContext"
|
|
||||||
|
|
||||||
export const trimSlash = str => str.replace(/^\/+|\/+$/g, "")
|
export const trimSlash = str => str.replace(/^\/+|\/+$/g, "")
|
||||||
|
|
||||||
export const bbFactory = ({
|
export const bbFactory = ({
|
||||||
store,
|
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
onScreenSlotRendered,
|
onScreenSlotRendered,
|
||||||
getCurrentState,
|
getCurrentState,
|
||||||
|
@ -45,13 +43,9 @@ export const bbFactory = ({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachChildren: attachChildren(attachParams),
|
attachChildren: attachChildren(attachParams),
|
||||||
context: treeNode.context,
|
|
||||||
props: treeNode.props,
|
props: treeNode.props,
|
||||||
call: safeCallEvent,
|
call: safeCallEvent,
|
||||||
setState,
|
setBinding: setBindableComponentProp(treeNode),
|
||||||
getContext: getContext(treeNode),
|
|
||||||
setContext: setContext(treeNode),
|
|
||||||
store: store,
|
|
||||||
api,
|
api,
|
||||||
parent,
|
parent,
|
||||||
// these parameters are populated by screenRouter
|
// these parameters are populated by screenRouter
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import { setState } from "./setState"
|
import api from "../api"
|
||||||
import { getState } from "./getState"
|
|
||||||
import { isArray, isUndefined } from "lodash/fp"
|
|
||||||
|
|
||||||
import { createApi } from "../api"
|
|
||||||
|
|
||||||
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
|
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
|
||||||
|
|
||||||
|
@ -12,21 +8,13 @@ export const eventHandlers = routeTo => {
|
||||||
parameters,
|
parameters,
|
||||||
})
|
})
|
||||||
|
|
||||||
const api = createApi({
|
|
||||||
setState,
|
|
||||||
getState: (path, fallback) => getState(path, fallback),
|
|
||||||
})
|
|
||||||
|
|
||||||
const setStateHandler = ({ path, value }) => setState(path, value)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"Set State": handler(["path", "value"], setStateHandler),
|
|
||||||
"Navigate To": handler(["url"], param => routeTo(param && param.url)),
|
"Navigate To": handler(["url"], param => routeTo(param && param.url)),
|
||||||
"Trigger Workflow": handler(["workflow"], api.triggerWorkflow),
|
"Trigger Workflow": handler(["workflow"], api.triggerWorkflow),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isEventType = prop =>
|
export const isEventType = prop =>
|
||||||
isArray(prop) &&
|
Array.isArray(prop) &&
|
||||||
prop.length > 0 &&
|
prop.length > 0 &&
|
||||||
!isUndefined(prop[0][EVENT_TYPE_MEMBER_NAME])
|
!prop[0][EVENT_TYPE_MEMBER_NAME] === undefined
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { get } from "svelte/store"
|
|
||||||
import getOr from "lodash/fp/getOr"
|
|
||||||
import { appStore } from "./store"
|
|
||||||
|
|
||||||
export const getState = (path, fallback) => {
|
|
||||||
if (!path || path.length === 0) return fallback
|
|
||||||
|
|
||||||
return getOr(fallback, path, get(appStore))
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
export default value => typeof value === "string" && value.includes("{{")
|
|
@ -0,0 +1,17 @@
|
||||||
|
import mustache from "mustache"
|
||||||
|
|
||||||
|
// this is a much more liberal version of mustache's escape function
|
||||||
|
// ...just ignoring < and > to prevent tags from user input
|
||||||
|
// original version here https://github.com/janl/mustache.js/blob/4b7908f5c9fec469a11cfaed2f2bed23c84e1c5c/mustache.js#L78
|
||||||
|
|
||||||
|
const entityMap = {
|
||||||
|
"<": "<",
|
||||||
|
">": ">",
|
||||||
|
}
|
||||||
|
|
||||||
|
mustache.escape = text =>
|
||||||
|
String(text).replace(/[&<>"'`=/]/g, function fromEntityMap(s) {
|
||||||
|
return entityMap[s]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default mustache.render
|
|
@ -0,0 +1,13 @@
|
||||||
|
import appStore from "./store"
|
||||||
|
|
||||||
|
export default treeNode => (propName, value) => {
|
||||||
|
if (!propName || propName.length === 0) return
|
||||||
|
if (!treeNode) return
|
||||||
|
const componentId = treeNode.props._id
|
||||||
|
|
||||||
|
appStore.update(state => {
|
||||||
|
state[componentId] = state[componentId] || {}
|
||||||
|
state[componentId][propName] = value
|
||||||
|
return state
|
||||||
|
}, treeNode.contextStoreKey)
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
import set from "lodash/fp/set"
|
|
||||||
import { appStore } from "./store"
|
|
||||||
|
|
||||||
export const setState = (path, value) => {
|
|
||||||
if (!path || path.length === 0) return
|
|
||||||
|
|
||||||
appStore.update(state => {
|
|
||||||
state = set(path, value, state)
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -4,9 +4,9 @@ import {
|
||||||
EVENT_TYPE_MEMBER_NAME,
|
EVENT_TYPE_MEMBER_NAME,
|
||||||
} from "./eventHandlers"
|
} from "./eventHandlers"
|
||||||
import { bbFactory } from "./bbComponentApi"
|
import { bbFactory } from "./bbComponentApi"
|
||||||
import mustache from "mustache"
|
import renderTemplateString from "./renderTemplateString"
|
||||||
import { get } from "svelte/store"
|
import appStore from "./store"
|
||||||
import { appStore } from "./store"
|
import hasBinding from "./hasBinding"
|
||||||
|
|
||||||
const doNothing = () => {}
|
const doNothing = () => {}
|
||||||
doNothing.isPlaceholder = true
|
doNothing.isPlaceholder = true
|
||||||
|
@ -37,41 +37,34 @@ export const createStateManager = ({
|
||||||
const getCurrentState = () => currentState
|
const getCurrentState = () => currentState
|
||||||
|
|
||||||
const bb = bbFactory({
|
const bb = bbFactory({
|
||||||
store: appStore,
|
|
||||||
getCurrentState,
|
getCurrentState,
|
||||||
componentLibraries,
|
componentLibraries,
|
||||||
onScreenSlotRendered,
|
onScreenSlotRendered,
|
||||||
})
|
})
|
||||||
|
|
||||||
const setup = _setup({ handlerTypes, getCurrentState, bb, store: appStore })
|
const setup = _setup({ handlerTypes, getCurrentState, bb })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setup,
|
setup,
|
||||||
destroy: () => {},
|
destroy: () => {},
|
||||||
getCurrentState,
|
getCurrentState,
|
||||||
store: appStore,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
|
const _setup = ({ handlerTypes, getCurrentState, bb }) => node => {
|
||||||
const props = node.props
|
const props = node.props
|
||||||
const context = node.context || {}
|
|
||||||
const initialProps = { ...props }
|
const initialProps = { ...props }
|
||||||
const currentStoreState = get(appStore)
|
|
||||||
|
|
||||||
for (let propName in props) {
|
for (let propName in props) {
|
||||||
if (isMetaProp(propName)) continue
|
if (isMetaProp(propName)) continue
|
||||||
|
|
||||||
const propValue = props[propName]
|
const propValue = props[propName]
|
||||||
|
|
||||||
// A little bit of a hack - won't bind if the string doesn't start with {{
|
const isBound = hasBinding(propValue)
|
||||||
const isBound = typeof propValue === "string" && propValue.includes("{{")
|
|
||||||
|
|
||||||
if (isBound) {
|
if (isBound) {
|
||||||
initialProps[propName] = mustache.render(propValue, {
|
const state = appStore.getState(node.contextStoreKey)
|
||||||
state: currentStoreState,
|
initialProps[propName] = renderTemplateString(propValue, state)
|
||||||
context,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!node.stateBound) {
|
if (!node.stateBound) {
|
||||||
node.stateBound = true
|
node.stateBound = true
|
||||||
|
@ -79,6 +72,7 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEventType(propValue)) {
|
if (isEventType(propValue)) {
|
||||||
|
const state = appStore.getState(node.contextStoreKey)
|
||||||
const handlersInfos = []
|
const handlersInfos = []
|
||||||
for (let event of propValue) {
|
for (let event of propValue) {
|
||||||
const handlerInfo = {
|
const handlerInfo = {
|
||||||
|
@ -90,10 +84,7 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
|
||||||
for (let paramName in handlerInfo.parameters) {
|
for (let paramName in handlerInfo.parameters) {
|
||||||
const paramValue = handlerInfo.parameters[paramName]
|
const paramValue = handlerInfo.parameters[paramName]
|
||||||
resolvedParams[paramName] = () =>
|
resolvedParams[paramName] = () =>
|
||||||
mustache.render(paramValue, {
|
renderTemplateString(paramValue, state)
|
||||||
state: getCurrentState(),
|
|
||||||
context,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerInfo.parameters = resolvedParams
|
handlerInfo.parameters = resolvedParams
|
||||||
|
@ -113,7 +104,7 @@ const _setup = ({ handlerTypes, getCurrentState, bb, store }) => node => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setup = _setup({ handlerTypes, getCurrentState, bb, store })
|
const setup = _setup({ handlerTypes, getCurrentState, bb })
|
||||||
initialProps._bb = bb(node, setup)
|
initialProps._bb = bb(node, setup)
|
||||||
|
|
||||||
return initialProps
|
return initialProps
|
||||||
|
|
|
@ -1,9 +1,104 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
const appStore = writable({})
|
// we assume that the reference to this state object
|
||||||
appStore.actions = {}
|
// will remain for the life of the application
|
||||||
|
const rootState = {}
|
||||||
|
const rootStore = writable(rootState)
|
||||||
|
const contextStores = {}
|
||||||
|
|
||||||
const routerStore = writable({})
|
// contextProviderId is the component id that provides the data for the context
|
||||||
routerStore.actions = {}
|
const contextStoreKey = (dataProviderId, childIndex) =>
|
||||||
|
`${dataProviderId}${childIndex >= 0 ? ":" + childIndex : ""}`
|
||||||
|
|
||||||
export { appStore, routerStore }
|
// creates a store for a datacontext (e.g. each item in a list component)
|
||||||
|
const create = (data, dataProviderId, childIndex, parentContextStoreId) => {
|
||||||
|
const key = contextStoreKey(dataProviderId, childIndex)
|
||||||
|
const state = { data }
|
||||||
|
|
||||||
|
// add reference to parent state object,
|
||||||
|
// so we can use bindings like state.parent.parent
|
||||||
|
// (if no parent, then parent is rootState )
|
||||||
|
state.parent = parentContextStoreId
|
||||||
|
? contextStores[parentContextStoreId].state
|
||||||
|
: rootState
|
||||||
|
|
||||||
|
if (!contextStores[key]) {
|
||||||
|
contextStores[key] = {
|
||||||
|
store: writable(state),
|
||||||
|
subscriberCount: 0,
|
||||||
|
state,
|
||||||
|
parentContextStoreId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscribe = (subscription, storeKey) => {
|
||||||
|
if (!storeKey) {
|
||||||
|
return rootStore.subscribe(subscription)
|
||||||
|
}
|
||||||
|
const contextStore = contextStores[storeKey]
|
||||||
|
|
||||||
|
// we are subscribing to multiple stores,
|
||||||
|
// we dont want to run our listener for every subscription, the first time
|
||||||
|
// as this could repeatedly run $set on the same component
|
||||||
|
// ... which already has its initial properties set properly
|
||||||
|
const ignoreFirstSubscription = () => {
|
||||||
|
let hasRunOnce = false
|
||||||
|
return () => {
|
||||||
|
if (hasRunOnce) subscription(contextStore.state)
|
||||||
|
hasRunOnce = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsubscribes = [rootStore.subscribe(ignoreFirstSubscription())]
|
||||||
|
|
||||||
|
// we subscribe to all stores in the hierarchy
|
||||||
|
const ancestorSubscribe = ctxStore => {
|
||||||
|
// unsubscribe func returned by svelte store
|
||||||
|
const svelteUnsub = ctxStore.store.subscribe(ignoreFirstSubscription())
|
||||||
|
|
||||||
|
// we wrap the svelte unsubscribe, so we can
|
||||||
|
// cleanup stores when they are no longer subscribed to
|
||||||
|
const unsub = () => {
|
||||||
|
ctxStore.subscriberCount = contextStore.subscriberCount - 1
|
||||||
|
// when no subscribers left, we delete the store
|
||||||
|
if (ctxStore.subscriberCount === 0) {
|
||||||
|
delete ctxStore[storeKey]
|
||||||
|
}
|
||||||
|
svelteUnsub()
|
||||||
|
}
|
||||||
|
unsubscribes.push(unsub)
|
||||||
|
if (ctxStore.parentContextStoreId) {
|
||||||
|
ancestorSubscribe(contextStores[ctxStore.parentContextStoreId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ancestorSubscribe(contextStore)
|
||||||
|
|
||||||
|
// our final unsubscribe function calls unsubscribe on all stores
|
||||||
|
return () => unsubscribes.forEach(u => u())
|
||||||
|
}
|
||||||
|
|
||||||
|
const findStore = (dataProviderId, childIndex) =>
|
||||||
|
dataProviderId
|
||||||
|
? contextStores[contextStoreKey(dataProviderId, childIndex)].store
|
||||||
|
: rootStore
|
||||||
|
|
||||||
|
const update = (updatefunc, dataProviderId, childIndex) =>
|
||||||
|
findStore(dataProviderId, childIndex).update(updatefunc)
|
||||||
|
|
||||||
|
const set = (value, dataProviderId, childIndex) =>
|
||||||
|
findStore(dataProviderId, childIndex).set(value)
|
||||||
|
|
||||||
|
const getState = contextStoreKey =>
|
||||||
|
contextStoreKey ? contextStores[contextStoreKey].state : rootState
|
||||||
|
|
||||||
|
export default {
|
||||||
|
subscribe,
|
||||||
|
update,
|
||||||
|
set,
|
||||||
|
getState,
|
||||||
|
create,
|
||||||
|
contextStoreKey,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
import { load, makePage, makeScreen } from "./testAppDef"
|
||||||
|
|
||||||
|
describe("binding", () => {
|
||||||
|
|
||||||
|
|
||||||
|
it("should bind to data in context", async () => {
|
||||||
|
const { dom } = await load(
|
||||||
|
makePage({
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
text: "header one",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
makeScreen("/", {
|
||||||
|
_component: "testlib/list",
|
||||||
|
data: dataArray,
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "testlib/h1",
|
||||||
|
text: "{{data.name}}",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const rootDiv = dom.window.document.body.children[0]
|
||||||
|
expect(rootDiv.children.length).toBe(1)
|
||||||
|
|
||||||
|
const screenRoot = rootDiv.children[0]
|
||||||
|
|
||||||
|
expect(screenRoot.children[0].children.length).toBe(2)
|
||||||
|
expect(screenRoot.children[0].children[0].innerText).toBe(dataArray[0].name)
|
||||||
|
expect(screenRoot.children[0].children[1].innerText).toBe(dataArray[1].name)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should bind to input in root", async () => {
|
||||||
|
const { dom } = await load(
|
||||||
|
makePage({
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
text: "header one",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
makeScreen("/", {
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "testlib/h1",
|
||||||
|
text: "{{inputid.value}}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "inputid",
|
||||||
|
_component: "testlib/input",
|
||||||
|
value: "hello"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const rootDiv = dom.window.document.body.children[0]
|
||||||
|
expect(rootDiv.children.length).toBe(1)
|
||||||
|
|
||||||
|
const screenRoot = rootDiv.children[0]
|
||||||
|
|
||||||
|
expect(screenRoot.children[0].children.length).toBe(2)
|
||||||
|
expect(screenRoot.children[0].children[0].innerText).toBe("hello")
|
||||||
|
|
||||||
|
// change value of input
|
||||||
|
const input = dom.window.document.getElementsByClassName("input-inputid")[0]
|
||||||
|
|
||||||
|
changeInputValue(dom, input, "new value")
|
||||||
|
expect(screenRoot.children[0].children[0].innerText).toBe("new value")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should bind to input in context", async () => {
|
||||||
|
const { dom } = await load(
|
||||||
|
makePage({
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
text: "header one",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
makeScreen("/", {
|
||||||
|
_component: "testlib/list",
|
||||||
|
data: dataArray,
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "testlib/h1",
|
||||||
|
text: "{{inputid.value}}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "inputid",
|
||||||
|
_component: "testlib/input",
|
||||||
|
value: "hello"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const rootDiv = dom.window.document.body.children[0]
|
||||||
|
expect(rootDiv.children.length).toBe(1)
|
||||||
|
|
||||||
|
const screenRoot = rootDiv.children[0]
|
||||||
|
expect(screenRoot.children[0].children.length).toBe(4)
|
||||||
|
|
||||||
|
const firstHeader = screenRoot.children[0].children[0]
|
||||||
|
const firstInput = screenRoot.children[0].children[1]
|
||||||
|
const secondHeader = screenRoot.children[0].children[2]
|
||||||
|
const secondInput = screenRoot.children[0].children[3]
|
||||||
|
|
||||||
|
expect(firstHeader.innerText).toBe("hello")
|
||||||
|
expect(secondHeader.innerText).toBe("hello")
|
||||||
|
|
||||||
|
changeInputValue(dom, firstInput, "first input value")
|
||||||
|
expect(firstHeader.innerText).toBe("first input value")
|
||||||
|
|
||||||
|
changeInputValue(dom, secondInput, "second input value")
|
||||||
|
expect(secondHeader.innerText).toBe("second input value")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should bind contextual component, to input in root context", async () => {
|
||||||
|
const { dom } = await load(
|
||||||
|
makePage({
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
text: "header one",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
makeScreen("/", {
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "inputid",
|
||||||
|
_component: "testlib/input",
|
||||||
|
value: "hello"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_component: "testlib/list",
|
||||||
|
data: dataArray,
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "testlib/h1",
|
||||||
|
text: "{{parent.inputid.value}}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const rootDiv = dom.window.document.body.children[0]
|
||||||
|
expect(rootDiv.children.length).toBe(1)
|
||||||
|
|
||||||
|
const screenRoot = rootDiv.children[0]
|
||||||
|
expect(screenRoot.children[0].children.length).toBe(2)
|
||||||
|
|
||||||
|
const input = screenRoot.children[0].children[0]
|
||||||
|
|
||||||
|
const firstHeader = screenRoot.children[0].children[1].children[0]
|
||||||
|
const secondHeader = screenRoot.children[0].children[1].children[0]
|
||||||
|
|
||||||
|
expect(firstHeader.innerText).toBe("hello")
|
||||||
|
expect(secondHeader.innerText).toBe("hello")
|
||||||
|
|
||||||
|
changeInputValue(dom, input, "new input value")
|
||||||
|
expect(firstHeader.innerText).toBe("new input value")
|
||||||
|
expect(secondHeader.innerText).toBe("new input value")
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeInputValue = (dom, input, newValue) => {
|
||||||
|
var event = new dom.window.Event("change")
|
||||||
|
input.value = newValue
|
||||||
|
input.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataArray = [
|
||||||
|
{
|
||||||
|
name: "katherine",
|
||||||
|
age: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "steve",
|
||||||
|
age: 41,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
|
@ -135,4 +135,38 @@ describe("initialiseApp", () => {
|
||||||
expect(screenRoot.children[0].children[0].innerText).toBe("header one")
|
expect(screenRoot.children[0].children[0].innerText).toBe("header one")
|
||||||
expect(screenRoot.children[0].children[1].innerText).toBe("header two")
|
expect(screenRoot.children[0].children[1].innerText).toBe("header two")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should repeat elements that pass an array of contexts", async () => {
|
||||||
|
const { dom } = await load(
|
||||||
|
makePage({
|
||||||
|
_component: "testlib/div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
text: "header one",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
makeScreen("/", {
|
||||||
|
_component: "testlib/list",
|
||||||
|
data: [1,2,3,4],
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_component: "testlib/h1",
|
||||||
|
text: "header",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const rootDiv = dom.window.document.body.children[0]
|
||||||
|
expect(rootDiv.children.length).toBe(1)
|
||||||
|
|
||||||
|
const screenRoot = rootDiv.children[0]
|
||||||
|
|
||||||
|
expect(screenRoot.children[0].children.length).toBe(4)
|
||||||
|
expect(screenRoot.children[0].children[0].innerText).toBe("header")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -194,4 +194,47 @@ const maketestlib = window => ({
|
||||||
set(opts.props)
|
set(opts.props)
|
||||||
opts.target.appendChild(node)
|
opts.target.appendChild(node)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
list: function(opts) {
|
||||||
|
const node = window.document.createElement("DIV")
|
||||||
|
|
||||||
|
let currentProps = { ...opts.props }
|
||||||
|
|
||||||
|
const set = props => {
|
||||||
|
currentProps = Object.assign(currentProps, props)
|
||||||
|
if (currentProps._children && currentProps._children.length > 0) {
|
||||||
|
currentProps._bb.attachChildren(node, {
|
||||||
|
context: currentProps.data || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$destroy = () => opts.target.removeChild(node)
|
||||||
|
|
||||||
|
this.$set = set
|
||||||
|
this._element = node
|
||||||
|
set(opts.props)
|
||||||
|
opts.target.appendChild(node)
|
||||||
|
},
|
||||||
|
|
||||||
|
input: function(opts) {
|
||||||
|
const node = window.document.createElement("INPUT")
|
||||||
|
let currentProps = { ...opts.props }
|
||||||
|
|
||||||
|
const set = props => {
|
||||||
|
currentProps = Object.assign(currentProps, props)
|
||||||
|
opts.props._bb.setBinding("value", props.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.addEventListener("change", e => {
|
||||||
|
opts.props._bb.setBinding("value", e.target.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$destroy = () => opts.target.removeChild(node)
|
||||||
|
|
||||||
|
this.$set = set
|
||||||
|
this._element = node
|
||||||
|
set(opts.props)
|
||||||
|
opts.target.appendChild(node)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
export let _bb
|
||||||
|
export let className = ""
|
||||||
|
|
||||||
|
let containerElement
|
||||||
|
let hasLoaded
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (containerElement) {
|
||||||
|
_bb.attachChildren(containerElement)
|
||||||
|
hasLoaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={containerElement} class={className} />
|
|
@ -14,9 +14,8 @@ module.exports = async function saveRecord({ args, context }) {
|
||||||
user: { instanceId: context.instanceId },
|
user: { instanceId: context.instanceId },
|
||||||
}
|
}
|
||||||
|
|
||||||
await recordController.save(ctx)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await recordController.save(ctx)
|
||||||
return {
|
return {
|
||||||
record: ctx.body,
|
record: ctx.body,
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,20 @@
|
||||||
lodash "^4.17.13"
|
lodash "^4.17.13"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@budibase/client@^0.1.19":
|
||||||
|
version "0.1.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.19.tgz#3906781423ab4626118c981657ecf7a4578c547c"
|
||||||
|
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"
|
||||||
|
|
||||||
"@cnakazawa/watch@^1.0.3":
|
"@cnakazawa/watch@^1.0.3":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
|
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
|
||||||
|
@ -354,6 +368,10 @@
|
||||||
path-to-regexp "1.x"
|
path-to-regexp "1.x"
|
||||||
urijs "^1.19.2"
|
urijs "^1.19.2"
|
||||||
|
|
||||||
|
"@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"
|
||||||
|
|
||||||
"@sendgrid/client@^7.1.1":
|
"@sendgrid/client@^7.1.1":
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.1.1.tgz#09a25e58ac7e5321d66807e7110ff0fb61bb534f"
|
resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.1.1.tgz#09a25e58ac7e5321d66807e7110ff0fb61bb534f"
|
||||||
|
@ -811,6 +829,10 @@ array-equal@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
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"
|
||||||
|
|
||||||
array-unique@^0.3.2:
|
array-unique@^0.3.2:
|
||||||
version "0.3.2"
|
version "0.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
|
@ -869,6 +891,12 @@ atomic-sleep@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
||||||
|
|
||||||
|
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"
|
||||||
|
dependencies:
|
||||||
|
array-filter "^1.0.0"
|
||||||
|
|
||||||
aws-sdk@^2.706.0:
|
aws-sdk@^2.706.0:
|
||||||
version "2.706.0"
|
version "2.706.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.706.0.tgz#09f65e9a91ecac5a635daf934082abae30eca953"
|
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.706.0.tgz#09f65e9a91ecac5a635daf934082abae30eca953"
|
||||||
|
@ -980,8 +1008,8 @@ bindings@^1.5.0:
|
||||||
file-uri-to-path "1.0.0"
|
file-uri-to-path "1.0.0"
|
||||||
|
|
||||||
bl@^4.0.1:
|
bl@^4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
|
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489"
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer "^5.5.0"
|
buffer "^5.5.0"
|
||||||
inherits "^2.0.4"
|
inherits "^2.0.4"
|
||||||
|
@ -1537,6 +1565,25 @@ decompress-response@^3.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-response "^1.0.0"
|
mimic-response "^1.0.0"
|
||||||
|
|
||||||
|
deep-equal@^2.0.1:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0"
|
||||||
|
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-equal@~1.0.1:
|
deep-equal@~1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||||
|
@ -1863,6 +1910,51 @@ 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.trimleft "^2.1.1"
|
||||||
string.prototype.trimright "^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"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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:
|
es-to-primitive@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||||
|
@ -2753,16 +2845,28 @@ is-accessor-descriptor@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
kind-of "^6.0.0"
|
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"
|
||||||
|
|
||||||
is-arrayish@^0.2.1:
|
is-arrayish@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
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"
|
||||||
|
|
||||||
is-binary-path@~2.1.0:
|
is-binary-path@~2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^2.0.0"
|
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"
|
||||||
|
|
||||||
is-buffer@^1.1.5:
|
is-buffer@^1.1.5:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
@ -2771,6 +2875,10 @@ is-callable@^1.1.4, is-callable@^1.1.5:
|
||||||
version "1.1.5"
|
version "1.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
|
||||||
|
|
||||||
|
is-callable@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
|
||||||
|
|
||||||
is-ci@^2.0.0:
|
is-ci@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
||||||
|
@ -2793,7 +2901,7 @@ is-data-descriptor@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
kind-of "^6.0.0"
|
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"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||||
|
|
||||||
|
@ -2856,10 +2964,22 @@ is-installed-globally@^0.3.1:
|
||||||
global-dirs "^2.0.1"
|
global-dirs "^2.0.1"
|
||||||
is-path-inside "^3.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"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
is-npm@^4.0.0:
|
is-npm@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
|
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
is-number@^3.0.0:
|
is-number@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||||
|
@ -2890,10 +3010,24 @@ is-regex@^1.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
has "^1.0.3"
|
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"
|
||||||
|
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"
|
||||||
|
|
||||||
is-stream@^1.1.0:
|
is-stream@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
is-symbol@^1.0.2:
|
is-symbol@^1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
||||||
|
@ -2908,10 +3042,27 @@ is-type-of@^1.0.0:
|
||||||
is-class-hotfix "~0.0.6"
|
is-class-hotfix "~0.0.6"
|
||||||
isstream "~0.1.2"
|
isstream "~0.1.2"
|
||||||
|
|
||||||
|
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"
|
||||||
|
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, is-typedarray@~1.0.0:
|
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
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"
|
||||||
|
|
||||||
|
is-weakset@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
|
||||||
|
|
||||||
is-windows@^1.0.2:
|
is-windows@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||||
|
@ -2932,6 +3083,10 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
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"
|
||||||
|
|
||||||
isbinaryfile@^4.0.6:
|
isbinaryfile@^4.0.6:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b"
|
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b"
|
||||||
|
@ -3871,6 +4026,10 @@ ltgt@~2.1.3:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34"
|
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34"
|
||||||
|
|
||||||
|
lunr@^2.3.5:
|
||||||
|
version "2.3.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
|
||||||
|
|
||||||
make-dir@^2.1.0:
|
make-dir@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||||
|
@ -4035,6 +4194,10 @@ nan@^2.12.1:
|
||||||
version "2.14.0"
|
version "2.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||||
|
|
||||||
|
nanoid@^2.1.0:
|
||||||
|
version "2.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
|
@ -4182,6 +4345,17 @@ object-inspect@^1.7.0:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
|
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"
|
||||||
|
|
||||||
|
object-is@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6"
|
||||||
|
dependencies:
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
es-abstract "^1.17.5"
|
||||||
|
|
||||||
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1:
|
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||||
|
@ -4828,6 +5002,17 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
extend-shallow "^3.0.2"
|
extend-shallow "^3.0.2"
|
||||||
safe-regex "^1.1.0"
|
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"
|
||||||
|
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"
|
||||||
|
|
||||||
regexpp@^2.0.1:
|
regexpp@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
|
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
|
||||||
|
@ -4996,10 +5181,14 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||||
|
|
||||||
|
safe-buffer@~5.2.0:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
|
||||||
safe-regex@^1.1.0:
|
safe-regex@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||||
|
@ -5119,6 +5308,19 @@ shellwords@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||||
|
|
||||||
|
shortid@^2.2.8:
|
||||||
|
version "2.2.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122"
|
||||||
|
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"
|
||||||
|
dependencies:
|
||||||
|
es-abstract "^1.18.0-next.0"
|
||||||
|
object-inspect "^1.8.0"
|
||||||
|
|
||||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||||
|
@ -5335,7 +5537,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
|
||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
strip-ansi "^6.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"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5358,7 +5560,7 @@ string.prototype.trimright@^2.1.1:
|
||||||
es-abstract "^1.17.5"
|
es-abstract "^1.17.5"
|
||||||
string.prototype.trimend "^1.0.0"
|
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"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5476,6 +5678,10 @@ supports-color@^7.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
|
svelte@^3.9.2:
|
||||||
|
version "3.24.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.24.1.tgz#aca364937dd1df27fe131e2a4c234acb6061db4b"
|
||||||
|
|
||||||
symbol-tree@^3.2.2:
|
symbol-tree@^3.2.2:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
|
@ -5908,10 +6114,40 @@ whatwg-url@^7.0.0:
|
||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
webidl-conversions "^4.0.2"
|
webidl-conversions "^4.0.2"
|
||||||
|
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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:
|
which-module@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
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"
|
||||||
|
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:
|
which@^1.2.9, which@^1.3.0:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
|
|
|
@ -93,6 +93,7 @@
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"name": "Input",
|
"name": "Input",
|
||||||
|
"bindable": "value",
|
||||||
"description": "An HTML input",
|
"description": "An HTML input",
|
||||||
"props": {
|
"props": {
|
||||||
"value": "string",
|
"value": "string",
|
||||||
|
@ -131,6 +132,7 @@
|
||||||
},
|
},
|
||||||
"select": {
|
"select": {
|
||||||
"name": "Select",
|
"name": "Select",
|
||||||
|
"bindable": "value",
|
||||||
"description": "An HTML <select> (dropdown)",
|
"description": "An HTML <select> (dropdown)",
|
||||||
"props": {
|
"props": {
|
||||||
"value": "string",
|
"value": "string",
|
||||||
|
@ -152,7 +154,10 @@
|
||||||
"children": false,
|
"children": false,
|
||||||
"props": {
|
"props": {
|
||||||
"text": "string",
|
"text": "string",
|
||||||
"type": {"type": "string", "default": "none"}
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "none"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"div",
|
"div",
|
||||||
|
@ -171,6 +176,7 @@
|
||||||
},
|
},
|
||||||
"checkbox": {
|
"checkbox": {
|
||||||
"name": "Checkbox",
|
"name": "Checkbox",
|
||||||
|
"bindable": "value",
|
||||||
"description": "A selectable checkbox component",
|
"description": "A selectable checkbox component",
|
||||||
"props": {
|
"props": {
|
||||||
"label": "string",
|
"label": "string",
|
||||||
|
@ -181,6 +187,7 @@
|
||||||
},
|
},
|
||||||
"radiobutton": {
|
"radiobutton": {
|
||||||
"name": "Radiobutton",
|
"name": "Radiobutton",
|
||||||
|
"bindable": "value",
|
||||||
"description": "A selectable radiobutton component",
|
"description": "A selectable radiobutton component",
|
||||||
"props": {
|
"props": {
|
||||||
"label": "string",
|
"label": "string",
|
||||||
|
@ -245,6 +252,7 @@
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"description": "A configurable data list that attaches to your backend models.",
|
"description": "A configurable data list that attaches to your backend models.",
|
||||||
|
"context": "model",
|
||||||
"children": true,
|
"children": true,
|
||||||
"data": true,
|
"data": true,
|
||||||
"props": {
|
"props": {
|
||||||
|
@ -265,6 +273,7 @@
|
||||||
},
|
},
|
||||||
"recorddetail": {
|
"recorddetail": {
|
||||||
"description": "Loads a record, using an ID in the url",
|
"description": "Loads a record, using an ID in the url",
|
||||||
|
"context": "model",
|
||||||
"children": true,
|
"children": true,
|
||||||
"data": true,
|
"data": true,
|
||||||
"props": {
|
"props": {
|
||||||
|
@ -288,7 +297,7 @@
|
||||||
"12rem",
|
"12rem",
|
||||||
"16rem",
|
"16rem",
|
||||||
"20rem",
|
"20rem",
|
||||||
"24rem"
|
"24rem"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"cardWidth": {
|
"cardWidth": {
|
||||||
|
@ -297,7 +306,7 @@
|
||||||
"options": [
|
"options": [
|
||||||
"16rem",
|
"16rem",
|
||||||
"20rem",
|
"20rem",
|
||||||
"24rem"
|
"24rem"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,7 +328,7 @@
|
||||||
"options": [
|
"options": [
|
||||||
"8rem",
|
"8rem",
|
||||||
"12rem",
|
"12rem",
|
||||||
"16rem"
|
"16rem"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"cardWidth": {
|
"cardWidth": {
|
||||||
|
@ -332,7 +341,7 @@
|
||||||
"40rem",
|
"40rem",
|
||||||
"48rem",
|
"48rem",
|
||||||
"60rem",
|
"60rem",
|
||||||
"100%"
|
"100%"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"imageHeight": {
|
"imageHeight": {
|
||||||
|
@ -341,7 +350,7 @@
|
||||||
"options": [
|
"options": [
|
||||||
"8rem",
|
"8rem",
|
||||||
"12rem",
|
"12rem",
|
||||||
"16rem"
|
"16rem"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,7 +401,7 @@
|
||||||
"lineGradient": "string",
|
"lineGradient": "string",
|
||||||
"titleText": "string",
|
"titleText": "string",
|
||||||
"valueLabel": "string"
|
"valueLabel": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stackedbar": {
|
"stackedbar": {
|
||||||
"description": "Stacked Bar Chart",
|
"description": "Stacked Bar Chart",
|
||||||
|
@ -421,7 +430,7 @@
|
||||||
"yAxisLabel": "string",
|
"yAxisLabel": "string",
|
||||||
"yAxisLabelOffset": "number",
|
"yAxisLabelOffset": "number",
|
||||||
"useLegend": "bool"
|
"useLegend": "bool"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stackarea": {
|
"stackarea": {
|
||||||
"description": "Step Chart",
|
"description": "Step Chart",
|
||||||
|
@ -450,7 +459,7 @@
|
||||||
"xAxisCustomFormat": "string",
|
"xAxisCustomFormat": "string",
|
||||||
"xAxisFormat": "string",
|
"xAxisFormat": "string",
|
||||||
"xAxisScale": "string",
|
"xAxisScale": "string",
|
||||||
"xAxisValueType":"string",
|
"xAxisValueType": "string",
|
||||||
"yTicks": "number",
|
"yTicks": "number",
|
||||||
"xTicks": "number",
|
"xTicks": "number",
|
||||||
"yAxisBaseline": "string",
|
"yAxisBaseline": "string",
|
||||||
|
@ -468,14 +477,14 @@
|
||||||
"xAxisLabel": "string",
|
"xAxisLabel": "string",
|
||||||
"xAxisLabelOffset": "string",
|
"xAxisLabelOffset": "string",
|
||||||
"yAxisLabel": "string",
|
"yAxisLabel": "string",
|
||||||
"yAxisLabelOffset": "string",
|
"yAxisLabelOffset": "string",
|
||||||
"yTicks": "string"
|
"yTicks": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scatterplot": {
|
"scatterplot": {
|
||||||
"description": "Scatterplot Chart",
|
"description": "Scatterplot Chart",
|
||||||
"data": true,
|
"data": true,
|
||||||
"props": {
|
"props": {
|
||||||
"model": "string",
|
"model": "string",
|
||||||
"color": "string",
|
"color": "string",
|
||||||
"height": "number",
|
"height": "number",
|
||||||
|
@ -491,7 +500,7 @@
|
||||||
"xTicks": "string",
|
"xTicks": "string",
|
||||||
"yAxisFormat": "string",
|
"yAxisFormat": "string",
|
||||||
"yAxisLabel": "string",
|
"yAxisLabel": "string",
|
||||||
"yAxisLabelOffset": "string",
|
"yAxisLabelOffset": "string",
|
||||||
"yTicks": "string"
|
"yTicks": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -547,7 +556,7 @@
|
||||||
"tooltipTitle": "string"
|
"tooltipTitle": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"brush":{
|
"brush": {
|
||||||
"description": "brush chart",
|
"description": "brush chart",
|
||||||
"data": true,
|
"data": true,
|
||||||
"props": {
|
"props": {
|
||||||
|
@ -575,7 +584,7 @@
|
||||||
"useLegend": "bool",
|
"useLegend": "bool",
|
||||||
"yAxisLabel": "string",
|
"yAxisLabel": "string",
|
||||||
"boxSize": "number"
|
"boxSize": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groupedbar": {
|
"groupedbar": {
|
||||||
"description": "Groupedbar chart",
|
"description": "Groupedbar chart",
|
||||||
|
@ -587,8 +596,8 @@
|
||||||
"color": "string",
|
"color": "string",
|
||||||
"height": "string",
|
"height": "string",
|
||||||
"width": "string",
|
"width": "string",
|
||||||
"margin":"string",
|
"margin": "string",
|
||||||
"grid":"string",
|
"grid": "string",
|
||||||
"groupLabel": "string",
|
"groupLabel": "string",
|
||||||
"isAnimated": "bool",
|
"isAnimated": "bool",
|
||||||
"isHorizontal": "bool",
|
"isHorizontal": "bool",
|
||||||
|
@ -600,7 +609,7 @@
|
||||||
"bullet": {
|
"bullet": {
|
||||||
"description": "Bullet chart",
|
"description": "Bullet chart",
|
||||||
"data": true,
|
"data": true,
|
||||||
"props": {
|
"props": {
|
||||||
"model": "string",
|
"model": "string",
|
||||||
"color": "string",
|
"color": "string",
|
||||||
"customSubtitle": "string",
|
"customSubtitle": "string",
|
||||||
|
@ -651,12 +660,12 @@
|
||||||
"styleBindingProperty": "font-family",
|
"styleBindingProperty": "font-family",
|
||||||
"options": [
|
"options": [
|
||||||
"initial",
|
"initial",
|
||||||
"Times New Roman",
|
"Times New Roman",
|
||||||
"Georgia",
|
"Georgia",
|
||||||
"Arial",
|
"Arial",
|
||||||
"Arial Black",
|
"Arial Black",
|
||||||
"Comic Sans MS",
|
"Comic Sans MS",
|
||||||
"Impact",
|
"Impact",
|
||||||
"Lucida Sans Unicode"
|
"Lucida Sans Unicode"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -698,7 +707,7 @@
|
||||||
],
|
],
|
||||||
"default": "div"
|
"default": "div"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"container": true,
|
"container": true,
|
||||||
"tags": [
|
"tags": [
|
||||||
"div",
|
"div",
|
||||||
|
@ -712,7 +721,7 @@
|
||||||
"props": {
|
"props": {
|
||||||
"className": "string",
|
"className": "string",
|
||||||
"text": "string",
|
"text": "string",
|
||||||
"type": {
|
"type": {
|
||||||
"type": "options",
|
"type": "options",
|
||||||
"default": "h1",
|
"default": "h1",
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -741,4 +750,4 @@
|
||||||
"className": "string"
|
"className": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
const onchange = ev => {
|
const onchange = ev => {
|
||||||
if (_bb) {
|
if (_bb) {
|
||||||
_bb.setStateFromBinding(_bb.props.value, ev.target.value)
|
const val = type === "checkbox" ? ev.target.checked : ev.target.value
|
||||||
|
_bb.setBinding("value", val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
const onchange = ev => {
|
const onchange = ev => {
|
||||||
if (_bb) {
|
if (_bb) {
|
||||||
_bb.setStateFromBinding(_bb.props.value, ev.target.value)
|
_bb.setBinding("value", ev.target.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue