types: add types to hooks.ts, containers.ts and useCanvas.ts (#1240)

This commit is contained in:
Gene 2025-03-29 10:12:50 +08:00 committed by GitHub
parent bec6f7c101
commit a481eff281
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 405 additions and 279 deletions

View File

@ -1,71 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./",
"jsx": "react",
"paths": {
"@/*": ["packages/*"],
"@opentiny/tiny-engine": ["packages/design-core/index.js"],
"@opentiny/tiny-engine-meta-register": ["packages/register/src/index.js"],
"@opentiny/tiny-engine-canvas": ["packages/canvas/src/index"],
"@opentiny/tiny-engine-plugin-materials": ["packages/plugins/materials/index"],
"@opentiny/tiny-engine-plugin-state": ["packages/plugins/state/index"],
"@opentiny/tiny-engine-plugin-script": ["packages/plugins/script/index"],
"@opentiny/tiny-engine-plugin-tree": ["packages/plugins/tree/index"],
"@opentiny/tiny-engine-plugin-help": ["packages/plugins/help/index"],
"@opentiny/tiny-engine-plugin-schema": ["packages/plugins/schema/index"],
"@opentiny/tiny-engine-plugin-page": ["packages/plugins/page/index"],
"@opentiny/tiny-engine-plugin-i18n": ["packages/plugins/i18n/index"],
"@opentiny/tiny-engine-plugin-bridge": ["packages/plugins/bridge/index"],
"@opentiny/tiny-engine-setting-events": ["packages/settings/events/index"],
"@opentiny/tiny-engine-setting-props": ["packages/settings/props/index"],
"@opentiny/tiny-engine-common": ["packages/common/index"],
"@opentiny/tiny-engine-setting-styles": ["packages/settings/styles/index"],
"@opentiny/tiny-engine-toolbar-breadcrumb": ["packages/toolbars/breadcrumb/index"],
"@opentiny/tiny-engine-toolbar-fullscreen": ["packages/toolbars/fullscreen/index"],
"@opentiny/tiny-engine-toolbar-lang": ["packages/toolbars/lang/index"],
"@opentiny/tiny-engine-toolbar-view-setting": ["packages/toolbars/view-setting/index"],
"@opentiny/tiny-engine-toolbar-layout": ["packages/toolbars/layout/index"],
"@opentiny/tiny-engine-toolbar-lock": ["packages/toolbars/lock/index"],
"@opentiny/tiny-engine-toolbar-logo": ["packages/toolbars/logo/index"],
"@opentiny/tiny-engine-toolbar-media": ["packages/toolbars/media/index"],
"@opentiny/tiny-engine-toolbar-preview": ["packages/toolbars/preview/index"],
"@opentiny/tiny-engine-toolbar-generate-code": ["packages/toolbars/generate-code/index"],
"@opentiny/tiny-engine-toolbar-clean": ["packages/toolbars/clean/index"],
"@opentiny/tiny-engine-toolbar-theme-switch": ["packages/toolbars/themeSwitch/index"],
"@opentiny/tiny-engine-toolbar-save": ["packages/toolbars/save/index"],
"tiny-engine-canvas": ["packages/canvas/index"],
"@opentiny/tiny-engine-svgs": ["packages/svgs/index"],
"@opentiny/tiny-engine-plugin-materials/*": ["packages/plugins/materials/*"],
"@opentiny/tiny-engine-plugin-state/*": ["packages/plugins/state/*"],
"@opentiny/tiny-engine-plugin-script/*": ["packages/plugins/script/*"],
"@opentiny/tiny-engine-plugin-tree/*": ["packages/plugins/tree/*"],
"@opentiny/tiny-engine-plugin-help/*": ["packages/plugins/help/*"],
"@opentiny/tiny-engine-plugin-schema/*": ["packages/plugins/schema/*"],
"@opentiny/tiny-engine-plugin-page/*": ["packages/plugins/page/*"],
"@opentiny/tiny-engine-plugin-i18n/*": ["packages/plugins/i18n/*"],
"@opentiny/tiny-engine-plugin-bridge/*": ["packages/plugins/bridge/*"],
"@opentiny/tiny-engine-setting-events/*": ["packages/settings/events/*"],
"@opentiny/tiny-engine-setting-props/*": ["packages/settings/props/*"],
"@opentiny/tiny-engine-common/*": ["packages/common/*"],
"@opentiny/tiny-engine-setting-styles/*": ["packages/settings/styles/*"],
"@opentiny/tiny-engine-toolbar-breadcrumb/*": ["packages/toolbars/breadcrumb/*"],
"@opentiny/tiny-engine-toolbar-fullscreen/*": ["packages/toolbars/fullscreen/*"],
"@opentiny/tiny-engine-toolbar-lang/*": ["packages/toolbars/lang/*"],
"@opentiny/tiny-engine-toolbar-view-setting/*": ["packages/toolbars/view-setting/*"],
"@opentiny/tiny-engine-toolbar-layout/*": ["packages/toolbars/layout/*"],
"@opentiny/tiny-engine-toolbar-lock/*": ["packages/toolbars/lock/*"],
"@opentiny/tiny-engine-toolbar-logo/*": ["packages/toolbars/logo/*"],
"@opentiny/tiny-engine-toolbar-media/*": ["packages/toolbars/media/*"],
"@opentiny/tiny-engine-toolbar-preview/*": ["packages/toolbars/preview/*"],
"@opentiny/tiny-engine-toolbar-clean/*": ["packages/toolbars/clean/*"],
"@opentiny/tiny-engine-toolbar-theme-switch/*": ["packages/toolbars/themeSwitch/*"],
"@opentiny/tiny-engine-toolbar-save/*": ["packages/toolbars/save/*"],
"@opentiny/tiny-engine-svgs/*": ["packages/svgs/*"],
"@opentiny/tiny-engine-utils": ["packages/utils/src/index"],
"@opentiny/tiny-engine-webcomponent-core": ["packages/webcomponent/src/lib"],
"@opentiny/tiny-engine-i18n-host": ["packages/i18n/src/lib"]
}
},
"include": ["packages/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@ -0,0 +1,53 @@
import type { Node, RootNode } from '../../../types'
export type PageSchema = RootNode
export interface PageState {
currentVm?: unknown
currentSchema?: unknown
currentType?: unknown
currentPage?: unknown
hoverVm?: unknown
pageSchema: RootNode | null
properties?: unknown
dataSource?: unknown
dataSourceMap?: unknown
isSaved: boolean
isLock: boolean
isBlock: boolean
nodesStatus: Record<string, any>
loading: boolean
}
export interface InsertOperation {
parentId: string
newNodeData: Node
position: string
referTargetNodeId?: string
}
export interface DeleteOperation {
id: string
}
export interface ChangePropsOperation {
id: string
value: {
props?: any
}
option?: {
overwrite?: boolean
}
}
export interface UpdateAttributesOperation {
id: string
value: any
overwrite?: boolean
}
export type NodeOperation =
| (InsertOperation & { type: 'insert' })
| (DeleteOperation & { type: 'delete' })
| (ChangePropsOperation & { type: 'changeProps' })
| (UpdateAttributesOperation & { type: 'updateAttributes' })

View File

@ -15,14 +15,26 @@ import * as jsonDiffPatch from 'jsondiffpatch'
import DiffMatchPatch from 'diff-match-patch' import DiffMatchPatch from 'diff-match-patch'
import { constants, utils } from '@opentiny/tiny-engine-utils' import { constants, utils } from '@opentiny/tiny-engine-utils'
import { useHistory, getMetaApi, useMessage } from '@opentiny/tiny-engine-meta-register' import { useHistory, getMetaApi, useMessage } from '@opentiny/tiny-engine-meta-register'
import type { canvasApi as CanvasApi } from '../../../container/src/container'
import type { Node, RootNode } from '../../../types'
import type {
ChangePropsOperation,
DeleteOperation,
InsertOperation,
NodeOperation,
PageSchema,
PageState,
UpdateAttributesOperation
} from './types'
const { COMPONENT_NAME } = constants const { COMPONENT_NAME } = constants
const { deepClone } = utils const { deepClone } = utils
const defaultPageState = { const defaultPageState: PageState = {
currentVm: null, currentVm: null,
currentSchema: null, currentSchema: null,
currentType: null, currentType: null,
currentPage: null,
pageSchema: null, pageSchema: null,
properties: null, properties: null,
dataSource: null, dataSource: null,
@ -34,7 +46,7 @@ const defaultPageState = {
loading: false loading: false
} }
const defaultSchema = { const defaultSchema: PageSchema = {
componentName: 'Page', componentName: 'Page',
fileName: '', fileName: '',
css: '', css: '',
@ -53,11 +65,11 @@ const defaultSchema = {
outputs: [] outputs: []
} }
const canvasApi = ref({}) const canvasApi = ref<Partial<typeof CanvasApi>>({})
const isCanvasApiReady = ref(false) const isCanvasApiReady = ref(false)
const nodesMap = ref(new Map()) const nodesMap = ref(new Map<string | number, { node: any; parent: any }>())
const initCanvasApi = (newCanvasApi) => { const initCanvasApi = (newCanvasApi: typeof CanvasApi) => {
canvasApi.value = newCanvasApi canvasApi.value = newCanvasApi
isCanvasApiReady.value = true isCanvasApiReady.value = true
} }
@ -72,7 +84,7 @@ const rootSchema = ref([
} }
]) ])
const handleTinyGridColumnsSlots = (node) => { const handleTinyGridColumnsSlots = (node: Node) => {
const columns = Array.isArray(node.props?.columns) ? node.props.columns : [] const columns = Array.isArray(node.props?.columns) ? node.props.columns : []
for (const columnItem of columns) { for (const columnItem of columns) {
if (!columnItem?.slots) { if (!columnItem?.slots) {
@ -81,7 +93,7 @@ const handleTinyGridColumnsSlots = (node) => {
for (const slotItem of Object.values(columnItem.slots)) { for (const slotItem of Object.values(columnItem.slots)) {
if (Array.isArray(slotItem?.value)) { if (Array.isArray(slotItem?.value)) {
slotItem.value.forEach((item) => { slotItem.value.forEach((item: Node) => {
if (!item.id) { if (!item.id) {
item.id = utils.guid() item.id = utils.guid()
} }
@ -98,13 +110,13 @@ const handleTinyGridColumnsSlots = (node) => {
} }
} }
const handleNodesInProps = (node) => { const handleNodesInProps = (node: Node) => {
if (node.componentName === 'TinyGrid') { if (node.componentName === 'TinyGrid') {
handleTinyGridColumnsSlots(node) handleTinyGridColumnsSlots(node)
} }
} }
const generateNodesMap = (nodes, parent) => { const generateNodesMap = (nodes: Node[], parent: RootNode | Node) => {
nodes.forEach((nodeItem) => { nodes.forEach((nodeItem) => {
if (!nodeItem.id) { if (!nodeItem.id) {
nodeItem.id = utils.guid() nodeItem.id = utils.guid()
@ -124,7 +136,7 @@ const generateNodesMap = (nodes, parent) => {
} }
const jsonDiffPatchInstance = jsonDiffPatch.create({ const jsonDiffPatchInstance = jsonDiffPatch.create({
objectHash: function (obj, index) { objectHash: function (obj: { fileName?: string; id?: string }, index) {
return obj.fileName || obj.id || `$$index:${index}` return obj.fileName || obj.id || `$$index:${index}`
}, },
arrays: { arrays: {
@ -135,8 +147,7 @@ const jsonDiffPatchInstance = jsonDiffPatch.create({
diffMatchPatch: DiffMatchPatch, diffMatchPatch: DiffMatchPatch,
minLength: 60 minLength: 60
}, },
// eslint-disable-next-line @typescript-eslint/no-unused-vars propertyFilter: function (name) {
propertyFilter: function (name, context) {
return name.slice(0, 1) !== '$' return name.slice(0, 1) !== '$'
}, },
cloneDiffValues: false cloneDiffValues: false
@ -145,7 +156,7 @@ const jsonDiffPatchInstance = jsonDiffPatch.create({
const { publish } = useMessage() const { publish } = useMessage()
// 重置画布数据 // 重置画布数据
const resetCanvasState = async (state = {}) => { const resetCanvasState = async (state: Partial<PageState> = {}) => {
const previousSchema = JSON.parse(JSON.stringify(pageState.pageSchema)) const previousSchema = JSON.parse(JSON.stringify(pageState.pageSchema))
Object.assign(pageState, defaultPageState, state) Object.assign(pageState, defaultPageState, state)
@ -178,14 +189,14 @@ const resetCanvasState = async (state = {}) => {
} }
// 页面重置画布数据 // 页面重置画布数据
const resetPageCanvasState = (state = {}) => { const resetPageCanvasState = (state: Partial<PageState> = {}) => {
state.isBlock = false state.isBlock = false
resetCanvasState(state) resetCanvasState(state)
useHistory().addHistory(state.pageSchema) useHistory().addHistory(state.pageSchema)
} }
// 区块重置画布数据 // 区块重置画布数据
const resetBlockCanvasState = async (state = {}) => { const resetBlockCanvasState = async (state: Partial<PageState> = {}) => {
state.isBlock = true state.isBlock = true
await resetCanvasState(state) await resetCanvasState(state)
} }
@ -225,7 +236,7 @@ const clearCanvas = () => {
const isBlock = () => pageState.isBlock const isBlock = () => pageState.isBlock
// 初始化页面数据 // 初始化页面数据
const initData = (schema = { ...defaultSchema }, currentPage) => { const initData = (schema: PageSchema = { ...defaultSchema }, currentPage: any) => {
if (schema.componentName === COMPONENT_NAME.Block) { if (schema.componentName === COMPONENT_NAME.Block) {
resetBlockCanvasState({ resetBlockCanvasState({
pageSchema: toRaw(schema), pageSchema: toRaw(schema),
@ -255,7 +266,7 @@ const getPageSchema = () => {
return pageState.pageSchema || {} return pageState.pageSchema || {}
} }
const setCurrentSchema = (schema) => { const setCurrentSchema = (schema: any) => {
pageState.currentSchema = schema pageState.currentSchema = schema
} }
@ -269,15 +280,15 @@ const clearCurrentState = () => {
} }
const getCurrentPage = () => pageState.currentPage const getCurrentPage = () => pageState.currentPage
const getNodeById = (id) => { const getNodeById = (id: string) => {
return nodesMap.value.get(id)?.node return nodesMap.value.get(id)?.node
} }
const getNodeWithParentById = (id) => { const getNodeWithParentById = (id: string) => {
return nodesMap.value.get(id) return nodesMap.value.get(id)
} }
const delNode = (id) => { const delNode = (id: string) => {
nodesMap.value.delete(id) nodesMap.value.delete(id)
} }
@ -285,18 +296,18 @@ const clearNodes = () => {
nodesMap.value.clear() nodesMap.value.clear()
} }
const setNode = (schema, parent) => { const setNode = (schema: Node, parent: Node | RootNode) => {
schema.id = schema.id || utils.guid() schema.id = schema.id || utils.guid()
nodesMap.value.set(schema.id, { node: schema, parent }) nodesMap.value.set(schema.id, { node: schema, parent })
} }
const getNode = (id, parent) => { const getNode = (id: string, parent?: boolean) => {
return parent ? nodesMap.value.get(id) : nodesMap.value.get(id)?.node return parent ? nodesMap.value.get(id) : nodesMap.value.get(id)?.node
} }
const operationTypeMap = { const operationTypeMap = {
insert: (operation) => { insert: (operation: InsertOperation) => {
const { parentId, newNodeData, position, referTargetNodeId } = operation const { parentId, newNodeData, position, referTargetNodeId } = operation
const parentNode = getNode(parentId) || pageState.pageSchema const parentNode = getNode(parentId) || pageState.pageSchema
// 1. 确认是否存在 ParentNode // 1. 确认是否存在 ParentNode
@ -359,7 +370,7 @@ const operationTypeMap = {
previous: undefined previous: undefined
} }
}, },
delete: (operation) => { delete: (operation: DeleteOperation) => {
const { id } = operation const { id } = operation
const targetNode = getNode(id, true) const targetNode = getNode(id, true)
@ -398,7 +409,7 @@ const operationTypeMap = {
previous: node previous: node
} }
}, },
changeProps: (operation) => { changeProps: (operation: ChangePropsOperation) => {
const { id, value, option: changeOption } = operation const { id, value, option: changeOption } = operation
let { node } = getNode(id, true) || {} let { node } = getNode(id, true) || {}
const previous = deepClone(node) const previous = deepClone(node)
@ -423,10 +434,10 @@ const operationTypeMap = {
previous previous
} }
}, },
updateAttributes: (operation) => { updateAttributes: (operation: UpdateAttributesOperation) => {
const { id, value, overwrite } = operation const { id, value, overwrite } = operation
const { id: _id, children, ...restAttr } = value const { id: _id, children, ...restAttr } = value
const node = getNode(id) const node: Node | RootNode = getNode(id)
// 其他属性直接浅 merge // 其他属性直接浅 merge
Object.assign(node, restAttr) Object.assign(node, restAttr)
@ -466,7 +477,7 @@ const operationTypeMap = {
const newChildrenSet = new Set(newChildren.map(({ id }) => id)) const newChildrenSet = new Set(newChildren.map(({ id }) => id))
// 被删除的项 // 被删除的项
const deletedIds = originChildrenIds.filter((id) => !newChildrenSet.has(id)) const deletedIds = originChildrenIds.filter((id: any) => !newChildrenSet.has(id))
const deletedIdsSet = new Set(deletedIds) const deletedIdsSet = new Set(deletedIds)
for (const id of deletedIds) { for (const id of deletedIds) {
@ -526,7 +537,7 @@ const lastUpdateType = ref('')
* @param {*} operation * @param {*} operation
* @returns * @returns
*/ */
const operateNode = async (operation) => { const operateNode = async (operation: NodeOperation) => {
if (!operationTypeMap[operation.type]) { if (!operationTypeMap[operation.type]) {
return return
} }
@ -546,11 +557,11 @@ const operateNode = async (operation) => {
} }
// 获取传入的 schema 与最新 schema 的 diff // 获取传入的 schema 与最新 schema 的 diff
const getSchemaDiff = (schema) => { const getSchemaDiff = (schema: unknown) => {
return jsonDiffPatchInstance.diff(schema, pageState.pageSchema) return jsonDiffPatchInstance.diff(schema, pageState.pageSchema)
} }
const patchLatestSchema = (schema) => { const patchLatestSchema = (schema: unknown) => {
// 这里 pageSchema 需要 deepClone不然 patch 的时候,会 patch 成同一个引用,造成画布无法更新 // 这里 pageSchema 需要 deepClone不然 patch 的时候,会 patch 成同一个引用,造成画布无法更新
const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema)) const diff = jsonDiffPatchInstance.diff(schema, deepClone(pageState.pageSchema))
@ -559,7 +570,7 @@ const patchLatestSchema = (schema) => {
} }
} }
const importSchema = (data) => { const importSchema = (data: any) => {
let importData = data let importData = data
if (typeof data === 'string') { if (typeof data === 'string') {
@ -585,7 +596,7 @@ const getSchema = () => {
return pageState.pageSchema || {} return pageState.pageSchema || {}
} }
const getNodePath = (id, nodes = []) => { const getNodePath = (id: string, nodes: { name: string; node: string }[] = []) => {
const { parent, node } = getNodeWithParentById(id) || {} const { parent, node } = getNodeWithParentById(id) || {}
if (node) { if (node) {
@ -601,7 +612,11 @@ const getNodePath = (id, nodes = []) => {
return nodes return nodes
} }
const updateSchema = (data) => { const updateSchema = (data: Partial<PageSchema>) => {
if (!pageState.pageSchema) {
return
}
Object.assign(pageState.pageSchema, data) Object.assign(pageState.pageSchema, data)
publish({ topic: 'schemaChange', data: {} }) publish({ topic: 'schemaChange', data: {} })

View File

@ -1,17 +1,30 @@
import { ref } from 'vue' import { ref } from 'vue'
import { getDocument, getRect, querySelectById } from '../container' import { getDocument, getRect, querySelectById } from '../container'
export interface MultiSelectedState {
id: string
left: number
height: number
top: number
width: number
componentName: string
doc: Document
schema: any
parent: any
type?: string
}
// 初始化多选节点 // 初始化多选节点
const multiSelectedStates = ref([]) const multiSelectedStates = ref<MultiSelectedState[]>([])
export const useMultiSelect = () => { export const useMultiSelect = () => {
/** /**
* state到多选列表 * state到多选列表
* @param {*} selectState * @param selectState
* @param {boolean} isMultiple * @param isMultiple
* @returns {boolean} truefalse * @returns truefalse
*/ */
const toggleMultiSelection = (selectState, isMultiple = false) => { const toggleMultiSelection = (selectState: MultiSelectedState, isMultiple = false) => {
if (!selectState || typeof selectState !== 'object') { if (!selectState || typeof selectState !== 'object') {
return false return false
} }

View File

@ -25,6 +25,18 @@ import { utils } from '@opentiny/tiny-engine-utils'
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments' import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
import Builtin from '../../render/src/builtin/builtin.json' //TODO 画布内外应该分开 import Builtin from '../../render/src/builtin/builtin.json' //TODO 画布内外应该分开
import { useMultiSelect } from './composables/useMultiSelect' import { useMultiSelect } from './composables/useMultiSelect'
import type { Node, RootNode } from '../../types'
export interface DragOffset {
offsetX: number
offsetY: number
horizontal: string
vertical: string
width: number
height: number
x: number
y: number
}
export const POSITION = Object.freeze({ export const POSITION = Object.freeze({
TOP: 'top', TOP: 'top',
@ -38,31 +50,34 @@ export const POSITION = Object.freeze({
const initialDragState = { const initialDragState = {
keydown: false, keydown: false,
draging: false, draging: false,
data: null, data: null as Node | null,
position: null, // ghost位置 position: null as { left: number; top: number } | null, // ghost位置
mouse: null, // iframe里鼠标位置 mouse: {} as { x: number; y: number }, // iframe里鼠标位置
element: null, element: null as Element | null,
offset: {} offset: {} as DragOffset,
timer: 0
} }
export const canvasState = shallowReactive({ export const canvasState = shallowReactive({
type: 'normal', type: 'normal',
schema: null, schema: null,
renderer: null, // 存放画布内的api renderer: null as any, // 存放画布内的api
iframe: null, iframe: {} as HTMLIFrameElement,
loading: true, loading: true,
current: null, current: null as any,
parent: null, parent: null as any,
loopId: null loopId: null as string | null,
controller: null as any,
emit: null as any
}) })
export const getRenderer = () => canvasState.renderer export const getRenderer = () => canvasState.renderer
export const getController = () => canvasState.controller export const getController = () => canvasState.controller
export const getDocument = () => canvasState.iframe.contentDocument export const getDocument = () => canvasState.iframe.contentDocument!
export const getWindow = () => canvasState.iframe.contentWindow export const getWindow = () => canvasState.iframe.contentWindow!
export const getCurrent = () => { export const getCurrent = () => {
return { return {
@ -74,7 +89,7 @@ export const getCurrent = () => {
export const getDesignMode = () => getRenderer()?.getDesignMode() export const getDesignMode = () => getRenderer()?.getDesignMode()
export const setDesignMode = (mode) => getRenderer()?.setDesignMode(mode) export const setDesignMode = (mode: string) => getRenderer()?.setDesignMode(mode)
export const getSchema = () => useCanvas().getPageSchema() export const getSchema = () => useCanvas().getPageSchema()
@ -103,7 +118,8 @@ const initialLineState = {
forbidden: false, forbidden: false,
id: '', id: '',
config: null, config: null,
doc: null doc: null,
configure: null
} }
// 鼠标移入画布中元素时的状态 // 鼠标移入画布中元素时的状态
@ -136,14 +152,14 @@ export const clearSelect = () => {
} }
const smoothScroll = { const smoothScroll = {
timmer: null, timmer: undefined as ReturnType<typeof setTimeout> | undefined,
/** /**
* *
* @param {*} up * @param {boolean} up
* @param {*} step * @param {number} step
* @param {*} time * @param {number} time
*/ */
start(up, step = 40, time = 100) { start(up: boolean, step = 40, time = 100) {
const dom = getDocument().documentElement const dom = getDocument().documentElement
const fn = () => { const fn = () => {
const top = up ? dom.scrollTop + step : dom.scrollTop - step const top = up ? dom.scrollTop + step : dom.scrollTop - step
@ -158,14 +174,14 @@ const smoothScroll = {
}, },
stop() { stop() {
clearTimeout(this.timmer) clearTimeout(this.timmer)
this.timmer = null this.timmer = undefined
} }
} }
export const dragStart = ( export const dragStart = (
data, data: Node,
element, element: Element,
{ offsetX = 0, offsetY = 0, horizontal, vertical, width, height, x, y } = {} { offsetX = 0, offsetY = 0, horizontal, vertical, width, height, x, y } = {} as DragOffset
) => { ) => {
// 表示鼠标按下开始拖拽 // 表示鼠标按下开始拖拽
dragState.keydown = true dragState.keydown = true
@ -188,8 +204,8 @@ export const dragEnd = () => {
const { element, data } = dragState const { element, data } = dragState
if (element && canvasState.type === 'absolute') { if (element && canvasState.type === 'absolute') {
data.props = data.props || {} data!.props = data!.props || {}
data.props.style = element.style.cssText data!.props.style = element.style.cssText
getController().addHistory() getController().addHistory()
} }
@ -202,15 +218,19 @@ export const dragEnd = () => {
smoothScroll.stop() smoothScroll.stop()
} }
export const getOffset = (element) => { export const getOffset = (element: Element) => {
if (element.ownerDocument === document) { if (element.ownerDocument === document) {
return { x: 0, y: 0 } return { x: 0, y: 0, bottom: 0, top: 0 }
} }
const { x, y, bottom, top } = canvasState.iframe.getBoundingClientRect() const { x, y, bottom, top } = canvasState.iframe.getBoundingClientRect()
return { x, y, bottom, top } return { x, y, bottom, top }
} }
export const getElement = (element) => { export const getElement = (element?: Element): Element | undefined => {
if (!element || element.nodeType !== 1) {
return undefined
}
// 如果当前元素是body // 如果当前元素是body
if (element === element.ownerDocument.body) { if (element === element.ownerDocument.body) {
return element return element
@ -221,10 +241,6 @@ export const getElement = (element) => {
return element.ownerDocument.body return element.ownerDocument.body
} }
if (!element || element.nodeType !== 1) {
return undefined
}
if (element.getAttribute(NODE_UID)) { if (element.getAttribute(NODE_UID)) {
return element return element
} else if (element.parentElement) { } else if (element.parentElement) {
@ -234,7 +250,7 @@ export const getElement = (element) => {
return undefined return undefined
} }
export const getInactiveElement = (element) => { export const getInactiveElement = (element?: Element): Element | undefined => {
if ( if (
!element || !element ||
element.nodeType !== 1 || element.nodeType !== 1 ||
@ -256,7 +272,7 @@ export const getInactiveElement = (element) => {
return undefined return undefined
} }
export const getRect = (element) => { export const getRect = (element: Element) => {
if (element === getDocument().body) { if (element === getDocument().body) {
const { innerWidth: width, innerHeight: height } = getWindow() const { innerWidth: width, innerHeight: height } = getWindow()
return { return {
@ -273,48 +289,54 @@ export const getRect = (element) => {
return element.getBoundingClientRect() return element.getBoundingClientRect()
} }
const insertAfter = ({ parent, node, data }) => { interface InsertOptions {
parent: Node | RootNode
node: Node | RootNode
data: Node
}
const insertAfter = ({ parent, node, data }: InsertOptions) => {
if (!data.id) { if (!data.id) {
data.id = utils.guid() data.id = utils.guid()
} }
useCanvas().operateNode({ useCanvas().operateNode({
type: 'insert', type: 'insert',
parentId: parent.id, parentId: parent.id || '',
newNodeData: data, newNodeData: data,
position: 'after', position: 'after',
referTargetNodeId: node.id referTargetNodeId: node.id
}) })
} }
const insertBefore = ({ parent, node, data }) => { const insertBefore = ({ parent, node, data }: InsertOptions) => {
if (!data.id) { if (!data.id) {
data.id = utils.guid() data.id = utils.guid()
} }
useCanvas().operateNode({ useCanvas().operateNode({
type: 'insert', type: 'insert',
parentId: parent.id, parentId: parent.id || '',
newNodeData: data, newNodeData: data,
position: 'before', position: 'before',
referTargetNodeId: node.id referTargetNodeId: node.id
}) })
} }
const insertInner = ({ node, data }, position) => { const insertInner = ({ node, data }: Omit<InsertOptions, 'parent'>, position: string = '') => {
if (!data.id) { if (!data.id) {
data.id = utils.guid() data.id = utils.guid()
} }
useCanvas().operateNode({ useCanvas().operateNode({
type: 'insert', type: 'insert',
parentId: node.id, parentId: node.id || '',
newNodeData: data, newNodeData: data,
position: [POSITION.TOP, POSITION.LEFT].includes(position) ? 'before' : 'after' position: ([POSITION.TOP, POSITION.LEFT] as string[]).includes(position) ? 'before' : 'after'
}) })
} }
export const removeNode = (id) => { export const removeNode = (id: string) => {
useCanvas().operateNode({ useCanvas().operateNode({
type: 'delete', type: 'delete',
id id
@ -322,21 +344,21 @@ export const removeNode = (id) => {
} }
// 添加外部容器 // 添加外部容器
const insertContainer = ({ parent, node, data }) => { const insertContainer = ({ parent, node, data }: InsertOptions) => {
if (!data.id) { if (!data.id) {
data.id = utils.guid() data.id = utils.guid()
} }
useCanvas().operateNode({ useCanvas().operateNode({
type: 'insert', type: 'insert',
parentId: parent.id, parentId: parent.id || '',
newNodeData: data, newNodeData: data,
position: POSITION.OUT, position: POSITION.OUT,
referTargetNodeId: node.id referTargetNodeId: node.id
}) })
} }
export const removeNodeById = (id) => { export const removeNodeById = (id: string) => {
if (!id) { if (!id) {
return return
} }
@ -347,9 +369,9 @@ export const removeNodeById = (id) => {
canvasState.emit('remove') canvasState.emit('remove')
} }
export const querySelectById = (id) => { export const querySelectById = (id: string) => {
let selector = `[${NODE_UID}="${id}"]` let selector = `[${NODE_UID}="${id}"]`
const doc = canvasState.iframe.contentDocument const doc = getDocument()
let element = doc.querySelector(selector) let element = doc.querySelector(selector)
const loopId = element?.getAttribute('loop-id') const loopId = element?.getAttribute('loop-id')
if (element && loopId) { if (element && loopId) {
@ -364,12 +386,12 @@ export const getCurrentElement = () => querySelectById(getCurrent().schema?.id)
// 滚动页面后,目标元素与页面边界至少保留的边距 // 滚动页面后,目标元素与页面边界至少保留的边距
const SCROLL_MARGIN = 15 const SCROLL_MARGIN = 15
export const scrollToNode = (element) => { export const scrollToNode = (element?: Element | null) => {
if (element) { if (element) {
const container = getDocument().documentElement const container = getDocument().documentElement
const { clientWidth, clientHeight } = container const { clientWidth, clientHeight } = container
const { left, right, top, bottom, width, height } = element.getBoundingClientRect() const { left, right, top, bottom, width, height } = element.getBoundingClientRect()
const option = {} const option: { left?: number; top?: number } = {}
if (right < 0) { if (right < 0) {
option.left = container.scrollLeft + left - SCROLL_MARGIN option.left = container.scrollLeft + left - SCROLL_MARGIN
@ -391,11 +413,15 @@ export const scrollToNode = (element) => {
return nextTick() return nextTick()
} }
const setSelectRect = (id, element, options = {}) => { const setSelectRect = (
id: string,
element?: Element | null,
options?: { type?: string; schema: any; isMultiple: boolean }
) => {
clearHover() clearHover()
const { type, isMultiple = false } = options const { type, isMultiple = false } = options || {}
const schema = options.schema || (useCanvas().getNodeWithParentById(id) || {}).node const schema = options?.schema || (useCanvas().getNodeWithParentById(id) || {}).node
element = element || querySelectById(id) || getDocument().body element = element || querySelectById(id) || getDocument().body
const { left, height, top, width } = getRect(element) const { left, height, top, width } = getRect(element)
@ -419,7 +445,7 @@ const setSelectRect = (id, element, options = {}) => {
) )
} }
export const updateRect = (id) => { export const updateRect = (id?: string) => {
id = (typeof id === 'string' && id) || getCurrent().schema?.id id = (typeof id === 'string' && id) || getCurrent().schema?.id
clearHover() clearHover()
@ -440,7 +466,7 @@ export const updateRect = (id) => {
} }
} }
export const getConfigure = (targetName) => { export const getConfigure = (targetName: string) => {
const material = getController().getMaterial(targetName) const material = getController().getMaterial(targetName)
// 这里如果是区块插槽,则返回标识为容器的对象 // 这里如果是区块插槽,则返回标识为容器的对象
@ -459,7 +485,7 @@ export const getConfigure = (targetName) => {
* @param {*} data schame数据 * @param {*} data schame数据
* @returns * @returns
*/ */
export const allowInsert = (configure = hoverState.configure || {}, data = dragState.data || {}) => { export const allowInsert = (configure: any = hoverState.configure || {}, data: Node | null = dragState.data) => {
const { nestingRule = {} } = configure const { nestingRule = {} } = configure
const { childWhitelist = [], descendantBlacklist = [] } = nestingRule const { childWhitelist = [], descendantBlacklist = [] } = nestingRule
@ -480,7 +506,7 @@ export const allowInsert = (configure = hoverState.configure || {}, data = dragS
return flag return flag
} }
const isAncestor = (ancestor, descendant) => { const isAncestor = (ancestor: string | Node, descendant: string | Node) => {
const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id
let descendantId = typeof descendant === 'string' ? descendant : descendant.id let descendantId = typeof descendant === 'string' ? descendant : descendant.id
@ -497,9 +523,13 @@ const isAncestor = (ancestor, descendant) => {
return false return false
} }
type Rect =
| DOMRect
| { left: number; top: number; right: number; bottom: number; width: number; height: number; x: number; y: number }
// 获取位置信息,返回状态 // 获取位置信息,返回状态
const lineAbs = 20 const lineAbs = 20
const getPosLine = (rect, configure) => { const getPosLine = (rect: Rect, configure: { isContainer: any }) => {
const mousePos = dragState.mouse const mousePos = dragState.mouse
const yAbs = Math.min(lineAbs, rect.height / 3) const yAbs = Math.min(lineAbs, rect.height / 3)
const xAbs = Math.min(lineAbs, rect.width / 3) const xAbs = Math.min(lineAbs, rect.width / 3)
@ -532,14 +562,14 @@ const getPosLine = (rect, configure) => {
return { type, forbidden } return { type, forbidden }
} }
const isBodyEl = (element) => element.nodeName === 'BODY' const isBodyEl = (element: Element) => element.nodeName === 'BODY'
const setHoverRect = (element, data) => { const setHoverRect = (element?: Element, data?: Node | null) => {
if (!element) { if (!element) {
return clearHover() return clearHover()
} }
const componentName = element.getAttribute(NODE_TAG) const componentName = element.getAttribute(NODE_TAG)!
const id = element.getAttribute(NODE_UID) const id = element.getAttribute(NODE_UID)!
const configure = getConfigure(componentName) const configure = getConfigure(componentName)
const rect = getRect(element) const rect = getRect(element)
const { left, height, top, width } = rect const { left, height, top, width } = rect
@ -561,7 +591,7 @@ const setHoverRect = (element, data) => {
// 如果容器盒子有子节点,则以最后一个子节点为拖拽参照物 // 如果容器盒子有子节点,则以最后一个子节点为拖拽参照物
const lastNode = children[children.length - 1] const lastNode = children[children.length - 1]
childEle = querySelectById(lastNode.id) childEle = querySelectById(lastNode.id)
const childComponentName = element.getAttribute(childEle) const childComponentName = childEle!.getAttribute(NODE_TAG)!
const Childconfigure = getConfigure(childComponentName) const Childconfigure = getConfigure(childComponentName)
lineState.id = lastNode.id lineState.id = lastNode.id
lineState.configure = Childconfigure lineState.configure = Childconfigure
@ -572,7 +602,7 @@ const setHoverRect = (element, data) => {
if (childEle) { if (childEle) {
const childRect = getRect(childEle) const childRect = getRect(childEle)
const { left, height, top, width } = childRect const { left, height, top, width } = childRect
const posLine = getPosLine(childRect, lineState.configure) const posLine = getPosLine(childRect, lineState.configure!)
Object.assign(lineState, { Object.assign(lineState, {
width, width,
height, height,
@ -627,13 +657,13 @@ const updateHoverRect = (id?: string) => {
}) })
} }
const setInactiveHoverRect = (element) => { const setInactiveHoverRect = (element?: Element) => {
if (!element) { if (!element) {
Object.assign(inactiveHoverState, initialRectState, { slot: null }) Object.assign(inactiveHoverState, initialRectState, { slot: null })
return return
} }
const componentName = element.getAttribute(NODE_TAG) const componentName = element.getAttribute(NODE_TAG)!
const id = element.getAttribute(NODE_INACTIVE_UID) const id = element.getAttribute(NODE_INACTIVE_UID)
const configure = getConfigure(componentName) const configure = getConfigure(componentName)
const rect = getRect(element) const rect = getRect(element)
@ -657,10 +687,10 @@ export const syncNodeScroll = () => {
updateHoverRect() updateHoverRect()
} }
let moveUpdateTimer = null let moveUpdateTimer: ReturnType<typeof setTimeout> | undefined = undefined
// 绝对布局 // 绝对布局
const absoluteMove = (event, element) => { const absoluteMove = (event: DragEvent, element: HTMLElement) => {
const { clientX, clientY } = event const { clientX, clientY } = event
const { offsetX, offsetY, horizontal, vertical, height, width, x, y } = dragState.offset const { offsetX, offsetY, horizontal, vertical, height, width, x, y } = dragState.offset
@ -693,7 +723,7 @@ const absoluteMove = (event, element) => {
clearTimeout(moveUpdateTimer) clearTimeout(moveUpdateTimer)
const { data } = dragState const data = dragState.data!
data.props = data.props || {} data.props = data.props || {}
// 防抖更新位置信息到 schema // 防抖更新位置信息到 schema
@ -706,7 +736,16 @@ const absoluteMove = (event, element) => {
updateRect() updateRect()
} }
const setDragPosition = ({ clientX, x, clientY, y, offsetBottom, offsetTop }) => { interface SetDragPositionOptions {
clientX: number
x: number
clientY: number
y: number
offsetBottom: number
offsetTop: number
}
const setDragPosition = ({ clientX, x, clientY, y, offsetBottom, offsetTop }: SetDragPositionOptions) => {
const left = clientX + x const left = clientX + x
const top = clientY + y const top = clientY + y
if (clientY < 20) { if (clientY < 20) {
@ -720,12 +759,14 @@ const setDragPosition = ({ clientX, x, clientY, y, offsetBottom, offsetTop }) =>
dragState.position = { left, top } dragState.position = { left, top }
} }
export const dragMove = (event, isHover) => { export const dragMove = (event: DragEvent, isHover: boolean) => {
if (!dragState.draging && dragState.keydown && new Date().getTime() - dragState.timer < 200) { if (!dragState.draging && dragState.keydown && new Date().getTime() - dragState.timer < 200) {
return return
} }
const { x, y, bottom: offsetBottom, top: offsetTop } = getOffset(event.target) const eventTarget = event.target as Element
const { x, y, bottom: offsetBottom, top: offsetTop } = getOffset(eventTarget)
const { clientX, clientY } = event const { clientX, clientY } = event
const { element } = dragState const { element } = dragState
const absolute = canvasState.type === 'absolute' const absolute = canvasState.type === 'absolute'
@ -737,24 +778,24 @@ export const dragMove = (event, isHover) => {
// 如果仅仅是mouseover事件直接return,并重置拖拽位置状态,优化性能 // 如果仅仅是mouseover事件直接return,并重置拖拽位置状态,优化性能
if (isHover) { if (isHover) {
lineState.position = '' lineState.position = ''
setHoverRect(getElement(event.target), null) setHoverRect(getElement(eventTarget), null)
setInactiveHoverRect(getInactiveElement(event.target)) setInactiveHoverRect(getInactiveElement(eventTarget))
return return
} }
setHoverRect(getElement(event.target), dragState.data) setHoverRect(getElement(eventTarget), dragState.data)
if (dragState.draging) { if (dragState.draging) {
// 绝对布局时走的逻辑 // 绝对布局时走的逻辑
if (element && absolute) { if (element && absolute) {
absoluteMove(event, element) absoluteMove(event, element as HTMLElement)
} }
setDragPosition({ clientX, x, clientY, y, offsetBottom, offsetTop }) setDragPosition({ clientX, x, clientY, y, offsetBottom, offsetTop })
} }
} }
// type == clickTree, 为点击大纲; type == loop-id=xxx ,为点击循环数据 // type == clickTree, 为点击大纲; type == loop-id=xxx ,为点击循环数据
export const selectNode = async (id, type, isMultiple = false) => { export const selectNode = async (id: string, type?: string, isMultiple = false) => {
const { node } = useCanvas().getNodeWithParentById(id) || {} const { node } = useCanvas().getNodeWithParentById(id) || {}
let element = querySelectById(id) let element = querySelectById(id)
@ -798,16 +839,20 @@ export const selectNode = async (id, type, isMultiple = false) => {
} }
} }
export const hoverNode = (id, data) => { export const hoverNode = (id: string, data: Node) => {
const element = querySelectById(id) const element = querySelectById(id)
if (element) { if (element) {
setHoverRect(element, data) setHoverRect(element, data)
} }
} }
export const insertNode = (node, position = POSITION.IN, select = true) => { export const insertNode = (
node: { node: Node; parent: Node; data: Node },
position: string = POSITION.IN,
select = true
) => {
if (!node.parent) { if (!node.parent) {
insertInner({ node: useCanvas().pageState.pageSchema, data: node.data }, position) insertInner({ node: useCanvas().pageState.pageSchema!, data: node.data }, position)
} else { } else {
switch (position) { switch (position) {
case POSITION.TOP: case POSITION.TOP:
@ -837,33 +882,34 @@ export const insertNode = (node, position = POSITION.IN, select = true) => {
getController().addHistory() getController().addHistory()
} }
export const addComponent = (data, position) => { export const addComponent = (data: Node, position: string) => {
const { schema, parent } = getCurrent() const { schema, parent } = getCurrent()
insertNode({ node: schema, parent, data }, position) insertNode({ node: schema, parent, data }, position)
} }
export const copyNode = (id) => { export const copyNode = (id: string) => {
if (!id) { if (!id) {
return return
} }
const { node, parent } = useCanvas().getNodeWithParentById(id) const { node, parent } = useCanvas().getNodeWithParentById(id)!
insertAfter({ parent, node, data: copyObject(node) }) insertAfter({ parent, node, data: copyObject(node) })
getController().addHistory() getController().addHistory()
} }
export const onMouseUp = () => { export const onMouseUp = () => {
const { draging, data } = dragState const { draging } = dragState
const { position, forbidden } = lineState const { position, forbidden } = lineState
const absolute = canvasState.type === 'absolute' const absolute = canvasState.type === 'absolute'
const sourceId = data?.id
const lineId = lineState.id const lineId = lineState.id
const { getNodeWithParentById, getSchema } = useCanvas() const { getNodeWithParentById, getSchema } = useCanvas()
if (draging && !forbidden) { if (draging && !forbidden) {
const { parent, node } = getNodeWithParentById(lineId) || {} // target const { parent, node } = getNodeWithParentById(lineId) || {} // target
const data = dragState.data!
const sourceId = data.id
const insertData = toRaw(data) const insertData = toRaw(data)
const targetNode = { parent, node, data: { ...insertData, children: insertData.children || [] } } const targetNode = { parent, node, data: { ...insertData, children: insertData.children || [] } }
@ -890,16 +936,16 @@ export const onMouseUp = () => {
dragEnd() dragEnd()
} }
export const addStyle = (href) => appendStyle(href, getDocument()) export const addStyle = (href: string) => appendStyle(href, getDocument())
export const addScript = (src) => appendScript(src, getDocument()) export const addScript = (src: string) => appendScript(src, getDocument())
/** /**
* *
* @param {*} messages * @param {*} messages
* @param {*} merge * @param {*} merge
*/ */
export const setLocales = (messages, merge) => { export const setLocales = (messages: any, merge: any) => {
const i18n = getRenderer().getI18n() const i18n = getRenderer().getI18n()
Object.keys(messages).forEach((lang) => { Object.keys(messages).forEach((lang) => {
@ -908,11 +954,11 @@ export const setLocales = (messages, merge) => {
}) })
} }
export const setConfigure = (configure) => { export const setConfigure = (configure: any) => {
getRenderer().setConfigure(configure) getRenderer().setConfigure(configure)
} }
export const setI18n = (data) => { export const setI18n = (data: any) => {
const messages = data || useTranslate().getData() const messages = data || useTranslate().getData()
const i18n = getRenderer().getI18n() const i18n = getRenderer().getI18n()
Object.keys(messages).forEach((lang) => { Object.keys(messages).forEach((lang) => {
@ -920,7 +966,7 @@ export const setI18n = (data) => {
}) })
} }
export const setCanvasType = (type) => { export const setCanvasType = (type: string) => {
canvasState.type = type || 'normal' canvasState.type = type || 'normal'
getDocument().body.className = type === 'absolute' ? 'canvas-grid-bg' : '' getDocument().body.className = type === 'absolute' ? 'canvas-grid-bg' : ''
} }
@ -932,7 +978,7 @@ export const getCanvasType = () => canvasState.type
* @param {string} name * @param {string} name
* @param {any} data * @param {any} data
*/ */
export const canvasDispatch = (name, data, doc = getDocument()) => { export const canvasDispatch = (name: string, data: any, doc = getDocument()) => {
if (!doc) return if (!doc) return
doc.dispatchEvent(new CustomEvent(name, data)) doc.dispatchEvent(new CustomEvent(name, data))
@ -964,15 +1010,15 @@ export const canvasApi = {
getConfigure, getConfigure,
allowInsert, allowInsert,
Builtin, Builtin,
removeBlockCompsCache: (...args) => { removeBlockCompsCache: (...args: any[]) => {
return canvasState.renderer.removeBlockCompsCache(...args) return canvasState.renderer.removeBlockCompsCache(...args)
}, },
updateCanvas: (...args) => { updateCanvas: (...args: any[]) => {
return canvasState.renderer.updateCanvas(...args) return canvasState.renderer.updateCanvas(...args)
} }
} }
export const initCanvas = ({ renderer, iframe, emit, controller }) => { export const initCanvas = ({ renderer, iframe, emit, controller }: any) => {
canvasState.iframe = iframe canvasState.iframe = iframe
canvasState.emit = emit canvasState.emit = emit
// 存放画布外层传进来的插件api // 存放画布外层传进来的插件api

View File

@ -53,6 +53,7 @@
}, },
"devDependencies": { "devDependencies": {
"@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*", "@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*",
"@types/diff-match-patch": "^1.0.36",
"@vitejs/plugin-vue": "^5.1.2", "@vitejs/plugin-vue": "^5.1.2",
"@vitejs/plugin-vue-jsx": "^4.0.1", "@vitejs/plugin-vue-jsx": "^4.0.1",
"rollup-plugin-polyfill-node": "^0.13.0", "rollup-plugin-polyfill-node": "^0.13.0",

View File

@ -8,6 +8,6 @@ let designMode = DESIGN_MODE.DESIGN
export const getDesignMode = () => designMode export const getDesignMode = () => designMode
export const setDesignMode = (mode) => { export const setDesignMode = (mode: string) => {
designMode = mode designMode = mode
} }

19
packages/canvas/types.ts Normal file
View File

@ -0,0 +1,19 @@
export interface Node {
id: string
componentName: string
props: Record<string, any> & { columns?: { slots?: Record<string, any> }[] }
children?: Node[]
}
export type RootNode = Omit<Node, 'id'> & {
id?: string
css?: string
fileName: string
methods?: Record<string, any>
state?: Record<string, any>
lifeCycles?: Record<string, any>
dataSource?: any
bridge?: any
inputs?: any[]
outputs?: any[]
}

View File

@ -7,7 +7,15 @@ const durationMap = {
error: 10000 error: 10000
} }
const useNotify = (config) => { export interface NotifyOptions {
[key: string]: any
customClass: string
title: string
type: keyof typeof durationMap
position: string
}
const useNotify = (config: NotifyOptions) => {
const { customClass, title, type = 'info', position = 'top-right', ...otherConfig } = config const { customClass, title, type = 'info', position = 'top-right', ...otherConfig } = config
Notify({ Notify({

View File

@ -10,6 +10,7 @@
* *
*/ */
import type { App } from 'vue'
import ConfigGroup from './ConfigGroup.vue' import ConfigGroup from './ConfigGroup.vue'
import ConfigItem from './ConfigItem.vue' import ConfigItem from './ConfigItem.vue'
export { default as PluginSetting } from './PluginSetting.vue' export { default as PluginSetting } from './PluginSetting.vue'
@ -54,12 +55,12 @@ export { default as Pane } from './Pane.vue'
export { default as I18nInput } from './I18nInput.vue' export { default as I18nInput } from './I18nInput.vue'
export { default as CanvasDragItem } from './CanvasDragItem.vue' export { default as CanvasDragItem } from './CanvasDragItem.vue'
export { default as ToolbarBase } from './ToolbarBase.vue' export { default as ToolbarBase } from './ToolbarBase.vue'
export { default as Modal } from './Modal.jsx' export { default as Modal } from './Modal'
export { default as Notify } from './Notify.jsx' export { default as Notify } from './Notify'
export { ConfigGroup, ConfigItem } export { ConfigGroup, ConfigItem }
export const injectGlobalComponents = { export const injectGlobalComponents = {
install: (app) => { install: (app: App<Element>) => {
const globalComponents = { const globalComponents = {
ConfigGroup, ConfigGroup,
ConfigItem ConfigItem

View File

@ -25,7 +25,9 @@ const helpState = {
} }
} }
const getDocsUrl = (plugin) => { type PluginName = keyof typeof helpState['docsUrl']
const getDocsUrl = (plugin: PluginName) => {
return `${getBaseUrl()}${helpState.docsUrl[plugin]}` return `${getBaseUrl()}${helpState.docsUrl[plugin]}`
} }

View File

@ -51,7 +51,7 @@ export const metaHashMap: Record<string, any> = {}
export const apisMap: Record<string, any> = {} export const apisMap: Record<string, any> = {}
export const optionsMap: Record<string, any> = {} export const optionsMap: Record<string, any> = {}
export const getMetaApi = (id: string, key: string) => { export const getMetaApi = (id: string, key?: string) => {
if (!apisMap[id]) { if (!apisMap[id]) {
return return
} }
@ -144,14 +144,14 @@ const handleRegistryProp = (id: string, value: any) => {
export const preprocessRegistry = (registry: Array<any> | { [s: string]: any }) => { export const preprocessRegistry = (registry: Array<any> | { [s: string]: any }) => {
// 元应用支持使用长度为2的数组来配置第一个参数为元应用第二个参数是额外的自定义配置。此函数判断数组是否属于这种配置格式 // 元应用支持使用长度为2的数组来配置第一个参数为元应用第二个参数是额外的自定义配置。此函数判断数组是否属于这种配置格式
const isArrayFormat = (arr) => Array.isArray(arr) && arr.length === 2 && arr[0].id const isArrayFormat = (arr: any) => Array.isArray(arr) && arr.length === 2 && arr[0].id
Object.values(registry) Object.values(registry)
.filter((metaApps) => Array.isArray(metaApps)) .filter((metaApps) => Array.isArray(metaApps))
.forEach((metaApps) => { .forEach((metaApps) => {
// normal: { plugins: [ Page, Block, ... ] } // normal: { plugins: [ Page, Block, ... ] }
// array format: { plugins: [ [ Page, { options: extraOptions } ], Block, ... ] } // array format: { plugins: [ [ Page, { options: extraOptions } ], Block, ... ] }
metaApps.forEach((metaApp, index) => { metaApps.forEach((metaApp: any, index: number) => {
if (isArrayFormat(metaApp)) { if (isArrayFormat(metaApp)) {
metaApps.splice(index, 1, { ...metaApp[0], ...metaApp[1] }) metaApps.splice(index, 1, { ...metaApp[0], ...metaApp[1] })
} }

View File

@ -1,3 +1,23 @@
import type {
NotifyParams,
NotifyResult,
UseBlockApi,
UseBreadcrumbApi,
UseCanvasApi,
UseDataSourceApi,
UseHelpApi,
UseHistoryApi,
UseLayoutApi,
UseMaterialApi,
UseModalApi,
UsePageApi,
UsePropertiesApi,
UsePropertyApi,
UseResourceApi,
UseSaveLocalApi,
UseTranslateApi
} from './types'
export const HOOK_NAME = { export const HOOK_NAME = {
useLayout: 'layout', useLayout: 'layout',
useCanvas: 'canvas', useCanvas: 'canvas',
@ -18,7 +38,9 @@ export const HOOK_NAME = {
useNotify: 'notify', useNotify: 'notify',
useCustom: 'custom', useCustom: 'custom',
useMaterial: 'material' useMaterial: 'material'
} } as const
type HookName = typeof HOOK_NAME[keyof typeof HOOK_NAME]
const hooksState = { const hooksState = {
[HOOK_NAME.useLayout]: {}, [HOOK_NAME.useLayout]: {},
@ -42,35 +64,35 @@ const hooksState = {
[HOOK_NAME.useCustom]: {} // 自定义 [HOOK_NAME.useCustom]: {} // 自定义
} }
const getHook = (hookName: string, args: any[]) => { const getHook = (hookName: HookName, args: any[]) => {
if (typeof hooksState[hookName] === 'function') { if (typeof hooksState[hookName] === 'function') {
return hooksState[hookName](...args) return hooksState[hookName](...args)
} }
return hooksState[hookName] return hooksState[hookName]
} }
export const useLayout = (...args: any[]) => getHook(HOOK_NAME.useLayout, args) export const useLayout = (...args: any[]): UseLayoutApi => getHook(HOOK_NAME.useLayout, args)
export const useCanvas = (...args: any[]) => getHook(HOOK_NAME.useCanvas, args) export const useCanvas = (...args: any[]): UseCanvasApi => getHook(HOOK_NAME.useCanvas, args)
export const useResource = (...args: any[]) => getHook(HOOK_NAME.useResource, args) export const useResource = (...args: any[]): UseResourceApi => getHook(HOOK_NAME.useResource, args)
export const useHistory = (...args: any[]) => getHook(HOOK_NAME.useHistory, args) export const useHistory = (...args: any[]): UseHistoryApi => getHook(HOOK_NAME.useHistory, args)
export const useProperties = (...args: any[]) => getHook(HOOK_NAME.useProperties, args) export const useProperties = (...args: any[]): UsePropertiesApi => getHook(HOOK_NAME.useProperties, args)
export const useSaveLocal = (...args: any[]) => getHook(HOOK_NAME.useSaveLocal, args) export const useSaveLocal = (...args: any[]): UseSaveLocalApi => getHook(HOOK_NAME.useSaveLocal, args)
export const useBlock = (...args: any[]) => getHook(HOOK_NAME.useBlock, args) export const useBlock = (...args: any[]): UseBlockApi => getHook(HOOK_NAME.useBlock, args)
export const useTranslate = (...args: any[]) => getHook(HOOK_NAME.useTranslate, args) export const useTranslate = (...args: any[]): UseTranslateApi => getHook(HOOK_NAME.useTranslate, args)
export const usePage = (...args: any[]) => getHook(HOOK_NAME.usePage, args) export const usePage = (...args: any[]): UsePageApi => getHook(HOOK_NAME.usePage, args)
export const useDataSource = (...args: any[]) => getHook(HOOK_NAME.useDataSource, args) export const useDataSource = (...args: any[]): UseDataSourceApi => getHook(HOOK_NAME.useDataSource, args)
export const useBreadcrumb = (...args: any[]) => getHook(HOOK_NAME.useBreadcrumb, args) export const useBreadcrumb = (...args: any[]): UseBreadcrumbApi => getHook(HOOK_NAME.useBreadcrumb, args)
export const useProperty = (...args: any[]) => getHook(HOOK_NAME.useProperty, args) export const useProperty = (...args: any[]): UsePropertyApi => getHook(HOOK_NAME.useProperty, args)
export const useHelp = (...args: any[]) => getHook(HOOK_NAME.useHelp, args) export const useHelp = (...args: any[]): UseHelpApi => getHook(HOOK_NAME.useHelp, args)
export const useHttp = (...args: any[]) => getHook(HOOK_NAME.useHttp, args) export const useHttp = (...args: any[]) => getHook(HOOK_NAME.useHttp, args)
export const useEnv = (...args: any[]) => getHook(HOOK_NAME.useEnv, args) export const useEnv = (...args: any[]): ImportMetaEnv => getHook(HOOK_NAME.useEnv, args)
export const useModal = (...args: any[]) => getHook(HOOK_NAME.useModal, args) export const useModal = (...args: any[]): UseModalApi => getHook(HOOK_NAME.useModal, args)
export const useNotify = (...args: any[]) => getHook(HOOK_NAME.useNotify, args) export const useNotify = (...args: NotifyParams): NotifyResult => getHook(HOOK_NAME.useNotify, args)
export const useMaterial = (...args: any[]) => getHook(HOOK_NAME.useMaterial, args) export const useMaterial = (...args: any[]): UseMaterialApi => getHook(HOOK_NAME.useMaterial, args)
export const useCustom = (...args: any[]) => getHook(HOOK_NAME.useCustom, args) export const useCustom = (...args: any[]) => getHook(HOOK_NAME.useCustom, args)
export function initHook( export function initHook(
hookName: string, hookName: HookName,
hookContent: any, hookContent: any,
{ useDefaultExport } = {} as { useDefaultExport?: boolean } { useDefaultExport } = {} as { useDefaultExport?: boolean }
) { ) {

View File

@ -0,0 +1,38 @@
import type { default as useCanvasApi } from '@opentiny/tiny-engine-canvas/DesignCanvas/src/api'
import type { LayoutService } from '@opentiny/tiny-engine-layout'
import type { BlockService } from '@opentiny/tiny-engine-plugin-block'
import type { DataSourceService } from '@opentiny/tiny-engine-plugin-datasource'
import type { HelpService } from '@opentiny/tiny-engine-plugin-help'
import type { TranslateService } from '@opentiny/tiny-engine-plugin-i18n'
import type { MaterialService, ResourceService } from '@opentiny/tiny-engine-plugin-materials'
import type { PageService } from '@opentiny/tiny-engine-plugin-page'
import type { PropertiesService, PropertyService } from '@opentiny/tiny-engine-setting-props'
import type { BreadcrumbService } from '@opentiny/tiny-engine-toolbar-breadcrumb'
import type { SaveLocalService } from '@opentiny/tiny-engine-toolbar-generate-code'
import type { HistoryService } from '@opentiny/tiny-engine-toolbar-redoundo'
import type { Modal, Notify } from '@opentiny/tiny-engine-common'
export type UseCanvasApi = ReturnType<typeof useCanvasApi>
export type UseLayoutApi = typeof LayoutService['apis']
// plugin
export type UseBlockApi = typeof BlockService['apis']
export type UseDataSourceApi = typeof DataSourceService['apis']
export type UseHelpApi = typeof HelpService['apis']
export type UseTranslateApi = typeof TranslateService['apis']
export type UseMaterialApi = typeof MaterialService['apis']
export type UseResourceApi = typeof ResourceService['apis']
export type UsePageApi = typeof PageService['apis']
// setting
export type UsePropertiesApi = typeof PropertiesService['apis']
export type UsePropertyApi = typeof PropertyService['apis']
// toolbar
export type UseBreadcrumbApi = typeof BreadcrumbService['apis']
export type UseSaveLocalApi = typeof SaveLocalService['apis']
export type UseHistoryApi = typeof HistoryService['apis']
export type UseModalApi = typeof Modal
export type NotifyParams = Parameters<typeof Notify>
export type NotifyResult = ReturnType<typeof Notify>

View File

@ -16,65 +16,44 @@
"paths": { "paths": {
"@/*": ["packages/*"], "@/*": ["packages/*"],
"@opentiny/tiny-engine": ["packages/design-core/index.js"], "@opentiny/tiny-engine": ["packages/design-core/index.js"],
"@opentiny/tiny-engine-meta-register": ["packages/register/src/index.js"], "@opentiny/tiny-engine-canvas": ["packages/canvas/index"],
"@opentiny/tiny-engine-canvas": ["packages/canvas/src/index"], "@opentiny/tiny-engine-canvas/*": ["packages/canvas/*"],
"@opentiny/tiny-engine-plugin-materials": ["packages/plugins/materials/index"], "@opentiny/tiny-engine-common": ["packages/common/index"],
"@opentiny/tiny-engine-plugin-state": ["packages/plugins/state/index"], "@opentiny/tiny-engine-common/*": ["packages/common/*"],
"@opentiny/tiny-engine-plugin-script": ["packages/plugins/script/index"], "@opentiny/tiny-engine-i18n-host": ["packages/i18n/src/lib"],
"@opentiny/tiny-engine-plugin-tree": ["packages/plugins/tree/index"], "@opentiny/tiny-engine-layout": ["packages/layout/index"],
"@opentiny/tiny-engine-plugin-help": ["packages/plugins/help/index"], "@opentiny/tiny-engine-meta-register": ["packages/register/src/index"],
"@opentiny/tiny-engine-plugin-schema": ["packages/plugins/schema/index"], "@opentiny/tiny-engine-plugin-block": ["packages/plugins/block/index"],
"@opentiny/tiny-engine-plugin-page": ["packages/plugins/page/index"],
"@opentiny/tiny-engine-plugin-i18n": ["packages/plugins/i18n/index"],
"@opentiny/tiny-engine-plugin-bridge": ["packages/plugins/bridge/index"], "@opentiny/tiny-engine-plugin-bridge": ["packages/plugins/bridge/index"],
"@opentiny/tiny-engine-plugin-datasource": ["packages/plugins/datasource/index"],
"@opentiny/tiny-engine-plugin-help": ["packages/plugins/help/index"],
"@opentiny/tiny-engine-plugin-i18n": ["packages/plugins/i18n/index"],
"@opentiny/tiny-engine-plugin-materials": ["packages/plugins/materials/index"],
"@opentiny/tiny-engine-plugin-page": ["packages/plugins/page/index"],
"@opentiny/tiny-engine-plugin-schema": ["packages/plugins/schema/index"],
"@opentiny/tiny-engine-plugin-script": ["packages/plugins/script/index"],
"@opentiny/tiny-engine-plugin-state": ["packages/plugins/state/index"],
"@opentiny/tiny-engine-plugin-tree": ["packages/plugins/tree/index"],
"@opentiny/tiny-engine-setting-events": ["packages/settings/events/index"], "@opentiny/tiny-engine-setting-events": ["packages/settings/events/index"],
"@opentiny/tiny-engine-setting-props": ["packages/settings/props/index"], "@opentiny/tiny-engine-setting-props": ["packages/settings/props/index"],
"@opentiny/tiny-engine-common": ["packages/common/index"],
"@opentiny/tiny-engine-setting-styles": ["packages/settings/styles/index"], "@opentiny/tiny-engine-setting-styles": ["packages/settings/styles/index"],
"@opentiny/tiny-engine-svgs": ["packages/svgs/index"],
"@opentiny/tiny-engine-toolbar-breadcrumb": ["packages/toolbars/breadcrumb/index"], "@opentiny/tiny-engine-toolbar-breadcrumb": ["packages/toolbars/breadcrumb/index"],
"@opentiny/tiny-engine-toolbar-clean": ["packages/toolbars/clean/index"],
"@opentiny/tiny-engine-toolbar-fullscreen": ["packages/toolbars/fullscreen/index"], "@opentiny/tiny-engine-toolbar-fullscreen": ["packages/toolbars/fullscreen/index"],
"@opentiny/tiny-engine-toolbar-generate-code": ["packages/toolbars/generate-code/index"],
"@opentiny/tiny-engine-toolbar-lang": ["packages/toolbars/lang/index"], "@opentiny/tiny-engine-toolbar-lang": ["packages/toolbars/lang/index"],
"@opentiny/tiny-engine-toolbar-view-setting": ["packages/toolbars/view-setting/index"],
"@opentiny/tiny-engine-toolbar-layout": ["packages/toolbars/layout/index"], "@opentiny/tiny-engine-toolbar-layout": ["packages/toolbars/layout/index"],
"@opentiny/tiny-engine-toolbar-lock": ["packages/toolbars/lock/index"], "@opentiny/tiny-engine-toolbar-lock": ["packages/toolbars/lock/index"],
"@opentiny/tiny-engine-toolbar-logo": ["packages/toolbars/logo/index"], "@opentiny/tiny-engine-toolbar-logo": ["packages/toolbars/logo/index"],
"@opentiny/tiny-engine-toolbar-media": ["packages/toolbars/media/index"], "@opentiny/tiny-engine-toolbar-media": ["packages/toolbars/media/index"],
"@opentiny/tiny-engine-toolbar-preview": ["packages/toolbars/preview/index"], "@opentiny/tiny-engine-toolbar-preview": ["packages/toolbars/preview/index"],
"@opentiny/tiny-engine-toolbar-generate-code": ["packages/toolbars/generate-code/index"], "@opentiny/tiny-engine-toolbar-redoundo": ["packages/toolbars/redoundo/index"],
"@opentiny/tiny-engine-toolbar-clean": ["packages/toolbars/clean/index"],
"@opentiny/tiny-engine-toolbar-theme-switch": ["packages/toolbars/themeSwitch/index"],
"@opentiny/tiny-engine-toolbar-save": ["packages/toolbars/save/index"], "@opentiny/tiny-engine-toolbar-save": ["packages/toolbars/save/index"],
"tiny-engine-canvas": ["packages/canvas/index"], "@opentiny/tiny-engine-toolbar-theme-switch": ["packages/toolbars/themeSwitch/index"],
"@opentiny/tiny-engine-svgs": ["packages/svgs/index"], "@opentiny/tiny-engine-toolbar-view-setting": ["packages/toolbars/view-setting/index"],
"@opentiny/tiny-engine-plugin-materials/*": ["packages/plugins/materials/*"], "@opentiny/tiny-engine-utils": ["packages/utils/src/index"],
"@opentiny/tiny-engine-plugin-state/*": ["packages/plugins/state/*"], "@opentiny/tiny-engine-webcomponent-core": ["packages/webcomponent/src/lib"]
"@opentiny/tiny-engine-plugin-script/*": ["packages/plugins/script/*"],
"@opentiny/tiny-engine-plugin-tree/*": ["packages/plugins/tree/*"],
"@opentiny/tiny-engine-plugin-help/*": ["packages/plugins/help/*"],
"@opentiny/tiny-engine-plugin-schema/*": ["packages/plugins/schema/*"],
"@opentiny/tiny-engine-plugin-page/*": ["packages/plugins/page/*"],
"@opentiny/tiny-engine-plugin-i18n/*": ["packages/plugins/i18n/*"],
"@opentiny/tiny-engine-plugin-bridge/*": ["packages/plugins/bridge/*"],
"@opentiny/tiny-engine-setting-events/*": ["packages/settings/events/*"],
"@opentiny/tiny-engine-setting-props/*": ["packages/settings/props/*"],
"@opentiny/tiny-engine-common/*": ["packages/common/*"],
"@opentiny/tiny-engine-setting-styles/*": ["packages/settings/styles/*"],
"@opentiny/tiny-engine-toolbar-breadcrumb/*": ["packages/toolbars/breadcrumb/*"],
"@opentiny/tiny-engine-toolbar-fullscreen/*": ["packages/toolbars/fullscreen/*"],
"@opentiny/tiny-engine-toolbar-lang/*": ["packages/toolbars/lang/*"],
"@opentiny/tiny-engine-toolbar-view-setting/*": ["packages/toolbars/view-setting/*"],
"@opentiny/tiny-engine-toolbar-layout/*": ["packages/toolbars/layout/*"],
"@opentiny/tiny-engine-toolbar-lock/*": ["packages/toolbars/lock/*"],
"@opentiny/tiny-engine-toolbar-logo/*": ["packages/toolbars/logo/*"],
"@opentiny/tiny-engine-toolbar-media/*": ["packages/toolbars/media/*"],
"@opentiny/tiny-engine-toolbar-preview/*": ["packages/toolbars/preview/*"],
"@opentiny/tiny-engine-toolbar-clean/*": ["packages/toolbars/clean/*"],
"@opentiny/tiny-engine-toolbar-theme-switch/*": ["packages/toolbars/themeSwitch/*"],
"@opentiny/tiny-engine-toolbar-save/*": ["packages/toolbars/save/*"],
"@opentiny/tiny-engine-svgs/*": ["packages/svgs/*"],
"@opentiny/tiny-engine-utils": ["packages/utils/src/index.ts"],
"@opentiny/tiny-engine-webcomponent-core": ["packages/webcomponent/src/lib"],
"@opentiny/tiny-engine-i18n-host": ["packages/i18n/src/lib"]
} }
}, },
"include": ["packages/**/*"], "include": ["packages/**/*"],