@yuuvis/client-framework 3.19.0 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/yuuvis-client-framework-datepicker.mjs +90 -34
- package/fesm2022/yuuvis-client-framework-datepicker.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-forms.mjs +14 -5
- package/fesm2022/yuuvis-client-framework-forms.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-details.mjs +2 -1
- package/fesm2022/yuuvis-client-framework-object-details.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-flavor.mjs +2 -1
- package/fesm2022/yuuvis-client-framework-object-flavor.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-form.mjs +3 -1
- package/fesm2022/yuuvis-client-framework-object-form.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs +9 -3
- package/fesm2022/yuuvis-client-framework-object-relationship.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-summary.mjs +4 -1
- package/fesm2022/yuuvis-client-framework-object-summary.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-renderer.mjs +22 -11
- package/fesm2022/yuuvis-client-framework-renderer.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-smart-search.mjs +54 -14
- package/fesm2022/yuuvis-client-framework-smart-search.mjs.map +1 -1
- package/lib/assets/i18n/en.json +4 -4
- package/package.json +6 -6
- package/types/yuuvis-client-framework-datepicker.d.ts +16 -1
- package/types/yuuvis-client-framework-object-form.d.ts +6 -0
- package/types/yuuvis-client-framework-renderer.d.ts +17 -4
- package/types/yuuvis-client-framework-smart-search.d.ts +29 -6
|
@@ -395,7 +395,11 @@ class DateInputElementComponent {
|
|
|
395
395
|
this.disabled = isDisabled;
|
|
396
396
|
}
|
|
397
397
|
_isValidInput(v) {
|
|
398
|
-
|
|
398
|
+
// digits only - parseInt alone would accept trailing garbage like '1x' and
|
|
399
|
+
// silently reduce it to 1
|
|
400
|
+
if (!/^\d+$/.test(`${v ?? ''}`))
|
|
401
|
+
return false;
|
|
402
|
+
const n = parseInt(`${v}`, 10);
|
|
399
403
|
return this._isNumber(n) && (!this.maxValue || n <= this.maxValue) && (!this.minValue || n >= this.minValue);
|
|
400
404
|
}
|
|
401
405
|
validate(c) {
|
|
@@ -1067,6 +1071,7 @@ class DateInputComponent {
|
|
|
1067
1071
|
this.date = null;
|
|
1068
1072
|
this._disabled = false;
|
|
1069
1073
|
this.invalid = signal(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
1074
|
+
this._dateInvalid = false;
|
|
1070
1075
|
this._refreshPlaceholder = true;
|
|
1071
1076
|
this._locale = this.datepickerService.DEFAULT_LANGUAGE;
|
|
1072
1077
|
this._withTime = false;
|
|
@@ -1117,12 +1122,20 @@ class DateInputComponent {
|
|
|
1117
1122
|
}
|
|
1118
1123
|
});
|
|
1119
1124
|
this._formStateSub = this.dateInputForm.statusChanges.subscribe({
|
|
1120
|
-
next: (
|
|
1121
|
-
this.
|
|
1125
|
+
next: () => {
|
|
1126
|
+
this._updateInvalidState();
|
|
1122
1127
|
}
|
|
1123
1128
|
});
|
|
1124
1129
|
}
|
|
1125
|
-
|
|
1130
|
+
/**
|
|
1131
|
+
* Reflects both sources of invalidity: a structurally incomplete/invalid form
|
|
1132
|
+
* (missing or out of range parts) and input that does not describe a real
|
|
1133
|
+
* calendar date (e.g. 31st of February). Both have to be taken into account
|
|
1134
|
+
* here because the form group may well be VALID while the entered parts still
|
|
1135
|
+
* do not add up to an existing date.
|
|
1136
|
+
*/
|
|
1137
|
+
_updateInvalidState() {
|
|
1138
|
+
const isInvalid = this.dateInputForm?.status === 'INVALID' || this._dateInvalid;
|
|
1126
1139
|
this.invalid.set(isInvalid);
|
|
1127
1140
|
if (isInvalid) {
|
|
1128
1141
|
this.datepickerService.setErrors({
|
|
@@ -1134,39 +1147,78 @@ class DateInputComponent {
|
|
|
1134
1147
|
}
|
|
1135
1148
|
}
|
|
1136
1149
|
_checkAndPropagateForm() {
|
|
1137
|
-
if (this.dateInputForm?.status
|
|
1138
|
-
|
|
1139
|
-
|
|
1150
|
+
if (this.dateInputForm?.status !== 'VALID') {
|
|
1151
|
+
// incomplete or out of range input, the status subscription reports the error
|
|
1152
|
+
return;
|
|
1140
1153
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
let hour = strHour?.length ? parseInt(strHour) : -1;
|
|
1149
|
-
if (this.hour12) {
|
|
1150
|
-
hour = this.currentDayPeriod === 'pm' && hour < 12 ? hour + 12 : hour;
|
|
1151
|
-
hour = this.currentDayPeriod === 'am' && hour === 12 ? 0 : hour;
|
|
1152
|
-
hour = hour === 24 ? 0 : hour;
|
|
1153
|
-
}
|
|
1154
|
-
dateString += `T${hour < 10 ? `0${hour}` : `${hour}`}:${g.controls['minute'].value}:00`;
|
|
1154
|
+
const parsed = this._formToDate(this.dateInputForm);
|
|
1155
|
+
if (!parsed.empty && !parsed.date) {
|
|
1156
|
+
// input that does not describe an existing date (e.g. 31.02.). Keep what the user
|
|
1157
|
+
// typed so it can be corrected, but do not propagate a silently shifted date
|
|
1158
|
+
this._dateInvalid = true;
|
|
1159
|
+
this._updateInvalidState();
|
|
1160
|
+
return;
|
|
1155
1161
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1162
|
+
this._dateInvalid = false;
|
|
1163
|
+
this.date = parsed.date;
|
|
1164
|
+
this.datepickerService.setValue(this.date);
|
|
1165
|
+
this._updateInvalidState();
|
|
1166
|
+
}
|
|
1167
|
+
_formToDate(group) {
|
|
1168
|
+
const keys = this.withTime ? ['day', 'month', 'year', 'hour', 'minute'] : ['day', 'month', 'year'];
|
|
1169
|
+
if (keys.every((key) => !`${group.controls[key].value ?? ''}`.length))
|
|
1170
|
+
return { date: null, empty: true };
|
|
1171
|
+
const invalid = { date: null, empty: false };
|
|
1172
|
+
const day = this._toInt(group.controls['day'].value);
|
|
1173
|
+
const month = this._toInt(group.controls['month'].value);
|
|
1174
|
+
// the year may be negative because of a locale year offset (e.g. locale 'th')
|
|
1175
|
+
const year = this._toInt(group.controls['year'].value, true);
|
|
1176
|
+
if (day === null || month === null || year === null)
|
|
1177
|
+
return invalid;
|
|
1178
|
+
if (month < 1 || month > 12 || day < 1 || day > this._daysInMonth(year, month))
|
|
1179
|
+
return invalid;
|
|
1180
|
+
const time = this._formToTime(group);
|
|
1181
|
+
if (!time)
|
|
1182
|
+
return invalid;
|
|
1183
|
+
// setFullYear with all three arguments assigns them at once, so no intermediate
|
|
1184
|
+
// rollover can happen, and it also covers years outside the parsable range
|
|
1185
|
+
const date = new Date(0);
|
|
1186
|
+
date.setFullYear(year, month - 1, day);
|
|
1187
|
+
date.setHours(time.hour, time.minute, 0, 0);
|
|
1188
|
+
// guard against any remaining shift (e.g. a local time skipped by a DST change)
|
|
1189
|
+
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day
|
|
1190
|
+
? { date, empty: false }
|
|
1191
|
+
: invalid;
|
|
1192
|
+
}
|
|
1193
|
+
_formToTime(group) {
|
|
1194
|
+
if (!this.withTime)
|
|
1195
|
+
return { hour: 0, minute: 0 };
|
|
1196
|
+
let hour = this._toInt(group.controls['hour'].value);
|
|
1197
|
+
const minute = this._toInt(group.controls['minute'].value);
|
|
1198
|
+
if (hour === null || minute === null)
|
|
1199
|
+
return null;
|
|
1200
|
+
if (this.hour12) {
|
|
1201
|
+
hour = this.currentDayPeriod === 'pm' && hour < 12 ? hour + 12 : hour;
|
|
1202
|
+
hour = this.currentDayPeriod === 'am' && hour === 12 ? 0 : hour;
|
|
1203
|
+
hour = hour === 24 ? 0 : hour;
|
|
1167
1204
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1205
|
+
return hour > 23 || minute > 59 ? null : { hour, minute };
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Strict integer parse - unlike parseInt it rejects values with trailing
|
|
1209
|
+
* garbage ('1x') so those cannot slip through as a valid date part.
|
|
1210
|
+
*/
|
|
1211
|
+
_toInt(value, allowNegative = false) {
|
|
1212
|
+
const str = `${value ?? ''}`;
|
|
1213
|
+
if (!(allowNegative ? /^-?\d+$/ : /^\d+$/).test(str))
|
|
1214
|
+
return null;
|
|
1215
|
+
return parseInt(str, 10);
|
|
1216
|
+
}
|
|
1217
|
+
_daysInMonth(year, month) {
|
|
1218
|
+
// day 0 of the following month is the last day of the given month
|
|
1219
|
+
const last = new Date(0);
|
|
1220
|
+
last.setFullYear(year, month, 0);
|
|
1221
|
+
return last.getDate();
|
|
1170
1222
|
}
|
|
1171
1223
|
_focusInput(offset) {
|
|
1172
1224
|
const inputs = this._getFocusableElements();
|
|
@@ -1291,6 +1343,10 @@ class DateInputComponent {
|
|
|
1291
1343
|
this.dateInputForm.patchValue(this._getPatch(vc.value), {
|
|
1292
1344
|
emitEvent: false
|
|
1293
1345
|
});
|
|
1346
|
+
// the value comes from the outside (calendar, model), so any previously
|
|
1347
|
+
// entered non existing date is gone now
|
|
1348
|
+
this._dateInvalid = false;
|
|
1349
|
+
this._updateInvalidState();
|
|
1294
1350
|
// this.dateInputForm?.updateValueAndValidity()
|
|
1295
1351
|
}
|
|
1296
1352
|
}));
|