* Copyright (c) 2021-2026 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 "bridge/declarative_frontend/jsview/js_datepicker.h"
#include <utility>
#include "base/log/ace_scoring_log.h"
#include "base/utils/utils.h"
#include "bridge/common/utils/engine_helper.h"
#include "bridge/declarative_frontend/engine/functions/js_event_function.h"
#include "bridge/declarative_frontend/jsview/js_interactable_view.h"
#include "bridge/declarative_frontend/jsview/js_utils.h"
#include "bridge/declarative_frontend/jsview/js_view_common_def.h"
#include "bridge/declarative_frontend/view_stack_processor.h"
#include "bridge/declarative_frontend/ark_theme/theme_apply/js_date_picker_theme.h"
#include "compatible/components/component_loader.h"
#include "core/common/dynamic_module_helper.h"
#include "core/components_ng/pattern/picker/picker_data.h"
#include "core/components_ng/pattern/picker/picker_theme.h"
#include "core/components_ng/base/view_stack_processor.h"
#include "core/components_ng/pattern/picker/datepicker_model_ng.h"
#include "core/components_ng/pattern/time_picker/timepicker_model.h"
#include "core/components_ng/pattern/time_picker/timepicker_model_ng.h"
#include "core/components_v2/inspector/inspector_constants.h"
#include "core/event/ace_event_helper.h"
#include "core/pipeline_ng/pipeline_context.h"
#include "core/common/dynamic_module_helper.h"
#include "core/common/resource/resource_object.h"
#include "core/common/resource/resource_parse_utils.h"
namespace OHOS::Ace {
namespace {
const DimensionOffset DATEPICKER_OFFSET_DEFAULT_TOP = DimensionOffset(0.0_vp, 40.0_vp);
const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
DialogAlignment::BOTTOM_END };
const std::vector<HoverModeAreaType> HOVER_MODE_AREA_TYPE = { HoverModeAreaType::TOP_SCREEN,
HoverModeAreaType::BOTTOM_SCREEN };
const char TIMEPICKER_OPTIONS_HOUR[] = "hour";
const char TIMEPICKER_OPTIONS_MINUTE[] = "minute";
const std::string TIMEPICKER_OPTIONS_NUMERIC_VAL = "numeric";
const std::string TIMEPICKER_OPTIONS_TWO_DIGIT_VAL = "2-digit";
}
std::unique_ptr<DatePickerModel> DatePickerModel::datePickerInstance_ = nullptr;
std::unique_ptr<DatePickerDialogModel> DatePickerDialogModel::datePickerDialogInstance_ = nullptr;
std::unique_ptr<TimePickerModel> TimePickerModel::timePickerInstance_ = nullptr;
std::unique_ptr<TimePickerDialogModel> TimePickerDialogModel::timePickerDialogInstance_ = nullptr;
std::once_flag DatePickerModel::onceFlag_;
std::once_flag DatePickerDialogModel::onceFlag_;
std::once_flag TimePickerModel::onceFlag_;
std::once_flag TimePickerDialogModel::onceFlag_;
DatePickerModel* DatePickerModel::GetInstance()
{
std::call_once(onceFlag_, []() {
#ifdef NG_BUILD
datePickerInstance_.reset(new NG::DatePickerModelNG());
#else
if (Container::IsCurrentUseNewPipeline()) {
datePickerInstance_.reset(new NG::DatePickerModelNG());
} else {
static auto loader = DynamicModuleHelper::GetInstance().GetLoaderByName("datePicker");
if (!loader) {
LOGF("Can't find datePicker loader");
return;
}
datePickerInstance_.reset(reinterpret_cast<DatePickerModel*>(loader->CreateModel()));
}
#endif
});
return datePickerInstance_.get();
}
DatePickerDialogModel* DatePickerDialogModel::GetInstance()
{
std::call_once(onceFlag_, []() {
#ifdef NG_BUILD
datePickerDialogInstance_.reset(new NG::DatePickerDialogModelNG());
#else
if (Container::IsCurrentUseNewPipeline()) {
datePickerDialogInstance_.reset(new NG::DatePickerDialogModelNG());
} else {
static auto loader = DynamicModuleHelper::GetInstance().GetLoaderByName("datePickerDialog");
if (!loader) {
LOGF("Can't find datePickerDialog loader");
return;
}
datePickerDialogInstance_.reset(reinterpret_cast<DatePickerDialogModel*>(loader->CreateModel()));
}
#endif
});
return datePickerDialogInstance_.get();
}
TimePickerModel* TimePickerModel::GetInstance()
{
auto* module = DynamicModuleHelper::GetInstance().GetDynamicModule("TimePicker");
if (module == nullptr) {
LOGF("Can't find TimePicker dynamic module");
abort();
}
auto* model = reinterpret_cast<const NG::TimePickerModelNG*>(module->GetModel());
CHECK_NULL_RETURN(model, nullptr);
std::call_once(onceFlag_, [model]() {
#ifdef NG_BUILD
timePickerInstance_.reset(const_cast<NG::TimePickerModelNG*>(model));
#else
if (Container::IsCurrentUseNewPipeline()) {
timePickerInstance_.reset(const_cast<NG::TimePickerModelNG*>(model));
} else {
static auto loader = DynamicModuleHelper::GetInstance().GetLoaderByName("timePicker");
if (!loader) {
LOGF("Can't find timePicker loader");
return;
}
timePickerInstance_.reset(reinterpret_cast<TimePickerModel*>(loader->CreateModel()));
}
#endif
});
return timePickerInstance_.get();
}
TimePickerDialogModel* TimePickerDialogModel::GetInstance()
{
auto* module = DynamicModuleHelper::GetInstance().GetDynamicModule("TimePickerDialog");
if (module == nullptr) {
LOGF("Can't find TimePickerDialog dynamic module");
abort();
}
auto* model = reinterpret_cast<const NG::TimePickerDialogModelNG*>(module->GetModel());
CHECK_NULL_RETURN(model, nullptr);
std::call_once(onceFlag_, [model]() {
#ifdef NG_BUILD
timePickerDialogInstance_.reset(const_cast<NG::TimePickerDialogModelNG*>(model));
#else
if (Container::IsCurrentUseNewPipeline()) {
timePickerDialogInstance_.reset(const_cast<NG::TimePickerDialogModelNG*>(model));
} else {
static auto loader = DynamicModuleHelper::GetInstance().GetLoaderByName("timePickerDialog");
if (!loader) {
LOGF("Can't find timePickerDialog loader");
return;
}
timePickerDialogInstance_.reset(reinterpret_cast<TimePickerDialogModel*>(loader->CreateModel()));
}
#endif
});
return timePickerDialogInstance_.get();
}
}
namespace OHOS::Ace::Framework {
namespace {
const std::vector<DistortionMode> DIALOG_DISTORTION_MODE = { DistortionMode::DISTORTION_AUTO,
DistortionMode::DISTORTION_ENABLED, DistortionMode::DISTORTION_DISABLED };
const std::vector<EdgeLightMode> DIALOG_EDGELIGHT_MODE = { EdgeLightMode::EDGELIGHT_AUTO,
EdgeLightMode::EDGELIGHT_ENABLED, EdgeLightMode::EDGELIGHT_DISABLED };
void ParsePickerDialogMaterial(PickerDialogInfo& pickerDialog, const JSRef<JSObject>& paramObject)
{
auto systemMaterialValue = paramObject->GetProperty("systemMaterial");
if (systemMaterialValue->IsObject()) {
auto systemUiMaterial = static_cast<UiMaterial*>(UnwrapNapiValue(systemMaterialValue));
pickerDialog.systemMaterial = systemUiMaterial ? systemUiMaterial->Copy() : nullptr;
}
auto distortionModeValue = paramObject->GetProperty("distortionMode");
if (distortionModeValue->IsNumber()) {
auto distortionModeVal = distortionModeValue->ToNumber<int32_t>();
if (distortionModeVal >= 0 && distortionModeVal < static_cast<int32_t>(DIALOG_DISTORTION_MODE.size())) {
pickerDialog.distortionMode = DIALOG_DISTORTION_MODE[distortionModeVal];
}
}
auto edgeLightModeValue = paramObject->GetProperty("edgeLightMode");
if (edgeLightModeValue->IsNumber()) {
auto edgeLightModeVal = edgeLightModeValue->ToNumber<int32_t>();
if (edgeLightModeVal >= 0 && edgeLightModeVal < static_cast<int32_t>(DIALOG_EDGELIGHT_MODE.size())) {
pickerDialog.edgeLightMode = DIALOG_EDGELIGHT_MODE[edgeLightModeVal];
}
}
}
JSRef<JSVal> DatePickerChangeEventToJSValue(const NG::DatePickerChangeEvent& eventInfo)
{
JSRef<JSObject> obj = JSRef<JSObject>::New();
std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(eventInfo.GetSelectedStr());
if (!argsPtr) {
return JSRef<JSVal>::Cast(obj);
}
std::vector<std::string> keys = { "year", "month", "day", "hour", "minute", "second" };
for (auto iter = keys.begin(); iter != keys.end(); iter++) {
const std::string key = *iter;
const auto value = argsPtr->GetValue(key);
if (!value || value->ToString().empty()) {
continue;
}
obj->SetProperty<int32_t>(key.c_str(), value->GetInt());
}
return JSRef<JSVal>::Cast(obj);
}
JSRef<JSVal> DatePickerDateChangeEventToJSValue(const NG::DatePickerChangeEvent& eventInfo)
{
JSRef<JSObject> obj = JSRef<JSObject>::New();
std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(eventInfo.GetSelectedStr());
if (!argsPtr) {
return JSRef<JSVal>::Cast(obj);
}
auto dateObj = JSDatePickerDialog::GetDateObj(argsPtr);
return JSRef<JSVal>::Cast(dateObj);
}
template<typename FuncT>
class JsDatePickerDialogVoidCallback {
public:
JsDatePickerDialogVoidCallback(const JSExecutionContext& execCtx, RefPtr<FuncT> func, WeakPtr<NG::FrameNode> node,
const char* scoringEvent)
: execCtx_(execCtx), func_(std::move(func)), node_(std::move(node)), scoringEvent_(scoringEvent)
{}
void operator()() const
{
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx_);
ACE_SCORING_EVENT(scoringEvent_);
PipelineContext::SetCallBackNode(node_);
func_->Execute();
}
private:
JSExecutionContext execCtx_;
RefPtr<FuncT> func_;
WeakPtr<NG::FrameNode> node_;
const char* scoringEvent_ = nullptr;
};
template<typename FuncT>
class JsDatePickerDialogStringCallback {
public:
JsDatePickerDialogStringCallback(const JSExecutionContext& execCtx, RefPtr<FuncT> func,
WeakPtr<NG::FrameNode> node, const char* scoringEvent)
: execCtx_(execCtx), func_(std::move(func)), node_(std::move(node)), scoringEvent_(scoringEvent)
{}
void operator()(const std::string& info) const
{
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx_);
std::vector<std::string> keys = { "year", "month", "day" };
ACE_SCORING_EVENT(scoringEvent_);
PipelineContext::SetCallBackNode(node_);
func_->Execute(keys, info);
}
private:
JSExecutionContext execCtx_;
RefPtr<FuncT> func_;
WeakPtr<NG::FrameNode> node_;
const char* scoringEvent_ = nullptr;
};
template<typename FuncT>
class JsDatePickerDialogJsonCallback {
public:
JsDatePickerDialogJsonCallback(const JSExecutionContext& execCtx, RefPtr<FuncT> func,
WeakPtr<NG::FrameNode> node, const char* scoringEvent)
: execCtx_(execCtx), func_(std::move(func)), node_(std::move(node)), scoringEvent_(scoringEvent)
{}
void operator()(const std::string& info) const
{
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx_);
ACE_SCORING_EVENT(scoringEvent_);
auto selectedJson = JsonUtil::ParseJsonString(info);
if (!selectedJson || selectedJson->IsNull()) {
return;
}
auto dateObj = JSDatePickerDialog::GetDateObj(selectedJson);
PipelineContext::SetCallBackNode(node_);
func_->ExecuteJS(1, &dateObj);
}
private:
JSExecutionContext execCtx_;
RefPtr<FuncT> func_;
WeakPtr<NG::FrameNode> node_;
const char* scoringEvent_ = nullptr;
};
template<typename FuncT>
class JsDatePickerDialogAcceptCallback {
public:
JsDatePickerDialogAcceptCallback(const JSExecutionContext& execCtx, RefPtr<FuncT> func,
WeakPtr<NG::FrameNode> node, const char* scoringEvent)
: execCtx_(execCtx), func_(std::move(func)), node_(std::move(node)), scoringEvent_(scoringEvent)
{}
void operator()(const std::string& info) const
{
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx_);
std::vector<std::string> keys = { "year", "month", "day", "hour", "minute", "second" };
ACE_SCORING_EVENT(scoringEvent_);
PipelineContext::SetCallBackNode(node_);
func_->Execute(keys, info);
}
private:
JSExecutionContext execCtx_;
RefPtr<FuncT> func_;
WeakPtr<NG::FrameNode> node_;
const char* scoringEvent_ = nullptr;
};
void ParseFontOfButtonStyle(const JSRef<JSObject>& pickerButtonParamObject, ButtonInfo& buttonInfo)
{
CalcDimension fontSize;
JSRef<JSVal> sizeProperty = pickerButtonParamObject->GetProperty("fontSize");
if (JSViewAbstract::ParseJsDimensionVpNG(sizeProperty, fontSize) && fontSize.Unit() != DimensionUnit::PERCENT &&
GreatOrEqual(fontSize.Value(), 0.0)) {
if (JSViewAbstract::ParseJsDimensionFp(sizeProperty, fontSize)) {
buttonInfo.fontSize = fontSize;
}
}
Color fontColor;
if (JSViewAbstract::ParseJsColor(pickerButtonParamObject->GetProperty("fontColor"), fontColor)) {
buttonInfo.fontColor = fontColor;
}
auto fontWeight = pickerButtonParamObject->GetProperty("fontWeight");
if (fontWeight->IsString() || fontWeight->IsNumber()) {
buttonInfo.fontWeight = ConvertStrToFontWeight(fontWeight->ToString(), FontWeight::MEDIUM);
}
JSRef<JSVal> style = pickerButtonParamObject->GetProperty("fontStyle");
if (style->IsNumber()) {
auto value = style->ToNumber<int32_t>();
if (value >= 0 && value < static_cast<int32_t>(FontStyle::NONE)) {
buttonInfo.fontStyle = static_cast<FontStyle>(value);
}
}
JSRef<JSVal> family = pickerButtonParamObject->GetProperty("fontFamily");
std::vector<std::string> fontFamilies;
if (JSViewAbstract::ParseJsFontFamilies(family, fontFamilies)) {
buttonInfo.fontFamily = fontFamilies;
}
}
ButtonInfo ParseButtonStyle(const JSRef<JSObject>& pickerButtonParamObject)
{
ButtonInfo buttonInfo;
if (pickerButtonParamObject->GetProperty("type")->IsNumber()) {
auto buttonTypeIntValue = pickerButtonParamObject->GetProperty("type")->ToNumber<int32_t>();
if (buttonTypeIntValue == static_cast<int32_t>(ButtonType::CAPSULE) ||
buttonTypeIntValue == static_cast<int32_t>(ButtonType::CIRCLE) ||
buttonTypeIntValue == static_cast<int32_t>(ButtonType::ARC) ||
buttonTypeIntValue == static_cast<int32_t>(ButtonType::NORMAL) ||
buttonTypeIntValue == static_cast<int32_t>(ButtonType::ROUNDED_RECTANGLE)) {
buttonInfo.type = static_cast<ButtonType>(buttonTypeIntValue);
}
}
if (pickerButtonParamObject->GetProperty("style")->IsNumber()) {
auto styleModeIntValue = pickerButtonParamObject->GetProperty("style")->ToNumber<int32_t>();
if (styleModeIntValue >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
styleModeIntValue <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
buttonInfo.buttonStyle = static_cast<ButtonStyleMode>(styleModeIntValue);
}
}
if (pickerButtonParamObject->GetProperty("role")->IsNumber()) {
auto buttonRoleIntValue = pickerButtonParamObject->GetProperty("role")->ToNumber<int32_t>();
if (buttonRoleIntValue >= static_cast<int32_t>(ButtonRole::NORMAL) &&
buttonRoleIntValue <= static_cast<int32_t>(ButtonRole::ERROR)) {
buttonInfo.role = static_cast<ButtonRole>(buttonRoleIntValue);
}
}
ParseFontOfButtonStyle(pickerButtonParamObject, buttonInfo);
Color backgroundColor;
if (JSViewAbstract::ParseJsColor(pickerButtonParamObject->GetProperty("backgroundColor"), backgroundColor)) {
buttonInfo.backgroundColor = backgroundColor;
}
auto radius = ParseBorderRadiusAttr(pickerButtonParamObject->GetProperty("borderRadius"));
if (radius.has_value()) {
buttonInfo.borderRadius = radius.value();
}
auto primaryValue = pickerButtonParamObject->GetProperty("primary");
if (primaryValue->IsBoolean()) {
buttonInfo.isPrimary = primaryValue->ToBoolean();
}
return buttonInfo;
}
std::vector<ButtonInfo> ParseButtonStyles(const JSRef<JSObject>& paramObject)
{
std::vector<ButtonInfo> buttonInfos;
auto acceptButtonStyle = paramObject->GetProperty("acceptButtonStyle");
if (acceptButtonStyle->IsObject()) {
auto acceptButtonStyleParamObject = JSRef<JSObject>::Cast(acceptButtonStyle);
buttonInfos.emplace_back(ParseButtonStyle(acceptButtonStyleParamObject));
buttonInfos[0].isAcceptButton = true;
} else {
ButtonInfo buttonInfo;
buttonInfos.emplace_back(buttonInfo);
}
auto cancelButtonStyle = paramObject->GetProperty("cancelButtonStyle");
if (cancelButtonStyle->IsObject()) {
auto cancelButtonStyleParamObject = JSRef<JSObject>::Cast(cancelButtonStyle);
buttonInfos.emplace_back(ParseButtonStyle(cancelButtonStyleParamObject));
}
return buttonInfos;
}
void ParseDatePickerHoverMode(PickerDialogInfo& pickerDialog, const JSRef<JSObject>& paramObject)
{
auto enableHoverModeValue = paramObject->GetProperty("enableHoverMode");
if (enableHoverModeValue->IsBoolean()) {
pickerDialog.enableHoverMode = enableHoverModeValue->ToBoolean();
}
auto hoverModeAreaValue = paramObject->GetProperty("hoverModeArea");
pickerDialog.hoverModeArea = HoverModeAreaType::BOTTOM_SCREEN;
if (hoverModeAreaValue->IsNumber()) {
auto hoverModeArea = hoverModeAreaValue->ToNumber<int32_t>();
if (hoverModeArea >= 0 && hoverModeArea < static_cast<int32_t>(HOVER_MODE_AREA_TYPE.size())) {
pickerDialog.hoverModeArea = HOVER_MODE_AREA_TYPE[hoverModeArea];
}
}
}
void ParseDatePickerBlurStyleOption(PickerDialogInfo& pickerDialog, const JSRef<JSObject>& paramObject)
{
auto blurStyleValue = paramObject->GetProperty("backgroundBlurStyleOptions");
if (blurStyleValue->IsObject()) {
if (!pickerDialog.blurStyleOption.has_value()) {
pickerDialog.blurStyleOption.emplace();
}
JSViewAbstract::ParseBlurStyleOption(blurStyleValue, pickerDialog.blurStyleOption.value());
}
}
void ParseDatePickerEffectOption(PickerDialogInfo& pickerDialog, const JSRef<JSObject>& paramObject)
{
auto effectOptionValue = paramObject->GetProperty("backgroundEffect");
if (effectOptionValue->IsObject()) {
if (!pickerDialog.effectOption.has_value()) {
pickerDialog.effectOption.emplace();
}
JSViewAbstract::ParseEffectOption(effectOptionValue, pickerDialog.effectOption.value());
}
}
}
void JSDatePicker::JSBind(BindingTarget globalObj)
{
JSClass<JSDatePicker>::Declare("DatePicker");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSDatePicker>::StaticMethod("create", &JSDatePicker::Create, opt);
JSClass<JSDatePicker>::StaticMethod("lunar", &JSDatePicker::SetLunar);
JSClass<JSDatePicker>::StaticMethod("canLoop", &JSDatePicker::SetCanLoop);
JSClass<JSDatePicker>::StaticMethod("onChange", &JSDatePicker::OnChange);
JSClass<JSDatePicker>::StaticMethod("onDateChange", &JSDatePicker::OnDateChange);
JSClass<JSDatePicker>::StaticMethod("backgroundColor", &JSDatePicker::PickerBackgroundColor);
JSClass<JSDatePicker>::StaticMethod("opacity", &JSDatePicker::JsOpacity);
JSClass<JSDatePicker>::StaticMethod("useMilitaryTime", &JSDatePicker::UseMilitaryTime);
JSClass<JSDatePicker>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
JSClass<JSDatePicker>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
JSClass<JSDatePicker>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
JSClass<JSDatePicker>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
JSClass<JSDatePicker>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
JSClass<JSDatePicker>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
JSClass<JSDatePicker>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
JSClass<JSDatePicker>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
JSClass<JSDatePicker>::StaticMethod("disappearTextStyle", &JSDatePicker::SetDisappearTextStyle);
JSClass<JSDatePicker>::StaticMethod("textStyle", &JSDatePicker::SetTextStyle);
JSClass<JSDatePicker>::StaticMethod("selectedTextStyle", &JSDatePicker::SetSelectedTextStyle);
JSClass<JSDatePicker>::StaticMethod("enableHapticFeedback", &JSDatePicker::SetEnableHapticFeedback);
JSClass<JSDatePicker>::StaticMethod("digitalCrownSensitivity", &JSDatePicker::SetDigitalCrownSensitivity);
JSClass<JSDatePicker>::InheritAndBind<JSViewAbstract>(globalObj);
}
void JSDatePicker::SetDigitalCrownSensitivity(const JSCallbackInfo& info)
{
int32_t value = OHOS::Ace::NG::DEFAULT_CROWNSENSITIVITY;
if (info.Length() >= 1 && info[0]->IsNumber()) {
value = info[0]->ToNumber<int32_t>();
}
DatePickerModel::GetInstance()->SetDigitalCrownSensitivity(value);
}
void JSDatePicker::Create(const JSCallbackInfo& info)
{
DatePickerType pickerType = DatePickerType::DATE;
JSRef<JSObject> paramObject;
if (info.Length() >= 1 && info[0]->IsObject()) {
paramObject = JSRef<JSObject>::Cast(info[0]);
auto type = paramObject->GetProperty("type");
if (type->IsNumber()) {
pickerType = static_cast<DatePickerType>(type->ToNumber<int32_t>());
}
}
switch (pickerType) {
case DatePickerType::TIME: {
CreateTimePicker(info, paramObject);
break;
}
case DatePickerType::DATE: {
CreateDatePicker(info, paramObject);
break;
}
default: {
break;
}
}
}
void JSDatePicker::SetLunar(bool isLunar)
{
DatePickerModel::GetInstance()->SetShowLunar(isLunar);
}
void JSDatePicker::SetCanLoop(const JSCallbackInfo& info)
{
bool value = true;
if (info.Length() >= 1 && info[0]->IsBoolean()) {
value = info[0]->ToBoolean();
}
DatePickerModel::GetInstance()->SetCanLoop(value);
}
void JSDatePicker::UseMilitaryTime(bool isUseMilitaryTime)
{
DatePickerModel::GetInstance()->SetHour24(isUseMilitaryTime);
}
void JSDatePicker::ParseTextProperties(const JSRef<JSObject>& paramObj, NG::PickerTextProperties& result)
{
auto disappearProperty = paramObj->GetProperty("disappearTextStyle");
auto normalProperty = paramObj->GetProperty("textStyle");
auto selectedProperty = paramObj->GetProperty("selectedTextStyle");
if (!disappearProperty->IsNull() && disappearProperty->IsObject()) {
JSRef<JSObject> disappearObj = JSRef<JSObject>::Cast(disappearProperty);
JSDatePicker::ParseTextStyle(disappearObj, result.disappearTextStyle_, "disappearTextStyle");
}
if (!normalProperty->IsNull() && normalProperty->IsObject()) {
JSRef<JSObject> noramlObj = JSRef<JSObject>::Cast(normalProperty);
JSDatePicker::ParseTextStyle(noramlObj, result.normalTextStyle_, "textStyle");
}
if (!selectedProperty->IsNull() && selectedProperty->IsObject()) {
JSRef<JSObject> selectedObj = JSRef<JSObject>::Cast(selectedProperty);
JSDatePicker::ParseTextStyle(selectedObj, result.selectedTextStyle_, "selectedTextStyle");
}
}
void JSDatePicker::IsUserDefinedFontFamily(const std::string& pos)
{
if (pos == "disappearTextStyle") {
DatePickerModel::GetInstance()->HasUserDefinedDisappearFontFamily(true);
} else if (pos == "textStyle") {
DatePickerModel::GetInstance()->HasUserDefinedNormalFontFamily(true);
} else if (pos == "selectedTextStyle") {
DatePickerModel::GetInstance()->HasUserDefinedSelectedFontFamily(true);
} else if (pos == "disappearTextStyleTime") {
TimePickerModel::GetInstance()->HasUserDefinedDisappearFontFamily(true);
} else if (pos == "textStyleTime") {
TimePickerModel::GetInstance()->HasUserDefinedNormalFontFamily(true);
} else if (pos == "selectedTextStyleTime") {
TimePickerModel::GetInstance()->HasUserDefinedSelectedFontFamily(true);
}
}
void JSDatePicker::ParseTextStyleFontSize(const JSRef<JSVal>& fontSize, NG::PickerTextStyle& textStyle)
{
if (fontSize->IsNull() || fontSize->IsUndefined()) {
textStyle.fontSize = Dimension(-1);
} else {
CalcDimension size;
if (!ParseJsDimensionFp(fontSize, size, textStyle.fontSizeResObj) || size.Unit() == DimensionUnit::PERCENT) {
textStyle.fontSize = Dimension(-1);
} else {
textStyle.fontSize = size;
}
}
}
void JSDatePicker::ParseTextStyle(
const JSRef<JSObject>& paramObj, NG::PickerTextStyle& textStyle, const std::string& pos)
{
auto fontColor = paramObj->GetProperty("color");
auto fontOptions = paramObj->GetProperty("font");
Color textColor;
if (JSViewAbstract::ParseJsColor(fontColor, textColor, textStyle.textColorResObj)) {
textStyle.textColor = textColor;
textStyle.textColorSetByUser = true;
}
if (!fontOptions->IsObject()) {
return;
}
JSRef<JSObject> fontObj = JSRef<JSObject>::Cast(fontOptions);
auto fontSize = fontObj->GetProperty("size");
auto fontWeight = fontObj->GetProperty("weight");
auto fontFamily = fontObj->GetProperty("family");
auto fontStyle = fontObj->GetProperty("style");
ParseTextStyleFontSize(fontSize, textStyle);
if (!fontWeight->IsNull() && !fontWeight->IsUndefined()) {
std::string weight;
if (fontWeight->IsNumber()) {
weight = std::to_string(fontWeight->ToNumber<int32_t>());
} else {
ParseJsString(fontWeight, weight);
}
textStyle.fontWeight = ConvertStrToFontWeight(weight);
}
if (!fontFamily->IsNull() && !fontFamily->IsUndefined()) {
std::vector<std::string> families;
if (ParseJsFontFamilies(fontFamily, families, textStyle.fontFamilyResObj)) {
textStyle.fontFamily = families;
IsUserDefinedFontFamily(pos);
}
}
if (fontStyle->IsNumber()) {
auto style = fontStyle->ToNumber<int32_t>();
if (style < 0 || style > 1) {
return;
}
textStyle.fontStyle = static_cast<FontStyle>(style);
}
}
void JSDatePicker::SetDisappearTextStyle(const JSCallbackInfo& info)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
NG::PickerTextStyle textStyle;
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
JSDatePickerTheme::ObtainTextStyle(textStyle);
}
if (info[0]->IsObject()) {
JSDatePicker::ParseTextStyle(info[0], textStyle, "disappearTextStyle");
}
DatePickerModel::GetInstance()->SetDisappearTextStyle(theme, textStyle);
}
void JSDatePicker::SetTextStyle(const JSCallbackInfo& info)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
NG::PickerTextStyle textStyle;
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
JSDatePickerTheme::ObtainTextStyle(textStyle);
}
if (info[0]->IsObject()) {
JSDatePicker::ParseTextStyle(info[0], textStyle, "textStyle");
}
DatePickerModel::GetInstance()->SetNormalTextStyle(theme, textStyle);
}
void JSDatePicker::SetEnableHapticFeedback(const JSCallbackInfo& info)
{
bool isEnableHapticFeedback = true;
if (info[0]->IsBoolean()) {
isEnableHapticFeedback = info[0]->ToBoolean();
}
DatePickerModel::GetInstance()->SetEnableHapticFeedback(isEnableHapticFeedback);
}
void JSDatePicker::SetSelectedTextStyle(const JSCallbackInfo& info)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
NG::PickerTextStyle textStyle;
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
if (!theme->IsCircleDial()) {
JSDatePickerTheme::ObtainSelectedTextStyle(textStyle);
}
}
if (info[0]->IsObject()) {
JSDatePicker::ParseTextStyle(info[0], textStyle, "selectedTextStyle");
}
DatePickerModel::GetInstance()->SetSelectedTextStyle(theme, textStyle);
if (textStyle.textColor.has_value() && theme->IsCircleDial()) {
DatePickerModel::GetInstance()->UpdateUserSetSelectColor();
}
}
void JSDatePicker::JsOpacity(const JSCallbackInfo& info)
{
JSViewAbstract::JsOpacity(info);
DatePickerModel::GetInstance()->HasUserDefinedOpacity();
}
void JSDatePicker::OnChange(const JSCallbackInfo& info)
{
if (!info[0]->IsFunction()) {
return;
}
auto jsFunc = AceType::MakeRefPtr<JsEventFunction<NG::DatePickerChangeEvent, 1>>(
JSRef<JSFunc>::Cast(info[0]), DatePickerChangeEventToJSValue);
WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
const BaseEventInfo* info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("datePicker.onChange");
PipelineContext::SetCallBackNode(node);
const auto* eventInfo = TypeInfoHelper::DynamicCast<NG::DatePickerChangeEvent>(info);
func->Execute(*eventInfo);
};
DatePickerModel::GetInstance()->SetOnChange(std::move(onChange));
}
void JSDatePicker::OnDateChange(const JSCallbackInfo& info)
{
if (!info[0]->IsFunction()) {
return;
}
auto jsFunc = AceType::MakeRefPtr<JsEventFunction<NG::DatePickerChangeEvent, 1>>(
JSRef<JSFunc>::Cast(info[0]), DatePickerDateChangeEventToJSValue);
WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onDateChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
const BaseEventInfo* info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("datePicker.onDateChange");
PipelineContext::SetCallBackNode(node);
const auto* eventInfo = TypeInfoHelper::DynamicCast<NG::DatePickerChangeEvent>(info);
func->Execute(*eventInfo);
};
DatePickerModel::GetInstance()->SetOnDateChange(std::move(onDateChange));
}
void JSDatePicker::PickerBackgroundColor(const JSCallbackInfo& info)
{
JSViewAbstract::JsBackgroundColor(info);
if (info.Length() < 1) {
return;
}
Color backgroundColor;
if (!ParseJsColor(info[0], backgroundColor)) {
return;
}
DatePickerModel::GetInstance()->SetBackgroundColor(backgroundColor);
}
PickerDate JSDatePicker::ParseDate(const JSRef<JSVal>& dateVal)
{
auto pickerDate = PickerDate();
if (!dateVal->IsObject()) {
return pickerDate;
}
auto dateObj = JSRef<JSObject>::Cast(dateVal);
auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
auto monthFuncJsVal = dateObj->GetProperty("getMonth");
auto dateFuncJsVal = dateObj->GetProperty("getDate");
if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
return pickerDate;
}
auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
JSRef<JSVal> year = yearFunc->Call(dateObj);
JSRef<JSVal> month = monthFunc->Call(dateObj);
JSRef<JSVal> date = dateFunc->Call(dateObj);
if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
pickerDate.SetYear(year->ToNumber<int32_t>());
pickerDate.SetMonth(month->ToNumber<int32_t>() + 1);
pickerDate.SetDay(date->ToNumber<int32_t>());
}
return pickerDate;
}
PickerTime JSDatePicker::ParseTime(const JSRef<JSVal>& timeVal)
{
auto pickerTime = PickerTime();
if (!timeVal->IsObject()) {
return pickerTime;
}
auto timeObj = JSRef<JSObject>::Cast(timeVal);
auto hourFuncJsVal = timeObj->GetProperty("getHours");
auto minuteFuncJsVal = timeObj->GetProperty("getMinutes");
auto secondFuncJsVal = timeObj->GetProperty("getSeconds");
if (!(hourFuncJsVal->IsFunction() && minuteFuncJsVal->IsFunction() && secondFuncJsVal->IsFunction())) {
return pickerTime;
}
auto hourFunc = JSRef<JSFunc>::Cast(hourFuncJsVal);
auto minuteFunc = JSRef<JSFunc>::Cast(minuteFuncJsVal);
auto secondFunc = JSRef<JSFunc>::Cast(secondFuncJsVal);
JSRef<JSVal> hour = hourFunc->Call(timeObj);
JSRef<JSVal> minute = minuteFunc->Call(timeObj);
JSRef<JSVal> second = secondFunc->Call(timeObj);
if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
pickerTime.SetHour(hour->ToNumber<int32_t>());
pickerTime.SetMinute(minute->ToNumber<int32_t>());
pickerTime.SetSecond(second->ToNumber<int32_t>());
}
return pickerTime;
}
void ParseSelectedDateTimeObject(const JSCallbackInfo& info, const JSRef<JSObject>& selectedObject, bool isDatePicker)
{
JSRef<JSVal> changeEventVal = selectedObject->GetProperty("changeEvent");
if (changeEventVal->IsUndefined() || !changeEventVal->IsFunction()) {
return;
}
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
node = targetNode, isDatePicker](const BaseEventInfo* info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("DatePicker.SelectedDateTimeChangeEvent");
const auto* eventInfo = TypeInfoHelper::DynamicCast<NG::DatePickerChangeEvent>(info);
CHECK_NULL_VOID(eventInfo);
auto selectedStr = eventInfo->GetSelectedStr();
auto sourceJson = JsonUtil::ParseJsonString(selectedStr);
if (!sourceJson || sourceJson->IsNull()) {
return;
}
auto dateObj = JSDatePickerDialog::GetDateObj(sourceJson, isDatePicker);
PipelineContext::SetCallBackNode(node);
func->ExecuteJS(1, &dateObj);
};
if (isDatePicker) {
DatePickerModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
} else {
TimePickerModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
}
}
void JSDatePicker::CreateDatePicker(const JSCallbackInfo& info, const JSRef<JSObject>& paramObj)
{
JSRef<JSVal> startDate;
JSRef<JSVal> endDate;
JSRef<JSVal> selectedDate;
JSRef<JSVal> mode;
if (!paramObj->IsUndefined()) {
startDate = paramObj->GetProperty("start");
endDate = paramObj->GetProperty("end");
selectedDate = paramObj->GetProperty("selected");
mode = paramObj->GetProperty("mode");
}
ParseStartEndDate(startDate, endDate);
PickerDate parseSelectedDate = PickerDate::Current();
if (selectedDate->IsObject()) {
JSRef<JSObject> selectedDateObj = JSRef<JSObject>::Cast(selectedDate);
JSRef<JSVal> changeEventVal = selectedDateObj->GetProperty("changeEvent");
if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
ParseSelectedDateTimeObject(info, selectedDateObj, true);
parseSelectedDate = ParseDate(selectedDateObj->GetProperty("value"));
} else {
parseSelectedDate = ParseDate(selectedDate);
}
}
DatePickerModel::GetInstance()->SetSelectedDate(parseSelectedDate);
ParseDatePickerMode(mode);
SetDefaultAttributes();
}
void JSDatePicker::SetDefaultAttributes()
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
NG::PickerTextStyle textStyle;
auto selectedStyle = theme->GetOptionStyle(true, false);
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
textStyle.textColor = selectedStyle.GetTextColor();
}
textStyle.fontSize = selectedStyle.GetFontSize();
textStyle.fontWeight = selectedStyle.GetFontWeight();
DatePickerModel::GetInstance()->SetSelectedTextStyle(theme, textStyle);
auto disappearStyle = theme->GetDisappearOptionStyle();
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
textStyle.textColor = disappearStyle.GetTextColor();
}
textStyle.fontSize = disappearStyle.GetFontSize();
textStyle.fontWeight = disappearStyle.GetFontWeight();
DatePickerModel::GetInstance()->SetDisappearTextStyle(theme, textStyle);
auto normalStyle = theme->GetOptionStyle(false, false);
if (!Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWENTY_SIX)) {
textStyle.textColor = normalStyle.GetTextColor();
}
textStyle.fontSize = normalStyle.GetFontSize();
textStyle.fontWeight = normalStyle.GetFontWeight();
DatePickerModel::GetInstance()->SetNormalTextStyle(theme, textStyle);
}
void JSDatePicker::ParseDatePickerMode(JSRef<JSVal> mode)
{
auto datePickerMode = DatePickerMode::DATE;
if (!mode->IsNull() && mode->IsNumber()) {
auto parseMode = mode->ToNumber<int32_t>();
if (parseMode >= static_cast<int32_t>(DatePickerMode::DATE) &&
parseMode <= static_cast<int32_t>(DatePickerMode::MONTH_AND_DAY)) {
datePickerMode = static_cast<DatePickerMode>(parseMode);
}
}
DatePickerModel::GetInstance()->SetMode(datePickerMode);
}
void JSDatePicker::ParseStartEndDate(JSRef<JSVal> startDate, JSRef<JSVal> endDate)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
auto parseStartDate = ParseDate(startDate);
auto parseEndDate = ParseDate(endDate);
if (parseStartDate.GetYear() <= 0) {
parseStartDate = theme->GetDefaultStartDate();
}
if (parseEndDate.GetYear() <= 0) {
parseEndDate = theme->GetDefaultEndDate();
}
auto startDays = parseStartDate.ToDays();
auto endDays = parseEndDate.ToDays();
if (startDays > endDays) {
parseStartDate = theme->GetDefaultStartDate();
parseEndDate = theme->GetDefaultEndDate();
}
DatePickerModel::GetInstance()->CreateDatePicker(theme);
if (startDate->IsObject()) {
DatePickerModel::GetInstance()->SetStartDate(parseStartDate);
}
if (endDate->IsObject()) {
DatePickerModel::GetInstance()->SetEndDate(parseEndDate);
}
}
void JSDatePicker::CreateTimePicker(const JSCallbackInfo& info, const JSRef<JSObject>& paramObj)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
DatePickerModel::GetInstance()->CreateTimePicker(theme);
auto selectedTime = paramObj->GetProperty("selected");
if (selectedTime->IsObject()) {
JSRef<JSObject> selectedTimeObj = JSRef<JSObject>::Cast(selectedTime);
JSRef<JSVal> changeEventVal = selectedTimeObj->GetProperty("changeEvent");
if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
ParseSelectedDateTimeObject(info, selectedTimeObj, true);
auto parseSelectedTime = ParseTime(selectedTimeObj->GetProperty("value"));
DatePickerModel::GetInstance()->SetSelectedTime(parseSelectedTime);
} else {
DatePickerModel::GetInstance()->SetSelectedTime(ParseTime(selectedTime));
}
}
}
void JSDatePickerDialog::JSBind(BindingTarget globalObj)
{
JSClass<JSDatePickerDialog>::Declare("DatePickerDialog");
JSClass<JSDatePickerDialog>::StaticMethod("show", &JSDatePickerDialog::Show);
JSClass<JSDatePickerDialog>::Bind<>(globalObj);
}
void DatePickerDialogAppearEvent(const JSCallbackInfo& info, PickerDialogEvent& pickerDialogEvent)
{
std::function<void()> didAppearEvent;
std::function<void()> willAppearEvent;
if (info.Length() == 0 || !info[0]->IsObject()) {
return;
}
auto paramObject = JSRef<JSObject>::Cast(info[0]);
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onDidAppear = paramObject->GetProperty("onDidAppear");
if (!onDidAppear->IsUndefined() && onDidAppear->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDidAppear));
didAppearEvent = JsDatePickerDialogVoidCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onDidAppear");
}
auto onWillAppear = paramObject->GetProperty("onWillAppear");
if (!onWillAppear->IsUndefined() && onWillAppear->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onWillAppear));
willAppearEvent = JsDatePickerDialogVoidCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onWillAppear");
}
pickerDialogEvent.onDidAppear = std::move(didAppearEvent);
pickerDialogEvent.onWillAppear = std::move(willAppearEvent);
}
void DatePickerDialogDisappearEvent(const JSCallbackInfo& info, PickerDialogEvent& pickerDialogEvent)
{
std::function<void()> didDisappearEvent;
std::function<void()> willDisappearEvent;
if (info.Length() == 0 || !info[0]->IsObject()) {
return;
}
auto paramObject = JSRef<JSObject>::Cast(info[0]);
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onDidDisappear = paramObject->GetProperty("onDidDisappear");
if (!onDidDisappear->IsUndefined() && onDidDisappear->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDidDisappear));
didDisappearEvent = JsDatePickerDialogVoidCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onDidDisappear");
}
auto onWillDisappear = paramObject->GetProperty("onWillDisappear");
if (!onWillDisappear->IsUndefined() && onWillDisappear->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onWillDisappear));
willDisappearEvent = JsDatePickerDialogVoidCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onWillDisappear");
}
pickerDialogEvent.onDidDisappear = std::move(didDisappearEvent);
pickerDialogEvent.onWillDisappear = std::move(willDisappearEvent);
}
std::function<void(const std::string&)> JSDatePickerDialog::GetDateChangeEvent(const JSRef<JSObject>& paramObject,
const JSCallbackInfo& info, const DatePickerType& pickerType, const WeakPtr<NG::FrameNode>& frameNode)
{
(void)pickerType;
std::function<void(const std::string&)> dateChangeEvent;
auto onDateChange = paramObject->GetProperty("onDateChange");
if (!onDateChange->IsUndefined() && onDateChange->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDateChange));
dateChangeEvent = JsDatePickerDialogJsonCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onDateChange");
}
return dateChangeEvent;
}
std::function<void(const std::string&)> JSDatePickerDialog::GetDateAcceptEvent(const JSRef<JSObject>& paramObject,
const JSCallbackInfo& info, const DatePickerType& pickerType, const WeakPtr<NG::FrameNode>& frameNode)
{
(void)pickerType;
std::function<void(const std::string&)> dateAcceptEvent;
auto onDateAccept = paramObject->GetProperty("onDateAccept");
if (!onDateAccept->IsUndefined() && onDateAccept->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDateAccept));
dateAcceptEvent = JsDatePickerDialogJsonCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onDateAccept");
}
return dateAcceptEvent;
}
JsiRef<JsiValue> JSDatePickerDialog::GetDateObj(const std::unique_ptr<JsonValue>& selectedJson, bool isDatePicker)
{
std::tm dateTime {};
auto year = selectedJson->GetValue("year");
if (year && year->IsNumber()) {
dateTime.tm_year = year->GetInt() - 1900;
}
auto month = selectedJson->GetValue("month");
if (month && month->IsNumber()) {
dateTime.tm_mon = month->GetInt();
}
auto day = selectedJson->GetValue("day");
if (day && day->IsNumber()) {
dateTime.tm_mday = day->GetInt();
}
auto hour = selectedJson->GetValue("hour");
if (hour && hour->IsNumber()) {
dateTime.tm_hour = hour->GetInt();
}
auto minute = selectedJson->GetValue("minute");
if (minute && minute->IsNumber()) {
dateTime.tm_min = minute->GetInt();
}
auto second = selectedJson->GetValue("second");
if (second && second->IsNumber()) {
dateTime.tm_sec = second->GetInt();
}
dateTime.tm_isdst = -1;
if (!isDatePicker) {
auto milliseconds = Date::GetMilliSecondsByDateTime(dateTime);
auto dateObj = JSDate::New(milliseconds);
return dateObj;
}
auto timestamp = std::chrono::system_clock::from_time_t(std::mktime(&dateTime));
auto duration = timestamp.time_since_epoch();
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
auto dateObj = JSDate::New(milliseconds);
return dateObj;
}
std::function<void(const std::string&)> JSDatePickerDialog::GetChangeEvent(const JSRef<JSObject>& paramObject,
const JSCallbackInfo& info, const DatePickerType& pickerType, const WeakPtr<NG::FrameNode>& frameNode)
{
(void)pickerType;
std::function<void(const std::string&)> changeEvent;
auto onChange = paramObject->GetProperty("onChange");
if (!onChange->IsUndefined() && onChange->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
changeEvent = JsDatePickerDialogStringCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onChange");
}
return changeEvent;
}
std::function<void(const std::string&)> JSDatePickerDialog::GetAcceptEvent(
const JSRef<JSObject>& paramObject, const JSCallbackInfo& info, const WeakPtr<NG::FrameNode>& frameNode)
{
std::function<void(const std::string&)> acceptEvent;
auto onAccept = paramObject->GetProperty("onAccept");
if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
acceptEvent = JsDatePickerDialogAcceptCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onAccept");
}
return acceptEvent;
}
std::function<void()> JSDatePickerDialog::GetCancelEvent(
const JSRef<JSObject>& paramObject, const JSCallbackInfo& info, const WeakPtr<NG::FrameNode>& frameNode)
{
std::function<void()> cancelEvent;
auto onCancel = paramObject->GetProperty("onCancel");
if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
cancelEvent = JsDatePickerDialogVoidCallback<JsFunction>(info.GetExecutionContext(), std::move(jsFunc),
frameNode, "DatePickerDialog.onCancel");
}
return cancelEvent;
}
void JSDatePickerDialog::UpdateLunarSwitchSettingData(
const JSRef<JSObject>& paramObject, NG::DatePickerSettingData& settingData)
{
auto selectedColorValue = paramObject->GetProperty("selectedColor");
auto unselectedColorValue = paramObject->GetProperty("unselectedColor");
auto strokeColorValue = paramObject->GetProperty("strokeColor");
Color selectedColor;
if (JSViewAbstract::ParseJsColor(selectedColorValue, selectedColor)) {
settingData.checkboxSettingData.selectedColor = selectedColor;
}
Color unselectedColor;
if (JSViewAbstract::ParseJsColor(unselectedColorValue, unselectedColor)) {
settingData.checkboxSettingData.unselectedColor = unselectedColor;
}
Color strokeColor;
if (JSViewAbstract::ParseJsColor(strokeColorValue, strokeColor)) {
settingData.checkboxSettingData.strokeColor = strokeColor;
}
}
void JSDatePickerDialog::UpdateDatePickerSettingData(
const JSRef<JSObject>& paramObject, NG::DatePickerSettingData& settingData)
{
auto lunar = paramObject->GetProperty("lunar");
auto lunarSwitch = paramObject->GetProperty("lunarSwitch");
auto sTime = paramObject->GetProperty("showTime");
auto useMilitary = paramObject->GetProperty("useMilitaryTime");
settingData.isLunar = lunar->ToBoolean();
settingData.lunarswitch = lunarSwitch->ToBoolean();
if (settingData.lunarswitch) {
auto lunarSwitchStyle = paramObject->GetProperty("lunarSwitchStyle");
if ((!lunarSwitchStyle->IsUndefined()) && lunarSwitchStyle->IsObject()) {
auto style = JSRef<JSObject>::Cast(lunarSwitchStyle);
UpdateLunarSwitchSettingData(style, settingData);
}
}
auto enableHapticFeedbackValue = paramObject->GetProperty("enableHapticFeedback");
if (enableHapticFeedbackValue->IsBoolean()) {
settingData.isEnableHapticFeedback = enableHapticFeedbackValue->ToBoolean();
}
settingData.showTime = sTime->ToBoolean();
settingData.useMilitary = useMilitary->ToBoolean();
auto mode = paramObject->GetProperty("mode");
auto datePickerMode = DatePickerMode::DATE;
if (!mode->IsNull() && mode->IsNumber()) {
auto parseMode = mode->ToNumber<int32_t>();
if (parseMode >= static_cast<int32_t>(DatePickerMode::DATE) &&
parseMode <= static_cast<int32_t>(DatePickerMode::MONTH_AND_DAY)) {
datePickerMode = static_cast<DatePickerMode>(parseMode);
}
}
settingData.mode = datePickerMode;
auto isLoop = paramObject->GetProperty("canLoop");
if (isLoop->IsBoolean()) {
settingData.canLoop = isLoop->ToBoolean();
} else {
settingData.canLoop = true;
}
auto dateTimeOptionsValue = paramObject->GetProperty("dateTimeOptions");
if (dateTimeOptionsValue->IsObject()) {
auto dateTimeOptionsObj = JSRef<JSObject>::Cast(dateTimeOptionsValue);
JSDatePickerDialog::ParseDateTimeOptions(dateTimeOptionsObj, settingData.dateTimeOptions);
}
JSDatePicker::ParseTextProperties(paramObject, settingData.properties);
}
void JSDatePickerDialog::UpdatePickerDialogTimeInfo(const JSRef<JSObject>& paramObject, PickerDialogInfo& pickerDialog)
{
auto theme = GetTheme<PickerTheme>();
CHECK_NULL_VOID(theme);
auto startDate = paramObject->GetProperty("start");
if (startDate->IsObject()) {
pickerDialog.isStartDate = true;
}
auto endDate = paramObject->GetProperty("end");
if (endDate->IsObject()) {
pickerDialog.isEndDate = true;
}
auto selectedDate = paramObject->GetProperty("selected");
if (selectedDate->IsObject()) {
pickerDialog.isSelectedDate = true;
}
auto parseStartDate = ParseDate(startDate);
auto parseEndDate = ParseDate(endDate);
if (parseStartDate.GetYear() <= 0) {
parseStartDate = theme->GetDefaultStartDate();
}
if (parseEndDate.GetYear() <= 0) {
parseEndDate = theme->GetDefaultEndDate();
}
auto startDays = parseStartDate.ToDays();
auto endDays = parseEndDate.ToDays();
if (startDays > endDays) {
parseStartDate = theme->GetDefaultStartDate();
parseEndDate = theme->GetDefaultEndDate();
}
pickerDialog.parseStartDate = parseStartDate;
pickerDialog.parseEndDate = parseEndDate;
pickerDialog.parseSelectedDate = ParseDate(selectedDate);
pickerDialog.pickerTime = ParseTime(selectedDate);
}
void JSDatePickerDialog::UpdatePickerDialogPositionInfo(
const JSRef<JSObject>& paramObject, PickerDialogInfo& pickerDialog)
{
auto alignmentValue = paramObject->GetProperty("alignment");
if (alignmentValue->IsNumber()) {
auto alignment = alignmentValue->ToNumber<int32_t>();
if (alignment >= 0 && alignment < static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
pickerDialog.alignment = DIALOG_ALIGNMENT[alignment];
}
if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
if (alignment == static_cast<int32_t>(DialogAlignment::TOP) ||
alignment == static_cast<int32_t>(DialogAlignment::TOP_START) ||
alignment == static_cast<int32_t>(DialogAlignment::TOP_END)) {
pickerDialog.offset = DATEPICKER_OFFSET_DEFAULT_TOP;
}
}
}
auto offsetValue = paramObject->GetProperty("offset");
if (offsetValue->IsObject()) {
auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
CalcDimension dx;
auto dxValue = offsetObj->GetProperty("dx");
ParseJsDimensionVp(dxValue, dx);
CalcDimension dy;
auto dyValue = offsetObj->GetProperty("dy");
ParseJsDimensionVp(dyValue, dy);
pickerDialog.offset = DimensionOffset(dx, dy);
}
}
void JSDatePickerDialog::UpdatePickerDialogInfo(const JSRef<JSObject>& paramObject, PickerDialogInfo& pickerDialog)
{
UpdatePickerDialogTimeInfo(paramObject, pickerDialog);
UpdatePickerDialogPositionInfo(paramObject, pickerDialog);
auto maskRectValue = paramObject->GetProperty("maskRect");
DimensionRect maskRect;
if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
pickerDialog.maskRect = maskRect;
}
auto backgroundColorValue = paramObject->GetProperty("backgroundColor");
Color backgroundColor;
if (JSViewAbstract::ParseJsColor(backgroundColorValue, backgroundColor)) {
pickerDialog.backgroundColor = backgroundColor;
}
auto backgroundBlurStyle = paramObject->GetProperty("backgroundBlurStyle");
if (backgroundBlurStyle->IsNumber()) {
auto blurStyle = backgroundBlurStyle->ToNumber<int32_t>();
if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
pickerDialog.backgroundBlurStyle = blurStyle;
}
}
auto shadowValue = paramObject->GetProperty("shadow");
Shadow shadow;
if ((shadowValue->IsObject() || shadowValue->IsNumber()) && JSViewAbstract::ParseShadowProps(shadowValue, shadow)) {
pickerDialog.shadow = shadow;
}
ParseDatePickerHoverMode(pickerDialog, paramObject);
ParseDatePickerBlurStyleOption(pickerDialog, paramObject);
ParseDatePickerEffectOption(pickerDialog, paramObject);
ParsePickerDialogMaterial(pickerDialog, paramObject);
}
void JSDatePickerDialog::Show(const JSCallbackInfo& info)
{
auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
CHECK_NULL_VOID(scopedDelegate);
if (!info[0]->IsObject()) {
return;
}
auto paramObject = info[0]->IsEmpty() ? (JSRef<JSObject>::New()) : JSRef<JSObject>::Cast(info[0]);
DatePickerType pickerType = DatePickerType::DATE;
auto type = paramObject->GetProperty("type");
if (type->IsNumber()) {
pickerType = static_cast<DatePickerType>(type->ToNumber<int32_t>());
}
std::function<void()> cancelEvent;
std::function<void(const std::string&)> acceptEvent;
std::function<void(const std::string&)> changeEvent;
std::function<void(const std::string&)> dateChangeEvent;
std::function<void(const std::string&)> dateAcceptEvent;
WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
changeEvent = GetChangeEvent(paramObject, info, pickerType, frameNode);
acceptEvent = GetAcceptEvent(paramObject, info, frameNode);
cancelEvent = GetCancelEvent(paramObject, info, frameNode);
dateChangeEvent = GetDateChangeEvent(paramObject, info, pickerType, frameNode);
dateAcceptEvent = GetDateAcceptEvent(paramObject, info, pickerType, frameNode);
NG::DatePickerSettingData settingData;
UpdateDatePickerSettingData(paramObject, settingData);
PickerDialogInfo pickerDialog;
UpdatePickerDialogInfo(paramObject, pickerDialog);
auto buttonInfos = ParseButtonStyles(paramObject);
PickerDialogEvent pickerDialogEvent { nullptr, nullptr, nullptr, nullptr };
DatePickerDialogAppearEvent(info, pickerDialogEvent);
DatePickerDialogDisappearEvent(info, pickerDialogEvent);
DatePickerDialogModel::GetInstance()->SetDatePickerDialogShow(pickerDialog, settingData, std::move(cancelEvent),
std::move(acceptEvent), std::move(changeEvent), std::move(dateAcceptEvent), std::move(dateChangeEvent),
pickerType, pickerDialogEvent, buttonInfos);
}
void JSDatePickerDialog::DatePickerDialogShow(const JSRef<JSObject>& paramObj,
const std::map<std::string, NG::DialogEvent>& dialogEvent,
const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent)
{
auto container = Container::CurrentSafely();
if (!container) {
return;
}
auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
if (!pipelineContext) {
return;
}
auto executor = pipelineContext->GetTaskExecutor();
if (!executor) {
return;
}
NG::DatePickerSettingData settingData;
auto startDate = paramObj->GetProperty("start");
auto endDate = paramObj->GetProperty("end");
auto selectedDate = paramObj->GetProperty("selected");
auto lunar = paramObj->GetProperty("lunar");
auto sTime = paramObj->GetProperty("showTime");
auto useMilitary = paramObj->GetProperty("useMilitaryTime");
settingData.isLunar = lunar->ToBoolean();
settingData.showTime = sTime->ToBoolean();
settingData.useMilitary = useMilitary->ToBoolean();
auto parseStartDate = ParseDate(startDate);
auto parseEndDate = ParseDate(endDate);
auto parseSelectedDate = ParseDate(selectedDate);
auto theme = GetTheme<DialogTheme>();
CHECK_NULL_VOID(theme);
DialogProperties properties;
properties.alignment = theme->GetAlignment();
if (properties.alignment == DialogAlignment::BOTTOM &&
Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) {
properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
}
properties.customStyle = false;
if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
}
std::map<std::string, PickerDate> datePickerProperty;
std::map<std::string, PickerTime> timePickerProperty;
if (startDate->IsObject()) {
settingData.datePickerProperty["start"] = parseStartDate;
}
if (endDate->IsObject()) {
settingData.datePickerProperty["end"] = parseEndDate;
}
if (selectedDate->IsObject()) {
settingData.datePickerProperty["selected"] = parseSelectedDate;
settingData.timePickerProperty["selected"] = ParseTime(selectedDate);
}
auto enableHapticFeedbackValue = paramObj->GetProperty("enableHapticFeedback");
if (enableHapticFeedbackValue->IsBoolean()) {
settingData.isEnableHapticFeedback = enableHapticFeedbackValue->ToBoolean();
}
JSDatePicker::ParseTextProperties(paramObj, settingData.properties);
auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
auto overlayManager = context ? context->GetOverlayManager() : nullptr;
executor->PostTask(
[properties, settingData, dialogEvent, dialogCancelEvent, weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
auto overlayManager = weak.Upgrade();
CHECK_NULL_VOID(overlayManager);
overlayManager->ShowDateDialog(properties, settingData, dialogEvent, dialogCancelEvent);
},
TaskExecutor::TaskType::UI, "ArkUIDialogShowDatePicker",
TaskExecutor::GetPriorityTypeWithCheck(PriorityType::VIP));
}
PickerDate JSDatePickerDialog::ParseDate(const JSRef<JSVal>& dateVal)
{
auto pickerDate = PickerDate();
if (!dateVal->IsObject()) {
return pickerDate;
}
auto dateObj = JSRef<JSObject>::Cast(dateVal);
auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
auto monthFuncJsVal = dateObj->GetProperty("getMonth");
auto dateFuncJsVal = dateObj->GetProperty("getDate");
if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
return pickerDate;
}
auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
JSRef<JSVal> year = yearFunc->Call(dateObj);
JSRef<JSVal> month = monthFunc->Call(dateObj);
JSRef<JSVal> date = dateFunc->Call(dateObj);
if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
pickerDate.SetYear(year->ToNumber<int32_t>());
pickerDate.SetMonth(month->ToNumber<int32_t>() + 1);
pickerDate.SetDay(date->ToNumber<int32_t>());
}
return pickerDate;
}
PickerTime JSDatePickerDialog::ParseTime(const JSRef<JSVal>& timeVal)
{
auto pickerTime = PickerTime();
if (!timeVal->IsObject()) {
return pickerTime;
}
auto timeObj = JSRef<JSObject>::Cast(timeVal);
auto hourFuncJsVal = timeObj->GetProperty("getHours");
auto minuteFuncJsVal = timeObj->GetProperty("getMinutes");
auto secondFuncJsVal = timeObj->GetProperty("getSeconds");
if (!(hourFuncJsVal->IsFunction() && minuteFuncJsVal->IsFunction() && secondFuncJsVal->IsFunction())) {
return pickerTime;
}
auto hourFunc = JSRef<JSFunc>::Cast(hourFuncJsVal);
auto minuteFunc = JSRef<JSFunc>::Cast(minuteFuncJsVal);
auto secondFunc = JSRef<JSFunc>::Cast(secondFuncJsVal);
JSRef<JSVal> hour = hourFunc->Call(timeObj);
JSRef<JSVal> minute = minuteFunc->Call(timeObj);
JSRef<JSVal> second = secondFunc->Call(timeObj);
if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
pickerTime.SetHour(hour->ToNumber<int32_t>());
pickerTime.SetMinute(minute->ToNumber<int32_t>());
pickerTime.SetSecond(second->ToNumber<int32_t>());
}
return pickerTime;
}
void JSDatePickerDialog::ParseDateTimeOptions(const JSRef<JSObject>& paramObj, DateTimeType& dateTimeOptions)
{
dateTimeOptions.hourType = ZeroPrefixType::AUTO;
dateTimeOptions.minuteType = ZeroPrefixType::AUTO;
dateTimeOptions.secondType = ZeroPrefixType::AUTO;
auto hourValue = paramObj->GetProperty(TIMEPICKER_OPTIONS_HOUR);
if (hourValue->IsString()) {
std::string hour = hourValue->ToString();
if (hour == TIMEPICKER_OPTIONS_TWO_DIGIT_VAL) {
dateTimeOptions.hourType = ZeroPrefixType::SHOW;
} else if (hour == TIMEPICKER_OPTIONS_NUMERIC_VAL) {
dateTimeOptions.hourType = ZeroPrefixType::HIDE;
}
}
auto minuteValue = paramObj->GetProperty(TIMEPICKER_OPTIONS_MINUTE);
if (minuteValue->IsString()) {
dateTimeOptions.minuteType = ZeroPrefixType::SHOW;
std::string minute = minuteValue->ToString();
if (minute == TIMEPICKER_OPTIONS_NUMERIC_VAL) {
dateTimeOptions.minuteType = ZeroPrefixType::HIDE;
}
}
}
}