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 {} export type ConfiguratorType = | 'page' | 'block' | 'view' export interface Exported { attrs: PageConfig data: Record> } 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> /** 到处数据 */ export: () => Exported } export const contextKey: InjectionKey> = Symbol.for('ddd:engine') export const parentViewIdKey: InjectionKey = Symbol.for('ddd:view:parent:id') const blockIdKey: InjectionKey = Symbol.for('ddd:block:id') export function useContext(): UnwrapNestedRefs { 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(source: string | undefined, fallback?: T): ComputedRef { const ctx = useContext() const blockId = useBlockId() return computed(() => { if (!blockId) throw new Error('without block') if (!source) return fallback return valueOf(ctx.sources[blockId], source) ?? fallback }) }