diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index 044a48b9568c807a9b3b284c97757e07ae8b7f53..d53ee40936306fd52b1326291c06d75f6df8f0a2 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -71,6 +71,7 @@ export enum DecoratorNames { LOCAL_STORAGE_LINK = 'LocalStorageLink', REUSABLE = 'Reusable', TRACK = 'Track', + ANIMATABLE_EXTEND = 'AnimatableExtend' } export enum DecoratorIntrinsicNames { @@ -101,6 +102,14 @@ export enum StateManagementTypes { INT_32 = 'int32', } +export enum AnimationNames { + ANIMATABLE_ARITHMETIC = 'AnimatableArithmetic', + CREATE_OR_SET_ANIMATABLEPROPERTY = '__createOrSetAnimatableProperty', + ANIMATION = 'animation', + ANIMATION_START = 'animationStart', + ANIMATION_STOP = 'animationStop', +} + export const DECORATOR_TYPE_MAP = new Map([ [DecoratorNames.STATE, StateManagementTypes.STATE_DECORATED], [DecoratorNames.LINK, StateManagementTypes.DECORATED_V1], @@ -167,6 +176,7 @@ export const INTERMEDIATE_IMPORT_SOURCE: Map = new Map = new Map [StateManagementTypes.OBSERVABLE_PROXY, 'arkui.stateManagement.runtime'], [StateManagementTypes.PROP_STATE, 'arkui.stateManagement.runtime'], [StateManagementTypes.INT_32, '@koalaui.runtime.common'], + [AnimationNames.ANIMATABLE_ARITHMETIC, 'arkui.component.common'] ]); diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 7171c74dde56200f6a946f4f45bb86907ca7857e..f8f8caa7a8f609e0bfb361da5ecff01069bcd9cb 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -45,7 +45,7 @@ import { import { isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; import { factory as PropertyFactory } from '../property-translators/factory'; import { ProjectConfig } from '../../common/plugin-context'; -import { BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames } from '../../common/predefines'; +import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; export class factory { @@ -569,14 +569,14 @@ export class factory { continue; } const property: arkts.Identifier = instanceCalls[curIdx].call.expression as arkts.Identifier; - if (property.name === BuilderLambdaNames.ANIMATION_NAME) { + if (property.name === AnimationNames.ANIMATION) { const aniStart: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_START), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_START), undefined, instanceCalls[curIdx].call.arguments ); const aniStop: arkts.CallExpression = arkts.factory.createCallExpression( - arkts.factory.createIdentifier(BuilderLambdaNames.ANIMATION_STOP), + arkts.factory.createIdentifier(AnimationNames.ANIMATION_STOP), undefined, instanceCalls[curIdx].call.arguments.map((arg) => arg.clone()) ); diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index f3f39c246245dae2a94bf760f4afe87d759e212c..776df56e05d7eae523b45ab7dc08bfee89ebffb8 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -15,7 +15,6 @@ import * as arkts from '@koalaui/libarkts'; import { - BuilderLambdaNames, addMemoAnnotation, CustomComponentNames, getCustomComponentOptionsName, @@ -41,6 +40,7 @@ import { ProjectConfig } from '../../common/plugin-context'; import { DeclarationCollector } from '../../common/declaration-collector'; import { ImportCollector } from '../../common/import-collector'; import { + AnimationNames, ARKUI_COMPONENT_COMMON_SOURCE_NAME, DecoratorNames, Dollars, @@ -296,14 +296,16 @@ export class factory { } /* - * add headers for animation in UICommonMethod + * add headers for animation & @AnimatableExtend in UICommonMethod */ static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { - const animationStart = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_START); - const animationStop = factory.createAnimationMethod(BuilderLambdaNames.ANIMATION_STOP); + const animationStart = factory.createAnimationMethod(AnimationNames.ANIMATION_START); + const animationStop = factory.createAnimationMethod(AnimationNames.ANIMATION_STOP); + const createOrSetAniProperty = factory.createOrSetAniProperty(); const updatedBody = arkts.factory.updateInterfaceBody(node.body!, [ animationStart, animationStop, + createOrSetAniProperty, ...node.body!.body, ]); return arkts.factory.updateInterfaceDeclaration( @@ -317,6 +319,83 @@ export class factory { ); } + /* + * helper to create value parameter for AnimatableExtend methods + */ + static createAniExtendValueParam(): 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(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([ + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('T')) + ), + ]) + ) + ); + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ); + } + + /* + * generate __createOrSetAnimatableProperty(...) for AnimatableExtend + */ + static createOrSetAniProperty(): 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 cbParam = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'callback', + arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [factory.createAniExtendValueParam()], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ); + const funcSig = arkts.factory.createFunctionSignature( + arkts.factory.createTypeParameterDeclaration( + [arkts.factory.createTypeParameter(arkts.factory.createIdentifier('T'))], + 0 + ), + [funcNameParam, factory.createAniExtendValueParam(), cbParam], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), + false + ); + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + arkts.factory.createScriptFunction( + undefined, + funcSig, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC + ), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + /* * generate animationStart(...) and animationStop(...) */ @@ -518,11 +597,35 @@ export class factory { return node; } if (isEtsGlobalClass(node)) { - node.definition.body.forEach( - (member: arkts.AstNode) => - arkts.isMethodDefinition(member) && propertyFactory.addMemoToBuilderClassMethod(member) + const updatedBody = node.definition.body.map((member: arkts.AstNode) => { + arkts.isMethodDefinition(member) && propertyFactory.addMemoToBuilderClassMethod(member); + if (arkts.isMethodDefinition(member) && hasDecorator(member, DecoratorNames.ANIMATABLE_EXTEND)) { + member = arkts.factory.updateMethodDefinition( + member, + member.kind, + member.name, + factory.transformAnimatableExtend(member.scriptFunction), + member.modifiers, + false + ); + } + return member; + }); + 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) + ) ); - return node; } const newClassDef = factory.updateObservedTrackClassDef(node.definition); return arkts.factory.updateClassDeclaration(node, newClassDef); @@ -630,4 +733,92 @@ export class factory { ...classScopeInfo.getters, ]; } + + /* + * helper for transformAnimatableExtend to create callback argument in __createAnimatableProperty + */ + static createAniExtendCbArg( + param: arkts.ETSParameterExpression, + originStatements: arkts.Statement[] + ): arkts.ArrowFunctionExpression { + 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 numberType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier('number')) + ); + const AnimatableArithmeticType = arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(AnimationNames.ANIMATABLE_ARITHMETIC), + arkts.factory.createTSTypeParameterInstantiation([param.type as arkts.TypeNode]) + ) + ); + ImportCollector.getInstance().collectImport(AnimationNames.ANIMATABLE_ARITHMETIC); + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock([assignmentExpr, ...originStatements]), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + param.identifier.name, + arkts.factory.createUnionType([numberType, AnimatableArithmeticType]) + ), + undefined + ), + ], + 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 paramValue: arkts.ETSParameterExpression = node.params[1]; + const originStatements: arkts.Statement[] = [...node.body.statements]; + const createOrSetStatement = arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createThisExpression(), + arkts.factory.createIdentifier(AnimationNames.CREATE_OR_SET_ANIMATABLEPROPERTY), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + funcName, + paramValue.identifier, + factory.createAniExtendCbArg(paramValue, originStatements.slice(0, -1)), + ] + ) + ); + return arkts.factory.updateScriptFunction( + node, + arkts.factory.createBlock([createOrSetStatement, originStatements[originStatements.length - 1]]), + arkts.FunctionSignature.createFunctionSignature( + node.typeParams, + node.params, + node.returnTypeAnnotation, + node.hasReceiver + ), + node.flags, + node.modifiers + ); + } } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index d23949753b1cb923460b434eda629197a7411caa..e66bf494dcd6eea2c483668845765eb8490e7b35 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -38,10 +38,7 @@ export enum BuilderLambdaNames { TRANSFORM_METHOD_NAME = '_instantiateImpl', STYLE_PARAM_NAME = 'style', STYLE_ARROW_PARAM_NAME = 'instance', - CONTENT_PARAM_NAME = 'content', - ANIMATION_NAME = 'animation', - ANIMATION_START = 'animationStart', - ANIMATION_STOP = 'animationStop', + CONTENT_PARAM_NAME = 'content' } export enum MemoNames {