1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "frameworks/bridge/declarative_frontend/jsview/js_calendar_picker.h"
17 
18 #include "base/log/ace_scoring_log.h"
19 #include "base/utils/date_util.h"
20 #include "bridge/common/utils/engine_helper.h"
21 #include "bridge/declarative_frontend/engine/functions/js_function.h"
22 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
23 #include "bridge/declarative_frontend/jsview/js_utils.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/calendar_picker_model_impl.h"
26 #include "core/components/calendar/calendar_theme.h"
27 #include "core/components/dialog/dialog_theme.h"
28 #include "core/components_ng/base/view_abstract_model.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/pattern/calendar_picker/calendar_picker_model_ng.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32 
33 namespace OHOS::Ace {
34 std::unique_ptr<CalendarPickerModel> CalendarPickerModel::instance_ = nullptr;
35 std::mutex CalendarPickerModel::mutex_;
GetInstance()36 CalendarPickerModel* CalendarPickerModel::GetInstance()
37 {
38     if (!instance_) {
39         std::lock_guard<std::mutex> lock(mutex_);
40         if (!instance_) {
41 #ifdef NG_BUILD
42             instance_.reset(new NG::CalendarPickerModelNG());
43 #else
44             if (Container::IsCurrentUseNewPipeline()) {
45                 instance_.reset(new NG::CalendarPickerModelNG());
46             } else {
47                 instance_.reset(new Framework::CalendarPickerModelImpl());
48             }
49 #endif
50         }
51     }
52     return instance_.get();
53 }
54 } // namespace OHOS::Ace
55 
56 namespace OHOS::Ace::Framework {
57 namespace {
ParseFontOfButtonStyle(const JSRef<JSObject> & pickerButtonParamObject,ButtonInfo & buttonInfo)58 void ParseFontOfButtonStyle(const JSRef<JSObject>& pickerButtonParamObject, ButtonInfo& buttonInfo)
59 {
60     CalcDimension fontSize;
61     JSRef<JSVal> sizeProperty = pickerButtonParamObject->GetProperty("fontSize");
62     if (JSViewAbstract::ParseJsDimensionVpNG(sizeProperty, fontSize) && fontSize.Unit() != DimensionUnit::PERCENT &&
63         GreatOrEqual(fontSize.Value(), 0.0)) {
64         if (JSViewAbstract::ParseJsDimensionFp(sizeProperty, fontSize)) {
65             buttonInfo.fontSize = fontSize;
66         }
67     }
68     Color fontColor;
69     if (JSViewAbstract::ParseJsColor(pickerButtonParamObject->GetProperty("fontColor"), fontColor)) {
70         buttonInfo.fontColor = fontColor;
71     }
72     auto fontWeight = pickerButtonParamObject->GetProperty("fontWeight");
73     if (fontWeight->IsString() || fontWeight->IsNumber()) {
74         buttonInfo.fontWeight = ConvertStrToFontWeight(fontWeight->ToString(), FontWeight::MEDIUM);
75     }
76     JSRef<JSVal> style = pickerButtonParamObject->GetProperty("fontStyle");
77     if (style->IsNumber()) {
78         auto value = style->ToNumber<int32_t>();
79         if (value >= 0 && value < static_cast<int32_t>(FontStyle::NONE)) {
80             buttonInfo.fontStyle = static_cast<FontStyle>(value);
81         }
82     }
83     JSRef<JSVal> family = pickerButtonParamObject->GetProperty("fontFamily");
84     std::vector<std::string> fontFamilies;
85     if (JSViewAbstract::ParseJsFontFamilies(family, fontFamilies)) {
86         buttonInfo.fontFamily = fontFamilies;
87     }
88 }
89 
ParseButtonStyle(const JSRef<JSObject> & pickerButtonParamObject)90 ButtonInfo ParseButtonStyle(const JSRef<JSObject>& pickerButtonParamObject)
91 {
92     ButtonInfo buttonInfo;
93     if (pickerButtonParamObject->GetProperty("type")->IsNumber()) {
94         buttonInfo.type =
95             static_cast<ButtonType>(pickerButtonParamObject->GetProperty("type")->ToNumber<int32_t>());
96     }
97     if (pickerButtonParamObject->GetProperty("style")->IsNumber()) {
98         auto styleModeIntValue = pickerButtonParamObject->GetProperty("style")->ToNumber<int32_t>();
99         if (styleModeIntValue >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
100             styleModeIntValue <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
101             buttonInfo.buttonStyle = static_cast<ButtonStyleMode>(styleModeIntValue);
102         }
103     }
104     if (pickerButtonParamObject->GetProperty("role")->IsNumber()) {
105         auto buttonRoleIntValue = pickerButtonParamObject->GetProperty("role")->ToNumber<int32_t>();
106         if (buttonRoleIntValue >= static_cast<int32_t>(ButtonRole::NORMAL) &&
107             buttonRoleIntValue <= static_cast<int32_t>(ButtonRole::ERROR)) {
108             buttonInfo.role = static_cast<ButtonRole>(buttonRoleIntValue);
109         }
110     }
111     ParseFontOfButtonStyle(pickerButtonParamObject, buttonInfo);
112     Color backgroundColor;
113     if (JSViewAbstract::ParseJsColor(pickerButtonParamObject->GetProperty("backgroundColor"), backgroundColor)) {
114         buttonInfo.backgroundColor = backgroundColor;
115     }
116     auto radius = ParseBorderRadiusAttr(pickerButtonParamObject->GetProperty("borderRadius"));
117     if (radius.has_value()) {
118         buttonInfo.borderRadius = radius.value();
119     }
120 
121     auto primaryValue = pickerButtonParamObject->GetProperty("primary");
122     if (primaryValue->IsBoolean()) {
123         buttonInfo.isPrimary = primaryValue->ToBoolean();
124     }
125 
126     return buttonInfo;
127 }
128 
ParseButtonStyles(const JSRef<JSObject> & paramObject)129 std::vector<ButtonInfo> ParseButtonStyles(const JSRef<JSObject>& paramObject)
130 {
131     std::vector<ButtonInfo> buttonInfos;
132     auto acceptButtonStyle = paramObject->GetProperty("acceptButtonStyle");
133     if (acceptButtonStyle->IsObject()) {
134         auto acceptButtonStyleParamObject = JSRef<JSObject>::Cast(acceptButtonStyle);
135         buttonInfos.emplace_back(ParseButtonStyle(acceptButtonStyleParamObject));
136         buttonInfos[0].isAcceptButton = true;
137     } else {
138         ButtonInfo buttonInfo;
139         buttonInfos.emplace_back(buttonInfo);
140     }
141     auto cancelButtonStyle = paramObject->GetProperty("cancelButtonStyle");
142     if (cancelButtonStyle->IsObject()) {
143         auto cancelButtonStyleParamObject = JSRef<JSObject>::Cast(cancelButtonStyle);
144         buttonInfos.emplace_back(ParseButtonStyle(cancelButtonStyleParamObject));
145     }
146 
147     return buttonInfos;
148 }
149 } // namespace
150 
GetMSByDate(const std::string & date)151 double GetMSByDate(const std::string& date)
152 {
153     auto json = JsonUtil::ParseJsonString(date);
154     if (!json || json->IsNull()) {
155         return 0.0f;
156     }
157 
158     std::tm dateTime = { 0 };
159     auto year = json->GetValue("year");
160     if (year && year->IsNumber()) {
161         dateTime.tm_year = year->GetInt() - 1900; // local date start from 1900
162     }
163     auto month = json->GetValue("month");
164     if (month && month->IsNumber()) {
165         dateTime.tm_mon = month->GetInt() - 1;
166     }
167     auto day = json->GetValue("day");
168     if (day && day->IsNumber()) {
169         dateTime.tm_mday = day->GetInt();
170     }
171     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
172     auto local = std::localtime(&now);
173     CHECK_NULL_RETURN(local, 0.0f);
174     dateTime.tm_hour = local->tm_hour;
175     dateTime.tm_min = local->tm_min;
176     dateTime.tm_sec = local->tm_sec;
177     return Date::GetMilliSecondsByDateTime(dateTime);
178 }
179 
JSBind(BindingTarget globalObj)180 void JSCalendarPicker::JSBind(BindingTarget globalObj)
181 {
182     JSClass<JSCalendarPicker>::Declare("CalendarPicker");
183     JSClass<JSCalendarPicker>::StaticMethod("create", &JSCalendarPicker::Create, MethodOptions::NONE);
184     JSClass<JSCalendarPicker>::StaticMethod("edgeAlign", &JSCalendarPicker::SetEdgeAlign);
185     JSClass<JSCalendarPicker>::StaticMethod("textStyle", &JSCalendarPicker::SetTextStyle);
186     JSClass<JSCalendarPicker>::StaticMethod("onChange", &JSCalendarPicker::SetOnChange);
187     JSClass<JSCalendarPicker>::StaticMethod("border", &JSCalendarPicker::SetBorder);
188     JSClass<JSCalendarPicker>::StaticMethod("padding", &JSCalendarPicker::JsPadding);
189     JSClass<JSCalendarPicker>::StaticMethod("height", &JSCalendarPicker::JsHeight);
190     JSClass<JSCalendarPicker>::StaticMethod("borderColor", &JSCalendarPicker::JsBorderColor);
191     JSClass<JSCalendarPicker>::StaticMethod("borderRadius", &JSCalendarPicker::JsBorderRadius);
192     JSClass<JSCalendarPicker>::InheritAndBind<JSViewAbstract>(globalObj);
193 }
194 
SetBorder(const JSCallbackInfo & info)195 void JSCalendarPicker::SetBorder(const JSCallbackInfo& info)
196 {
197     if (!info[0]->IsObject()) {
198         CalendarPickerModel::GetInstance()->ClearBorder();
199         return;
200     }
201     JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
202     auto valueWidth = object->GetProperty("width");
203     CalcDimension value;
204     if (ParseJsDimensionVpNG(valueWidth, value) || valueWidth->IsObject()) {
205         ParseBorderWidth(valueWidth);
206     } else {
207         CalendarPickerModel::GetInstance()->ClearBorderWidth();
208     }
209 
210     // use default value when undefined.
211     ParseCalendarPickerBorderColor(object->GetProperty("color"));
212 
213     auto valueRadius = object->GetProperty("radius");
214     if (!valueRadius->IsUndefined()) {
215         ParseBorderRadius(valueRadius);
216     }
217     // use default value when undefined.
218     ParseBorderStyle(object->GetProperty("style"));
219 
220     info.ReturnSelf();
221 }
222 
ParseCalendarPickerBorderColor(const JSRef<JSVal> & args)223 void JSCalendarPicker::ParseCalendarPickerBorderColor(const JSRef<JSVal>& args)
224 {
225     auto pipelineContext = PipelineContext::GetCurrentContext();
226     CHECK_NULL_VOID(pipelineContext);
227     RefPtr<CalendarTheme> theme = pipelineContext->GetTheme<CalendarTheme>();
228     CHECK_NULL_VOID(theme);
229     if (!args->IsObject() && !args->IsNumber() && !args->IsString()) {
230         ViewAbstractModel::GetInstance()->SetBorderColor(theme->GetEntryBorderColor());
231     } else {
232         JSViewAbstract::ParseBorderColor(args);
233     }
234 }
235 
SetEdgeAlign(const JSCallbackInfo & info)236 void JSCalendarPicker::SetEdgeAlign(const JSCallbackInfo& info)
237 {
238     NG::CalendarEdgeAlign alignType = NG::CalendarEdgeAlign::EDGE_ALIGN_END;
239     DimensionOffset offset;
240     if (info[0]->IsNumber()) {
241         alignType = static_cast<NG::CalendarEdgeAlign>(info[0]->ToNumber<int32_t>());
242     }
243 
244     if (!info[1]->IsObject()) {
245         CalendarPickerModel::GetInstance()->SetEdgeAlign(alignType, offset);
246         return;
247     }
248     auto offsetObj = JSRef<JSObject>::Cast(info[1]);
249     CalcDimension dx;
250     auto dxValue = offsetObj->GetProperty("dx");
251     ParseJsDimensionVp(dxValue, dx);
252     CalcDimension dy;
253     auto dyValue = offsetObj->GetProperty("dy");
254     ParseJsDimensionVp(dyValue, dy);
255     offset = DimensionOffset(dx, dy);
256 
257     CalendarPickerModel::GetInstance()->SetEdgeAlign(alignType, offset);
258 }
259 
SetTextStyle(const JSCallbackInfo & info)260 void JSCalendarPicker::SetTextStyle(const JSCallbackInfo& info)
261 {
262     auto pipeline = PipelineBase::GetCurrentContext();
263     CHECK_NULL_VOID(pipeline);
264     RefPtr<CalendarTheme> calendarTheme = pipeline->GetTheme<CalendarTheme>();
265     CHECK_NULL_VOID(calendarTheme);
266     NG::PickerTextStyle textStyle;
267     textStyle.fontSize = calendarTheme->GetEntryFontSize();
268     textStyle.textColor = calendarTheme->GetEntryFontColor();
269     textStyle.fontWeight = FontWeight::NORMAL;
270     if (!info[0]->IsObject()) {
271         CalendarPickerModel::GetInstance()->SetTextStyle(textStyle);
272         return;
273     }
274     JSCalendarPicker::ParseTextStyle(info[0], textStyle);
275     CalendarPickerModel::GetInstance()->SetTextStyle(textStyle);
276 }
277 
SetOnChange(const JSCallbackInfo & info)278 void JSCalendarPicker::SetOnChange(const JSCallbackInfo& info)
279 {
280     if (!info[0]->IsFunction()) {
281         return;
282     }
283 
284     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
285     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
286     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
287                         const std::string& info) {
288         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
289         ACE_SCORING_EVENT("CalendarPicker.onChange");
290         PipelineContext::SetCallBackNode(node);
291         auto dateObj = JSDate::New(GetMSByDate(info));
292         func->ExecuteJS(1, &dateObj);
293     };
294     CalendarPickerModel::GetInstance()->SetOnChange(std::move(onChange));
295 }
296 
JsPadding(const JSCallbackInfo & info)297 void JSCalendarPicker::JsPadding(const JSCallbackInfo& info)
298 {
299     NG::PaddingProperty padding;
300     if (info[0]->IsObject()) {
301         std::optional<CalcDimension> left;
302         std::optional<CalcDimension> right;
303         std::optional<CalcDimension> top;
304         std::optional<CalcDimension> bottom;
305         JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
306 
307         CalcDimension leftDimen;
308         if (ParseJsDimensionVpNG(paddingObj->GetProperty("left"), leftDimen) && leftDimen.IsNonNegative()) {
309             left = leftDimen;
310         }
311         CalcDimension rightDimen;
312         if (ParseJsDimensionVpNG(paddingObj->GetProperty("right"), rightDimen) && rightDimen.IsNonNegative()) {
313             right = rightDimen;
314         }
315         CalcDimension topDimen;
316         if (ParseJsDimensionVpNG(paddingObj->GetProperty("top"), topDimen) && topDimen.IsNonNegative()) {
317             top = topDimen;
318         }
319         CalcDimension bottomDimen;
320         if (ParseJsDimensionVpNG(paddingObj->GetProperty("bottom"), bottomDimen) &&
321             bottomDimen.IsNonNegative()) {
322             bottom = bottomDimen;
323         }
324         if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
325             padding = SetPaddings(top, bottom, left, right);
326             CalendarPickerModel::GetInstance()->SetPadding(padding);
327             return;
328         }
329     }
330 
331     CalcDimension length(-1);
332     if (ParseJsDimensionVpNG(info[0], length) && length.IsNonNegative()) {
333         padding.SetEdges(NG::CalcLength(length));
334         CalendarPickerModel::GetInstance()->SetPadding(padding);
335     } else {
336         CalendarPickerModel::GetInstance()->ClearPadding();
337     }
338 }
339 
JsHeight(const JSCallbackInfo & info)340 void JSCalendarPicker::JsHeight(const JSCallbackInfo& info)
341 {
342     auto jsValue = info[0];
343     CalcDimension value;
344     if (ParseJsDimensionVpNG(jsValue, value) && value.IsValid()) {
345         JSViewAbstract::JsHeight(info);
346     } else {
347         CalendarPickerModel::GetInstance()->ClearHeight();
348     }
349 }
350 
JsBorderColor(const JSCallbackInfo & info)351 void JSCalendarPicker::JsBorderColor(const JSCallbackInfo& info)
352 {
353     Color borderColor;
354     auto jsValue = info[0];
355     if (ParseJsColor(jsValue, borderColor) || jsValue->IsObject()) {
356         JSViewAbstract::JsBorderColor(info);
357     } else {
358         CalendarPickerModel::GetInstance()->ClearBorderColor();
359     }
360 }
361 
JsBorderRadius(const JSCallbackInfo & info)362 void JSCalendarPicker::JsBorderRadius(const JSCallbackInfo& info)
363 {
364     auto jsValue = info[0];
365     CalcDimension value;
366     if (ParseJsDimensionVpNG(jsValue, value) || jsValue->IsObject()) {
367         JSViewAbstract::JsBorderRadius(info);
368     } else {
369         CalendarPickerModel::GetInstance()->ClearBorderRadius();
370     }
371 }
372 
SetPaddings(const std::optional<CalcDimension> & top,const std::optional<CalcDimension> & bottom,const std::optional<CalcDimension> & left,const std::optional<CalcDimension> & right)373 NG::PaddingProperty JSCalendarPicker::SetPaddings(const std::optional<CalcDimension>& top,
374     const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
375     const std::optional<CalcDimension>& right)
376 {
377     NG::PaddingProperty paddings;
378     if (top.has_value()) {
379         if (top.value().Unit() == DimensionUnit::CALC) {
380             paddings.top =
381                 NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
382         } else {
383             paddings.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
384         }
385     }
386     if (bottom.has_value()) {
387         if (bottom.value().Unit() == DimensionUnit::CALC) {
388             paddings.bottom = NG::CalcLength(
389                 bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
390         } else {
391             paddings.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
392         }
393     }
394     if (left.has_value()) {
395         if (left.value().Unit() == DimensionUnit::CALC) {
396             paddings.left =
397                 NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
398         } else {
399             paddings.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
400         }
401     }
402     if (right.has_value()) {
403         if (right.value().Unit() == DimensionUnit::CALC) {
404             paddings.right =
405                 NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
406         } else {
407             paddings.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
408         }
409     }
410 
411     return paddings;
412 }
413 
ParseSelectedDateObject(const JSCallbackInfo & info,const JSRef<JSObject> & selectedObject)414 void JSCalendarPicker::ParseSelectedDateObject(const JSCallbackInfo& info, const JSRef<JSObject>& selectedObject)
415 {
416     JSRef<JSVal> changeEventVal = selectedObject->GetProperty("changeEvent");
417     if (!changeEventVal->IsFunction()) {
418         return;
419     }
420     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
421     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
422     auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
423                            const std::string& info) {
424         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
425         ACE_SCORING_EVENT("DatePicker.SelectedDateTimeChangeEvent");
426         PipelineContext::SetCallBackNode(node);
427         auto dateObj = JSDate::New(GetMSByDate(info));
428         func->ExecuteJS(1, &dateObj);
429     };
430     CalendarPickerModel::GetInstance()->SetChangeEvent(std::move(changeEvent));
431 }
432 
Create(const JSCallbackInfo & info)433 void JSCalendarPicker::Create(const JSCallbackInfo& info)
434 {
435     NG::CalendarSettingData settingData;
436     RefPtr<CalendarTheme> calendarTheme = GetTheme<CalendarTheme>();
437     CHECK_NULL_VOID(calendarTheme);
438     CalcDimension dayRadius;
439     if (info[0]->IsObject()) {
440         auto obj = JSRef<JSObject>::Cast(info[0]);
441         if (!ParseJsDimensionVpNG(obj->GetProperty("hintRadius"), dayRadius)) {
442             dayRadius = calendarTheme->GetCalendarDayRadius();
443         }
444         auto selected = obj->GetProperty("selected");
445         auto parseSelectedDate = ParseDate(selected);
446         if (selected->IsObject() && parseSelectedDate.GetYear() != 0) {
447             JSRef<JSObject> selectedDateObj = JSRef<JSObject>::Cast(selected);
448             JSRef<JSVal> changeEventVal = selectedDateObj->GetProperty("changeEvent");
449             if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
450                 ParseSelectedDateObject(info, selectedDateObj);
451                 settingData.selectedDate = ParseDate(selectedDateObj->GetProperty("value"));
452             } else {
453                 settingData.selectedDate = parseSelectedDate;
454             }
455         }
456     } else {
457         dayRadius = calendarTheme->GetCalendarDayRadius();
458     }
459     settingData.dayRadius = dayRadius;
460     CalendarPickerModel::GetInstance()->Create(settingData);
461 }
462 
ParseTextStyle(const JSRef<JSObject> & paramObj,NG::PickerTextStyle & textStyle)463 void JSCalendarPicker::ParseTextStyle(const JSRef<JSObject>& paramObj, NG::PickerTextStyle& textStyle)
464 {
465     auto fontColor = paramObj->GetProperty("color");
466     auto fontStyle = paramObj->GetProperty("font");
467 
468     Color color;
469     if (ParseJsColor(fontColor, color)) {
470         textStyle.textColor = color;
471     }
472 
473     if (!fontStyle->IsObject()) {
474         return;
475     }
476     JSRef<JSObject> fontObj = JSRef<JSObject>::Cast(fontStyle);
477     auto fontSize = fontObj->GetProperty("size");
478     auto fontWeight = fontObj->GetProperty("weight");
479     if (fontSize->IsNull() || fontSize->IsUndefined()) {
480         textStyle.fontSize = Dimension(-1);
481     } else {
482         CalcDimension size;
483         if (!ParseJsDimensionFpNG(fontSize, size) || size.Unit() == DimensionUnit::PERCENT) {
484             textStyle.fontSize = Dimension(-1);
485         } else {
486             textStyle.fontSize = size;
487         }
488     }
489 
490     if (!fontWeight->IsNull() && !fontWeight->IsUndefined()) {
491         std::string weight;
492         if (fontWeight->IsNumber()) {
493             weight = std::to_string(fontWeight->ToNumber<int32_t>());
494         } else {
495             ParseJsString(fontWeight, weight);
496         }
497         textStyle.fontWeight = ConvertStrToFontWeight(weight);
498     }
499 }
500 
ParseDate(const JSRef<JSVal> & dateVal)501 PickerDate JSCalendarPicker::ParseDate(const JSRef<JSVal>& dateVal)
502 {
503     auto pickerDate = PickerDate::Current();
504     if (!dateVal->IsObject()) {
505         return pickerDate;
506     }
507     auto dateObj = JSRef<JSObject>::Cast(dateVal);
508     auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
509     auto monthFuncJsVal = dateObj->GetProperty("getMonth");
510     auto dateFuncJsVal = dateObj->GetProperty("getDate");
511     if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
512         return pickerDate;
513     }
514     auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
515     auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
516     auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
517     JSRef<JSVal> year = yearFunc->Call(dateObj);
518     JSRef<JSVal> month = monthFunc->Call(dateObj);
519     JSRef<JSVal> date = dateFunc->Call(dateObj);
520 
521     if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
522         pickerDate.SetYear(year->ToNumber<int32_t>());
523         pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
524         pickerDate.SetDay(date->ToNumber<int32_t>());
525     }
526     return pickerDate;
527 }
528 
JSBind(BindingTarget globalObj)529 void JSCalendarPickerDialog::JSBind(BindingTarget globalObj)
530 {
531     JSClass<JSCalendarPickerDialog>::Declare("CalendarPickerDialog");
532     JSClass<JSCalendarPickerDialog>::StaticMethod("show", &JSCalendarPickerDialog::Show);
533     JSClass<JSCalendarPickerDialog>::Bind<>(globalObj);
534 }
535 
Show(const JSCallbackInfo & info)536 void JSCalendarPickerDialog::Show(const JSCallbackInfo& info)
537 {
538     auto scopedDelegate = EngineHelper::GetCurrentDelegateSafely();
539     CHECK_NULL_VOID(scopedDelegate);
540     if (!info[0]->IsObject()) {
541         return;
542     }
543 
544     if (Container::IsCurrentUseNewPipeline()) {
545         auto paramObject = JSRef<JSObject>::Cast(info[0]);
546         auto buttonInfos = ParseButtonStyles(paramObject);
547         auto dialogEvent = ChangeDialogEvent(info);
548         auto dialogCancelEvent = DialogCancelEvent(info);
549         auto dialogLifeCycleEvent = LifeCycleDialogEvent(info);
550         CalendarPickerDialogShow(paramObject, dialogEvent, dialogCancelEvent, dialogLifeCycleEvent, buttonInfos);
551     }
552 }
553 
ChangeDialogEvent(const JSCallbackInfo & info)554 std::map<std::string, NG::DialogEvent> JSCalendarPickerDialog::ChangeDialogEvent(const JSCallbackInfo& info)
555 {
556     std::map<std::string, NG::DialogEvent> dialogEvent;
557     if (!info[0]->IsObject()) {
558         return dialogEvent;
559     }
560     auto paramObject = JSRef<JSObject>::Cast(info[0]);
561     auto onChange = paramObject->GetProperty("onChange");
562     if (!onChange->IsUndefined() && onChange->IsFunction()) {
563         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
564         auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
565         auto changeId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
566                             const std::string& info) {
567             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
568             ACE_SCORING_EVENT("CalendarDialog.onChange");
569             PipelineContext::SetCallBackNode(node);
570             auto dateObj = JSDate::New(GetMSByDate(info));
571             func->ExecuteJS(1, &dateObj);
572         };
573         dialogEvent["changeId"] = changeId;
574     }
575     auto onAccept = paramObject->GetProperty("onAccept");
576     if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
577         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
578         auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
579         auto acceptId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
580                             const std::string& info) {
581             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
582             ACE_SCORING_EVENT("CalendarDialog.onAccept");
583             PipelineContext::SetCallBackNode(node);
584             auto dateObj = JSDate::New(GetMSByDate(info));
585             func->ExecuteJS(1, &dateObj);
586         };
587         dialogEvent["acceptId"] = acceptId;
588     }
589     return dialogEvent;
590 }
591 
DialogCancelEvent(const JSCallbackInfo & info)592 std::map<std::string, NG::DialogGestureEvent> JSCalendarPickerDialog::DialogCancelEvent(const JSCallbackInfo& info)
593 {
594     std::map<std::string, NG::DialogGestureEvent> dialogCancelEvent;
595     if (!info[0]->IsObject()) {
596         return dialogCancelEvent;
597     }
598     auto paramObject = JSRef<JSObject>::Cast(info[0]);
599     auto onCancel = paramObject->GetProperty("onCancel");
600     if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
601         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
602         auto targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
603         auto cancelId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
604                             const GestureEvent& /* info */) {
605             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
606             ACE_SCORING_EVENT("CalendarDialog.onCancel");
607             PipelineContext::SetCallBackNode(node);
608             func->Execute();
609         };
610         dialogCancelEvent["cancelId"] = cancelId;
611     }
612     return dialogCancelEvent;
613 }
614 
AppearDialogEvent(const JSCallbackInfo & info,std::map<std::string,NG::DialogCancelEvent> & dialogLifeCycleEvent)615 void AppearDialogEvent(const JSCallbackInfo& info, std::map<std::string, NG::DialogCancelEvent>& dialogLifeCycleEvent)
616 {
617     if (!info[0]->IsObject()) {
618         return;
619     }
620     auto paramObject = JSRef<JSObject>::Cast(info[0]);
621     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
622     auto onDidAppear = paramObject->GetProperty("onDidAppear");
623     if (!onDidAppear->IsUndefined() && onDidAppear->IsFunction()) {
624         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDidAppear));
625         auto didAppearId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]() {
626             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
627             ACE_SCORING_EVENT("CalendarDialog.onDidAppear");
628             PipelineContext::SetCallBackNode(node);
629             func->Execute();
630         };
631         dialogLifeCycleEvent["didAppearId"] = didAppearId;
632     }
633     auto onWillAppear = paramObject->GetProperty("onWillAppear");
634     if (!onWillAppear->IsUndefined() && onWillAppear->IsFunction()) {
635         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onWillAppear));
636         auto willAppearId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]() {
637             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
638             ACE_SCORING_EVENT("CalendarDialog.onWillAppear");
639             PipelineContext::SetCallBackNode(node);
640             func->Execute();
641         };
642         dialogLifeCycleEvent["willAppearId"] = willAppearId;
643     }
644 }
645 
DisappearDialogEvent(const JSCallbackInfo & info,std::map<std::string,NG::DialogCancelEvent> & dialogLifeCycleEvent)646 void DisappearDialogEvent(
647     const JSCallbackInfo& info, std::map<std::string, NG::DialogCancelEvent>& dialogLifeCycleEvent)
648 {
649     if (!info[0]->IsObject()) {
650         return;
651     }
652     auto paramObject = JSRef<JSObject>::Cast(info[0]);
653     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
654     auto onDidDisappear = paramObject->GetProperty("onDidDisappear");
655     if (!onDidDisappear->IsUndefined() && onDidDisappear->IsFunction()) {
656         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onDidDisappear));
657         auto didDisappearId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]() {
658             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
659             ACE_SCORING_EVENT("CalendarDialog.onDidDisappear");
660             PipelineContext::SetCallBackNode(node);
661             func->Execute();
662         };
663         dialogLifeCycleEvent["didDisappearId"] = didDisappearId;
664     }
665     auto onWillDisappear = paramObject->GetProperty("onWillDisappear");
666     if (!onWillDisappear->IsUndefined() && onWillDisappear->IsFunction()) {
667         auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onWillDisappear));
668         auto willDisappearId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
669                                       node = targetNode]() {
670             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
671             ACE_SCORING_EVENT("CalendarDialog.onWillDisappear");
672             PipelineContext::SetCallBackNode(node);
673             func->Execute();
674         };
675         dialogLifeCycleEvent["willDisappearId"] = willDisappearId;
676     }
677 }
678 
LifeCycleDialogEvent(const JSCallbackInfo & info)679 std::map<std::string, NG::DialogCancelEvent> JSCalendarPickerDialog::LifeCycleDialogEvent(const JSCallbackInfo& info)
680 {
681     std::map<std::string, NG::DialogCancelEvent> dialogLifeCycleEvent;
682     if (!info[0]->IsObject()) {
683         return dialogLifeCycleEvent;
684     }
685     AppearDialogEvent(info, dialogLifeCycleEvent);
686     DisappearDialogEvent(info, dialogLifeCycleEvent);
687     return dialogLifeCycleEvent;
688 }
689 
ParseDate(const JSRef<JSVal> & dateVal)690 PickerDate JSCalendarPickerDialog::ParseDate(const JSRef<JSVal>& dateVal)
691 {
692     auto pickerDate = PickerDate();
693     if (!dateVal->IsObject()) {
694         return pickerDate;
695     }
696     auto dateObj = JSRef<JSObject>::Cast(dateVal);
697 
698     auto yearFuncJsVal = dateObj->GetProperty("getFullYear");
699     auto monthFuncJsVal = dateObj->GetProperty("getMonth");
700     auto dateFuncJsVal = dateObj->GetProperty("getDate");
701     if (!(yearFuncJsVal->IsFunction() && monthFuncJsVal->IsFunction() && dateFuncJsVal->IsFunction())) {
702         return pickerDate;
703     }
704     auto yearFunc = JSRef<JSFunc>::Cast(yearFuncJsVal);
705     auto monthFunc = JSRef<JSFunc>::Cast(monthFuncJsVal);
706     auto dateFunc = JSRef<JSFunc>::Cast(dateFuncJsVal);
707     JSRef<JSVal> year = yearFunc->Call(dateObj);
708     JSRef<JSVal> month = monthFunc->Call(dateObj);
709     JSRef<JSVal> date = dateFunc->Call(dateObj);
710 
711     if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
712         pickerDate.SetYear(year->ToNumber<int32_t>());
713         pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
714         pickerDate.SetDay(date->ToNumber<int32_t>());
715     }
716     return pickerDate;
717 }
718 
CalendarPickerDialogShow(const JSRef<JSObject> & paramObj,const std::map<std::string,NG::DialogEvent> & dialogEvent,const std::map<std::string,NG::DialogGestureEvent> & dialogCancelEvent,const std::map<std::string,NG::DialogCancelEvent> & dialogLifeCycleEvent,const std::vector<ButtonInfo> & buttonInfos)719 void JSCalendarPickerDialog::CalendarPickerDialogShow(const JSRef<JSObject>& paramObj,
720     const std::map<std::string, NG::DialogEvent>& dialogEvent,
721     const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent,
722     const std::map<std::string, NG::DialogCancelEvent>& dialogLifeCycleEvent,
723     const std::vector<ButtonInfo>& buttonInfos)
724 {
725     auto container = Container::CurrentSafely();
726     CHECK_NULL_VOID(container);
727     auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
728     CHECK_NULL_VOID(pipelineContext);
729     auto executor = pipelineContext->GetTaskExecutor();
730     CHECK_NULL_VOID(executor);
731 
732     auto theme = GetTheme<DialogTheme>();
733     CHECK_NULL_VOID(theme);
734     auto calendarTheme = pipelineContext->GetTheme<CalendarTheme>();
735     NG::CalendarSettingData settingData;
736     auto selectedDate = paramObj->GetProperty("selected");
737     auto parseSelectedDate = ParseDate(selectedDate);
738     if (selectedDate->IsObject() && parseSelectedDate.GetYear() != 0) {
739         settingData.selectedDate = parseSelectedDate;
740     }
741 
742     CalcDimension radius;
743     if (ParseJsDimensionVpNG(paramObj->GetProperty("hintRadius"), radius)) {
744         settingData.dayRadius = radius;
745     }
746 
747     DialogProperties properties;
748     properties.alignment = theme->GetAlignment();
749     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
750         properties.alignment = DialogAlignment::CENTER;
751     }
752 
753     auto backgroundColorValue = paramObj->GetProperty("backgroundColor");
754     Color backgroundColor;
755     if (JSViewAbstract::ParseJsColor(backgroundColorValue, backgroundColor)) {
756         properties.backgroundColor = backgroundColor;
757     }
758 
759     auto backgroundBlurStyle = paramObj->GetProperty("backgroundBlurStyle");
760     if (backgroundBlurStyle->IsNumber()) {
761         auto blurStyle = backgroundBlurStyle->ToNumber<int32_t>();
762         if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
763             blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
764             properties.backgroundBlurStyle = blurStyle;
765         }
766     }
767 
768     auto shadowValue = paramObj->GetProperty("shadow");
769     Shadow shadow;
770     if ((shadowValue->IsObject() || shadowValue->IsNumber()) && JSViewAbstract::ParseShadowProps(shadowValue, shadow)) {
771         properties.shadow = shadow;
772     }
773 
774     properties.customStyle = false;
775     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TWELVE)) {
776         properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
777         NG::BorderRadiusProperty dialogRadius;
778         dialogRadius.SetRadius(calendarTheme->GetDialogBorderRadius());
779         properties.borderRadius = dialogRadius;
780     }
781     JSViewAbstract::SetDialogHoverModeProperties(paramObj, properties);
782 
783     auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
784     auto overlayManager = context ? context->GetOverlayManager() : nullptr;
785     executor->PostTask(
786         [properties, settingData, dialogEvent, dialogCancelEvent, dialogLifeCycleEvent, buttonInfos,
787             weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
788             auto overlayManager = weak.Upgrade();
789             CHECK_NULL_VOID(overlayManager);
790             overlayManager->ShowCalendarDialog(
791                 properties, settingData, dialogEvent, dialogCancelEvent, dialogLifeCycleEvent, buttonInfos);
792         },
793         TaskExecutor::TaskType::UI, "ArkUIDialogShowCalendarPicker");
794 }
795 } // namespace OHOS::Ace::Framework
796