Merge branch 'master' into contributor-enhancements
This commit is contained in:
commit
4a13cd27e8
|
@ -37,7 +37,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let overBackground
|
export let overBackground = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
|
|
@ -900,7 +900,7 @@ export const getSchemaForDatasourcePlus = (resourceId, options) => {
|
||||||
* optional and only needed for "provider" datasource types.
|
* optional and only needed for "provider" datasource types.
|
||||||
* @param datasource the datasource definition
|
* @param datasource the datasource definition
|
||||||
* @param options options for generating the schema
|
* @param options options for generating the schema
|
||||||
* @return {{schema: Object, table: Object}}
|
* @return {{schema: Object, table: Table}}
|
||||||
*/
|
*/
|
||||||
export const getSchemaForDatasource = (asset, datasource, options) => {
|
export const getSchemaForDatasource = (asset, datasource, options) => {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
import { writable, Writable, Readable } from "svelte/store"
|
import { writable, Writable, Readable } from "svelte/store"
|
||||||
|
import {
|
||||||
|
createLocalStorageStore,
|
||||||
|
createSessionStorageStore,
|
||||||
|
} from "@budibase/frontend-core"
|
||||||
|
|
||||||
|
export enum PersistenceType {
|
||||||
|
NONE = "none",
|
||||||
|
LOCAL = "local",
|
||||||
|
SESSION = "session",
|
||||||
|
}
|
||||||
|
|
||||||
interface BudiStoreOpts {
|
interface BudiStoreOpts {
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
|
persistence?: {
|
||||||
|
type: PersistenceType
|
||||||
|
key: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BudiStore<T> {
|
export class BudiStore<T> {
|
||||||
|
@ -11,7 +25,21 @@ export class BudiStore<T> {
|
||||||
set: Writable<T>["set"]
|
set: Writable<T>["set"]
|
||||||
|
|
||||||
constructor(init: T, opts?: BudiStoreOpts) {
|
constructor(init: T, opts?: BudiStoreOpts) {
|
||||||
this.store = writable<T>(init)
|
if (opts?.persistence) {
|
||||||
|
switch (opts.persistence.type) {
|
||||||
|
case PersistenceType.LOCAL:
|
||||||
|
this.store = createLocalStorageStore(opts.persistence.key, init)
|
||||||
|
break
|
||||||
|
case PersistenceType.SESSION:
|
||||||
|
this.store = createSessionStorageStore(opts.persistence.key, init)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
this.store = writable<T>(init)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.store = writable<T>(init)
|
||||||
|
}
|
||||||
|
|
||||||
this.subscribe = this.store.subscribe
|
this.subscribe = this.store.subscribe
|
||||||
this.update = this.store.update
|
this.update = this.store.update
|
||||||
this.set = this.store.set
|
this.set = this.store.set
|
||||||
|
|
|
@ -86,7 +86,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||||
super(INITIAL_APP_META_STATE)
|
super(INITIAL_APP_META_STATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset() {
|
||||||
this.store.set({ ...INITIAL_APP_META_STATE })
|
this.store.set({ ...INITIAL_APP_META_STATE })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||||
application: App
|
application: App
|
||||||
clientLibPath: string
|
clientLibPath: string
|
||||||
hasLock: boolean
|
hasLock: boolean
|
||||||
}): void {
|
}) {
|
||||||
const { application: app, clientLibPath, hasLock } = pkg
|
const { application: app, clientLibPath, hasLock } = pkg
|
||||||
|
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
|
@ -121,7 +121,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
syncClientFeatures(features: Partial<ClientFeatures>): void {
|
syncClientFeatures(features: Partial<ClientFeatures>) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
clientFeatures: {
|
clientFeatures: {
|
||||||
|
@ -131,14 +131,14 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
syncClientTypeSupportPresets(typeSupportPresets: TypeSupportPresets): void {
|
syncClientTypeSupportPresets(typeSupportPresets: TypeSupportPresets) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
typeSupportPresets,
|
typeSupportPresets,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncAppRoutes(): Promise<void> {
|
async syncAppRoutes() {
|
||||||
const resp = await API.fetchAppRoutes()
|
const resp = await API.fetchAppRoutes()
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
|
@ -147,7 +147,7 @@ export class AppMetaStore extends BudiStore<AppMetaState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returned from socket
|
// Returned from socket
|
||||||
syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }): void {
|
syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }) {
|
||||||
const { name, url, icon } = metadata
|
const { name, url, icon } = metadata
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -54,7 +54,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
this.startBuilderOnboarding = this.startBuilderOnboarding.bind(this)
|
this.startBuilderOnboarding = this.startBuilderOnboarding.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(app: App): void {
|
init(app: App) {
|
||||||
if (!app?.appId) {
|
if (!app?.appId) {
|
||||||
console.error("BuilderStore: No appId supplied for websocket")
|
console.error("BuilderStore: No appId supplied for websocket")
|
||||||
return
|
return
|
||||||
|
@ -64,46 +64,46 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(): void {
|
refresh() {
|
||||||
const currentState = get(this.store)
|
const currentState = get(this.store)
|
||||||
this.store.set(currentState)
|
this.store.set(currentState)
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(): void {
|
reset() {
|
||||||
this.store.set({ ...INITIAL_BUILDER_STATE })
|
this.store.set({ ...INITIAL_BUILDER_STATE })
|
||||||
this.websocket?.disconnect()
|
this.websocket?.disconnect()
|
||||||
this.websocket = undefined
|
this.websocket = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightSetting(key?: string, type?: string): void {
|
highlightSetting(key?: string, type?: string) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
highlightedSetting: key ? { key, type: type || "info" } : null,
|
highlightedSetting: key ? { key, type: type || "info" } : null,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
propertyFocus(key: string | null): void {
|
propertyFocus(key: string | null) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
propertyFocus: key,
|
propertyFocus: key,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
showBuilderSidePanel(): void {
|
showBuilderSidePanel() {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
builderSidePanel: true,
|
builderSidePanel: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
hideBuilderSidePanel(): void {
|
hideBuilderSidePanel() {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
builderSidePanel: false,
|
builderSidePanel: false,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreviousTopNavPath(route: string, url: string): void {
|
setPreviousTopNavPath(route: string, url: string) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
previousTopNavPath: {
|
previousTopNavPath: {
|
||||||
|
@ -113,13 +113,13 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
selectResource(id: string): void {
|
selectResource(id: string) {
|
||||||
this.websocket?.emit(BuilderSocketEvent.SelectResource, {
|
this.websocket?.emit(BuilderSocketEvent.SelectResource, {
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
registerTourNode(tourStepKey: string, node: HTMLElement): void {
|
registerTourNode(tourStepKey: string, node: HTMLElement) {
|
||||||
this.update(state => {
|
this.update(state => {
|
||||||
const update = {
|
const update = {
|
||||||
...state,
|
...state,
|
||||||
|
@ -132,7 +132,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyTourNode(tourStepKey: string): void {
|
destroyTourNode(tourStepKey: string) {
|
||||||
const store = get(this.store)
|
const store = get(this.store)
|
||||||
if (store.tourNodes?.[tourStepKey]) {
|
if (store.tourNodes?.[tourStepKey]) {
|
||||||
const nodes = { ...store.tourNodes }
|
const nodes = { ...store.tourNodes }
|
||||||
|
@ -144,7 +144,7 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startBuilderOnboarding(): void {
|
startBuilderOnboarding() {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
onboarding: true,
|
onboarding: true,
|
||||||
|
@ -152,14 +152,14 @@ export class BuilderStore extends BudiStore<BuilderState> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
endBuilderOnboarding(): void {
|
endBuilderOnboarding() {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
onboarding: false,
|
onboarding: false,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
setTour(tourKey?: string | null): void {
|
setTour(tourKey?: string | null) {
|
||||||
this.update(state => ({
|
this.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
tourStepKey: null,
|
tourStepKey: null,
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
import { get } from "svelte/store"
|
|
||||||
import { createSessionStorageStore } from "@budibase/frontend-core"
|
|
||||||
import { selectedScreen as selectedScreenStore } from "./screens"
|
|
||||||
import { findComponentPath } from "@/helpers/components"
|
|
||||||
|
|
||||||
const baseStore = createSessionStorageStore("openNodes", {})
|
|
||||||
|
|
||||||
const toggleNode = componentId => {
|
|
||||||
baseStore.update(openNodes => {
|
|
||||||
openNodes[`nodeOpen-${componentId}`] = !openNodes[`nodeOpen-${componentId}`]
|
|
||||||
|
|
||||||
return openNodes
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandNodes = componentIds => {
|
|
||||||
baseStore.update(openNodes => {
|
|
||||||
const newNodes = Object.fromEntries(
|
|
||||||
componentIds.map(id => [`nodeOpen-${id}`, true])
|
|
||||||
)
|
|
||||||
|
|
||||||
return { ...openNodes, ...newNodes }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const collapseNodes = componentIds => {
|
|
||||||
baseStore.update(openNodes => {
|
|
||||||
const newNodes = Object.fromEntries(
|
|
||||||
componentIds.map(id => [`nodeOpen-${id}`, false])
|
|
||||||
)
|
|
||||||
|
|
||||||
return { ...openNodes, ...newNodes }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Will ensure all parents of a node are expanded so that it is visible in the tree
|
|
||||||
const makeNodeVisible = componentId => {
|
|
||||||
const selectedScreen = get(selectedScreenStore)
|
|
||||||
|
|
||||||
const path = findComponentPath(selectedScreen.props, componentId)
|
|
||||||
|
|
||||||
const componentIds = path.map(component => component._id)
|
|
||||||
|
|
||||||
baseStore.update(openNodes => {
|
|
||||||
const newNodes = Object.fromEntries(
|
|
||||||
componentIds.map(id => [`nodeOpen-${id}`, true])
|
|
||||||
)
|
|
||||||
|
|
||||||
return { ...openNodes, ...newNodes }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNodeExpanded = componentId => {
|
|
||||||
const openNodes = get(baseStore)
|
|
||||||
return !!openNodes[`nodeOpen-${componentId}`]
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = {
|
|
||||||
subscribe: baseStore.subscribe,
|
|
||||||
toggleNode,
|
|
||||||
expandNodes,
|
|
||||||
makeNodeVisible,
|
|
||||||
collapseNodes,
|
|
||||||
isNodeExpanded,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default store
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
import { selectedScreen as selectedScreenStore } from "./screens"
|
||||||
|
import { findComponentPath } from "@/helpers/components"
|
||||||
|
import { Screen, Component } from "@budibase/types"
|
||||||
|
import { BudiStore, PersistenceType } from "@/stores/BudiStore"
|
||||||
|
|
||||||
|
interface OpenNodesState {
|
||||||
|
[key: string]: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComponentTreeNodesStore extends BudiStore<OpenNodesState> {
|
||||||
|
constructor() {
|
||||||
|
super({} as OpenNodesState, {
|
||||||
|
persistence: {
|
||||||
|
type: PersistenceType.SESSION,
|
||||||
|
key: "openNodes",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleNode(componentId: string) {
|
||||||
|
this.update((openNodes: OpenNodesState) => {
|
||||||
|
openNodes[`nodeOpen-${componentId}`] =
|
||||||
|
!openNodes[`nodeOpen-${componentId}`]
|
||||||
|
|
||||||
|
return openNodes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
expandNodes(componentIds: string[]) {
|
||||||
|
this.update((openNodes: OpenNodesState) => {
|
||||||
|
const newNodes = Object.fromEntries(
|
||||||
|
componentIds.map(id => [`nodeOpen-${id}`, true])
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...openNodes, ...newNodes }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
collapseNodes(componentIds: string[]) {
|
||||||
|
this.update((openNodes: OpenNodesState) => {
|
||||||
|
const newNodes = Object.fromEntries(
|
||||||
|
componentIds.map(id => [`nodeOpen-${id}`, false])
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...openNodes, ...newNodes }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will ensure all parents of a node are expanded so that it is visible in the tree
|
||||||
|
makeNodeVisible(componentId: string) {
|
||||||
|
const selectedScreen: Screen = get(selectedScreenStore)
|
||||||
|
|
||||||
|
const path = findComponentPath(selectedScreen.props, componentId)
|
||||||
|
|
||||||
|
const componentIds = path.map((component: Component) => component._id)
|
||||||
|
|
||||||
|
this.update((openNodes: OpenNodesState) => {
|
||||||
|
const newNodes = Object.fromEntries(
|
||||||
|
componentIds.map((id: string) => [`nodeOpen-${id}`, true])
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...openNodes, ...newNodes }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
isNodeExpanded(componentId: string): boolean {
|
||||||
|
const openNodes = get(this)
|
||||||
|
return !!openNodes[`nodeOpen-${componentId}`]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new ComponentTreeNodesStore()
|
|
@ -30,10 +30,40 @@ import {
|
||||||
} from "@/constants/backend"
|
} from "@/constants/backend"
|
||||||
import { BudiStore } from "../BudiStore"
|
import { BudiStore } from "../BudiStore"
|
||||||
import { Utils } from "@budibase/frontend-core"
|
import { Utils } from "@budibase/frontend-core"
|
||||||
import { FieldType } from "@budibase/types"
|
import { Component, FieldType, Screen, Table } from "@budibase/types"
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
export const INITIAL_COMPONENTS_STATE = {
|
interface ComponentDefinition {
|
||||||
|
component: string
|
||||||
|
name: string
|
||||||
|
friendlyName?: string
|
||||||
|
hasChildren?: boolean
|
||||||
|
settings?: ComponentSetting[]
|
||||||
|
features?: Record<string, boolean>
|
||||||
|
typeSupportPresets?: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentSetting {
|
||||||
|
key: string
|
||||||
|
type: string
|
||||||
|
section?: string
|
||||||
|
name?: string
|
||||||
|
defaultValue?: any
|
||||||
|
selectAllFields?: boolean
|
||||||
|
resetOn?: string | string[]
|
||||||
|
settings?: ComponentSetting[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentState {
|
||||||
|
components: Record<string, ComponentDefinition>
|
||||||
|
customComponents: string[]
|
||||||
|
selectedComponentId: string | null
|
||||||
|
componentToPaste?: Component | null
|
||||||
|
settingsCache: Record<string, ComponentSetting[]>
|
||||||
|
selectedScreenId?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const INITIAL_COMPONENTS_STATE: ComponentState = {
|
||||||
components: {},
|
components: {},
|
||||||
customComponents: [],
|
customComponents: [],
|
||||||
selectedComponentId: null,
|
selectedComponentId: null,
|
||||||
|
@ -41,7 +71,7 @@ export const INITIAL_COMPONENTS_STATE = {
|
||||||
settingsCache: {},
|
settingsCache: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ComponentStore extends BudiStore {
|
export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(INITIAL_COMPONENTS_STATE)
|
super(INITIAL_COMPONENTS_STATE)
|
||||||
|
|
||||||
|
@ -89,14 +119,14 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {string} appId
|
* @param {string} appId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async refreshDefinitions(appId) {
|
async refreshDefinitions(appId: string) {
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch definitions and filter out custom component definitions so we
|
// Fetch definitions and filter out custom component definitions so we
|
||||||
// can flag them
|
// can flag them
|
||||||
const components = await API.fetchComponentLibDefinitions(appId)
|
const components: any = await API.fetchComponentLibDefinitions(appId)
|
||||||
const customComponents = Object.keys(components).filter(key =>
|
const customComponents = Object.keys(components).filter(key =>
|
||||||
key.startsWith("plugin/")
|
key.startsWith("plugin/")
|
||||||
)
|
)
|
||||||
|
@ -122,7 +152,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* '@budibase/standard-components/container'
|
* '@budibase/standard-components/container'
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
getDefinition(componentType) {
|
getDefinition(componentType: string) {
|
||||||
if (!componentType) {
|
if (!componentType) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -169,7 +199,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {object} enrichedComponent
|
* @param {object} enrichedComponent
|
||||||
* @returns {object} migrated Component
|
* @returns {object} migrated Component
|
||||||
*/
|
*/
|
||||||
migrateSettings(enrichedComponent) {
|
migrateSettings(enrichedComponent: Component) {
|
||||||
const componentPrefix = "@budibase/standard-components"
|
const componentPrefix = "@budibase/standard-components"
|
||||||
let migrated = false
|
let migrated = false
|
||||||
|
|
||||||
|
@ -224,7 +254,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {object} opts
|
* @param {object} opts
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
enrichEmptySettings(component, opts) {
|
enrichEmptySettings(component: Component, opts: any) {
|
||||||
if (!component?._component) {
|
if (!component?._component) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -235,7 +265,7 @@ export class ComponentStore extends BudiStore {
|
||||||
if (!screen) {
|
if (!screen) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
settings.forEach(setting => {
|
settings.forEach((setting: ComponentSetting) => {
|
||||||
const value = component[setting.key]
|
const value = component[setting.key]
|
||||||
|
|
||||||
// Fill empty settings
|
// Fill empty settings
|
||||||
|
@ -257,7 +287,7 @@ export class ComponentStore extends BudiStore {
|
||||||
} else if (setting.type === "dataProvider") {
|
} else if (setting.type === "dataProvider") {
|
||||||
// Pick closest data provider where required
|
// Pick closest data provider where required
|
||||||
const path = findComponentPath(screen.props, treeId)
|
const path = findComponentPath(screen.props, treeId)
|
||||||
const providers = path.filter(component =>
|
const providers = path.filter((component: Component) =>
|
||||||
component._component?.endsWith("/dataprovider")
|
component._component?.endsWith("/dataprovider")
|
||||||
)
|
)
|
||||||
if (providers.length) {
|
if (providers.length) {
|
||||||
|
@ -278,7 +308,8 @@ export class ComponentStore extends BudiStore {
|
||||||
const form = findClosestMatchingComponent(
|
const form = findClosestMatchingComponent(
|
||||||
screen.props,
|
screen.props,
|
||||||
treeId,
|
treeId,
|
||||||
x => x._component === "@budibase/standard-components/form"
|
(x: Component) =>
|
||||||
|
x._component === "@budibase/standard-components/form"
|
||||||
)
|
)
|
||||||
const usedFields = Object.keys(buildFormSchema(form) || {})
|
const usedFields = Object.keys(buildFormSchema(form) || {})
|
||||||
|
|
||||||
|
@ -302,7 +333,8 @@ export class ComponentStore extends BudiStore {
|
||||||
// Validate data provider exists, or else clear it
|
// Validate data provider exists, or else clear it
|
||||||
const providers = findAllMatchingComponents(
|
const providers = findAllMatchingComponents(
|
||||||
screen?.props,
|
screen?.props,
|
||||||
x => x._component === "@budibase/standard-components/dataprovider"
|
(x: Component) =>
|
||||||
|
x._component === "@budibase/standard-components/dataprovider"
|
||||||
)
|
)
|
||||||
const valid = providers?.some(dp => value.includes?.(dp._id))
|
const valid = providers?.some(dp => value.includes?.(dp._id))
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
|
@ -325,10 +357,14 @@ export class ComponentStore extends BudiStore {
|
||||||
if (cardKeys.every(key => !component[key]) && !component.cardImageURL) {
|
if (cardKeys.every(key => !component[key]) && !component.cardImageURL) {
|
||||||
const { _id, dataSource } = component
|
const { _id, dataSource } = component
|
||||||
if (dataSource) {
|
if (dataSource) {
|
||||||
const { schema, table } = getSchemaForDatasource(screen, dataSource)
|
const {
|
||||||
|
schema,
|
||||||
|
table,
|
||||||
|
}: { schema: Record<string, any>; table: Table } =
|
||||||
|
getSchemaForDatasource(screen, dataSource, {})
|
||||||
|
|
||||||
// Finds fields by types from the schema of the configured datasource
|
// Finds fields by types from the schema of the configured datasource
|
||||||
const findFieldTypes = fieldTypes => {
|
const findFieldTypes = (fieldTypes: any) => {
|
||||||
if (!Array.isArray(fieldTypes)) {
|
if (!Array.isArray(fieldTypes)) {
|
||||||
fieldTypes = [fieldTypes]
|
fieldTypes = [fieldTypes]
|
||||||
}
|
}
|
||||||
|
@ -345,7 +381,11 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserts a card binding for a certain setting
|
// Inserts a card binding for a certain setting
|
||||||
const addBinding = (key, fallback, ...parts) => {
|
const addBinding = (
|
||||||
|
key: string,
|
||||||
|
fallback: string | null,
|
||||||
|
...parts: any[]
|
||||||
|
) => {
|
||||||
if (parts.some(x => x == null)) {
|
if (parts.some(x => x == null)) {
|
||||||
component[key] = fallback
|
component[key] = fallback
|
||||||
} else {
|
} else {
|
||||||
|
@ -363,7 +403,7 @@ export class ComponentStore extends BudiStore {
|
||||||
...findFieldTypes(FieldType.NUMBER),
|
...findFieldTypes(FieldType.NUMBER),
|
||||||
]
|
]
|
||||||
const longFields = findFieldTypes(FieldType.LONGFORM)
|
const longFields = findFieldTypes(FieldType.LONGFORM)
|
||||||
if (schema?.[table?.primaryDisplay]) {
|
if (table?.primaryDisplay && schema?.[table.primaryDisplay]) {
|
||||||
shortFields.unshift(table.primaryDisplay)
|
shortFields.unshift(table.primaryDisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +439,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {object} parent
|
* @param {object} parent
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
createInstance(componentName, presetProps, parent) {
|
createInstance(componentName: string, presetProps: any, parent: any) {
|
||||||
const definition = this.getDefinition(componentName)
|
const definition = this.getDefinition(componentName)
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
return null
|
return null
|
||||||
|
@ -433,7 +473,7 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom post processing for creation only
|
// Custom post processing for creation only
|
||||||
let extras = {}
|
let extras: any = {}
|
||||||
if (definition.hasChildren) {
|
if (definition.hasChildren) {
|
||||||
extras._children = []
|
extras._children = []
|
||||||
}
|
}
|
||||||
|
@ -443,10 +483,11 @@ export class ComponentStore extends BudiStore {
|
||||||
const parentForm = findClosestMatchingComponent(
|
const parentForm = findClosestMatchingComponent(
|
||||||
get(selectedScreen).props,
|
get(selectedScreen).props,
|
||||||
get(selectedComponent)._id,
|
get(selectedComponent)._id,
|
||||||
component => component._component.endsWith("/form")
|
(component: Component) => component._component.endsWith("/form")
|
||||||
)
|
)
|
||||||
const formSteps = findAllMatchingComponents(parentForm, component =>
|
const formSteps = findAllMatchingComponents(
|
||||||
component._component.endsWith("/formstep")
|
parentForm,
|
||||||
|
(component: Component) => component._component.endsWith("/formstep")
|
||||||
)
|
)
|
||||||
extras.step = formSteps.length + 1
|
extras.step = formSteps.length + 1
|
||||||
extras._instanceName = `Step ${formSteps.length + 1}`
|
extras._instanceName = `Step ${formSteps.length + 1}`
|
||||||
|
@ -466,7 +507,12 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async create(componentName, presetProps, parent, index) {
|
async create(
|
||||||
|
componentName: string,
|
||||||
|
presetProps: any,
|
||||||
|
parent: any,
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
const componentInstance = this.createInstance(
|
const componentInstance = this.createInstance(
|
||||||
componentName,
|
componentName,
|
||||||
|
@ -479,23 +525,23 @@ export class ComponentStore extends BudiStore {
|
||||||
|
|
||||||
// Insert in position if specified
|
// Insert in position if specified
|
||||||
if (parent && index != null) {
|
if (parent && index != null) {
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
let parentComponent = findComponent(screen.props, parent)
|
let parentComponent = findComponent(screen.props, parent)
|
||||||
if (!parentComponent._children?.length) {
|
if (!parentComponent._children?.length) {
|
||||||
parentComponent._children = [componentInstance]
|
parentComponent._children = [componentInstance]
|
||||||
} else {
|
} else {
|
||||||
parentComponent._children.splice(index, 0, componentInstance)
|
parentComponent._children.splice(index, 0, componentInstance)
|
||||||
}
|
}
|
||||||
})
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we work out where this component should be inserted
|
// Otherwise we work out where this component should be inserted
|
||||||
else {
|
else {
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
// Find the selected component
|
// Find the selected component
|
||||||
let selectedComponentId = state.selectedComponentId
|
let selectedComponentId = state.selectedComponentId
|
||||||
if (selectedComponentId.startsWith(`${screen._id}-`)) {
|
if (selectedComponentId?.startsWith(`${screen._id}-`)) {
|
||||||
selectedComponentId = screen?.props._id
|
selectedComponentId = screen.props._id || null
|
||||||
}
|
}
|
||||||
const currentComponent = findComponent(
|
const currentComponent = findComponent(
|
||||||
screen.props,
|
screen.props,
|
||||||
|
@ -533,7 +579,7 @@ export class ComponentStore extends BudiStore {
|
||||||
parentComponent._children = []
|
parentComponent._children = []
|
||||||
}
|
}
|
||||||
parentComponent._children.push(componentInstance)
|
parentComponent._children.push(componentInstance)
|
||||||
})
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select new component
|
// Select new component
|
||||||
|
@ -559,18 +605,22 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {string} screenId
|
* @param {string} screenId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async patch(patchFn, componentId, screenId) {
|
async patch(
|
||||||
|
patchFn: (component: Component, screen: Screen) => any,
|
||||||
|
componentId?: string,
|
||||||
|
screenId?: string
|
||||||
|
) {
|
||||||
// Use selected component by default
|
// Use selected component by default
|
||||||
if (!componentId || !screenId) {
|
if (!componentId || !screenId) {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
componentId = componentId || state.selectedComponentId
|
componentId = componentId ?? state.selectedComponentId ?? undefined
|
||||||
const screenState = get(screenStore)
|
const screenState = get(screenStore)
|
||||||
screenId = screenId || screenState.selectedScreenId
|
screenId = screenId || screenState.selectedScreenId
|
||||||
}
|
}
|
||||||
if (!componentId || !screenId || !patchFn) {
|
if (!componentId || !screenId || !patchFn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const patchScreen = screen => {
|
const patchScreen = (screen: Screen) => {
|
||||||
let component = findComponent(screen.props, componentId)
|
let component = findComponent(screen.props, componentId)
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return false
|
return false
|
||||||
|
@ -594,7 +644,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {object} component
|
* @param {object} component
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async delete(component) {
|
async delete(component: Component) {
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -602,7 +652,7 @@ export class ComponentStore extends BudiStore {
|
||||||
// Determine the next component to select, and select it before deletion
|
// Determine the next component to select, and select it before deletion
|
||||||
// to avoid an intermediate state of no component selection
|
// to avoid an intermediate state of no component selection
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
let nextId
|
let nextId: string | null = ""
|
||||||
if (state.selectedComponentId === component._id) {
|
if (state.selectedComponentId === component._id) {
|
||||||
nextId = this.getNext()
|
nextId = this.getNext()
|
||||||
if (!nextId) {
|
if (!nextId) {
|
||||||
|
@ -621,7 +671,7 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch screen
|
// Patch screen
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
// Check component exists
|
// Check component exists
|
||||||
component = findComponent(screen.props, component._id)
|
component = findComponent(screen.props, component._id)
|
||||||
if (!component) {
|
if (!component) {
|
||||||
|
@ -634,12 +684,12 @@ export class ComponentStore extends BudiStore {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
parent._children = parent._children.filter(
|
parent._children = parent._children.filter(
|
||||||
child => child._id !== component._id
|
(child: Component) => child._id !== component._id
|
||||||
)
|
)
|
||||||
})
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(component, cut = false, selectParent = true) {
|
copy(component: Component, cut = false, selectParent = true) {
|
||||||
// Update store with copied component
|
// Update store with copied component
|
||||||
this.update(state => {
|
this.update(state => {
|
||||||
state.componentToPaste = cloneDeep(component)
|
state.componentToPaste = cloneDeep(component)
|
||||||
|
@ -665,7 +715,7 @@ export class ComponentStore extends BudiStore {
|
||||||
*
|
*
|
||||||
* @param {string} componentId
|
* @param {string} componentId
|
||||||
*/
|
*/
|
||||||
select(componentId) {
|
select(componentId: string) {
|
||||||
this.update(state => {
|
this.update(state => {
|
||||||
state.selectedComponentId = componentId
|
state.selectedComponentId = componentId
|
||||||
return state
|
return state
|
||||||
|
@ -679,12 +729,17 @@ export class ComponentStore extends BudiStore {
|
||||||
* @param {object} targetScreen
|
* @param {object} targetScreen
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async paste(targetComponent, mode, targetScreen, selectComponent = true) {
|
async paste(
|
||||||
|
targetComponent: Component,
|
||||||
|
mode: string,
|
||||||
|
targetScreen: Screen,
|
||||||
|
selectComponent = true
|
||||||
|
) {
|
||||||
const state = get(this.store)
|
const state = get(this.store)
|
||||||
if (!state.componentToPaste) {
|
if (!state.componentToPaste) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let newComponentId
|
let newComponentId: string | null = ""
|
||||||
|
|
||||||
// Remove copied component if cutting, regardless if pasting works
|
// Remove copied component if cutting, regardless if pasting works
|
||||||
let componentToPaste = cloneDeep(state.componentToPaste)
|
let componentToPaste = cloneDeep(state.componentToPaste)
|
||||||
|
@ -696,7 +751,7 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch screen
|
// Patch screen
|
||||||
const patch = screen => {
|
const patch = (screen: Screen) => {
|
||||||
// Get up to date ref to target
|
// Get up to date ref to target
|
||||||
targetComponent = findComponent(screen.props, targetComponent._id)
|
targetComponent = findComponent(screen.props, targetComponent._id)
|
||||||
if (!targetComponent) {
|
if (!targetComponent) {
|
||||||
|
@ -712,7 +767,7 @@ export class ComponentStore extends BudiStore {
|
||||||
if (!cut) {
|
if (!cut) {
|
||||||
componentToPaste = makeComponentUnique(componentToPaste)
|
componentToPaste = makeComponentUnique(componentToPaste)
|
||||||
}
|
}
|
||||||
newComponentId = componentToPaste._id
|
newComponentId = componentToPaste._id!
|
||||||
|
|
||||||
// Strip grid position metadata if pasting into a new screen, but keep
|
// Strip grid position metadata if pasting into a new screen, but keep
|
||||||
// alignment metadata
|
// alignment metadata
|
||||||
|
@ -732,7 +787,7 @@ export class ComponentStore extends BudiStore {
|
||||||
const parent = findComponentParent(screen.props, originalId)
|
const parent = findComponentParent(screen.props, originalId)
|
||||||
if (parent?._children) {
|
if (parent?._children) {
|
||||||
parent._children = parent._children.filter(
|
parent._children = parent._children.filter(
|
||||||
component => component._id !== originalId
|
(component: Component) => component._id !== originalId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,7 +795,7 @@ export class ComponentStore extends BudiStore {
|
||||||
// Check inside is valid
|
// Check inside is valid
|
||||||
if (mode === "inside") {
|
if (mode === "inside") {
|
||||||
const definition = this.getDefinition(targetComponent._component)
|
const definition = this.getDefinition(targetComponent._component)
|
||||||
if (!definition.hasChildren) {
|
if (!definition?.hasChildren) {
|
||||||
mode = "below"
|
mode = "below"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -758,14 +813,16 @@ export class ComponentStore extends BudiStore {
|
||||||
if (!parent?._children) {
|
if (!parent?._children) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const targetIndex = parent._children.findIndex(component => {
|
const targetIndex = parent._children.findIndex(
|
||||||
return component._id === targetComponent._id
|
(component: Component) => {
|
||||||
})
|
return component._id === targetComponent._id
|
||||||
|
}
|
||||||
|
)
|
||||||
const index = mode === "above" ? targetIndex : targetIndex + 1
|
const index = mode === "above" ? targetIndex : targetIndex + 1
|
||||||
parent._children.splice(index, 0, componentToPaste)
|
parent._children.splice(index, 0, componentToPaste)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const targetScreenId = targetScreen?._id || state.selectedScreenId
|
const targetScreenId = targetScreen?._id ?? state.selectedScreenId ?? null
|
||||||
await screenStore.patch(patch, targetScreenId)
|
await screenStore.patch(patch, targetScreenId)
|
||||||
|
|
||||||
// Select the new component
|
// Select the new component
|
||||||
|
@ -785,7 +842,9 @@ export class ComponentStore extends BudiStore {
|
||||||
const componentId = state.selectedComponentId
|
const componentId = state.selectedComponentId
|
||||||
const screen = get(selectedScreen)
|
const screen = get(selectedScreen)
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
const index = parent?._children.findIndex(x => x._id === componentId)
|
const index = parent?._children.findIndex(
|
||||||
|
(x: Component) => x._id === componentId
|
||||||
|
)
|
||||||
|
|
||||||
// Check for screen and navigation component edge cases
|
// Check for screen and navigation component edge cases
|
||||||
const screenComponentId = `${screen._id}-screen`
|
const screenComponentId = `${screen._id}-screen`
|
||||||
|
@ -832,7 +891,9 @@ export class ComponentStore extends BudiStore {
|
||||||
const componentId = component?._id
|
const componentId = component?._id
|
||||||
const screen = get(selectedScreen)
|
const screen = get(selectedScreen)
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
const index = parent?._children.findIndex(x => x._id === componentId)
|
const index = parent?._children.findIndex(
|
||||||
|
(x: Component) => x._id === componentId
|
||||||
|
)
|
||||||
|
|
||||||
// Check for screen and navigation component edge cases
|
// Check for screen and navigation component edge cases
|
||||||
const screenComponentId = `${screen._id}-screen`
|
const screenComponentId = `${screen._id}-screen`
|
||||||
|
@ -862,7 +923,7 @@ export class ComponentStore extends BudiStore {
|
||||||
let target = parent
|
let target = parent
|
||||||
let targetParent = findComponentParent(screen.props, target._id)
|
let targetParent = findComponentParent(screen.props, target._id)
|
||||||
let targetIndex = targetParent?._children.findIndex(
|
let targetIndex = targetParent?._children.findIndex(
|
||||||
child => child._id === target._id
|
(child: Component) => child._id === target._id
|
||||||
)
|
)
|
||||||
while (
|
while (
|
||||||
targetParent != null &&
|
targetParent != null &&
|
||||||
|
@ -871,7 +932,7 @@ export class ComponentStore extends BudiStore {
|
||||||
target = targetParent
|
target = targetParent
|
||||||
targetParent = findComponentParent(screen.props, target._id)
|
targetParent = findComponentParent(screen.props, target._id)
|
||||||
targetIndex = targetParent?._children.findIndex(
|
targetIndex = targetParent?._children.findIndex(
|
||||||
child => child._id === target._id
|
(child: Component) => child._id === target._id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (targetParent) {
|
if (targetParent) {
|
||||||
|
@ -901,13 +962,15 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveUp(component) {
|
async moveUp(component: Component) {
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
const componentId = component?._id
|
const componentId = component?._id
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
|
|
||||||
// Check we aren't right at the top of the tree
|
// Check we aren't right at the top of the tree
|
||||||
const index = parent?._children.findIndex(x => x._id === componentId)
|
const index = parent?._children.findIndex(
|
||||||
|
(x: Component) => x._id === componentId
|
||||||
|
)
|
||||||
if (!parent || (index === 0 && parent._id === screen.props._id)) {
|
if (!parent || (index === 0 && parent._id === screen.props._id)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -915,7 +978,7 @@ export class ComponentStore extends BudiStore {
|
||||||
// Copy original component and remove it from the parent
|
// Copy original component and remove it from the parent
|
||||||
const originalComponent = cloneDeep(parent._children[index])
|
const originalComponent = cloneDeep(parent._children[index])
|
||||||
parent._children = parent._children.filter(
|
parent._children = parent._children.filter(
|
||||||
component => component._id !== componentId
|
(component: Component) => component._id !== componentId
|
||||||
)
|
)
|
||||||
|
|
||||||
// If we have siblings above us, move up
|
// If we have siblings above us, move up
|
||||||
|
@ -925,7 +988,7 @@ export class ComponentStore extends BudiStore {
|
||||||
const previousSibling = parent._children[index - 1]
|
const previousSibling = parent._children[index - 1]
|
||||||
const definition = this.getDefinition(previousSibling._component)
|
const definition = this.getDefinition(previousSibling._component)
|
||||||
if (
|
if (
|
||||||
definition.hasChildren &&
|
definition?.hasChildren &&
|
||||||
componentTreeNodesStore.isNodeExpanded(previousSibling._id)
|
componentTreeNodesStore.isNodeExpanded(previousSibling._id)
|
||||||
) {
|
) {
|
||||||
previousSibling._children.push(originalComponent)
|
previousSibling._children.push(originalComponent)
|
||||||
|
@ -942,15 +1005,15 @@ export class ComponentStore extends BudiStore {
|
||||||
else if (parent._id !== screen.props._id) {
|
else if (parent._id !== screen.props._id) {
|
||||||
const grandParent = findComponentParent(screen.props, parent._id)
|
const grandParent = findComponentParent(screen.props, parent._id)
|
||||||
const parentIndex = grandParent._children.findIndex(
|
const parentIndex = grandParent._children.findIndex(
|
||||||
child => child._id === parent._id
|
(child: Component) => child._id === parent._id
|
||||||
)
|
)
|
||||||
grandParent._children.splice(parentIndex, 0, originalComponent)
|
grandParent._children.splice(parentIndex, 0, originalComponent)
|
||||||
}
|
}
|
||||||
})
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveDown(component) {
|
async moveDown(component: Component) {
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
const componentId = component?._id
|
const componentId = component?._id
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
|
|
||||||
|
@ -960,7 +1023,9 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check we aren't right at the bottom of the tree
|
// Check we aren't right at the bottom of the tree
|
||||||
const index = parent._children.findIndex(x => x._id === componentId)
|
const index = parent._children.findIndex(
|
||||||
|
(x: Component) => x._id === componentId
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
index === parent._children.length - 1 &&
|
index === parent._children.length - 1 &&
|
||||||
parent._id === screen.props._id
|
parent._id === screen.props._id
|
||||||
|
@ -971,7 +1036,7 @@ export class ComponentStore extends BudiStore {
|
||||||
// Copy the original component and remove from parent
|
// Copy the original component and remove from parent
|
||||||
const originalComponent = cloneDeep(parent._children[index])
|
const originalComponent = cloneDeep(parent._children[index])
|
||||||
parent._children = parent._children.filter(
|
parent._children = parent._children.filter(
|
||||||
component => component._id !== componentId
|
(component: Component) => component._id !== componentId
|
||||||
)
|
)
|
||||||
|
|
||||||
// Move below the next sibling if we are not the last sibling
|
// Move below the next sibling if we are not the last sibling
|
||||||
|
@ -980,7 +1045,7 @@ export class ComponentStore extends BudiStore {
|
||||||
const nextSibling = parent._children[index]
|
const nextSibling = parent._children[index]
|
||||||
const definition = this.getDefinition(nextSibling._component)
|
const definition = this.getDefinition(nextSibling._component)
|
||||||
if (
|
if (
|
||||||
definition.hasChildren &&
|
definition?.hasChildren &&
|
||||||
componentTreeNodesStore.isNodeExpanded(nextSibling._id)
|
componentTreeNodesStore.isNodeExpanded(nextSibling._id)
|
||||||
) {
|
) {
|
||||||
nextSibling._children.splice(0, 0, originalComponent)
|
nextSibling._children.splice(0, 0, originalComponent)
|
||||||
|
@ -996,15 +1061,15 @@ export class ComponentStore extends BudiStore {
|
||||||
else {
|
else {
|
||||||
const grandParent = findComponentParent(screen.props, parent._id)
|
const grandParent = findComponentParent(screen.props, parent._id)
|
||||||
const parentIndex = grandParent._children.findIndex(
|
const parentIndex = grandParent._children.findIndex(
|
||||||
child => child._id === parent._id
|
(child: Component) => child._id === parent._id
|
||||||
)
|
)
|
||||||
grandParent._children.splice(parentIndex + 1, 0, originalComponent)
|
grandParent._children.splice(parentIndex + 1, 0, originalComponent)
|
||||||
}
|
}
|
||||||
})
|
}, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStyle(name, value) {
|
async updateStyle(name: string, value: string) {
|
||||||
await this.patch(component => {
|
await this.patch((component: Component) => {
|
||||||
if (value == null || value === "") {
|
if (value == null || value === "") {
|
||||||
delete component._styles.normal[name]
|
delete component._styles.normal[name]
|
||||||
} else {
|
} else {
|
||||||
|
@ -1013,8 +1078,8 @@ export class ComponentStore extends BudiStore {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStyles(styles, id) {
|
async updateStyles(styles: Record<string, string>, id: string) {
|
||||||
const patchFn = component => {
|
const patchFn = (component: Component) => {
|
||||||
component._styles.normal = {
|
component._styles.normal = {
|
||||||
...component._styles.normal,
|
...component._styles.normal,
|
||||||
...styles,
|
...styles,
|
||||||
|
@ -1023,24 +1088,24 @@ export class ComponentStore extends BudiStore {
|
||||||
await this.patch(patchFn, id)
|
await this.patch(patchFn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCustomStyle(style) {
|
async updateCustomStyle(style: Record<string, string>) {
|
||||||
await this.patch(component => {
|
await this.patch((component: Component) => {
|
||||||
component._styles.custom = style
|
component._styles.custom = style
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateConditions(conditions) {
|
async updateConditions(conditions: Record<string, any>) {
|
||||||
await this.patch(component => {
|
await this.patch((component: Component) => {
|
||||||
component._conditions = conditions
|
component._conditions = conditions
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSetting(name, value) {
|
async updateSetting(name: string, value: any) {
|
||||||
await this.patch(this.updateComponentSetting(name, value))
|
await this.patch(this.updateComponentSetting(name, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
updateComponentSetting(name, value) {
|
updateComponentSetting(name: string, value: any) {
|
||||||
return component => {
|
return (component: Component) => {
|
||||||
if (!name || !component) {
|
if (!name || !component) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1050,10 +1115,12 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = this.getComponentSettings(component._component)
|
const settings = this.getComponentSettings(component._component)
|
||||||
const updatedSetting = settings.find(setting => setting.key === name)
|
const updatedSetting = settings.find(
|
||||||
|
(setting: ComponentSetting) => setting.key === name
|
||||||
|
)
|
||||||
|
|
||||||
// Reset dependent fields
|
// Reset dependent fields
|
||||||
settings.forEach(setting => {
|
settings.forEach((setting: ComponentSetting) => {
|
||||||
const needsReset =
|
const needsReset =
|
||||||
name === setting.resetOn ||
|
name === setting.resetOn ||
|
||||||
(Array.isArray(setting.resetOn) && setting.resetOn.includes(name))
|
(Array.isArray(setting.resetOn) && setting.resetOn.includes(name))
|
||||||
|
@ -1066,15 +1133,16 @@ export class ComponentStore extends BudiStore {
|
||||||
updatedSetting?.type === "dataSource" ||
|
updatedSetting?.type === "dataSource" ||
|
||||||
updatedSetting?.type === "table"
|
updatedSetting?.type === "table"
|
||||||
) {
|
) {
|
||||||
const { schema } = getSchemaForDatasource(null, value)
|
const { schema }: { schema: Record<string, any> } =
|
||||||
|
getSchemaForDatasource(null, value, null)
|
||||||
const columnNames = Object.keys(schema || {})
|
const columnNames = Object.keys(schema || {})
|
||||||
const multifieldKeysToSelectAll = settings
|
const multifieldKeysToSelectAll = settings
|
||||||
.filter(setting => {
|
.filter((setting: ComponentSetting) => {
|
||||||
return setting.type === "multifield" && setting.selectAllFields
|
return setting.type === "multifield" && setting.selectAllFields
|
||||||
})
|
})
|
||||||
.map(setting => setting.key)
|
.map((setting: ComponentSetting) => setting.key)
|
||||||
|
|
||||||
multifieldKeysToSelectAll.forEach(key => {
|
multifieldKeysToSelectAll.forEach((key: string) => {
|
||||||
component[key] = columnNames
|
component[key] = columnNames
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1083,14 +1151,14 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestEjectBlock(componentId) {
|
requestEjectBlock(componentId: string) {
|
||||||
previewStore.sendEvent("eject-block", componentId)
|
previewStore.sendEvent("eject-block", componentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEjectBlock(componentId, ejectedDefinition) {
|
async handleEjectBlock(componentId: string, ejectedDefinition: Component) {
|
||||||
let nextSelectedComponentId
|
let nextSelectedComponentId: string | null = null
|
||||||
|
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
const block = findComponent(screen.props, componentId)
|
const block = findComponent(screen.props, componentId)
|
||||||
const parent = findComponentParent(screen.props, componentId)
|
const parent = findComponentParent(screen.props, componentId)
|
||||||
|
|
||||||
|
@ -1108,7 +1176,7 @@ export class ComponentStore extends BudiStore {
|
||||||
// _containsSlot flag to know where to insert them
|
// _containsSlot flag to know where to insert them
|
||||||
const slotContainer = findAllMatchingComponents(
|
const slotContainer = findAllMatchingComponents(
|
||||||
ejectedDefinition,
|
ejectedDefinition,
|
||||||
x => x._containsSlot
|
(x: Component) => x._containsSlot
|
||||||
)[0]
|
)[0]
|
||||||
if (slotContainer) {
|
if (slotContainer) {
|
||||||
delete slotContainer._containsSlot
|
delete slotContainer._containsSlot
|
||||||
|
@ -1120,10 +1188,12 @@ export class ComponentStore extends BudiStore {
|
||||||
|
|
||||||
// Replace block with ejected definition
|
// Replace block with ejected definition
|
||||||
ejectedDefinition = makeComponentUnique(ejectedDefinition)
|
ejectedDefinition = makeComponentUnique(ejectedDefinition)
|
||||||
const index = parent._children.findIndex(x => x._id === componentId)
|
const index = parent._children.findIndex(
|
||||||
|
(x: Component) => x._id === componentId
|
||||||
|
)
|
||||||
parent._children[index] = ejectedDefinition
|
parent._children[index] = ejectedDefinition
|
||||||
nextSelectedComponentId = ejectedDefinition._id
|
nextSelectedComponentId = ejectedDefinition._id ?? null
|
||||||
})
|
}, null)
|
||||||
|
|
||||||
// Select new root component
|
// Select new root component
|
||||||
if (nextSelectedComponentId) {
|
if (nextSelectedComponentId) {
|
||||||
|
@ -1134,7 +1204,7 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addParent(componentId, parentType) {
|
async addParent(componentId: string, parentType: string) {
|
||||||
if (!componentId || !parentType) {
|
if (!componentId || !parentType) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1146,7 +1216,7 @@ export class ComponentStore extends BudiStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace component with a version wrapped in a new parent
|
// Replace component with a version wrapped in a new parent
|
||||||
await screenStore.patch(screen => {
|
await screenStore.patch((screen: Screen) => {
|
||||||
// Get this component definition and parent definition
|
// Get this component definition and parent definition
|
||||||
let definition = findComponent(screen.props, componentId)
|
let definition = findComponent(screen.props, componentId)
|
||||||
let oldParentDefinition = findComponentParent(screen.props, componentId)
|
let oldParentDefinition = findComponentParent(screen.props, componentId)
|
||||||
|
@ -1156,7 +1226,7 @@ export class ComponentStore extends BudiStore {
|
||||||
|
|
||||||
// Replace component with parent
|
// Replace component with parent
|
||||||
const index = oldParentDefinition._children.findIndex(
|
const index = oldParentDefinition._children.findIndex(
|
||||||
component => component._id === componentId
|
(component: Component) => component._id === componentId
|
||||||
)
|
)
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return false
|
return false
|
||||||
|
@ -1165,7 +1235,7 @@ export class ComponentStore extends BudiStore {
|
||||||
...newParentDefinition,
|
...newParentDefinition,
|
||||||
_children: [definition],
|
_children: [definition],
|
||||||
}
|
}
|
||||||
})
|
}, null)
|
||||||
|
|
||||||
// Select the new parent
|
// Select the new parent
|
||||||
this.update(state => {
|
this.update(state => {
|
||||||
|
@ -1181,7 +1251,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* '@budibase/standard-components/container'
|
* '@budibase/standard-components/container'
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
isCached(componentType) {
|
isCached(componentType: string) {
|
||||||
const settings = get(this.store).settingsCache
|
const settings = get(this.store).settingsCache
|
||||||
return componentType in settings
|
return componentType in settings
|
||||||
}
|
}
|
||||||
|
@ -1194,8 +1264,8 @@ export class ComponentStore extends BudiStore {
|
||||||
* '@budibase/standard-components/container'
|
* '@budibase/standard-components/container'
|
||||||
* @returns {array} the settings
|
* @returns {array} the settings
|
||||||
*/
|
*/
|
||||||
cacheSettings(componentType, definition) {
|
cacheSettings(componentType: string, definition: ComponentDefinition | null) {
|
||||||
let settings = []
|
let settings: ComponentSetting[] = []
|
||||||
if (definition) {
|
if (definition) {
|
||||||
settings = definition.settings?.filter(setting => !setting.section) ?? []
|
settings = definition.settings?.filter(setting => !setting.section) ?? []
|
||||||
definition.settings
|
definition.settings
|
||||||
|
@ -1229,7 +1299,7 @@ export class ComponentStore extends BudiStore {
|
||||||
* '@budibase/standard-components/container'
|
* '@budibase/standard-components/container'
|
||||||
* @returns {Array<object>}
|
* @returns {Array<object>}
|
||||||
*/
|
*/
|
||||||
getComponentSettings(componentType) {
|
getComponentSettings(componentType: string) {
|
||||||
if (!componentType) {
|
if (!componentType) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
import { writable } from "svelte/store"
|
|
||||||
import { API } from "@/api"
|
|
||||||
|
|
||||||
const createIntegrationsStore = () => {
|
|
||||||
const store = writable({})
|
|
||||||
|
|
||||||
const init = async () => {
|
|
||||||
const integrations = await API.getIntegrations()
|
|
||||||
store.set(integrations)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...store,
|
|
||||||
init,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const integrations = createIntegrationsStore()
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { writable, type Writable } from "svelte/store"
|
||||||
|
import { API } from "@/api"
|
||||||
|
import { Integration } from "@budibase/types"
|
||||||
|
|
||||||
|
type IntegrationsState = Record<string, Integration>
|
||||||
|
|
||||||
|
const INITIAL_STATE: IntegrationsState = {}
|
||||||
|
|
||||||
|
const createIntegrationsStore = () => {
|
||||||
|
const store: Writable<IntegrationsState> = writable(INITIAL_STATE)
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
const response = await API.getIntegrations()
|
||||||
|
|
||||||
|
// Filter out undefineds
|
||||||
|
const integrations = Object.entries(response).reduce(
|
||||||
|
(acc, [key, value]) => {
|
||||||
|
if (value) {
|
||||||
|
acc[key] = value
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{} as IntegrationsState
|
||||||
|
)
|
||||||
|
store.set(integrations)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...store,
|
||||||
|
init,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const integrations = createIntegrationsStore()
|
|
@ -274,7 +274,7 @@ export class ScreenStore extends BudiStore {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} patchFn
|
* @param {function} patchFn
|
||||||
* @param {string} screenId
|
* @param {string | null} screenId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async patch(patchFn, screenId) {
|
async patch(patchFn, screenId) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
|
@ -1,4 +1,4 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { setContext, onMount } from "svelte"
|
import { setContext, onMount } from "svelte"
|
||||||
import { writable, derived } from "svelte/store"
|
import { writable, derived } from "svelte/store"
|
||||||
import { fade } from "svelte/transition"
|
import { fade } from "svelte/transition"
|
||||||
|
@ -53,17 +53,16 @@
|
||||||
const gridID = `grid-${Math.random().toString().slice(2)}`
|
const gridID = `grid-${Math.random().toString().slice(2)}`
|
||||||
|
|
||||||
// Store props in a store for reference in other stores
|
// Store props in a store for reference in other stores
|
||||||
const props = writable($$props)
|
const props: any = writable($$props)
|
||||||
|
|
||||||
// Build up context
|
// Build up context
|
||||||
let context = {
|
let context = attachStores({
|
||||||
API: API || createAPIClient(),
|
API: API || createAPIClient(),
|
||||||
Constants,
|
Constants,
|
||||||
gridID,
|
gridID,
|
||||||
props,
|
props,
|
||||||
}
|
...createEventManagers(),
|
||||||
context = { ...context, ...createEventManagers() }
|
})
|
||||||
context = attachStores(context)
|
|
||||||
|
|
||||||
// Reference some stores for local use
|
// Reference some stores for local use
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export const createEventManagers = () => {
|
export const createEventManagers = () => {
|
||||||
const svelteDispatch = createEventDispatcher()
|
const svelteDispatch = createEventDispatcher()
|
||||||
let subscribers = {}
|
let subscribers: Record<string, ((...params: any) => void)[]> = {}
|
||||||
|
|
||||||
// Dispatches an event, notifying subscribers and also emitting a normal
|
// Dispatches an event, notifying subscribers and also emitting a normal
|
||||||
// svelte event
|
// svelte event
|
||||||
const dispatch = (event, payload) => {
|
const dispatch = (event: string, payload: any) => {
|
||||||
svelteDispatch(event, payload)
|
svelteDispatch(event, payload)
|
||||||
const subs = subscribers[event] || []
|
const subs = subscribers[event] || []
|
||||||
for (let i = 0; i < subs.length; i++) {
|
for (let i = 0; i < subs.length; i++) {
|
||||||
|
@ -15,7 +15,7 @@ export const createEventManagers = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribes to events
|
// Subscribes to events
|
||||||
const subscribe = (event, callback) => {
|
const subscribe = (event: string, callback: () => void) => {
|
||||||
const subs = subscribers[event] || []
|
const subs = subscribers[event] || []
|
||||||
subscribers[event] = [...subs, callback]
|
subscribers[event] = [...subs, callback]
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { derived, writable } from "svelte/store"
|
|
||||||
|
|
||||||
export const createStores = () => {
|
|
||||||
const bounds = writable({
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Derive height and width as primitives to avoid wasted computation
|
|
||||||
const width = derived(bounds, $bounds => $bounds.width, 0)
|
|
||||||
const height = derived(bounds, $bounds => $bounds.height, 0)
|
|
||||||
|
|
||||||
return { bounds, height, width }
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { derived, Readable, Writable, writable } from "svelte/store"
|
||||||
|
|
||||||
|
interface BoundsStore {
|
||||||
|
bounds: Writable<{
|
||||||
|
left: number
|
||||||
|
top: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}>
|
||||||
|
height: Readable<number>
|
||||||
|
width: Readable<number>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = BoundsStore
|
||||||
|
|
||||||
|
export const createStores = (): BoundsStore => {
|
||||||
|
const bounds = writable({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Derive height and width as primitives to avoid wasted computation
|
||||||
|
const width = derived(bounds, $bounds => $bounds.width, 0)
|
||||||
|
const height = derived(bounds, $bounds => $bounds.height, 0)
|
||||||
|
|
||||||
|
return { bounds, height, width }
|
||||||
|
}
|
|
@ -1,16 +1,31 @@
|
||||||
export const createActions = context => {
|
import { FindTableResponse } from "@budibase/types"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
|
||||||
|
interface CacheActionStore {
|
||||||
|
cache: {
|
||||||
|
actions: {
|
||||||
|
getPrimaryDisplayForTableId: (tableId: string) => Promise<string>
|
||||||
|
getTable: (tableId: string) => Promise<FindTableResponse>
|
||||||
|
resetCache: () => any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = CacheActionStore
|
||||||
|
|
||||||
|
export const createActions = (context: StoreContext): CacheActionStore => {
|
||||||
const { API } = context
|
const { API } = context
|
||||||
|
|
||||||
// Cache for the primary display columns of different tables.
|
// Cache for the primary display columns of different tables.
|
||||||
// If we ever need to cache table definitions for other purposes then we can
|
// If we ever need to cache table definitions for other purposes then we can
|
||||||
// expand this to be a more generic cache.
|
// expand this to be a more generic cache.
|
||||||
let tableCache = {}
|
let tableCache: Record<string, Promise<FindTableResponse>> = {}
|
||||||
|
|
||||||
const resetCache = () => {
|
const resetCache = () => {
|
||||||
tableCache = {}
|
tableCache = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTable = async tableId => {
|
const fetchTable = async (tableId: string) => {
|
||||||
// If we've never encountered this tableId before then store a promise that
|
// If we've never encountered this tableId before then store a promise that
|
||||||
// resolves to the primary display so that subsequent invocations before the
|
// resolves to the primary display so that subsequent invocations before the
|
||||||
// promise completes can reuse this promise
|
// promise completes can reuse this promise
|
||||||
|
@ -21,13 +36,13 @@ export const createActions = context => {
|
||||||
return await tableCache[tableId]
|
return await tableCache[tableId]
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPrimaryDisplayForTableId = async tableId => {
|
const getPrimaryDisplayForTableId = async (tableId: string) => {
|
||||||
const table = await fetchTable(tableId)
|
const table = await fetchTable(tableId)
|
||||||
const display = table?.primaryDisplay || table?.schema?.[0]?.name
|
const display = table?.primaryDisplay || table?.schema?.[0]?.name
|
||||||
return display
|
return display
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTable = async tableId => {
|
const getTable = async (tableId: string) => {
|
||||||
const table = await fetchTable(tableId)
|
const table = await fetchTable(tableId)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
@ -43,7 +58,7 @@ export const createActions = context => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = (context: StoreContext) => {
|
||||||
const { datasource, cache } = context
|
const { datasource, cache } = context
|
||||||
|
|
||||||
// Wipe the caches whenever the datasource changes to ensure we aren't
|
// Wipe the caches whenever the datasource changes to ensure we aren't
|
|
@ -161,7 +161,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
columns.set([])
|
columns.set([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const $columns = get(columns)
|
|
||||||
const $displayColumn = get(displayColumn)
|
const $displayColumn = get(displayColumn)
|
||||||
|
|
||||||
// Find primary display
|
// Find primary display
|
||||||
|
@ -176,16 +176,15 @@ export const initialise = (context: StoreContext) => {
|
||||||
Object.keys($enrichedSchema)
|
Object.keys($enrichedSchema)
|
||||||
.map(field => {
|
.map(field => {
|
||||||
const fieldSchema = $enrichedSchema[field]
|
const fieldSchema = $enrichedSchema[field]
|
||||||
const oldColumn = $columns?.find(col => col.name === field)
|
|
||||||
const column: UIColumn = {
|
const column: UIColumn = {
|
||||||
type: fieldSchema.type,
|
type: fieldSchema.type,
|
||||||
name: field,
|
name: field,
|
||||||
label: fieldSchema.displayName || field,
|
label: fieldSchema.displayName || field,
|
||||||
schema: fieldSchema,
|
schema: fieldSchema,
|
||||||
width: fieldSchema.width || oldColumn?.width || DefaultColumnWidth,
|
width: fieldSchema.width || DefaultColumnWidth,
|
||||||
visible: fieldSchema.visible ?? true,
|
visible: fieldSchema.visible ?? true,
|
||||||
readonly: fieldSchema.readonly,
|
readonly: fieldSchema.readonly,
|
||||||
order: fieldSchema.order ?? oldColumn?.order,
|
order: fieldSchema.order,
|
||||||
conditions: fieldSchema.conditions,
|
conditions: fieldSchema.conditions,
|
||||||
related: fieldSchema.related,
|
related: fieldSchema.related,
|
||||||
calculationType: fieldSchema.calculationType,
|
calculationType: fieldSchema.calculationType,
|
||||||
|
|
|
@ -122,7 +122,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
}
|
}
|
||||||
$fetch?.update({
|
$fetch?.update({
|
||||||
sortOrder: $sort.order || SortOrder.ASCENDING,
|
sortOrder: $sort.order || SortOrder.ASCENDING,
|
||||||
sortColumn: $sort.column,
|
sortColumn: $sort.column ?? undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -139,7 +139,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
}
|
}
|
||||||
$fetch.update({
|
$fetch.update({
|
||||||
sortOrder: $sort.order || SortOrder.ASCENDING,
|
sortOrder: $sort.order || SortOrder.ASCENDING,
|
||||||
sortColumn: $sort.column,
|
sortColumn: $sort.column ?? undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -173,7 +173,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
await datasource.actions.saveDefinition({
|
await datasource.actions.saveDefinition({
|
||||||
...$view,
|
...$view,
|
||||||
sort: {
|
sort: {
|
||||||
field: $sort.column,
|
field: $sort.column!,
|
||||||
order: $sort.order || SortOrder.ASCENDING,
|
order: $sort.order || SortOrder.ASCENDING,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -187,7 +187,7 @@ export const initialise = (context: StoreContext) => {
|
||||||
}
|
}
|
||||||
$fetch.update({
|
$fetch.update({
|
||||||
sortOrder: $sort.order,
|
sortOrder: $sort.order,
|
||||||
sortColumn: $sort.column,
|
sortColumn: $sort.column ?? undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Readable, Writable } from "svelte/store"
|
import { Writable } from "svelte/store"
|
||||||
import type { APIClient } from "../../../api/types"
|
import type { APIClient } from "../../../api/types"
|
||||||
|
|
||||||
import * as Bounds from "./bounds"
|
import * as Bounds from "./bounds"
|
||||||
|
@ -25,6 +25,7 @@ import * as NonPlus from "./datasources/nonPlus"
|
||||||
import * as Cache from "./cache"
|
import * as Cache from "./cache"
|
||||||
import * as Conditions from "./conditions"
|
import * as Conditions from "./conditions"
|
||||||
import { SortOrder, UIDatasource, UISearchFilter } from "@budibase/types"
|
import { SortOrder, UIDatasource, UISearchFilter } from "@budibase/types"
|
||||||
|
import * as Constants from "../lib/constants"
|
||||||
|
|
||||||
const DependencyOrderedStores = [
|
const DependencyOrderedStores = [
|
||||||
Sort,
|
Sort,
|
||||||
|
@ -73,12 +74,16 @@ export interface BaseStoreProps {
|
||||||
canEditColumns?: boolean
|
canEditColumns?: boolean
|
||||||
canExpandRows?: boolean
|
canExpandRows?: boolean
|
||||||
canSaveSchema?: boolean
|
canSaveSchema?: boolean
|
||||||
|
minHeight?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseStore {
|
export interface BaseStore {
|
||||||
API: APIClient
|
API: APIClient
|
||||||
gridID: string
|
gridID: string
|
||||||
props: Writable<BaseStoreProps>
|
props: Writable<BaseStoreProps>
|
||||||
|
subscribe: any
|
||||||
|
dispatch: (event: string, data: any) => any
|
||||||
|
Constants: typeof Constants
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Store = BaseStore &
|
export type Store = BaseStore &
|
||||||
|
@ -93,22 +98,19 @@ export type Store = BaseStore &
|
||||||
Filter.Store &
|
Filter.Store &
|
||||||
UI.Store &
|
UI.Store &
|
||||||
Clipboard.Store &
|
Clipboard.Store &
|
||||||
Scroll.Store & {
|
Scroll.Store &
|
||||||
// TODO while typing the rest of stores
|
Rows.Store &
|
||||||
sort: Writable<any>
|
|
||||||
subscribe: any
|
|
||||||
dispatch: (event: string, data: any) => any
|
|
||||||
notifications: Writable<any>
|
|
||||||
width: Writable<number>
|
|
||||||
bounds: Readable<any>
|
|
||||||
height: Readable<number>
|
|
||||||
} & Rows.Store &
|
|
||||||
Reorder.Store &
|
Reorder.Store &
|
||||||
Resize.Store &
|
Resize.Store &
|
||||||
Config.Store &
|
Config.Store &
|
||||||
Conditions.Store
|
Conditions.Store &
|
||||||
|
Cache.Store &
|
||||||
|
Viewport.Store &
|
||||||
|
Notifications.Store &
|
||||||
|
Sort.Store &
|
||||||
|
Bounds.Store
|
||||||
|
|
||||||
export const attachStores = (context: Store): Store => {
|
export const attachStores = (context: BaseStore): Store => {
|
||||||
// Atomic store creation
|
// Atomic store creation
|
||||||
for (let store of DependencyOrderedStores) {
|
for (let store of DependencyOrderedStores) {
|
||||||
if ("createStores" in store) {
|
if ("createStores" in store) {
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
import { notifications as BBUINotifications } from "@budibase/bbui"
|
import { notifications as BBUINotifications } from "@budibase/bbui"
|
||||||
import { derived } from "svelte/store"
|
import { derived, Readable } from "svelte/store"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
|
||||||
export const createStores = context => {
|
interface NotificationStore {
|
||||||
|
notifications: Readable<{
|
||||||
|
success: (message: string) => void
|
||||||
|
error: (message: string) => void
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = NotificationStore
|
||||||
|
|
||||||
|
export const createStores = (context: StoreContext): NotificationStore => {
|
||||||
const { notifySuccess, notifyError } = context
|
const { notifySuccess, notifyError } = context
|
||||||
|
|
||||||
// Normally we would not derive a store in "createStores" as it should be
|
// Normally we would not derive a store in "createStores" as it should be
|
|
@ -1,6 +1,7 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = (context: StoreContext) => {
|
||||||
const { scrolledRowCount, rows, visualRowCapacity } = context
|
const { scrolledRowCount, rows, visualRowCapacity } = context
|
||||||
|
|
||||||
// Derive how many rows we have in total
|
// Derive how many rows we have in total
|
|
@ -1,8 +1,18 @@
|
||||||
import { derived, get } from "svelte/store"
|
import { derived, get, Writable } from "svelte/store"
|
||||||
import { memo } from "../../../utils"
|
import { memo } from "../../../utils"
|
||||||
import { SortOrder } from "@budibase/types"
|
import { SortOrder } from "@budibase/types"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
|
||||||
export const createStores = context => {
|
interface SortStore {
|
||||||
|
sort: Writable<{
|
||||||
|
column: string | null | undefined
|
||||||
|
order: SortOrder
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = SortStore
|
||||||
|
|
||||||
|
export const createStores = (context: StoreContext): SortStore => {
|
||||||
const { props } = context
|
const { props } = context
|
||||||
const $props = get(props)
|
const $props = get(props)
|
||||||
|
|
||||||
|
@ -17,7 +27,7 @@ export const createStores = context => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialise = context => {
|
export const initialise = (context: StoreContext) => {
|
||||||
const { sort, initialSortColumn, initialSortOrder, schema } = context
|
const { sort, initialSortColumn, initialSortOrder, schema } = context
|
||||||
|
|
||||||
// Reset sort when initial sort props change
|
// Reset sort when initial sort props change
|
|
@ -1,7 +1,18 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived, Readable } from "svelte/store"
|
||||||
import { MinColumnWidth } from "../lib/constants"
|
import { MinColumnWidth } from "../lib/constants"
|
||||||
|
import { Store as StoreContext } from "."
|
||||||
|
import { Row } from "@budibase/types"
|
||||||
|
|
||||||
export const deriveStores = context => {
|
interface ViewportDerivedStore {
|
||||||
|
scrolledRowCount: Readable<number>
|
||||||
|
visualRowCapacity: Readable<number>
|
||||||
|
renderedRows: Readable<Row>
|
||||||
|
columnRenderMap: Readable<Record<string, true>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Store = ViewportDerivedStore
|
||||||
|
|
||||||
|
export const deriveStores = (context: StoreContext): ViewportDerivedStore => {
|
||||||
const {
|
const {
|
||||||
rowHeight,
|
rowHeight,
|
||||||
scrollableColumns,
|
scrollableColumns,
|
||||||
|
@ -77,7 +88,7 @@ export const deriveStores = context => {
|
||||||
leftEdge += $scrollableColumns[endColIdx].width
|
leftEdge += $scrollableColumns[endColIdx].width
|
||||||
endColIdx++
|
endColIdx++
|
||||||
}
|
}
|
||||||
let next = {}
|
let next: Record<string, true> = {}
|
||||||
$scrollableColumns
|
$scrollableColumns
|
||||||
.slice(Math.max(0, startColIdx), endColIdx)
|
.slice(Math.max(0, startColIdx), endColIdx)
|
||||||
.forEach(col => {
|
.forEach(col => {
|
|
@ -122,7 +122,7 @@
|
||||||
"server-destroy": "1.0.1",
|
"server-destroy": "1.0.1",
|
||||||
"snowflake-sdk": "^1.15.0",
|
"snowflake-sdk": "^1.15.0",
|
||||||
"socket.io": "4.8.1",
|
"socket.io": "4.8.1",
|
||||||
"svelte": "^4.2.10",
|
"svelte": "4.2.19",
|
||||||
"tar": "6.2.1",
|
"tar": "6.2.1",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"to-json-schema": "0.2.5",
|
"to-json-schema": "0.2.5",
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
"@budibase/pro": ["./packages/pro/src"],
|
"@budibase/pro": ["./packages/pro/src"],
|
||||||
"@budibase/string-templates": ["./packages/string-templates/src"],
|
"@budibase/string-templates": ["./packages/string-templates/src"],
|
||||||
"@budibase/string-templates/*": ["./packages/string-templates/*"],
|
"@budibase/string-templates/*": ["./packages/string-templates/*"],
|
||||||
"@budibase/frontend-core": ["./packages/frontend-core/src"]
|
"@budibase/frontend-core": ["./packages/frontend-core/src"],
|
||||||
|
"@budibase/bbui": ["./packages/bbui/src"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exclude": []
|
"exclude": []
|
||||||
|
|
|
@ -18991,7 +18991,7 @@ svelte-spa-router@^4.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
regexparam "2.0.2"
|
regexparam "2.0.2"
|
||||||
|
|
||||||
svelte@4.2.19, svelte@^4.2.10:
|
svelte@4.2.19:
|
||||||
version "4.2.19"
|
version "4.2.19"
|
||||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c"
|
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c"
|
||||||
integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==
|
integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==
|
||||||
|
|
Loading…
Reference in New Issue