diff --git a/packages/builder/package.json b/packages/builder/package.json index e4b9040088..8645f285af 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -112,7 +112,7 @@ "rollup-plugin-terser": "^7.0.2", "rollup-plugin-url": "^2.2.2", "start-server-and-test": "^1.11.0", - "svelte": "^3.24.1", + "svelte": "^3.29.0", "svelte-jester": "^1.0.6" }, "gitHead": "115189f72a850bfb52b65ec61d932531bf327072" diff --git a/packages/builder/src/builderStore/getNewComponentName.js b/packages/builder/src/builderStore/getNewComponentName.js index b3ddc4e953..9ab8ef16dc 100644 --- a/packages/builder/src/builderStore/getNewComponentName.js +++ b/packages/builder/src/builderStore/getNewComponentName.js @@ -8,8 +8,9 @@ export default function(component, state) { const findMatches = props => { walkProps(props, c => { - if ((c._instanceName || "").startsWith(capitalised)) { - matchingComponents.push(c._instanceName) + const thisInstanceName = get_capitalised_name(c._instanceName) + if ((thisInstanceName || "").startsWith(capitalised)) { + matchingComponents.push(thisInstanceName) } }) } diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index 70b88eb778..6941c74b22 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -124,17 +124,18 @@ const saveScreen = store => screen => { } const _saveScreen = async (store, s, screen) => { - const currentPageScreens = s.pages[s.currentPageName]._screens + const pageName = s.currentPageName || "main" + const currentPageScreens = s.pages[pageName]._screens await api - .post(`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`, screen) + .post(`/_builder/api/${s.appId}/pages/${pageName}/screen`, screen) .then(() => { if (currentPageScreens.includes(screen)) return const screens = [...currentPageScreens, screen] store.update(innerState => { - innerState.pages[s.currentPageName]._screens = screens + innerState.pages[pageName]._screens = screens innerState.screens = screens innerState.currentPreviewItem = screen innerState.allScreens = [...innerState.allScreens, screen] @@ -153,27 +154,17 @@ const _saveScreen = async (store, s, screen) => { return s } -const createScreen = store => (screenName, route, layoutComponentName) => { +const createScreen = store => async screen => { + let savePromise store.update(state => { - const rootComponent = state.components[layoutComponentName] - - const newScreen = { - description: "", - url: "", - _css: "", - props: createProps(rootComponent).props, - } - newScreen.route = route - newScreen.name = newScreen.props._id - newScreen.props._instanceName = screenName || "" - state.currentPreviewItem = newScreen - state.currentComponentInfo = newScreen.props + state.currentPreviewItem = screen + state.currentComponentInfo = screen.props state.currentFrontEndType = "screen" - - _saveScreen(store, state, newScreen) - + savePromise = _saveScreen(store, state, screen) + regenerateCssForCurrentScreen(state) return state }) + await savePromise } const setCurrentScreen = store => screenName => { diff --git a/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js new file mode 100644 index 0000000000..a8ab27df3d --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js @@ -0,0 +1,22 @@ +export default { + name: `Create from scratch`, + create: () => createScreen(), +} + +const createScreen = () => ({ + props: { + _id: "", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + type: "div", + _children: [], + _instanceName: "", + }, + route: "", + name: "screen-id", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/emptyNewRecordScreen.js b/packages/builder/src/builderStore/store/screenTemplates/emptyNewRecordScreen.js new file mode 100644 index 0000000000..db6910809f --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/emptyNewRecordScreen.js @@ -0,0 +1,22 @@ +export default { + name: `New Row (Empty)`, + create: () => createScreen(), +} + +const createScreen = () => ({ + props: { + _id: "", + _component: "@budibase/standard-components/newrow", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [], + _instanceName: "", + model: "", + }, + route: "", + name: "screen-id", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/emptyRecordDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/emptyRecordDetailScreen.js new file mode 100644 index 0000000000..5c0b31a2df --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/emptyRecordDetailScreen.js @@ -0,0 +1,22 @@ +export default { + name: `Row Detail (Empty)`, + create: () => createScreen(), +} + +const createScreen = () => ({ + props: { + _id: "", + _component: "@budibase/standard-components/rowdetail", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [], + _instanceName: "", + model: "", + }, + route: "", + name: "screen-id", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/index.js b/packages/builder/src/builderStore/store/screenTemplates/index.js new file mode 100644 index 0000000000..55a93c6e4d --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/index.js @@ -0,0 +1,35 @@ +import newRecordScreen from "./newRecordScreen" +import recordDetailScreen from "./recordDetailScreen" +import recordListScreen from "./recordListScreen" +import emptyNewRecordScreen from "./emptyNewRecordScreen" +import createFromScratchScreen from "./createFromScratchScreen" +import emptyRecordDetailScreen from "./emptyRecordDetailScreen" +import { generateNewIdsForComponent } from "../../storeUtils" +import { uuid } from "builderStore/uuid" + +const allTemplates = models => [ + createFromScratchScreen, + ...newRecordScreen(models), + ...recordDetailScreen(models), + ...recordListScreen(models), + emptyNewRecordScreen, + emptyRecordDetailScreen, +] + +// allows us to apply common behaviour to all create() functions +const createTemplateOverride = (frontendState, create) => () => { + const screen = create() + for (let component of screen.props._children) { + generateNewIdsForComponent(component, frontendState, false) + } + screen.props._id = uuid() + screen.name = screen.props._id + screen.route = screen.route.toLowerCase() + return screen +} + +export default (frontendState, models) => + allTemplates(models).map(template => ({ + ...template, + create: createTemplateOverride(frontendState, template.create), + })) diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js new file mode 100644 index 0000000000..bbe9b0d911 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js @@ -0,0 +1,135 @@ +export default function(models) { + return models.map(model => { + const fields = Object.keys(model.schema) + const heading = fields.length > 0 ? `{{ data.${fields[0]} }}` : "Add Row" + return { + name: `${model.name} - New`, + create: () => createScreen(model, heading), + id: NEW_RECORD_TEMPLATE, + } + }) +} + +export const NEW_RECORD_TEMPLATE = "NEW_RECORD_TEMPLATE" + +const createScreen = (model, heading) => ({ + props: { + _id: "", + _component: "@budibase/standard-components/newrow", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + model: model._id, + _children: [ + { + _id: "", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + text: heading, + type: "h1", + _instanceName: "Heading 1", + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/dataform", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + _instanceName: `${model.name} Form`, + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/container", + _styles: { + normal: { + display: "flex", + "flex-direction": "row", + "align-items": "center", + "justify-content": "flex-end", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + type: "div", + _instanceName: "Buttons Container", + _children: [ + { + _id: "", + _component: "@budibase/standard-components/button", + _styles: { + normal: { + "margin-right": "20px", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + text: "Back", + className: "", + disabled: false, + onClick: [ + { + parameters: { + url: `/${model.name.toLowerCase()}`, + }, + "##eventHandlerType": "Navigate To", + }, + ], + _instanceName: "Back Button", + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/button", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + text: "Save", + className: "", + disabled: false, + onClick: [ + { + parameters: { + contextPath: "data", + modelId: model._id, + }, + "##eventHandlerType": "Save Record", + }, + ], + _instanceName: "Save Button", + _children: [], + }, + ], + }, + ], + _instanceName: `${model.name} - New`, + _code: "", + }, + route: `/${model.name.toLowerCase()}/new`, + name: "", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js new file mode 100644 index 0000000000..fb4c3230ee --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js @@ -0,0 +1,135 @@ +export default function(models) { + return models.map(model => { + const fields = Object.keys(model.schema) + const heading = fields.length > 0 ? `{{ data.${fields[0]} }}` : "Detail" + return { + name: `${model.name} - Detail`, + create: () => createScreen(model, heading), + id: RECORD_DETAIL_TEMPLATE, + } + }) +} + +export const RECORD_DETAIL_TEMPLATE = "RECORD_DETAIL_TEMPLATE" + +const createScreen = (model, heading) => ({ + props: { + _id: "", + _component: "@budibase/standard-components/rowdetail", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + model: model._id, + _children: [ + { + _id: "", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + text: heading, + type: "h1", + _instanceName: "Heading 1", + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/dataform", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + _instanceName: `${model.name} Form`, + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/container", + _styles: { + normal: { + display: "flex", + "flex-direction": "row", + "align-items": "center", + "justify-content": "flex-end", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + type: "div", + _instanceName: "Buttons Container", + _children: [ + { + _id: "", + _component: "@budibase/standard-components/button", + _styles: { + normal: { + "margin-right": "20px", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + text: "Back", + className: "", + disabled: false, + onClick: [ + { + parameters: { + url: `/${model.name.toLowerCase()}`, + }, + "##eventHandlerType": "Navigate To", + }, + ], + _instanceName: "Back Button", + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/button", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + text: "Save", + className: "", + disabled: false, + onClick: [ + { + parameters: { + contextPath: "data", + modelId: model._id, + }, + "##eventHandlerType": "Save Record", + }, + ], + _instanceName: "Save Button", + _children: [], + }, + ], + }, + ], + _instanceName: `${model.name} - Detail`, + _code: "", + }, + route: `/${model.name.toLowerCase()}/:id`, + name: "", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/recordListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/recordListScreen.js new file mode 100644 index 0000000000..32f4d0696b --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/recordListScreen.js @@ -0,0 +1,118 @@ +export default function(models) { + return models.map(model => { + return { + name: `${model.name} - List`, + create: () => createScreen(model), + id: RECORD_LIST_TEMPLATE, + } + }) +} + +export const RECORD_LIST_TEMPLATE = "RECORD_LIST_TEMPLATE" + +const createScreen = model => ({ + props: { + _id: "", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + type: "div", + _children: [ + { + _id: "", + _component: "@budibase/standard-components/container", + _styles: { + normal: { + display: "flex", + "flex-direction": "row", + "justify-content": "space-between", + "align-items": "center", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + type: "div", + _instanceName: "Header", + _children: [ + { + _id: "", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + text: `${model.name} List`, + type: "h1", + _instanceName: "Heading 1", + _children: [], + }, + { + _id: "", + _component: "@budibase/standard-components/button", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + text: "Create New", + className: "", + disabled: false, + onClick: [ + { + parameters: { + url: `/${model.name}/new`, + }, + "##eventHandlerType": "Navigate To", + }, + ], + _instanceName: "Create New Button", + _children: [], + }, + ], + }, + { + _id: "", + _component: "@budibase/standard-components/datatable", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + datasource: { + label: "Deals", + name: `all_${model._id}`, + modelId: model._id, + isModel: true, + }, + stripeColor: "", + borderColor: "", + backgroundColor: "", + color: "", + _instanceName: `${model.name} Table`, + _children: [], + }, + ], + _instanceName: `${model.name} - List`, + _code: "", + className: "", + onLoad: [], + }, + route: `/${model.name.toLowerCase()}`, + name: "", +}) diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js index 2efffc9d4c..aeff3f2528 100644 --- a/packages/builder/src/builderStore/storeUtils.js +++ b/packages/builder/src/builderStore/storeUtils.js @@ -85,10 +85,10 @@ export const regenerateCssForCurrentScreen = state => { return state } -export const generateNewIdsForComponent = (c, state) => +export const generateNewIdsForComponent = (c, state, changeName = true) => walkProps(c, p => { p._id = uuid() - p._instanceName = getNewComponentName(p._component, state) + if (changeName) p._instanceName = getNewComponentName(p._component, state) }) export const getComponentDefinition = (state, name) => diff --git a/packages/builder/src/components/backend/ModelNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/ModelNavigator/modals/CreateTableModal.svelte index 1cc0e8894c..c4b62d6cec 100644 --- a/packages/builder/src/components/backend/ModelNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/ModelNavigator/modals/CreateTableModal.svelte @@ -1,11 +1,21 @@ diff --git a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte index ec2122d6bf..828c99a2a0 100644 --- a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte @@ -68,18 +68,8 @@ } const copyComponent = () => { - store.update(s => { - const parent = getParent(s.currentPreviewItem.props, component) - const copiedComponent = cloneDeep(component) - walkProps(copiedComponent, p => { - p._id = uuid() - }) - parent._children = [...parent._children, copiedComponent] - saveCurrentPreviewItem(s) - s.currentComponentInfo = copiedComponent - regenerateCssForCurrentScreen(s) - return s - }) + storeComponentForCopy(false) + pasteComponent("below") } const deleteComponent = () => { diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte index a6608307c3..b9bcf2fa95 100644 --- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte +++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte @@ -41,14 +41,6 @@ const onStyleChanged = store.setComponentStyle - function onPropChanged(key, value) { - if ($store.currentView !== "component") { - store.setPageOrScreenProp(key, value) - return - } - store.setComponentProp(key, value) - } - $: isComponentOrScreen = $store.currentView === "component" || $store.currentFrontEndType === "screen" @@ -103,7 +95,8 @@ {componentDefinition} {panelDefinition} displayNameField={displayName} - onChange={onPropChanged} + onChange={store.setComponentProp} + onScreenPropChange={store.setPageOrScreenProp} screenOrPageInstance={$store.currentView !== 'component' && $store.currentPreviewItem} /> {/if} diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/SaveFields.svelte b/packages/builder/src/components/userInterface/EventsEditor/actions/SaveFields.svelte index 5011f8f173..cac4ed761a 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/actions/SaveFields.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/actions/SaveFields.svelte @@ -73,7 +73,7 @@ {#if fields} {#each fields as field} - + + + {/each} + + {/if} + + {#if parameters.contextPath} + + {/if} + + + + diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/index.js b/packages/builder/src/components/userInterface/EventsEditor/actions/index.js index e1a85e307e..85f90d124b 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/actions/index.js +++ b/packages/builder/src/components/userInterface/EventsEditor/actions/index.js @@ -1,6 +1,5 @@ import NavigateTo from "./NavigateTo.svelte" -import UpdateRecord from "./UpdateRecord.svelte" -import CreateRecord from "./CreateRecord.svelte" +import SaveRecord from "./SaveRecord.svelte" // defines what actions are available, when adding a new one // the component is the setup panel for the action @@ -9,15 +8,11 @@ import CreateRecord from "./CreateRecord.svelte" export default [ { - name: "Create Record", - component: CreateRecord, + name: "Save Record", + component: SaveRecord, }, { name: "Navigate To", component: NavigateTo, }, - { - name: "Update Record", - component: UpdateRecord, - }, ] diff --git a/packages/builder/src/components/userInterface/NewScreenModal.svelte b/packages/builder/src/components/userInterface/NewScreenModal.svelte index d38d4bbb7b..0a9bc81987 100644 --- a/packages/builder/src/components/userInterface/NewScreenModal.svelte +++ b/packages/builder/src/components/userInterface/NewScreenModal.svelte @@ -1,27 +1,49 @@ + + + + - + diff --git a/packages/builder/src/components/userInterface/SettingsView.svelte b/packages/builder/src/components/userInterface/SettingsView.svelte index 159093fe8e..0460e15ad6 100644 --- a/packages/builder/src/components/userInterface/SettingsView.svelte +++ b/packages/builder/src/components/userInterface/SettingsView.svelte @@ -11,6 +11,7 @@ export let componentDefinition = {} export let componentInstance = {} export let onChange = () => {} + export let onScreenPropChange = () => {} export let displayNameField = false export let screenOrPageInstance @@ -91,7 +92,7 @@ label={def.label} key={def.key} value={screenOrPageInstance[def.key]} - {onChange} + onChange={onScreenPropChange} props={{ ...excludeProps(def, ['control', 'label']) }} /> {/each}
diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js index 296ae26606..3f853efcfb 100644 --- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js +++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js @@ -583,23 +583,7 @@ export default { icon: "ri-file-edit-line", properties: { design: { ...all }, - settings: [ - { - label: "Table", - key: "model", - control: ModelSelect, - }, - { - label: "Title", - key: "title", - control: Input, - }, - { - label: "Button Text", - key: "buttonText", - control: Input, - }, - ], + settings: [], }, }, { @@ -608,23 +592,7 @@ export default { icon: "ri-file-edit-line", properties: { design: { ...all }, - settings: [ - { - label: "Table", - key: "model", - control: ModelSelect, - }, - { - label: "Title", - key: "title", - control: Input, - }, - { - label: "Button Text", - key: "buttonText", - control: Input, - }, - ], + settings: [], }, }, ], @@ -1125,8 +1093,8 @@ export default { // children: [], // }, { - name: "Record Detail", - _component: "@budibase/standard-components/recorddetail", + name: "Row Detail", + _component: "@budibase/standard-components/rowdetail", description: "Loads a record, using an id from the URL, which can be used with {{ context }}, in children", icon: "ri-profile-line", @@ -1136,6 +1104,18 @@ export default { }, children: [], }, + { + name: "New Row", + _component: "@budibase/standard-components/newrow", + description: + "Sets up a new record for creation, which can be used with {{ context }}, in children", + icon: "ri-profile-line", + properties: { + design: { ...all }, + settings: [{ label: "Table", key: "model", control: ModelSelect }], + }, + children: [], + }, // { // name: "Map", // _component: "@budibase/standard-components/datamap", diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index 94a7501bce..dfeebc7214 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -5850,10 +5850,10 @@ svelte-portal@^1.0.0: resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3" integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q== -svelte@^3.24.1: - version "3.25.1" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.25.1.tgz#218def1243fea5a97af6eb60f5e232315bb57ac4" - integrity sha512-IbrVKTmuR0BvDw4ii8/gBNy8REu7nWTRy9uhUz+Yuae5lIjWgSGwKlWtJGC2Vg95s+UnXPqDu0Kk/sUwe0t2GQ== +svelte@^3.29.0: + version "3.29.0" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.29.0.tgz#80acac4254341ad8f3301e5ef03f4127ea967d96" + integrity sha512-f+A65eyOQ5ujETLy+igNXtlr6AEjAQLYd1yJE1VwNiXMQO5Z/Vmiy3rL+zblV/9jd7rtTTWqO1IcuXsP2Qv0OA== symbol-observable@^1.1.0: version "1.2.0" diff --git a/packages/client/src/api/index.js b/packages/client/src/api/index.js index 34441d563b..f66cfcf69b 100644 --- a/packages/client/src/api/index.js +++ b/packages/client/src/api/index.js @@ -52,14 +52,14 @@ const apiOpts = { delete: del, } -const createRecord = async params => +const saveRecord = async (params, state) => await post({ url: `/api/${params.modelId}/records`, - body: makeRecordRequestBody(params), + body: makeRecordRequestBody(params, state), }) -const updateRecord = async params => { - const record = makeRecordRequestBody(params) +const updateRecord = async (params, state) => { + const record = makeRecordRequestBody(params, state) record._id = params._id await patch({ url: `/api/${params.modelId}/records/${params._id}`, @@ -67,8 +67,14 @@ const updateRecord = async params => { }) } -const makeRecordRequestBody = parameters => { - const body = {} +const makeRecordRequestBody = (parameters, state) => { + // start with the record thats currently in context + const body = { ...(state.data || {}) } + + // dont send the model + if (body._model) delete body._model + + // then override with supplied parameters for (let fieldName in parameters.fields) { const field = parameters.fields[fieldName] @@ -95,6 +101,6 @@ const makeRecordRequestBody = parameters => { export default { authenticate: authenticate(apiOpts), - createRecord, + saveRecord, updateRecord, } diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js index 05d8ef2fa3..fa80605b42 100644 --- a/packages/client/src/state/eventHandlers.js +++ b/packages/client/src/state/eventHandlers.js @@ -6,8 +6,8 @@ export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType" export const eventHandlers = routeTo => { const handlers = { "Navigate To": param => routeTo(param && param.url), - "Create Record": api.createRecord, "Update Record": api.updateRecord, + "Save Record": api.saveRecord, "Trigger Workflow": api.triggerWorkflow, } @@ -19,7 +19,7 @@ export const eventHandlers = routeTo => { const handler = handlers[action[EVENT_TYPE_MEMBER_NAME]] const parameters = createParameters(action.parameters, state) if (handler) { - await handler(parameters) + await handler(parameters, state) } } } diff --git a/packages/client/src/state/store.js b/packages/client/src/state/store.js index 6464589b03..cf284ec08e 100644 --- a/packages/client/src/state/store.js +++ b/packages/client/src/state/store.js @@ -95,7 +95,7 @@ const getState = contextStoreKey => contextStoreKey ? contextStores[contextStoreKey].state : rootState const getStore = contextStoreKey => - contextStoreKey ? contextStores[contextStoreKey] : rootStore + contextStoreKey ? contextStores[contextStoreKey].store : rootStore export default { subscribe, diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json index e600b264be..c7d59ddde4 100644 --- a/packages/standard-components/components.json +++ b/packages/standard-components/components.json @@ -218,20 +218,12 @@ "dataform": { "description": "an HTML table that fetches data from a table or view and displays it.", "data": true, - "props": { - "model": "models", - "title": "string", - "buttonText": "string" - } + "props": {} }, "dataformwide": { "description": "an HTML table that fetches data from a table or view and displays it.", "data": true, - "props": { - "model": "models", - "title": "string", - "buttonText": "string" - } + "props": {} }, "datalist": { "description": "A configurable data list that attaches to your backend models.", @@ -269,11 +261,22 @@ "destinationUrl": "string" } }, - "recorddetail": { + "rowdetail": { "description": "Loads a record, using an ID in the url", "context": "model", "children": true, "data": true, + "baseComponent": true, + "props": { + "model": "models" + } + }, + "newrow": { + "description": "Prepares a new record for creation", + "context": "model", + "children": true, + "data": true, + "baseComponent": true, "props": { "model": "models" } @@ -715,7 +718,7 @@ "default": "div" } }, - "container": true, + "baseComponent": true, "tags": [ "div", "container", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index af12bfbf19..8ed9aeab0f 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -16,17 +16,17 @@ "@budibase/client": "^0.2.0", "@rollup/plugin-commonjs": "^11.1.0", "lodash": "^4.17.15", - "rollup": "^1.11.0", + "rollup": "^2.11.2", "rollup-plugin-commonjs": "^10.0.2", "rollup-plugin-json": "^4.0.0", "rollup-plugin-livereload": "^1.0.1", "rollup-plugin-node-resolve": "^5.0.0", "rollup-plugin-postcss": "^3.1.5", - "rollup-plugin-svelte": "^5.0.0", - "rollup-plugin-terser": "^5.1.1", + "rollup-plugin-svelte": "^5.0.3", + "rollup-plugin-terser": "^7.0.2", "shortid": "^2.2.15", "sirv-cli": "^0.4.4", - "svelte": "^3.12.1" + "svelte": "^3.29.0" }, "keywords": [ "svelte" diff --git a/packages/standard-components/src/Form.svelte b/packages/standard-components/src/Form.svelte index 937a58f271..c9777b52b4 100644 --- a/packages/standard-components/src/Form.svelte +++ b/packages/standard-components/src/Form.svelte @@ -1,168 +1,61 @@ -
- {#if title} -

{title}

- {/if} -
- - {#each fields as field} -
- {#if !(schema[field].type === 'boolean' && !wide)} - - {/if} - {#if schema[field].type === 'options'} - - {:else if schema[field].type === 'datetime'} - - {:else if schema[field].type === 'boolean'} - - {:else if schema[field].type === 'number'} - - {:else if schema[field].type === 'string'} - - {:else if schema[field].type === 'attachment'} - - {:else if schema[field].type === 'link'} - - {/if} -
- {/each} -
- +
+ + {#each fields as field} +
+ {#if !(schema[field].type === 'boolean' && !wide)} + + {/if} + {#if schema[field].type === 'options'} + + {:else if schema[field].type === 'datetime'} + + {:else if schema[field].type === 'boolean'} + + {:else if schema[field].type === 'number'} + + {:else if schema[field].type === 'string'} + + {:else if schema[field].type === 'attachment'} + + {:else if schema[field].type === 'link'} + + {/if}
-
- + {/each} +
diff --git a/packages/standard-components/src/NewRow.svelte b/packages/standard-components/src/NewRow.svelte new file mode 100644 index 0000000000..fe63fd27dd --- /dev/null +++ b/packages/standard-components/src/NewRow.svelte @@ -0,0 +1,37 @@ + + +
diff --git a/packages/standard-components/src/RecordDetail.svelte b/packages/standard-components/src/RowDetail.svelte similarity index 70% rename from packages/standard-components/src/RecordDetail.svelte rename to packages/standard-components/src/RowDetail.svelte index aa9d39c0cc..dc767a6b6e 100644 --- a/packages/standard-components/src/RecordDetail.svelte +++ b/packages/standard-components/src/RowDetail.svelte @@ -20,6 +20,7 @@ if (response.status === 200) { const allRecords = await response.json() if (allRecords.length > 0) return allRecords[0] + return { modelId: model } } } @@ -29,31 +30,35 @@ let record // if srcdoc, then we assume this is the builder preview if (pathParts.length === 0 || pathParts[0] === "srcdoc") { - record = await fetchFirstRecord() - } else { - const id = pathParts[pathParts.length - 1] - const GET_RECORD_URL = `/api/${model}/records/${id}` + if (model) record = await fetchFirstRecord() + } else if (_bb.routeParams().id) { + const GET_RECORD_URL = `/api/${model}/records/${_bb.routeParams().id}` const response = await _bb.api.get(GET_RECORD_URL) if (response.status === 200) { record = await response.json() + } else { + throw new Error("Failed to fetch record.", response) } + } else { + throw new Exception("Record ID was not supplied to RowDetail") } if (record) { // Fetch model schema so we can check for linked records - const model = await fetchModel(record.modelId) - for (let key of Object.keys(model.schema)) { - if (model.schema[key].type === "link") { + const modelObj = await fetchModel(record.modelId) + for (let key of Object.keys(modelObj.schema)) { + if (modelObj.schema[key].type === "link") { record[key] = Array.isArray(record[key]) ? record[key].length : 0 } } + record._model = modelObj + _bb.attachChildren(target, { - hydrate: false, context: record, }) } else { - throw new Error("Failed to fetch record.", response) + _bb.attachChildren(target) } } diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js index 0ce5088cb8..c5a3c6085a 100644 --- a/packages/standard-components/src/index.js +++ b/packages/standard-components/src/index.js @@ -26,7 +26,8 @@ export { default as embed } from "./Embed.svelte" export { default as stackedlist } from "./StackedList.svelte" export { default as card } from "./Card.svelte" export { default as cardhorizontal } from "./CardHorizontal.svelte" -export { default as recorddetail } from "./RecordDetail.svelte" +export { default as rowdetail } from "./RowDetail.svelte" +export { default as newrow } from "./NewRow.svelte" export { default as datepicker } from "./DatePicker.svelte" export * from "./Chart" export { default as icon } from "./Icon.svelte"