@yuuvis/client-framework 3.22.0 → 3.22.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.
@@ -1062,6 +1062,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
1062
1062
  }], ctorParameters: () => [], propDecorators: { readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], propertyName: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyName", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], classifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "classifications", required: false }] }], situation: [{ type: i0.Input, args: [{ isSignal: true, alias: "situation", required: false }] }] } });
1063
1063
 
1064
1064
  var _a;
1065
+ /**
1066
+ * Splits a number into the digits of its mantissa and its decimal exponent, so that
1067
+ * digit counting stays correct for values JavaScript renders in exponential notation
1068
+ * (`1e-7`, `1e21`).
1069
+ */
1070
+ function splitDigits(value) {
1071
+ const [mantissa, exponent] = Math.abs(value).toString().split('e');
1072
+ const [intPart, fractionPart = ''] = mantissa.split('.');
1073
+ return { intPart, fractionPart, exponent: Number(exponent ?? 0) };
1074
+ }
1075
+ /** Decimal places of a number: `12.5` → 1, `12` → 0, `1e-7` → 7. */
1076
+ function decimalPlaces(value) {
1077
+ const { fractionPart, exponent } = splitDigits(value);
1078
+ return Math.max(0, fractionPart.length - exponent);
1079
+ }
1080
+ /** Digits before the decimal separator, sign excluded: `-1234.5` → 4, `0.5` → 1. */
1081
+ function integerDigits(value) {
1082
+ const { intPart, exponent } = splitDigits(value);
1083
+ return Math.max(1, intPart.length + exponent);
1084
+ }
1065
1085
  /**
1066
1086
  * Creates form input for number values.
1067
1087
  *
@@ -1207,16 +1227,16 @@ class NumberComponent extends AbstractMatFormField {
1207
1227
  errors.push({ key: 'number' });
1208
1228
  }
1209
1229
  else {
1210
- const split = control.value.replaceAll(this.transformPipe.separator, '').split(this.transformPipe.decimalSeparator);
1230
+ // Digits are counted on the parsed value, never on the typed text: the text
1231
+ // carries locale separators, a sign and — for the filesize classification —
1232
+ // a unit ("1.5 MB"), none of which say anything about the value being stored.
1211
1233
  // check precision
1212
- if (this.#precision() !== undefined) {
1213
- const prePointDigits = this.#precision() - this.#scale();
1214
- if (split[0]?.length > prePointDigits) {
1215
- errors.push({ key: 'precision', params: { prePointDigits } });
1216
- }
1234
+ const prePointDigits = this.#precision() - this.#scale();
1235
+ if (integerDigits(val) > prePointDigits) {
1236
+ errors.push({ key: 'precision', params: { prePointDigits } });
1217
1237
  }
1218
- // check scale
1219
- if (this.#scale() && split[1]?.length > this.#scale()) {
1238
+ // check scale: a scale of 0 (set explicitly or left unset) allows no decimals at all
1239
+ if (decimalPlaces(val) > this.#scale()) {
1220
1240
  errors.push({ key: 'scale', params: { scale: this.#scale() } });
1221
1241
  }
1222
1242
  // min max
@@ -1632,7 +1652,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
1632
1652
  args: [{ selector: 'yuv-organization-set', imports: [FormsModule, YuvAutocompleteModule, MatTooltipModule, MatIconModule, ReactiveFormsModule, TranslatePipe$1], providers: [{ provide: MatFormFieldControl, useExisting: OrganizationSetComponent }], template: "<yuv-autocomplete\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n #autocomplete\n [placeholder]=\"placeholder\"\n [disabled]=\"readonly()\"\n [autocompleteValues]=\"autocompleteRes\"\n [forceSelection]=\"true\"\n (autocompleteFnc)=\"autocompleteFn($event)\"\n [multiple]=\"true\"\n [maxItems]=\"multiselect() ? -1 : 1\"\n>\n <!-- template for item inside the dropdown -->\n <ng-template #optionTemplate let-item>\n <span class=\"chip\">{{ item.value.title }}</span>\n </ng-template>\n\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n <span class=\"chip\">\n {{ item.value.title || item.label || '...' }}\n </span>\n </ng-template>\n</yuv-autocomplete>\n\n<mat-icon class=\"ymt-icon--size-s\" [matTooltip]=\"'yuv.form.element.organization-set.classify.icon.title' | translate\">\n {{ multiselect() ? 'group' : 'person' }}\n</mat-icon>\n", styles: [":host{display:flex;align-items:center}:host yuv-autocomplete{flex:1}:host mat-icon{color:var(--ymt-text-color-subtle)}\n"] }]
1633
1653
  }], propDecorators: { situation: [{ type: i0.Input, args: [{ isSignal: true, alias: "situation", required: false }] }], multiselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiselect", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], withMetadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "withMetadata", required: false }] }], autocompleteMinLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocompleteMinLength", required: false }] }], classifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "classifications", required: false }] }], types: [{ type: i0.Input, args: [{ isSignal: true, alias: "types", required: false }] }] } });
1634
1654
 
1635
- const mapOrganizationNode = (innerValue, excludeUserId) => map$1((nodes) => nodes
1655
+ /**
1656
+ * Maps IDM results to autocomplete items. The `user` / `role` discriminator is supplied by the
1657
+ * caller instead of being inferred from the node's shape: the organization set already carries
1658
+ * a correct `type`, and every result of `queryUser()` is a user by definition.
1659
+ */
1660
+ const mapOrganizationNode = (innerValue, nodeType, excludeUserId) => map$1((nodes) => nodes
1636
1661
  .filter((node) => !innerValue?.some((value) => value?.id === node.id))
1637
1662
  // `excludeMe` cannot be left to the backend: `/idm/search` has no such parameter at all,
1638
1663
  // and `/idm/users?excludeMe=true` is not guaranteed to honour it. Dropping the current
@@ -1644,7 +1669,7 @@ const mapOrganizationNode = (innerValue, excludeUserId) => map$1((nodes) => node
1644
1669
  value: {
1645
1670
  id: node.id,
1646
1671
  title: node.title,
1647
- type: 'user' in node ? 'user' : 'role'
1672
+ type: nodeType(node)
1648
1673
  }
1649
1674
  })));
