@leafer/decorator
Version:
350 lines (295 loc) • 12.5 kB
text/typescript
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)
}