/* * Copyright (c) 2021-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/declarative_frontend/jsview/js_tabs.h" #if !defined(PREVIEW) && defined(OHOS_PLATFORM) #include "interfaces/inner_api/ui_session/ui_session_manager.h" #endif #include "base/log/ace_scoring_log.h" #include "bridge/declarative_frontend/engine/functions/js_swiper_function.h" #include "bridge/declarative_frontend/engine/functions/js_tabs_function.h" #include "bridge/declarative_frontend/jsview/js_scrollable.h" #include "bridge/declarative_frontend/jsview/js_tabs_controller.h" #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/jsview/models/tabs_model_impl.h" #include "core/components/common/layout/constants.h" #include "core/components/common/properties/decoration.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/pattern/tabs/tabs_model_ng.h" #include "core/components_ng/pattern/tabs/tab_content_transition_proxy.h" namespace OHOS::Ace { std::unique_ptr TabsModel::instance_ = nullptr; std::mutex TabsModel::mutex_; TabsModel* TabsModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::TabsModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::TabsModelNG()); } else { instance_.reset(new Framework::TabsModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { namespace { constexpr int32_t SM_COLUMN_NUM = 4; constexpr int32_t MD_COLUMN_NUM = 8; constexpr int32_t LG_COLUMN_NUM = 12; constexpr int32_t DEFAULT_CUSTOM_ANIMATION_TIMEOUT = 1000; const std::vector BAR_POSITIONS = { BarPosition::START, BarPosition::END }; const std::vector BAR_BLURSTYLE = { BlurStyle::NO_MATERIAL, BlurStyle::THIN, BlurStyle::REGULAR, BlurStyle::THICK, BlurStyle::BACKGROUND_THIN, BlurStyle::BACKGROUND_REGULAR, BlurStyle::BACKGROUND_THICK, BlurStyle::BACKGROUND_ULTRA_THICK, BlurStyle::COMPONENT_ULTRA_THIN, BlurStyle::COMPONENT_THIN, BlurStyle::COMPONENT_REGULAR, BlurStyle::COMPONENT_THICK, BlurStyle::COMPONENT_ULTRA_THICK, }; JSRef TabContentChangeEventToJSValue(const TabContentChangeEvent& eventInfo) { return JSRef::Make(ToJSValue(eventInfo.GetIndex())); } } // namespace void JSTabs::SetOnChange(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto changeHandler = AceType::MakeRefPtr>( JSRef::Cast(info[0]), TabContentChangeEventToJSValue); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onChange = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler), node = targetNode]( const BaseEventInfo* info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); const auto* tabsInfo = TypeInfoHelper::DynamicCast(info); if (!tabsInfo) { TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onChange callback execute failed."); return; } ACE_SCORING_EVENT("Tabs.onChange"); ACE_SCOPED_TRACE("Tabs.onChange index %d", tabsInfo->GetIndex()); PipelineContext::SetCallBackNode(node); func->Execute(*tabsInfo); }; TabsModel::GetInstance()->SetOnChange(std::move(onChange)); } void JSTabs::SetOnTabBarClick(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto changeHandler = AceType::MakeRefPtr>( JSRef::Cast(info[0]), TabContentChangeEventToJSValue); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onTabBarClick = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler), node = targetNode](const BaseEventInfo* info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); const auto* tabsInfo = TypeInfoHelper::DynamicCast(info); if (!tabsInfo) { TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onTabBarClick callback execute failed."); return; } ACE_SCORING_EVENT("Tabs.onTabBarClick"); PipelineContext::SetCallBackNode(node); func->Execute(*tabsInfo); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Tabs.onTabBarClick"); #endif }; TabsModel::GetInstance()->SetOnTabBarClick(std::move(onTabBarClick)); } void JSTabs::SetOnAnimationStart(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto animationStartHandler = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onAnimationStart = [executionContext = info.GetExecutionContext(), func = std::move(animationStartHandler)]( int32_t index, int32_t targetIndex, const AnimationCallbackInfo& info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); ACE_SCORING_EVENT("Tabs.onAnimationStart"); func->Execute(index, targetIndex, info); }; TabsModel::GetInstance()->SetOnAnimationStart(std::move(onAnimationStart)); } void JSTabs::SetOnAnimationEnd(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto animationEndHandler = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onAnimationEnd = [executionContext = info.GetExecutionContext(), func = std::move(animationEndHandler)]( int32_t index, const AnimationCallbackInfo& info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); ACE_SCORING_EVENT("Tabs.onAnimationEnd"); func->Execute(index, info); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Tabs.onAnimationEnd"); #endif }; TabsModel::GetInstance()->SetOnAnimationEnd(std::move(onAnimationEnd)); } void JSTabs::SetOnGestureSwipe(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto gestureSwipeHandler = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto onGestureSwipe = [executionContext = info.GetExecutionContext(), func = std::move(gestureSwipeHandler)]( int32_t index, const AnimationCallbackInfo& info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); ACE_SCORING_EVENT("Tabs.onGestureSwipe"); func->Execute(index, info); }; TabsModel::GetInstance()->SetOnGestureSwipe(std::move(onGestureSwipe)); } void ParseTabsIndexObject(const JSCallbackInfo& info, const JSRef& changeEventVal) { CHECK_NULL_VOID(changeEventVal->IsFunction()); auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(changeEventVal)); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onChangeEvent = [executionContext = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const BaseEventInfo* info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext); const auto* tabsInfo = TypeInfoHelper::DynamicCast(info); if (!tabsInfo) { TAG_LOGW(AceLogTag::ACE_TABS, "ParseTabsIndexObject execute onChange event failed."); return; } ACE_SCORING_EVENT("Tabs.onChangeEvent"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(ToJSValue(tabsInfo->GetIndex())); func->ExecuteJS(1, &newJSVal); }; TabsModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent)); } void JSTabs::Create(const JSCallbackInfo& info) { BarPosition barPosition = BarPosition::START; RefPtr tabController; RefPtr tabsController; int32_t index = -1; JSRef changeEventVal; auto jsValue = info[0]; if (jsValue->IsObject()) { JSRef obj = JSRef::Cast(jsValue); JSRef val = obj->GetProperty("barPosition"); if (val->IsNumber()) { auto barPositionVal = val->ToNumber(); if (barPositionVal >= 0 && barPositionVal < static_cast(BAR_POSITIONS.size())) { barPosition = BAR_POSITIONS[barPositionVal]; } } JSRef controller = obj->GetProperty("controller"); if (controller->IsObject()) { auto* jsTabsController = JSRef::Cast(controller)->Unwrap(); if (jsTabsController) { jsTabsController->SetInstanceId(Container::CurrentId()); tabController = jsTabsController->GetController(); tabsController = jsTabsController->GetTabsController(); } } JSRef indexVal = obj->GetProperty("index"); if (indexVal->IsNumber()) { index = indexVal->ToNumber(); index = index < 0 ? 0 : index; if (!tabController) { tabController = JSTabsController::CreateController(); } #ifndef NG_BUILD tabController->SetInitialIndex(index); #endif } else if (indexVal->IsObject()) { JSRef indexObj = JSRef::Cast(indexVal); auto indexValueProperty = indexObj->GetProperty("value"); if (indexValueProperty->IsNumber()) { index = indexValueProperty->ToNumber(); index = index < 0 ? 0 : index; } changeEventVal = indexObj->GetProperty("changeEvent"); } } TabsModel::GetInstance()->Create(barPosition, index, tabController, tabsController); if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) { ParseTabsIndexObject(info, changeEventVal); } } void JSTabs::Pop() { TabsModel::GetInstance()->Pop(); } void JSTabs::SetBarPosition(const JSCallbackInfo& info) { BarPosition barVal = BarPosition::START; if (info.Length() > 0 && info[0]->IsNumber()) { auto barPositionVal = info[0]->ToNumber(); if (barPositionVal >= 0 && barPositionVal < static_cast(BAR_POSITIONS.size())) { barVal = BAR_POSITIONS[barPositionVal]; } } TabsModel::GetInstance()->SetTabBarPosition(barVal); } void JSTabs::SetVertical(const std::string& value) { TabsModel::GetInstance()->SetIsVertical(StringToBool(value)); } void JSTabs::SetScrollable(const std::string& value) { if (value == "undefined") { TabsModel::GetInstance()->SetScrollable(true); return; } TabsModel::GetInstance()->SetScrollable(StringToBool(value)); } void JSTabs::SetBarMode(const JSCallbackInfo& info) { TabBarMode barMode = TabBarMode::FIXED; if (info.Length() < 1) { TabsModel::GetInstance()->SetTabBarMode(barMode); return; } auto barModeInfo = info[0]; if (barModeInfo->IsString()) { barMode = ConvertStrToTabBarMode(barModeInfo->ToString()); } if (barMode == TabBarMode::SCROLLABLE) { if (info.Length() > 1 && info[1]->IsObject()) { SetScrollableBarModeOptions(info[1]); } else { ScrollableBarModeOptions option; TabsModel::GetInstance()->SetScrollableBarModeOptions(option); } } TabsModel::GetInstance()->SetTabBarMode(barMode); } void JSTabs::SetBarWidth(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension width = Dimension(-1.0, DimensionUnit::VP); if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) { if (!ParseJsDimensionVpNG(info[0], width)) { width = Dimension(-1.0, DimensionUnit::VP); TabsModel::GetInstance()->SetTabBarWidth(width); return; } } else { ParseJsDimensionVp(info[0], width); } TabsModel::GetInstance()->SetTabBarWidth(width); } void JSTabs::SetBarHeight(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension height = Dimension(-1.0, DimensionUnit::VP); bool adaptiveHeight = false; auto barHeightInfo = info[0]; if (barHeightInfo->IsString() && barHeightInfo->ToString() == "auto") { adaptiveHeight = true; } else { if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) { if (!ParseJsDimensionVpNG(barHeightInfo, height)) { height = Dimension(-1.0, DimensionUnit::VP); } } else { ParseJsDimensionVp(barHeightInfo, height); } } TabsModel::GetInstance()->SetBarAdaptiveHeight(adaptiveHeight); TabsModel::GetInstance()->SetTabBarHeight(height); } void JSTabs::SetWidth(const JSCallbackInfo& info) { JSViewAbstract::JsWidth(info); if (info.Length() < 1) { return; } if (info[0]->IsString() && info[0]->ToString().empty()) { return; } if (info[0]->IsString() && info[0]->ToString() == "auto") { ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true); TabsModel::GetInstance()->SetWidthAuto(true); return; } TabsModel::GetInstance()->SetWidthAuto(false); } void JSTabs::SetHeight(const JSCallbackInfo& info) { JSViewAbstract::JsHeight(info); if (info.Length() < 1) { return; } if (info[0]->IsString() && info[0]->ToString().empty()) { return; } if (info[0]->IsString() && info[0]->ToString() == "auto") { ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false); TabsModel::GetInstance()->SetHeightAuto(true); return; } TabsModel::GetInstance()->SetHeightAuto(false); } void JSTabs::SetIndex(int32_t index) { TabsModel::GetInstance()->SetIndex(index); } void JSTabs::SetAnimationDuration(const JSCallbackInfo& info) { if (info.Length() <= 0) { TabsModel::GetInstance()->SetAnimationDuration(-1); return; } auto animationDurationInfo = info[0]; if ((!animationDurationInfo->IsNull() && !animationDurationInfo->IsNumber()) || (animationDurationInfo->IsNull() && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN))) { TabsModel::GetInstance()->SetAnimationDuration(-1); return; } auto value = animationDurationInfo->IsNumber() ? animationDurationInfo->ToNumber() : 0; TabsModel::GetInstance()->SetAnimationDuration(value); } void JSTabs::SetFadingEdge(const JSCallbackInfo& info) { bool fadingEdge = true; if (info.Length() > 0) { ParseJsBool(info[0], fadingEdge); } TabsModel::GetInstance()->SetFadingEdge(fadingEdge); } void JSTabs::SetBarOverlap(const JSCallbackInfo& info) { bool barOverlap = false; if (info.Length() > 0) { ParseJsBool(info[0], barOverlap); } TabsModel::GetInstance()->SetBarOverlap(barOverlap); } void JSTabs::SetBarBackgroundColor(const JSCallbackInfo& info) { Color backgroundColor = Color::BLACK.BlendOpacity(0.0f); if (info.Length() > 0) { ConvertFromJSValue(info[0], backgroundColor); } TabsModel::GetInstance()->SetBarBackgroundColor(backgroundColor); } void JSTabs::SetBarBackgroundBlurStyle(const JSCallbackInfo& info) { BlurStyle blurStyle = BlurStyle::NO_MATERIAL; if (info.Length() > 0 && info[0]->IsNumber()) { auto barBlurStyle = info[0]->ToNumber(); if (barBlurStyle >= 0 && barBlurStyle < static_cast(BAR_BLURSTYLE.size())) { blurStyle = BAR_BLURSTYLE[barBlurStyle]; } } TabsModel::GetInstance()->SetBarBackgroundBlurStyle(blurStyle); } void JSTabs::SetDivider(const JSCallbackInfo& info) { TabsItemDivider divider; CalcDimension dividerStrokeWidth; CalcDimension dividerStartMargin; CalcDimension dividerEndMargin; RefPtr tabTheme = GetTheme(); CHECK_NULL_VOID(tabTheme); if (info.Length() > 0) { auto dividerInfo = info[0]; JSRef obj = JSRef::New(); if (dividerInfo->IsObject()) { obj = JSRef::Cast(dividerInfo); } if (dividerInfo->IsNull()) { divider.isNull = true; } else { if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("strokeWidth"), dividerStrokeWidth) || dividerStrokeWidth.Value() < 0.0f || dividerStrokeWidth.Unit() == DimensionUnit::PERCENT) { divider.strokeWidth.Reset(); } else { divider.strokeWidth = dividerStrokeWidth; } if (!dividerInfo->IsObject() || !ConvertFromJSValue(obj->GetProperty("color"), divider.color)) { divider.color = tabTheme->GetDividerColor(); } if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("startMargin"), dividerStartMargin) || dividerStartMargin.Value() < 0.0f || dividerStartMargin.Unit() == DimensionUnit::PERCENT) { divider.startMargin.Reset(); } else { divider.startMargin = dividerStartMargin; } if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("endMargin"), dividerEndMargin) || dividerEndMargin.Value() < 0.0f || dividerEndMargin.Unit() == DimensionUnit::PERCENT) { divider.endMargin.Reset(); } else { divider.endMargin = dividerEndMargin; } } } TabsModel::GetInstance()->SetDivider(divider); } void JSTabs::SetClip(const JSCallbackInfo& info) { if (info[0]->IsObject() || !Container::IsCurrentUseNewPipeline()) { JSViewAbstract::JsClip(info); return; } if (info[0]->IsBoolean()) { TabsModel::GetInstance()->SetClipEdge(info[0]->ToBoolean()); } } void JSTabs::SetScrollableBarModeOptions(const JSRef& info) { ScrollableBarModeOptions option; auto optionParam = JSRef::Cast(info); CalcDimension margin = Dimension(0.0, DimensionUnit::VP); if (!ParseJsDimensionVp(optionParam->GetProperty("margin"), margin) || Negative(margin.Value()) || margin.Unit() == DimensionUnit::PERCENT) { option.margin = 0.0_vp; } else { option.margin = margin; } auto nonScrollableLayoutStyle = optionParam->GetProperty("nonScrollableLayoutStyle"); int32_t layoutStyle; if (!ConvertFromJSValue(nonScrollableLayoutStyle, layoutStyle)) { option.nonScrollableLayoutStyle = LayoutStyle::ALWAYS_CENTER; } else { option.nonScrollableLayoutStyle = (static_cast(layoutStyle)); } TabsModel::GetInstance()->SetScrollableBarModeOptions(option); } void JSTabs::SetBarGridAlign(const JSCallbackInfo& info) { BarGridColumnOptions columnOption; if (info.Length() > 0 && info[0]->IsObject()) { auto gridParam = JSRef::Cast(info[0]); auto sm = gridParam->GetProperty("sm"); if (sm->IsNumber() && sm->ToNumber() >= 0 && sm->ToNumber() <= SM_COLUMN_NUM && sm->ToNumber() % 2 == 0) { columnOption.sm = sm->ToNumber(); } auto md = gridParam->GetProperty("md"); if (md->IsNumber() && md->ToNumber() >= 0 && md->ToNumber() <= MD_COLUMN_NUM && md->ToNumber() % 2 == 0) { columnOption.md = md->ToNumber(); } auto lg = gridParam->GetProperty("lg"); if (lg->IsNumber() && lg->ToNumber() >= 0 && lg->ToNumber() <= LG_COLUMN_NUM && lg->ToNumber() % 2 == 0) { columnOption.lg = lg->ToNumber(); } CalcDimension columnGutter; if (ParseJsDimensionVp(gridParam->GetProperty("gutter"), columnGutter) && NonNegative(columnGutter.Value()) && columnGutter.Unit() != DimensionUnit::PERCENT) { columnOption.gutter = columnGutter; } CalcDimension columnMargin; if (ParseJsDimensionVp(gridParam->GetProperty("margin"), columnMargin) && NonNegative(columnMargin.Value()) && columnMargin.Unit() != DimensionUnit::PERCENT) { columnOption.margin = columnMargin; } } TabsModel::GetInstance()->SetBarGridAlign(columnOption); } void JSTabs::SetCustomContentTransition(const JSCallbackInfo& info) { if (info.Length() != 1) { return; } auto customContentTransitionInfo = info[0]; if (customContentTransitionInfo->IsUndefined() || !customContentTransitionInfo->IsFunction()) { TabsModel::GetInstance()->SetIsCustomAnimation(false); return; } RefPtr jsCustomAnimationFunc = AceType::MakeRefPtr(JSRef::Cast(customContentTransitionInfo)); auto onCustomAnimation = [execCtx = info.GetExecutionContext(), func = std::move(jsCustomAnimationFunc)]( int32_t from, int32_t to) -> TabContentAnimatedTransition { TabContentAnimatedTransition transitionInfo; JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, transitionInfo); auto ret = func->Execute(from, to); if (!ret->IsObject()) { return transitionInfo; } auto transitionObj = JSRef::Cast(ret); JSRef timeoutProperty = transitionObj->GetProperty("timeout"); if (timeoutProperty->IsNumber()) { auto timeout = timeoutProperty->ToNumber(); transitionInfo.timeout = timeout < 0 ? DEFAULT_CUSTOM_ANIMATION_TIMEOUT : timeout; } else { transitionInfo.timeout = DEFAULT_CUSTOM_ANIMATION_TIMEOUT; } JSRef transition = transitionObj->GetProperty("transition"); if (transition->IsFunction()) { RefPtr jsOnTransition = AceType::MakeRefPtr(JSRef::Cast(transition)); auto onTransition = [execCtx, func = std::move(jsOnTransition)]( const RefPtr& proxy) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onTransition"); func->Execute(proxy); }; transitionInfo.transition = std::move(onTransition); } return transitionInfo; }; TabsModel::GetInstance()->SetIsCustomAnimation(true); TabsModel::GetInstance()->SetOnCustomAnimation(std::move(onCustomAnimation)); } void JSTabs::SetOnContentWillChange(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto handler = AceType::MakeRefPtr(JSRef::Cast(info[0])); auto callback = [execCtx = info.GetExecutionContext(), func = std::move(handler)] (int32_t currentIndex, int32_t comingIndex) -> bool { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, true); ACE_SCORING_EVENT("Tabs.onContentWillChange"); auto ret = func->Execute(currentIndex, comingIndex); if (!ret->IsBoolean()) { return true; } return ret->ToBoolean(); }; TabsModel::GetInstance()->SetOnContentWillChange(std::move(callback)); } void JSTabs::SetAnimateMode(const JSCallbackInfo& info) { JSRef args = info[0]; if (!args->IsNumber()) { TabsModel::GetInstance()->SetAnimateMode(TabAnimateMode::CONTENT_FIRST); return; } uint32_t value = args->ToNumber(); if (value >= static_cast(TabAnimateMode::MAX_VALUE)) { TabsModel::GetInstance()->SetAnimateMode(TabAnimateMode::CONTENT_FIRST); return; } TabsModel::GetInstance()->SetAnimateMode(static_cast(value)); } void JSTabs::SetEdgeEffect(const JSCallbackInfo& info) { auto edgeEffect = EdgeEffect::SPRING; if (info.Length() > 0) { edgeEffect = JSScrollable::ParseEdgeEffect(info[0], EdgeEffect::SPRING); } TabsModel::GetInstance()->SetEdgeEffect(edgeEffect); } void JSTabs::JSBind(BindingTarget globalObj) { JsTabContentTransitionProxy::JSBind(globalObj); JSClass::Declare("Tabs"); JSClass::StaticMethod("create", &JSTabs::Create); JSClass::StaticMethod("pop", &JSTabs::Pop); JSClass::StaticMethod("vertical", &JSTabs::SetVertical); JSClass::StaticMethod("barPosition", &JSTabs::SetBarPosition); JSClass::StaticMethod("barBackgroundBlurStyle", &JSTabs::SetBarBackgroundBlurStyle); JSClass::StaticMethod("scrollable", &JSTabs::SetScrollable); JSClass::StaticMethod("barMode", &JSTabs::SetBarMode); JSClass::StaticMethod("barWidth", &JSTabs::SetBarWidth); JSClass::StaticMethod("barHeight", &JSTabs::SetBarHeight); JSClass::StaticMethod("width", &JSTabs::SetWidth); JSClass::StaticMethod("height", &JSTabs::SetHeight); JSClass::StaticMethod("index", &JSTabs::SetIndex); JSClass::StaticMethod("animationDuration", &JSTabs::SetAnimationDuration); JSClass::StaticMethod("divider", &JSTabs::SetDivider); JSClass::StaticMethod("onChange", &JSTabs::SetOnChange); JSClass::StaticMethod("onTabBarClick", &JSTabs::SetOnTabBarClick); JSClass::StaticMethod("onAnimationStart", &JSTabs::SetOnAnimationStart); JSClass::StaticMethod("onAnimationEnd", &JSTabs::SetOnAnimationEnd); JSClass::StaticMethod("onGestureSwipe", &JSTabs::SetOnGestureSwipe); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::StaticMethod("onHover", &JSInteractableView::JsOnHover); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete); JSClass::StaticMethod("onClick", &JSInteractableView::JsOnClick); JSClass::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage); JSClass::StaticMethod("fadingEdge", &JSTabs::SetFadingEdge); JSClass::StaticMethod("barOverlap", &JSTabs::SetBarOverlap); JSClass::StaticMethod("barBackgroundColor", &JSTabs::SetBarBackgroundColor); JSClass::StaticMethod("clip", &JSTabs::SetClip); JSClass::StaticMethod("barGridAlign", &JSTabs::SetBarGridAlign); JSClass::StaticMethod("customContentTransition", &JSTabs::SetCustomContentTransition); JSClass::StaticMethod("onContentWillChange", &JSTabs::SetOnContentWillChange); JSClass::StaticMethod("animationMode", &JSTabs::SetAnimateMode); JSClass::StaticMethod("edgeEffect", &JSTabs::SetEdgeEffect); JSClass::InheritAndBind(globalObj); } } // namespace OHOS::Ace::Framework