UNPKG

@leafer/decorator

Version:
350 lines (295 loc) 12.5 kB
import { ILeafData, ILeaf, IObject, IValue, ILeafAttrDescriptor, ILeafAttrDescriptorFn, IValueFunction, IMatrixWithBoundsScaleData } from '@leafer/interface' import { DataHelper, isEmptyData, isObject, isUndefined } from '@leafer/data' import { defineKey, getDescriptor, createDescriptor } from './object' // name export function decorateLeafAttr(defaultValue?: IValue, descriptorFn?: ILeafAttrDescriptorFn) { return (target: ILeaf, key: string) => defineLeafAttr(target, key, defaultValue, descriptorFn && descriptorFn(key)) } export function attr(partDescriptor?: ILeafAttrDescriptor): ILeafAttrDescriptor { return partDescriptor } export function defineLeafAttr(target: ILeaf, key: string, defaultValue?: IValue, partDescriptor?: ILeafAttrDescriptor): void { const defaultDescriptor: ILeafAttrDescriptor = { get() { return this.__getAttr(key) }, set(value: IValue) { this.__setAttr(key, value) } } defineKey(target, key, Object.assign(defaultDescriptor, partDescriptor || {})) defineDataProcessor(target, key, defaultValue) } export function dataType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue) } export function positionType(defaultValue?: IValue, checkFiniteNumber?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value, checkFiniteNumber) && (this.__layout.matrixChanged || this.__layout.matrixChange()) } })) } export function scrollType(defaultValue?: IValue, checkFiniteNumber?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value, checkFiniteNumber)) { this.__layout.matrixChanged || this.__layout.matrixChange() this.__scrollWorld || (this.__scrollWorld = {} as IMatrixWithBoundsScaleData) } } })) } export function autoLayoutType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value)) { this.__hasAutoLayout = !!(this.origin || this.around || this.flow) if (!this.__local) this.__layout.createLocal() doBoundsType(this) } } })) } export function scaleType(defaultValue?: IValue, checkFiniteNumber?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value, checkFiniteNumber) && (this.__layout.scaleChanged || this.__layout.scaleChange()) } })) } export function rotationType(defaultValue?: IValue, checkFiniteNumber?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value, checkFiniteNumber) && (this.__layout.rotationChanged || this.__layout.rotationChange()) } })) } export function boundsType(defaultValue?: IValue, checkFiniteNumber?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value, checkFiniteNumber) && doBoundsType(this) } })) } export function naturalBoundsType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value) && (doBoundsType(this), this.__.__removeNaturalSize()) } })) } export function doBoundsType(leaf: ILeaf): void { leaf.__layout.boxChanged || leaf.__layout.boxChange() if (leaf.__hasAutoLayout) leaf.__layout.matrixChanged || leaf.__layout.matrixChange() } export function pathInputType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { const data = this.__ if (data.__pathInputed !== 2) data.__pathInputed = value ? 1 : 0 if (!value) data.__pathForRender = undefined this.__setAttr(key, value) doBoundsType(this) } })) } export const pathType = boundsType export function affectStrokeBoundsType(defaultValue?: IValue, useStroke?: boolean) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value)) { doStrokeType(this) if (useStroke) this.__.__useStroke = true } } })) } export function doStrokeType(leaf: ILeaf): void { leaf.__layout.strokeChanged || leaf.__layout.strokeChange() if (leaf.__.__useArrow) doBoundsType(leaf) } export const strokeType = affectStrokeBoundsType export function affectRenderBoundsType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value) this.__layout.renderChanged || this.__layout.renderChange() } })) } export function surfaceType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value) && (this.__layout.surfaceChanged || this.__layout.surfaceChange()) } })) } export function dimType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value)) { const data = this.__ DataHelper.stintSet(data, '__useDim', (data.dim || data.bright || data.dimskip) as boolean) //this.__layout.surfaceChange() } } })) } export function opacityType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value) && (this.__layout.opacityChanged || this.__layout.opacityChange()) if (this.mask) checkMask(this) } })) } export function visibleType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { const oldValue = this.visible if (oldValue === true && value === 0) { if (this.animationOut) return this.__runAnimation('out', () => doVisible(this, key, value, oldValue)) // hide } else if (oldValue === 0 && value === true) { if (this.animation) this.__runAnimation('in') // show } doVisible(this, key, value, oldValue) if (this.mask) checkMask(this) } })) } function checkMask(leaf: ILeaf): void { // mask 的透明度和可见性变更,会影响parent const { parent } = leaf if (parent) { const { __hasMask } = parent parent.__updateMask() if (__hasMask !== parent.__hasMask) parent.forceUpdate() } } function doVisible(leaf: ILeaf, key: string, value: IValue, oldValue: IValue): void { if (leaf.__setAttr(key, value)) { leaf.__layout.opacityChanged || leaf.__layout.opacityChange() // this.__layout.surfaceChange() if (oldValue === 0 || value === 0) doBoundsType(leaf) // 0 = display: none } } export function sortType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value)) { // this.__layout.surfaceChange() this.waitParent(() => { const { parent } = this parent.__layout.childrenSortChange() if (parent.__.flow) parent.__layout.boxChange() }) } } })) } export function maskType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: boolean) { if (this.__setAttr(key, value)) { this.__layout.boxChanged || this.__layout.boxChange() this.waitParent(() => { this.parent.__updateMask(value) }) } } })) } export function eraserType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: boolean) { this.__setAttr(key, value) && this.waitParent(() => { this.parent.__updateEraser(value) }) } })) } export function hitType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { if (this.__setAttr(key, value)) { this.__layout.hitCanvasChanged = true //if (Debug.showBounds === 'hit') this.__layout.surfaceChange() if (this.leafer) this.leafer.updateCursor() } } })) } export function cursorType(defaultValue?: IValue) { return decorateLeafAttr(defaultValue, (key: string) => attr({ set(value: IValue) { this.__setAttr(key, value) if (this.leafer) this.leafer.updateCursor() } })) } // get export function dataProcessor(processor: IObject) { return (target: IObject, _key?: string) => { defineKey(target, '__DataProcessor', { get() { return processor } }) } } export function layoutProcessor(processor: IObject) { return (target: IObject, _key?: string) => { defineKey(target, '__LayoutProcessor', { get() { return processor } }) } } // other function getSetMethodName(key: string): string { return 'set' + key.charAt(0).toUpperCase() + key.slice(1) } // define leaf.__[key] getter/setter export function defineDataProcessor(target: ILeaf, key: string, defaultValue?: IValue | IValueFunction): void { const data = target.__DataProcessor.prototype const computedKey = '_' + key const setMethodName = getSetMethodName(key) const property: IObject & ThisType<ILeafData> = createDescriptor(key, defaultValue) if (isUndefined(defaultValue)) { property.get = function () { return this[computedKey] } } else if (typeof defaultValue === 'function') { property.get = function () { const v = this[computedKey] return v == null ? defaultValue((this as ILeafData).__leaf) : v } } else if (isObject(defaultValue)) { const isEmpty = isEmptyData(defaultValue) property.get = function () { const v = this[computedKey] return v == null ? this[computedKey] = isEmpty ? {} : DataHelper.clone(defaultValue) : v } } const isBox = target.isBranchLeaf // 仅用于 width / height if (key === 'width') { property.get = function () { const v = this[computedKey] if (v == null) { const t = this as ILeafData, naturalWidth = t.__naturalWidth, leaf = t.__leaf if (!defaultValue || leaf.pathInputed) return leaf.boxBounds.width if (naturalWidth) return (t as IObject)._height && t.__useNaturalRatio ? (t as IObject)._height * naturalWidth / t.__naturalHeight : naturalWidth return (isBox && leaf.children.length) ? leaf.boxBounds.width : defaultValue // 返回 Box / Group / Text 等的实际宽度 } else return v } } else if (key === 'height') { property.get = function () { const v = this[computedKey] if (v == null) { const t = this as ILeafData, naturalHeight = t.__naturalHeight, leaf = t.__leaf if (!defaultValue || leaf.pathInputed) return leaf.boxBounds.height if (naturalHeight) return (t as IObject)._width && t.__useNaturalRatio ? (t as IObject)._width * naturalHeight / t.__naturalWidth : naturalHeight return (isBox && leaf.children.length) ? leaf.boxBounds.height : defaultValue // 返回 Box / Group / Text 等的实际高度 } else return v } } // find parent proto let descriptor, find = data while (!descriptor && find) { descriptor = getDescriptor(find, key) find = find.__proto__ } if (descriptor && descriptor.set) property.set = descriptor.set // use exist set if (data[setMethodName]) { property.set = data[setMethodName] // first use custom setKey(value) delete data[setMethodName] } defineKey(data, key, property) }