diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 1218495d38a5f432084f2ee3742441a106a4914f..b095ef8490a8da408ff41a8dd4cdebbba706fef2 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -57,6 +57,7 @@ export const OUTPUT_DEPENDENCY_MAP: Map = new Map { + const updatedBody = node.definition.body.map((member: arkts.AstNode) => { if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.BUILDER)) { member.scriptFunction.setAnnotations([annotation('memo')]); } + if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { + member = arkts.factory.updateMethodDefinition( + member, + member.kind, + member.name, + arkts.factory.createFunctionExpression(structFactory.transformAnimatableExtend(member.scriptFunction)), + member.modifiers, + false + ); + } return member; }); - return node; + return arkts.factory.updateClassDeclaration( + node, + arkts.factory.updateClassDefinition( + node.definition, + node.definition.ident, + node.definition.typeParams, + node.definition.superTypeParams, + node.definition.implements, + undefined, + node.definition.super, + updatedBody, + node.definition.modifiers, + arkts.classDefinitionFlags(node.definition) + ) + ); } /** diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index acdae5b2f429773f12dfebe872c367badac4f615..16b217a0ec9b39264745ebe0f8977069fea2fe36 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -35,6 +35,7 @@ export enum DecoratorNames { LOCAL_STORAGE_LINK = 'LocalStorageLink', REUSABLE = 'Reusable', TRACK = 'Track', + ANIMATABLE_EXTEND = 'AnimatableExtend' } export function collectPropertyDecorators(property: arkts.ClassProperty): string[] { @@ -52,7 +53,7 @@ export function isDecoratorAnnotation(anno: arkts.AnnotationUsage, decoratorName } export function hasDecorator( - property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, + property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition | arkts.ScriptFunction, decoratorName: DecoratorNames ): boolean { if (arkts.isMethodDefinition(property)) { diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index 918d2ebac872e2e132fb509632213adb56994413..7313a035021cdbbe4aba520dc8e01b7a0a891ed8 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -281,9 +281,11 @@ export class factory { static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.AstNode { const animationStart = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_START); const animationStop = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_STOP); + const animatableExtendMethods = factory.createAnimatableExtendMethods(); const updatedBody = arkts.factory.updateInterfaceBody(node.body!, [ animationStart, animationStop, + ...animatableExtendMethods, ...node.body!.body, ]); return arkts.factory.updateInterfaceDeclaration( @@ -297,6 +299,136 @@ export class factory { ); } + /* + * create AnimatableExtend methods in UICommonMethod + */ + static createAnimatableExtendMethods(): arkts.MethodDefinition[] { + const funcNameParam: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'functionName', + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('string')) + ) + ), + undefined + ); + const createAnimatableProperty = factory.aniExtendMethodDef( + '__createAnimatableProperty', + [funcNameParam, factory.createAniExtendValueParam(false, true), factory.createAniExtendCbParam(true)], + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + true + ); + const createAnimatableProperty2 = factory.aniExtendMethodDef( + '__createAnimatableProperty', + [ + funcNameParam.clone(), + factory.createAniExtendValueParam(false, false), + factory.createAniExtendCbParam(false), + ], + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + true + ); + const updateAnimatableProperty = factory.aniExtendMethodDef( + '__updateAnimatableProperty', + [funcNameParam.clone(), factory.createAniExtendValueParam(true, false)], + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID, + true + ); + const hasAnimatableProperty = factory.aniExtendMethodDef( + '__hasAnimatableProperty', + [funcNameParam.clone()], + arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN, + false + ); + return [createAnimatableProperty, createAnimatableProperty2, updateAnimatableProperty, hasAnimatableProperty]; + } + + /* + * helper to create value parameter for AnimatableExtend methods + */ + static createAniExtendValueParam(isUnion: boolean, isNumber: boolean): arkts.ETSParameterExpression { + const numberType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('number')) + ); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('AnimatableArithmetic'), + arkts.factory.createTSTypeParameterInstantiation([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('T')) + ), + ]) + ) + ); + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + isUnion + ? arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + : isNumber + ? numberType + : AnimatableArithmeticType + ), + undefined + ); + } + + /* + * helper to create callback parameter for AnimatableExtend methods + */ + static createAniExtendCbParam(isNumber: boolean): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'callback', + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [factory.createAniExtendValueParam(false, isNumber)], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + } + + /* + * helper to create method definitions for AnimatableExtend methods + */ + static aniExtendMethodDef( + methodName: string, + params: arkts.ETSParameterExpression[], + returnType: arkts.Es2pandaPrimitiveType, + requiresT: boolean + ): arkts.MethodDefinition { + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(methodName), + arkts.factory.createFunctionExpression( + arkts.factory.createScriptFunction( + undefined, + arkts.factory.createFunctionSignature( + requiresT + ? arkts.factory.createTypeParameterDeclaration( + [arkts.factory.createTypeParameter(arkts.factory.createIdentifier('T'))], + 0 + ) + : undefined, + params, + arkts.factory.createPrimitiveType(returnType), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ) + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + /* * generate animationStart(...) and animationStop(...) */ @@ -305,14 +437,12 @@ export class factory { arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( 'value', - arkts.factory.createUnionType( - [ - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) - ), - arkts.factory.createETSUndefinedType() - ] - ) + arkts.factory.createUnionType([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('AnimateParam')) + ), + arkts.factory.createETSUndefinedType(), + ]) ), undefined ), @@ -342,9 +472,7 @@ export class factory { arkts.factory.createIdentifier( 'packageInfo', arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('string') - ) + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('string')) ) ), undefined @@ -384,4 +512,111 @@ export class factory { } return node; } + + /* + * helper for transformAnimatableExtend to create callback argument in __createAnimatableProperty + */ + static createAniExtendCbArg( + param: arkts.ETSParameterExpression, + originStatements: arkts.Statement[] + ): arkts.ArrowFunctionExpression { + const isNumberType = + param.type && + arkts.isETSTypeReference(param.type) && + param.type.part && + param.type.part.name && + arkts.isIdentifier(param.type.part.name) && + param.type.part.name.name === 'number'; + const assignmentExpr = arkts.factory.createExpressionStatement( + arkts.factory.createAssignmentExpression( + param.identifier.clone(), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createTSAsExpression(param.identifier.clone(), param.type as arkts.TypeNode, false) + ) + ); + const AniArithmeticParam = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + param.identifier.name, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('AnimatableArithmetic'), + arkts.factory.createTSTypeParameterInstantiation([param.type as arkts.TypeNode]) + ) + ) + ), + undefined + ); + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock([...(isNumberType ? [] : [assignmentExpr]), ...originStatements]), + arkts.factory.createFunctionSignature( + undefined, + isNumberType ? [param.clone()] : [AniArithmeticParam], + undefined, + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE + ) + ); + } + + /* + * transform @AnimatableExtend method + */ + static transformAnimatableExtend(node: arkts.ScriptFunction): arkts.ScriptFunction { + if (!arkts.isEtsParameterExpression(node.params[1]) || !node.body || !arkts.isBlockStatement(node.body)) { + return node; + } + const funcName: arkts.StringLiteral = arkts.factory.createStringLiteral(node.id?.name!); + const paramValueName: arkts.Identifier = node.params[1].identifier; + const originStatements: arkts.Statement[] = node.body.statements.slice(0, -1); + const test = factory.AniExtendCallExpr('__hasAnimatableProperty', [funcName]); + const consequent = arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + factory.AniExtendCallExpr('__updateAnimatableProperty', [funcName.clone(), paramValueName.clone()]) + ), + ]); + const alternate = arkts.factory.createBlock([ + arkts.factory.createExpressionStatement( + factory.AniExtendCallExpr('__createAnimatableProperty', [ + funcName.clone(), + paramValueName.clone(), + factory.createAniExtendCbArg(node.params[1], originStatements), + ]) + ), + ...originStatements.map((statment) => statment.clone()), + ]); + const ifStatement = arkts.factory.createIfStatement(test, consequent, alternate); + const returnStatement = arkts.factory.createReturnStatement(arkts.factory.createThisExpression()); + return arkts.factory.updateScriptFunction( + node, + arkts.factory.createBlock([ifStatement, returnStatement]), + arkts.FunctionSignature.createFunctionSignature( + node.typeParams, + node.params, + node.returnTypeAnnotation, + node.hasReceiver + ), + node.flags, + node.modifiers + ); + } + + /* + * helper for transformAnimatableExtend to create CallExpression + */ + static AniExtendCallExpr(propName: string, args: arkts.AstNode[]): arkts.CallExpression { + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(propName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + args + ); + } }