Merge branch 'master' into ts/util-schema

This commit is contained in:
Adria Navarro 2025-01-10 15:35:08 +01:00 committed by GitHub
commit 441e0fb46c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 82 additions and 81 deletions

View File

@ -1,6 +1,6 @@
{ {
"$schema": "node_modules/lerna/schemas/lerna-schema.json", "$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "3.2.37", "version": "3.2.38",
"npmClient": "yarn", "npmClient": "yarn",
"concurrency": 20, "concurrency": 20,
"command": { "command": {

View File

@ -1,6 +1,10 @@
import { createAPIClient } from "@budibase/frontend-core" import { createAPIClient } from "@budibase/frontend-core"
import { authStore } from "../stores/auth.js" import { authStore } from "../stores/auth"
import { notificationStore, devToolsEnabled, devToolsStore } from "../stores/" import {
notificationStore,
devToolsEnabled,
devToolsStore,
} from "../stores/index"
import { get } from "svelte/store" import { get } from "svelte/store"
export const API = createAPIClient({ export const API = createAPIClient({

View File

@ -1,5 +1,5 @@
import { API } from "./api.js" import { API } from "./api"
import { patchAPI } from "./patches.js" import { patchAPI } from "./patches"
// Certain endpoints which return rows need patched so that they transform // Certain endpoints which return rows need patched so that they transform
// and enrich the row docs, so that they can be correctly handled by the // and enrich the row docs, so that they can be correctly handled by the

5
packages/client/src/index.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
interface Window {
"##BUDIBASE_APP_ID##": string
"##BUDIBASE_IN_BUILDER##": string
MIGRATING_APP: boolean
}

View File

@ -2,7 +2,9 @@ import { API } from "api"
import { writable } from "svelte/store" import { writable } from "svelte/store"
const createAuthStore = () => { const createAuthStore = () => {
const store = writable(null) const store = writable<{
csrfToken?: string
} | null>(null)
// Fetches the user object if someone is logged in and has reloaded the page // Fetches the user object if someone is logged in and has reloaded the page
const fetchUser = async () => { const fetchUser = async () => {

View File

@ -1,7 +1,7 @@
import { derived } from "svelte/store" import { derived } from "svelte/store"
import { Constants } from "@budibase/frontend-core" import { Constants } from "@budibase/frontend-core"
import { devToolsStore } from "../devTools.js" import { devToolsStore } from "../devTools.js"
import { authStore } from "../auth.js" import { authStore } from "../auth"
import { devToolsEnabled } from "./devToolsEnabled.js" import { devToolsEnabled } from "./devToolsEnabled.js"
// Derive the current role of the logged-in user // Derive the current role of the logged-in user

View File

@ -6,7 +6,7 @@ const DEFAULT_NOTIFICATION_TIMEOUT = 3000
const createNotificationStore = () => { const createNotificationStore = () => {
let block = false let block = false
const store = writable([]) const store = writable<{ id: string; message: string; count: number }[]>([])
const blockNotifications = (timeout = 1000) => { const blockNotifications = (timeout = 1000) => {
block = true block = true
@ -14,11 +14,11 @@ const createNotificationStore = () => {
} }
const send = ( const send = (
message, message: string,
type = "info", type = "info",
icon, icon: string,
autoDismiss = true, autoDismiss = true,
duration, duration?: number,
count = 1 count = 1
) => { ) => {
if (block) { if (block) {
@ -66,7 +66,7 @@ const createNotificationStore = () => {
} }
} }
const dismiss = id => { const dismiss = (id: string) => {
store.update(state => { store.update(state => {
return state.filter(n => n.id !== id) return state.filter(n => n.id !== id)
}) })
@ -76,13 +76,13 @@ const createNotificationStore = () => {
subscribe: store.subscribe, subscribe: store.subscribe,
actions: { actions: {
send, send,
info: (msg, autoDismiss, duration) => info: (msg: string, autoDismiss?: boolean, duration?: number) =>
send(msg, "info", "Info", autoDismiss ?? true, duration), send(msg, "info", "Info", autoDismiss ?? true, duration),
success: (msg, autoDismiss, duration) => success: (msg: string, autoDismiss?: boolean, duration?: number) =>
send(msg, "success", "CheckmarkCircle", autoDismiss ?? true, duration), send(msg, "success", "CheckmarkCircle", autoDismiss ?? true, duration),
warning: (msg, autoDismiss, duration) => warning: (msg: string, autoDismiss?: boolean, duration?: number) =>
send(msg, "warning", "Alert", autoDismiss ?? true, duration), send(msg, "warning", "Alert", autoDismiss ?? true, duration),
error: (msg, autoDismiss, duration) => error: (msg: string, autoDismiss?: boolean, duration?: number) =>
send(msg, "error", "Alert", autoDismiss ?? false, duration), send(msg, "error", "Alert", autoDismiss ?? false, duration),
blockNotifications, blockNotifications,
dismiss, dismiss,

View File

@ -4,8 +4,24 @@ import { API } from "api"
import { peekStore } from "./peek" import { peekStore } from "./peek"
import { builderStore } from "./builder" import { builderStore } from "./builder"
interface Route {
path: string
screenId: string
}
interface StoreType {
routes: Route[]
routeParams: {}
activeRoute?: Route | null
routeSessionId: number
routerLoaded: boolean
queryParams?: {
peek?: boolean
}
}
const createRouteStore = () => { const createRouteStore = () => {
const initialState = { const initialState: StoreType = {
routes: [], routes: [],
routeParams: {}, routeParams: {},
activeRoute: null, activeRoute: null,
@ -22,7 +38,7 @@ const createRouteStore = () => {
} catch (error) { } catch (error) {
routeConfig = null routeConfig = null
} }
let routes = [] const routes: Route[] = []
Object.values(routeConfig?.routes || {}).forEach(route => { Object.values(routeConfig?.routes || {}).forEach(route => {
Object.entries(route.subpaths || {}).forEach(([path, config]) => { Object.entries(route.subpaths || {}).forEach(([path, config]) => {
routes.push({ routes.push({
@ -43,13 +59,13 @@ const createRouteStore = () => {
return state return state
}) })
} }
const setRouteParams = routeParams => { const setRouteParams = (routeParams: StoreType["routeParams"]) => {
store.update(state => { store.update(state => {
state.routeParams = routeParams state.routeParams = routeParams
return state return state
}) })
} }
const setQueryParams = queryParams => { const setQueryParams = (queryParams: { peek?: boolean }) => {
store.update(state => { store.update(state => {
state.queryParams = { state.queryParams = {
...queryParams, ...queryParams,
@ -60,13 +76,13 @@ const createRouteStore = () => {
return state return state
}) })
} }
const setActiveRoute = route => { const setActiveRoute = (route: string) => {
store.update(state => { store.update(state => {
state.activeRoute = state.routes.find(x => x.path === route) state.activeRoute = state.routes.find(x => x.path === route)
return state return state
}) })
} }
const navigate = (url, peek, externalNewTab) => { const navigate = (url: string, peek: boolean, externalNewTab: boolean) => {
if (get(builderStore).inBuilder) { if (get(builderStore).inBuilder) {
return return
} }
@ -93,7 +109,7 @@ const createRouteStore = () => {
const setRouterLoaded = () => { const setRouterLoaded = () => {
store.update(state => ({ ...state, routerLoaded: true })) store.update(state => ({ ...state, routerLoaded: true }))
} }
const createFullURL = relativeURL => { const createFullURL = (relativeURL: string) => {
if (!relativeURL?.startsWith("/")) { if (!relativeURL?.startsWith("/")) {
return relativeURL return relativeURL
} }

View File

@ -68,13 +68,13 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
): Promise<APIError> => { ): Promise<APIError> => {
// Try to read a message from the error // Try to read a message from the error
let message = response.statusText let message = response.statusText
let json: any = null let json = null
try { try {
json = await response.json() json = await response.json()
if (json?.message) { if (json?.message) {
message = json.message message = json.message
} else if (json?.error) { } else if (json?.error) {
message = json.error message = JSON.stringify(json.error)
} }
} catch (error) { } catch (error) {
// Do nothing // Do nothing
@ -93,7 +93,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
// Generates an error object from a string // Generates an error object from a string
const makeError = ( const makeError = (
message: string, message: string,
url?: string, url: string,
method?: HTTPMethod method?: HTTPMethod
): APIError => { ): APIError => {
return { return {
@ -226,7 +226,7 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
return await handler(callConfig) return await handler(callConfig)
} catch (error) { } catch (error) {
if (config?.onError) { if (config?.onError) {
config.onError(error) config.onError(error as APIError)
} }
throw error throw error
} }
@ -239,13 +239,9 @@ export const createAPIClient = (config: APIClientConfig = {}): APIClient => {
patch: requestApiCall(HTTPMethod.PATCH), patch: requestApiCall(HTTPMethod.PATCH),
delete: requestApiCall(HTTPMethod.DELETE), delete: requestApiCall(HTTPMethod.DELETE),
put: requestApiCall(HTTPMethod.PUT), put: requestApiCall(HTTPMethod.PUT),
error: (message: string) => {
throw makeError(message)
},
invalidateCache: () => { invalidateCache: () => {
cache = {} cache = {}
}, },
// Generic utility to extract the current app ID. Assumes that any client // Generic utility to extract the current app ID. Assumes that any client
// that exists in an app context will be attaching our app ID header. // that exists in an app context will be attaching our app ID header.
getAppID: (): string => { getAppID: (): string => {

View File

@ -46,7 +46,7 @@ export type Headers = Record<string, string>
export type APIClientConfig = { export type APIClientConfig = {
enableCaching?: boolean enableCaching?: boolean
attachHeaders?: (headers: Headers) => void attachHeaders?: (headers: Headers) => void
onError?: (error: any) => void onError?: (error: APIError) => void
onMigrationDetected?: (migration: string) => void onMigrationDetected?: (migration: string) => void
} }
@ -86,14 +86,13 @@ export type BaseAPIClient = {
patch: <RequestT = null, ResponseT = void>( patch: <RequestT = null, ResponseT = void>(
params: APICallParams<RequestT, ResponseT> params: APICallParams<RequestT, ResponseT>
) => Promise<ResponseT> ) => Promise<ResponseT>
error: (message: string) => void
invalidateCache: () => void invalidateCache: () => void
getAppID: () => string getAppID: () => string
} }
export type APIError = { export type APIError = {
message?: string message?: string
url?: string url: string
method?: HTTPMethod method?: HTTPMethod
json: any json: any
status: number status: number

View File

@ -1,4 +1,4 @@
import { FieldType } from "@budibase/types" import { FieldType, UIColumn } from "@budibase/types"
import OptionsCell from "../cells/OptionsCell.svelte" import OptionsCell from "../cells/OptionsCell.svelte"
import DateCell from "../cells/DateCell.svelte" import DateCell from "../cells/DateCell.svelte"
@ -40,13 +40,23 @@ const TypeComponentMap = {
// Custom types for UI only // Custom types for UI only
role: RoleCell, role: RoleCell,
} }
export const getCellRenderer = column => {
function getCellRendererByType(type: FieldType | "role" | undefined) {
if (!type) {
return
}
return TypeComponentMap[type as keyof typeof TypeComponentMap]
}
export const getCellRenderer = (column: UIColumn) => {
if (column.calculationType) { if (column.calculationType) {
return NumberCell return NumberCell
} }
return ( return (
TypeComponentMap[column?.schema?.cellRenderType] || getCellRendererByType(column.schema?.cellRenderType) ||
TypeComponentMap[column?.schema?.type] || getCellRendererByType(column.schema?.type) ||
TextCell TextCell
) )
} }

View File

@ -1,32 +0,0 @@
// TODO: remove when all stores are typed
import { GeneratedIDPrefix, CellIDSeparator } from "./constants"
import { Helpers } from "@budibase/bbui"
export const parseCellID = cellId => {
if (!cellId) {
return { rowId: undefined, field: undefined }
}
const parts = cellId.split(CellIDSeparator)
const field = parts.pop()
return { rowId: parts.join(CellIDSeparator), field }
}
export const getCellID = (rowId, fieldName) => {
return `${rowId}${CellIDSeparator}${fieldName}`
}
export const parseEventLocation = e => {
return {
x: e.clientX ?? e.touches?.[0]?.clientX,
y: e.clientY ?? e.touches?.[0]?.clientY,
}
}
export const generateRowID = () => {
return `${GeneratedIDPrefix}${Helpers.uuid()}`
}
export const isGeneratedRowID = id => {
return id?.startsWith(GeneratedIDPrefix)
}

View File

@ -1,12 +1,14 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import { createWebsocket } from "../../../utils" import { createWebsocket } from "../../../utils"
import { SocketEvent, GridSocketEvent } from "@budibase/shared-core" import { SocketEvent, GridSocketEvent } from "@budibase/shared-core"
import { Store } from "../stores"
import { UIDatasource, UIUser } from "@budibase/types"
export const createGridWebsocket = context => { export const createGridWebsocket = (context: Store) => {
const { rows, datasource, users, focusedCellId, definition, API } = context const { rows, datasource, users, focusedCellId, definition, API } = context
const socket = createWebsocket("/socket/grid") const socket = createWebsocket("/socket/grid")
const connectToDatasource = datasource => { const connectToDatasource = (datasource: UIDatasource) => {
if (!socket.connected) { if (!socket.connected) {
return return
} }
@ -18,7 +20,7 @@ export const createGridWebsocket = context => {
datasource, datasource,
appId, appId,
}, },
({ users: gridUsers }) => { ({ users: gridUsers }: { users: UIUser[] }) => {
users.set(gridUsers) users.set(gridUsers)
} }
) )
@ -65,7 +67,7 @@ export const createGridWebsocket = context => {
GridSocketEvent.DatasourceChange, GridSocketEvent.DatasourceChange,
({ datasource: newDatasource }) => { ({ datasource: newDatasource }) => {
// Listen builder renames, as these aren't handled otherwise // Listen builder renames, as these aren't handled otherwise
if (newDatasource?.name !== get(definition).name) { if (newDatasource?.name !== get(definition)?.name) {
definition.set(newDatasource) definition.set(newDatasource)
} }
} }

View File

@ -209,6 +209,9 @@ export const buildFormBlockButtonConfig = props => {
{ {
"##eventHandlerType": "Close Side Panel", "##eventHandlerType": "Close Side Panel",
}, },
{
"##eventHandlerType": "Close Modal",
},
...(actionUrl ...(actionUrl
? [ ? [

View File

@ -15,5 +15,5 @@ export interface GetGlobalSelfResponse extends User {
license: License license: License
budibaseAccess: boolean budibaseAccess: boolean
accountPortalAccess: boolean accountPortalAccess: boolean
csrfToken: boolean csrfToken: string
} }

View File

@ -33,11 +33,6 @@ export interface ScreenRoutesViewOutput extends Document {
export type ScreenRoutingJson = Record< export type ScreenRoutingJson = Record<
string, string,
{ {
subpaths: Record< subpaths: Record<string, any>
string,
{
screens: Record<string, string>
}
>
} }
> >

View File

@ -14,6 +14,7 @@ export type UIColumn = FieldSchema & {
type: FieldType type: FieldType
readonly: boolean readonly: boolean
autocolumn: boolean autocolumn: boolean
cellRenderType?: FieldType | "role"
} }
calculationType: CalculationType calculationType: CalculationType
__idx: number __idx: number