2020-11-06 13:30:30 +01:00
|
|
|
import { get, writable } from "svelte/store"
|
2020-11-06 13:31:47 +01:00
|
|
|
import { cloneDeep } from "lodash/fp"
|
|
|
|
import {
|
|
|
|
createProps,
|
|
|
|
getBuiltin,
|
|
|
|
makePropsSafe,
|
2020-11-25 18:56:09 +01:00
|
|
|
} from "components/userInterface/assetParsing/createProps"
|
2020-12-01 17:22:06 +01:00
|
|
|
import {
|
|
|
|
allScreens,
|
|
|
|
backendUiStore,
|
|
|
|
currentAsset,
|
|
|
|
mainLayout,
|
2020-12-07 16:27:46 +01:00
|
|
|
selectedComponent,
|
2020-12-01 17:22:06 +01:00
|
|
|
} from "builderStore"
|
2020-11-04 17:13:50 +01:00
|
|
|
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
|
|
|
import api from "../api"
|
2020-12-02 17:15:14 +01:00
|
|
|
import { FrontendTypes } from "../../constants"
|
2020-11-04 17:13:50 +01:00
|
|
|
import getNewComponentName from "../getNewComponentName"
|
|
|
|
import analytics from "analytics"
|
2020-11-06 13:31:47 +01:00
|
|
|
import {
|
|
|
|
findChildComponentType,
|
|
|
|
generateNewIdsForComponent,
|
|
|
|
getComponentDefinition,
|
2020-12-07 16:27:46 +01:00
|
|
|
findParent,
|
2020-11-06 13:31:47 +01:00
|
|
|
} from "../storeUtils"
|
2020-11-04 17:13:50 +01:00
|
|
|
|
|
|
|
const INITIAL_FRONTEND_STATE = {
|
|
|
|
apps: [],
|
|
|
|
name: "",
|
|
|
|
description: "",
|
2020-12-02 17:15:14 +01:00
|
|
|
layouts: [],
|
2020-11-24 19:11:34 +01:00
|
|
|
screens: [],
|
2020-11-04 17:13:50 +01:00
|
|
|
components: [],
|
|
|
|
currentFrontEndType: "none",
|
2020-11-24 19:11:34 +01:00
|
|
|
currentAssetId: "",
|
2020-12-07 16:27:46 +01:00
|
|
|
selectedComponentId: "",
|
2020-11-04 17:13:50 +01:00
|
|
|
errors: [],
|
|
|
|
hasAppPackage: false,
|
|
|
|
libraries: null,
|
|
|
|
appId: "",
|
2020-11-19 22:07:25 +01:00
|
|
|
routes: {},
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export const getFrontendStore = () => {
|
|
|
|
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
|
|
|
|
|
|
|
store.actions = {
|
|
|
|
initialise: async pkg => {
|
2020-12-01 17:22:06 +01:00
|
|
|
const { layouts, screens, application } = pkg
|
|
|
|
|
2020-11-05 15:38:44 +01:00
|
|
|
store.update(state => {
|
2020-11-24 19:11:34 +01:00
|
|
|
state.appId = application._id
|
2020-11-05 15:38:44 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-11-04 18:09:45 +01:00
|
|
|
|
|
|
|
const components = await fetchComponentLibDefinitions(pkg.application._id)
|
|
|
|
|
|
|
|
store.update(state => ({
|
|
|
|
...state,
|
|
|
|
libraries: pkg.application.componentLibraries,
|
|
|
|
components,
|
|
|
|
name: pkg.application.name,
|
|
|
|
description: pkg.application.description,
|
|
|
|
appId: pkg.application._id,
|
2020-11-24 19:11:34 +01:00
|
|
|
layouts,
|
|
|
|
screens,
|
2020-11-04 18:09:45 +01:00
|
|
|
hasAppPackage: true,
|
|
|
|
builtins: [getBuiltin("##builtin/screenslot")],
|
|
|
|
appInstance: pkg.application.instance,
|
|
|
|
}))
|
|
|
|
|
|
|
|
await backendUiStore.actions.database.select(pkg.application.instance)
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-19 22:07:25 +01:00
|
|
|
routing: {
|
|
|
|
fetch: async () => {
|
|
|
|
const response = await api.get("/api/routing")
|
|
|
|
const json = await response.json()
|
|
|
|
|
|
|
|
store.update(state => {
|
|
|
|
state.routes = json.routes
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
2020-11-04 17:13:50 +01:00
|
|
|
screens: {
|
2020-12-09 13:22:30 +01:00
|
|
|
select: screenId => {
|
2020-11-04 17:13:50 +01:00
|
|
|
store.update(state => {
|
2020-12-09 17:01:16 +01:00
|
|
|
let screens = get(allScreens)
|
|
|
|
let screen =
|
|
|
|
screens.find(screen => screen._id === screenId) || screens[0]
|
2020-12-08 17:55:43 +01:00
|
|
|
if (!screen) return state
|
2020-12-01 17:22:06 +01:00
|
|
|
state.currentFrontEndType = FrontendTypes.SCREEN
|
2020-12-09 16:58:34 +01:00
|
|
|
state.currentAssetId = screen._id
|
2020-11-04 17:13:50 +01:00
|
|
|
state.currentView = "detail"
|
2020-12-08 17:55:43 +01:00
|
|
|
state.selectedComponentId = screen.props?._id
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
create: async screen => {
|
2020-12-03 13:08:33 +01:00
|
|
|
screen = await store.actions.screens.save(screen)
|
2020-11-04 17:13:50 +01:00
|
|
|
store.update(state => {
|
2020-12-07 21:29:41 +01:00
|
|
|
state.currentAssetId = screen._id
|
|
|
|
state.selectedComponentId = screen.props._id
|
2020-12-01 17:22:06 +01:00
|
|
|
state.currentFrontEndType = FrontendTypes.SCREEN
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-12-03 13:08:33 +01:00
|
|
|
return screen
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
save: async screen => {
|
2020-11-09 11:28:49 +01:00
|
|
|
const creatingNewScreen = screen._id === undefined
|
2020-11-25 18:56:09 +01:00
|
|
|
const response = await api.post(`/api/screens`, screen)
|
2020-12-03 13:08:33 +01:00
|
|
|
screen = await response.json()
|
2020-11-04 17:13:50 +01:00
|
|
|
|
|
|
|
store.update(state => {
|
2020-12-01 17:22:06 +01:00
|
|
|
const foundScreen = state.screens.findIndex(
|
|
|
|
el => el._id === screen._id
|
|
|
|
)
|
2020-11-25 18:56:09 +01:00
|
|
|
if (foundScreen !== -1) {
|
|
|
|
state.screens.splice(foundScreen, 1)
|
|
|
|
}
|
|
|
|
state.screens.push(screen)
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-12-09 15:51:42 +01:00
|
|
|
|
|
|
|
if (creatingNewScreen) {
|
|
|
|
store.actions.screens.select(screen._id)
|
|
|
|
}
|
|
|
|
|
2020-12-03 13:08:33 +01:00
|
|
|
return screen
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-19 12:15:29 +01:00
|
|
|
delete: async screens => {
|
|
|
|
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
|
|
|
|
2020-11-25 18:56:09 +01:00
|
|
|
const screenDeletePromises = []
|
2020-11-04 17:13:50 +01:00
|
|
|
store.update(state => {
|
2020-11-19 12:15:29 +01:00
|
|
|
for (let screenToDelete of screensToDelete) {
|
2020-12-01 17:22:06 +01:00
|
|
|
state.screens = state.screens.filter(
|
|
|
|
screen => screen._id !== screenToDelete._id
|
2020-11-19 12:15:29 +01:00
|
|
|
)
|
2020-12-01 17:22:06 +01:00
|
|
|
screenDeletePromises.push(
|
|
|
|
api.delete(
|
|
|
|
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
|
|
|
|
)
|
2020-11-04 17:13:50 +01:00
|
|
|
)
|
2020-12-08 17:55:43 +01:00
|
|
|
if (screenToDelete._id === state.currentAssetId) {
|
|
|
|
state.currentAssetId = ""
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
|
|
|
return state
|
|
|
|
})
|
2020-11-25 18:56:09 +01:00
|
|
|
await Promise.all(screenDeletePromises)
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
preview: {
|
2020-12-07 21:29:41 +01:00
|
|
|
saveSelected: async () => {
|
2020-11-04 17:13:50 +01:00
|
|
|
const state = get(store)
|
2020-12-07 21:29:41 +01:00
|
|
|
const selectedAsset = get(currentAsset)
|
|
|
|
|
2020-12-01 17:22:06 +01:00
|
|
|
if (state.currentFrontEndType !== FrontendTypes.LAYOUT) {
|
2020-12-02 14:41:00 +01:00
|
|
|
await store.actions.screens.save(selectedAsset)
|
2020-12-03 16:15:14 +01:00
|
|
|
} else {
|
|
|
|
await store.actions.layouts.save(selectedAsset)
|
2020-11-09 16:55:36 +01:00
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
2020-11-24 19:11:34 +01:00
|
|
|
layouts: {
|
2020-12-09 13:22:30 +01:00
|
|
|
select: layoutId => {
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
2020-12-02 15:15:07 +01:00
|
|
|
const layout = store.actions.layouts.find(layoutId)
|
2020-12-09 16:57:32 +01:00
|
|
|
if (!layout) return
|
2020-12-01 17:22:06 +01:00
|
|
|
state.currentFrontEndType = FrontendTypes.LAYOUT
|
2020-11-04 18:09:45 +01:00
|
|
|
state.currentView = "detail"
|
2020-12-07 16:27:46 +01:00
|
|
|
state.currentAssetId = layout._id
|
2020-12-08 17:55:43 +01:00
|
|
|
state.selectedComponentId = layout.props?._id
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-25 18:56:09 +01:00
|
|
|
save: async layout => {
|
2020-12-02 14:41:00 +01:00
|
|
|
const layoutToSave = cloneDeep(layout)
|
2020-12-09 15:51:42 +01:00
|
|
|
const creatingNewLayout = layoutToSave._id === undefined
|
2020-12-02 14:41:00 +01:00
|
|
|
const response = await api.post(`/api/layouts`, layoutToSave)
|
2020-12-09 15:51:42 +01:00
|
|
|
const savedLayout = await response.json()
|
2020-11-13 11:29:54 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
2020-12-05 10:43:00 +01:00
|
|
|
const layoutIdx = state.layouts.findIndex(
|
2020-12-09 15:51:42 +01:00
|
|
|
stateLayout => stateLayout._id === savedLayout._id
|
2020-12-01 17:22:06 +01:00
|
|
|
)
|
2020-12-05 10:43:00 +01:00
|
|
|
if (layoutIdx >= 0) {
|
|
|
|
// update existing layout
|
2020-12-09 15:51:42 +01:00
|
|
|
state.layouts.splice(layoutIdx, 1, savedLayout)
|
2020-12-05 00:16:07 +01:00
|
|
|
} else {
|
2020-12-05 10:43:00 +01:00
|
|
|
// save new layout
|
2020-12-09 15:51:42 +01:00
|
|
|
state.layouts.push(savedLayout)
|
2020-11-25 18:56:09 +01:00
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-12-09 15:51:42 +01:00
|
|
|
|
|
|
|
// Select layout if creating a new one
|
|
|
|
if (creatingNewLayout) {
|
|
|
|
store.actions.layouts.select(savedLayout._id)
|
|
|
|
}
|
|
|
|
|
|
|
|
return savedLayout
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
2020-12-02 15:15:07 +01:00
|
|
|
find: layoutId => {
|
|
|
|
if (!layoutId) {
|
2020-11-25 18:56:09 +01:00
|
|
|
return get(mainLayout)
|
|
|
|
}
|
|
|
|
const storeContents = get(store)
|
2020-12-02 15:15:07 +01:00
|
|
|
return storeContents.layouts.find(layout => layout._id === layoutId)
|
2020-11-25 18:56:09 +01:00
|
|
|
},
|
2020-12-05 00:42:22 +01:00
|
|
|
delete: async layoutToDelete => {
|
|
|
|
const response = await api.delete(
|
|
|
|
`/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}`
|
|
|
|
)
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
const json = await response.json()
|
|
|
|
throw new Error(json.message)
|
|
|
|
}
|
|
|
|
|
|
|
|
store.update(state => {
|
|
|
|
state.layouts = state.layouts.filter(
|
|
|
|
layout => layout._id !== layoutToDelete._id
|
|
|
|
)
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
|
|
|
components: {
|
|
|
|
select: component => {
|
|
|
|
store.update(state => {
|
2020-12-07 16:27:46 +01:00
|
|
|
state.selectedComponentId = component._id
|
2020-11-04 18:09:45 +01:00
|
|
|
state.currentView = "component"
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
create: (componentToAdd, presetProps) => {
|
2020-12-07 16:27:46 +01:00
|
|
|
const selectedAsset = get(currentAsset)
|
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
|
|
|
function findSlot(component_array) {
|
2020-11-25 18:56:09 +01:00
|
|
|
if (!component_array) {
|
|
|
|
return false
|
|
|
|
}
|
2020-11-19 12:15:29 +01:00
|
|
|
for (let component of component_array) {
|
|
|
|
if (component._component === "##builtin/screenslot") {
|
2020-11-04 18:09:45 +01:00
|
|
|
return true
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-19 12:15:29 +01:00
|
|
|
if (component._children) findSlot(component)
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
return false
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
if (
|
|
|
|
componentToAdd.startsWith("##") &&
|
2020-12-07 16:27:46 +01:00
|
|
|
findSlot(selectedAsset?.props._children)
|
2020-11-04 18:09:45 +01:00
|
|
|
) {
|
|
|
|
return state
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const component = getComponentDefinition(state, componentToAdd)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const instanceId = get(backendUiStore).selectedDatabase._id
|
|
|
|
const instanceName = getNewComponentName(component, state)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-06 13:31:47 +01:00
|
|
|
const newComponent = createProps(component, {
|
|
|
|
...presetProps,
|
|
|
|
_instanceId: instanceId,
|
|
|
|
_instanceName: instanceName,
|
|
|
|
})
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
const selected = get(selectedComponent)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
const currentComponentDefinition =
|
|
|
|
state.components[selected._component]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
const allowsChildren = currentComponentDefinition.children
|
|
|
|
|
|
|
|
// Determine where to put the new component.
|
|
|
|
let targetParent
|
|
|
|
if (allowsChildren) {
|
|
|
|
// Child of the selected component
|
|
|
|
targetParent = selected
|
|
|
|
} else {
|
|
|
|
// Sibling of selected component
|
|
|
|
targetParent = findParent(selectedAsset.props, selected)
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
// Don't continue if there's no parent
|
|
|
|
if (!targetParent) return state
|
|
|
|
|
|
|
|
// Push the new component
|
|
|
|
targetParent._children.push(newComponent.props)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
|
|
|
|
state.currentView = "component"
|
2020-12-07 16:27:46 +01:00
|
|
|
state.selectedComponentId = newComponent.props._id
|
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
analytics.captureEvent("Added Component", {
|
|
|
|
name: newComponent.props._component,
|
|
|
|
})
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
copy: (component, cut = false) => {
|
2020-12-07 16:27:46 +01:00
|
|
|
const selectedAsset = get(currentAsset)
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
2020-11-06 13:30:30 +01:00
|
|
|
state.componentToPaste = cloneDeep(component)
|
2020-11-04 18:09:45 +01:00
|
|
|
state.componentToPaste.isCut = cut
|
|
|
|
if (cut) {
|
2020-12-07 16:27:46 +01:00
|
|
|
const parent = findParent(selectedAsset.props, component._id)
|
2020-11-04 18:09:45 +01:00
|
|
|
parent._children = parent._children.filter(
|
2020-11-18 23:04:18 +01:00
|
|
|
child => child._id !== component._id
|
2020-11-04 18:09:45 +01:00
|
|
|
)
|
|
|
|
store.actions.components.select(parent)
|
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
2020-11-25 18:56:09 +01:00
|
|
|
paste: async (targetComponent, mode) => {
|
2020-12-07 16:27:46 +01:00
|
|
|
const selectedAsset = get(currentAsset)
|
2020-11-25 18:56:09 +01:00
|
|
|
let promises = []
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
|
|
|
if (!state.componentToPaste) return state
|
|
|
|
|
|
|
|
const componentToPaste = cloneDeep(state.componentToPaste)
|
|
|
|
// retain the same ids as things may be referencing this component
|
|
|
|
if (componentToPaste.isCut) {
|
|
|
|
// in case we paste a second time
|
|
|
|
state.componentToPaste.isCut = false
|
|
|
|
} else {
|
|
|
|
generateNewIdsForComponent(componentToPaste, state)
|
|
|
|
}
|
|
|
|
delete componentToPaste.isCut
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
if (mode === "inside") {
|
|
|
|
targetComponent._children.push(componentToPaste)
|
2020-11-04 17:13:50 +01:00
|
|
|
return state
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
const parent = findParent(selectedAsset.props, targetComponent)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
const targetIndex = parent._children.indexOf(targetComponent)
|
|
|
|
const index = mode === "above" ? targetIndex : targetIndex + 1
|
|
|
|
parent._children.splice(index, 0, cloneDeep(componentToPaste))
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-25 18:56:09 +01:00
|
|
|
promises.push(store.actions.preview.saveSelected())
|
2020-11-04 18:09:45 +01:00
|
|
|
store.actions.components.select(componentToPaste)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-11-25 18:56:09 +01:00
|
|
|
await Promise.all(promises)
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
2020-11-25 18:56:09 +01:00
|
|
|
updateStyle: async (type, name, value) => {
|
|
|
|
let promises = []
|
2020-12-07 16:27:46 +01:00
|
|
|
const selected = get(selectedComponent)
|
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
store.update(state => {
|
2020-12-07 16:27:46 +01:00
|
|
|
if (!selected._styles) {
|
|
|
|
selected._styles = {}
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
2020-12-07 16:27:46 +01:00
|
|
|
selected._styles[type][name] = value
|
2020-11-04 18:09:45 +01:00
|
|
|
|
|
|
|
// save without messing with the store
|
2020-11-25 18:56:09 +01:00
|
|
|
promises.push(store.actions.preview.saveSelected())
|
2020-11-04 18:09:45 +01:00
|
|
|
return state
|
|
|
|
})
|
2020-11-25 18:56:09 +01:00
|
|
|
await Promise.all(promises)
|
2020-11-04 18:09:45 +01:00
|
|
|
},
|
|
|
|
updateProp: (name, value) => {
|
|
|
|
store.update(state => {
|
2020-12-07 16:27:46 +01:00
|
|
|
let current_component = get(selectedComponent)
|
2020-11-04 18:09:45 +01:00
|
|
|
current_component[name] = value
|
|
|
|
|
2020-12-07 16:27:46 +01:00
|
|
|
state.selectedComponentId = current_component._id
|
2020-11-04 18:09:45 +01:00
|
|
|
store.actions.preview.saveSelected()
|
|
|
|
return state
|
|
|
|
})
|
|
|
|
},
|
|
|
|
findRoute: component => {
|
|
|
|
// Gets all the components to needed to construct a path.
|
2020-12-07 16:27:46 +01:00
|
|
|
const selectedAsset = get(currentAsset)
|
2020-11-04 18:09:45 +01:00
|
|
|
let pathComponents = []
|
|
|
|
let parent = component
|
|
|
|
let root = false
|
|
|
|
while (!root) {
|
2020-12-07 16:27:46 +01:00
|
|
|
parent = findParent(selectedAsset.props, parent)
|
2020-11-04 18:09:45 +01:00
|
|
|
if (!parent) {
|
|
|
|
root = true
|
|
|
|
} else {
|
|
|
|
pathComponents.push(parent)
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
}
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-25 18:56:09 +01:00
|
|
|
// Remove root entry since it's the screen or layout.
|
2020-11-04 18:09:45 +01:00
|
|
|
// Reverse array since we need the correct order of the IDs
|
|
|
|
const reversedComponents = pathComponents.reverse().slice(1)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Add component
|
|
|
|
const allComponents = [...reversedComponents, component]
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Map IDs
|
|
|
|
const IdList = allComponents.map(c => c._id)
|
2020-11-04 17:13:50 +01:00
|
|
|
|
2020-11-04 18:09:45 +01:00
|
|
|
// Construct ID Path:
|
2020-11-06 13:30:30 +01:00
|
|
|
return IdList.join("/")
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
2020-11-05 12:44:18 +01:00
|
|
|
links: {
|
|
|
|
save: async (url, title) => {
|
2020-11-25 18:56:09 +01:00
|
|
|
let promises = []
|
|
|
|
const layout = get(mainLayout)
|
2020-11-05 12:44:18 +01:00
|
|
|
store.update(state => {
|
2020-11-25 18:56:09 +01:00
|
|
|
// Try to extract a nav component from the master layout
|
2020-11-05 12:44:18 +01:00
|
|
|
const nav = findChildComponentType(
|
2020-11-25 18:56:09 +01:00
|
|
|
layout,
|
2020-12-02 19:10:46 +01:00
|
|
|
"@budibase/standard-components/navigation"
|
2020-11-05 12:44:18 +01:00
|
|
|
)
|
|
|
|
if (nav) {
|
|
|
|
let newLink
|
|
|
|
|
|
|
|
// Clone an existing link if one exists
|
|
|
|
if (nav._children && nav._children.length) {
|
|
|
|
// Clone existing link style
|
|
|
|
newLink = cloneDeep(nav._children[0])
|
|
|
|
|
|
|
|
// Manipulate IDs to ensure uniqueness
|
|
|
|
generateNewIdsForComponent(newLink, state, false)
|
|
|
|
|
|
|
|
// Set our new props
|
|
|
|
newLink._instanceName = `${title} Link`
|
|
|
|
newLink.url = url
|
|
|
|
newLink.text = title
|
|
|
|
} else {
|
|
|
|
// Otherwise create vanilla new link
|
|
|
|
const component = getComponentDefinition(
|
|
|
|
state,
|
|
|
|
"@budibase/standard-components/link"
|
|
|
|
)
|
|
|
|
const instanceId = get(backendUiStore).selectedDatabase._id
|
|
|
|
newLink = createProps(component, {
|
|
|
|
url,
|
|
|
|
text: title,
|
|
|
|
_instanceName: `${title} Link`,
|
|
|
|
_instanceId: instanceId,
|
|
|
|
}).props
|
|
|
|
}
|
|
|
|
|
2020-12-09 13:22:30 +01:00
|
|
|
// Save layout
|
2020-11-05 12:44:18 +01:00
|
|
|
nav._children = [...nav._children, newLink]
|
2020-11-25 18:56:09 +01:00
|
|
|
promises.push(store.actions.layouts.save(layout))
|
2020-11-05 12:44:18 +01:00
|
|
|
}
|
|
|
|
return state
|
|
|
|
})
|
2020-11-25 18:56:09 +01:00
|
|
|
await Promise.all(promises)
|
2020-11-05 12:44:18 +01:00
|
|
|
},
|
|
|
|
},
|
2020-11-04 17:13:50 +01:00
|
|
|
},
|
|
|
|
}
|
2020-11-04 18:09:45 +01:00
|
|
|
|
|
|
|
return store
|
2020-11-04 17:13:50 +01:00
|
|
|
}
|