You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

138 lines
3.3 KiB

import type { ComputedRef, InjectionKey, UnwrapNestedRefs } from 'vue'
import { computed, inject, provide } from 'vue'
import type { Block, Category, Module, Page } from './types'
import { valueOf } from './utils'
/** 画布配置 */
export interface CanvasConfig {
/** 画布是否聚焦 */
focused: boolean
/** 画布缩放比例 */
scale: number
}
export interface PageConfig extends Omit<Page, 'body'> {}
export type ConfiguratorType =
| 'page'
| 'block'
| 'view'
export interface Exported {
attrs: PageConfig
data: Record<string, Record<string, unknown>>
}
export interface EngineContext {
/** 目标 */
target?: 'app' | 'routine'
/** 当前选中的模板 */
activeBlockId?: string
/** 当前选中的可编辑组件 */
activeViewId?: string
/** 鼠标进入的可编辑组件 */
hoverViewId?: string
/** 正在被拖拽的模板 */
draggingViewId?: string
/** 当前配置 */
configurator?: ConfiguratorType
/** 页面配置 */
page: PageConfig
/** 画布配置 */
canvas: CanvasConfig
/** 可以分类 */
categories: Category[]
/** 布局视图列表 */
blocks: Block[]
/** 内部唯一标识 */
nextId: number
/** 数据源 */
sources: Record<string, Record<string, unknown>>
/** 到处数据 */
export: () => Exported
}
export const contextKey: InjectionKey<UnwrapNestedRefs<EngineContext>> = Symbol.for('ddd:engine')
export const parentViewIdKey: InjectionKey<string> = Symbol.for('ddd:view:parent:id')
const blockIdKey: InjectionKey<string> = Symbol.for('ddd:block:id')
export function useContext(): UnwrapNestedRefs<EngineContext> {
const config = inject(contextKey)
if (config == null)
throw new Error('no config found')
return config
}
export function useParentViewId(): string | undefined {
return inject(parentViewIdKey)
}
export function provideParentViewId(id: string | undefined): void {
if (id)
provide(parentViewIdKey, id)
}
export function useBlockId(): string | undefined {
return inject(blockIdKey)
}
export function provideBlockId(id: string): void {
provide(blockIdKey, id)
}
export function useModule() {
const ctx = useContext()
return computed((): Module | undefined => {
for (const block of ctx.blocks) {
if (block.vid !== ctx.activeBlockId)
continue
for (const category of ctx.categories) {
for (const module of category.modules) {
if (module.mid === block.mid)
return module
}
}
break
}
return undefined
})
}
export function useScale() {
const ctx = useContext()
const minScaleValue = 0.6
const maxScaleValue = 3.0
const scaleStep = 0.2
const setScale = (delta: number): void => {
ctx.canvas.scale = Math.max(
Math.min(
ctx.canvas.scale + scaleStep * delta,
maxScaleValue,
),
minScaleValue,
)
}
return {
min: minScaleValue,
max: maxScaleValue,
step: scaleStep,
incr: () => setScale(1),
decr: () => setScale(-1),
}
}
export function useSource<T = unknown>(source: string | undefined, fallback?: T): ComputedRef<T | undefined> {
const ctx = useContext()
const blockId = useBlockId()
return computed<T | undefined>(() => {
if (!blockId)
throw new Error('without block')
if (!source)
return fallback
return valueOf(ctx.sources[blockId], source) ?? fallback
})
}