1650
1675
  /**
@@ -1928,10 +1953,10 @@ class OrganizationComponent extends AbstractMatFormField {
1928
1953
  this.acFormControl.updateValueAndValidity({ emitEvent: false });
1929
1954
  }
1930
1955
  #resolveOrganization(query, excludeMe, filterRoles) {
1931
- return this.#userService.queryUser(query, excludeMe, filterRoles).pipe(mapOrganizationNode(this.innerValue, excludeMe ? this.#ownUserId() : undefined), catchError(() => of([])));
1956
+ return this.#userService.queryUser(query, excludeMe, filterRoles).pipe(mapOrganizationNode(this.innerValue, () => 'user', excludeMe ? this.#ownUserId() : undefined), catchError(() => of([])));
1932
1957
  }
1933
1958
  #resolveOrganizationSet(query, excludeMe, filterRoles) {
1934
- return this.#idmService.queryOrganizationEntity(query, filterRoles).pipe(mapOrganizationNode(this.innerValue, excludeMe ? this.#ownUserId() : undefined), catchError(() => of([])));
1959
+ return this.#idmService.queryOrganizationEntity(query, filterRoles).pipe(mapOrganizationNode(this.innerValue, (node) => node.type, excludeMe ? this.#ownUserId() : undefined), catchError(() => of([])));
1935
1960
  }
1936
1961
  /** Id of the signed-in user — the value {@link excludeMe} filters out of the suggestions. */
1937
1962
  #ownUserId() {