@yuuvis/client-framework 3.11.1 → 3.12.0

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.
@@ -6,7 +6,7 @@ import { FormControl, Validators, ReactiveFormsModule, FormBuilder, NG_VALUE_ACC
6
6
  import { MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field';
7
7
  import * as i1 from '@angular/material/select';
8
8
  import { MatSelectModule } from '@angular/material/select';
9
- import { SystemService, TranslateService, Classification, TranslatePipe, Situation, Operator, OperatorLabel, Utils, CatalogService, LocaleNumberPipe, FileSizePipe, IdmService, UserService, SearchUtils, LocaleDatePipe, ClassificationPrefix } from '@yuuvis/client-core';
9
+ import { SystemService, TranslateService, Classification, TranslatePipe, Situation, Operator, OperatorLabel, Utils, CatalogService, LocaleNumberPipe, FileSizePipe, IdmService, UserService, SearchUtils, LocaleDatePipe, SearchService, DmsService, ObjectConfigService, DmsObject, BaseObjectTypeField, ClassificationPrefix } from '@yuuvis/client-core';
10
10
  import { AbstractMatFormField, injectNgControl, FormTranslateService, DialogComponent, ScrollButtonsComponent } from '@yuuvis/client-framework/common';
11
11
  import * as i1$3 from '@angular/common';
12
12
  import { NgClass, CommonModule } from '@angular/common';
@@ -33,6 +33,7 @@ import { DatepickerComponent, YuvDatepickerModule } from '@yuuvis/client-framewo
33
33
  import * as i2$2 from '@yuuvis/client-framework/autocomplete';
34
34
  import { YuvAutocompleteModule } from '@yuuvis/client-framework/autocomplete';
35
35
  import { map as map$1, catchError } from 'rxjs/operators';
36
+ import { ShellService } from '@yuuvis/client-shell-core';
36
37
  import { ENTER, COMMA } from '@angular/cdk/keycodes';
37
38
  import * as i1$4 from '@angular/material/chips';
38
39
  import { MatChipsModule } from '@angular/material/chips';
@@ -1049,7 +1050,6 @@ class NumberComponent extends AbstractMatFormField {
1049
1050
  this.#dRef = inject(DestroyRef);
1050
1051
  this.translate = inject(TranslateService);
1051
1052
  // innerValue: string | null = null;
1052
- this.validationErrors = [];
1053
1053
  this.transformPipe = new LocaleNumberPipe(this.translate);
1054
1054
  this.innerCtrl = new FormControl(null);
1055
1055
  this.ngControl = injectNgControl(this);
@@ -1130,27 +1130,54 @@ class NumberComponent extends AbstractMatFormField {
1130
1130
  }
1131
1131
  // called when the input looses focus
1132
1132
  format() {
1133
- if (!this.readonly() && typeof this.value === 'number' && this.validationErrors.length === 0) {
1134
- this.innerCtrl.patchValue(this.transformPipe.numberToString(this.value, this.#grouping(), this.groupPattern(), this.#scale()), { emitEvent: false });
1133
+ // The blur handler runs before Angular's value accessor marks the control as
1134
+ // touched, so mark it explicitly to make sure the error becomes visible.
1135
+ this.innerCtrl.markAsTouched();
1136
+ if (!this.readonly() && typeof this.value === 'number' && this.innerCtrl.valid) {
1137
+ const formatted = this.transformPipe.numberToString(this.value, this.#grouping(), this.groupPattern(), this.#scale());
1138
+ this.innerCtrl.patchValue(formatted, { emitEvent: false });
1135
1139
  }
1140
+ this.#syncErrorState();
1136
1141
  }
1137
1142
  // called when the input get focus
1138
1143
  unformat() {
1139
- if (!this.readonly() && typeof this.value === 'number' && this.validationErrors.length === 0) {
1144
+ if (!this.readonly() && typeof this.value === 'number' && this.innerCtrl.valid) {
1140
1145
  this.innerCtrl.patchValue(this.transformPipe.transform(this.value, false), {
1141
1146
  emitEvent: false
1142
1147
  });
1143
1148
  }
1144
1149
  }
1150
+ /**
1151
+ * Mirrors the inner control's validity onto the component's `errorState` and the
1152
+ * hosting `ngControl`. Invoked from the `statusChanges` subscription and from
1153
+ * `format()` (blur): blur re-formats via an `emitEvent: false` patch, which does
1154
+ * not fire `statusChanges`, so the error state has to be refreshed by hand there.
1155
+ */
1156
+ #syncErrorState() {
1157
+ this.errorState = FormUtils.getErrorState(this.innerCtrl);
1158
+ if (this.ngControl?.control) {
1159
+ this.ngControl.control.setErrors(this.innerCtrl.errors);
1160
+ }
1161
+ }
1162
+ /** Updates the model value and notifies the form, but only when it actually changed. */
1163
+ #setValue(val) {
1164
+ if (this.value !== val) {
1165
+ this.value = val;
1166
+ this.propagateChange(this.value);
1167
+ }
1168
+ }
1145
1169
  #getValidator() {
1146
1170
  return (control) => {
1147
- this.validationErrors = [];
1171
+ // Pure validator: derive errors from the control value only, never mutate
1172
+ // component state here. State written as a side-effect gets out of sync
1173
+ // with the displayed value (validators also run on `emitEvent: false` writes).
1174
+ const errors = [];
1148
1175
  if (Utils.isEmpty(control.value))
1149
1176
  return null;
1150
1177
  const val = this.transformPipe.stringToNumber(control.value);
1151
1178
  // general number validation
1152
1179
  if (isNaN(val) || typeof val !== 'number') {
1153
- this.validationErrors.push({ key: 'number' });
1180
+ errors.push({ key: 'number' });
1154
1181
  }
1155
1182
  else {
1156
1183
  const split = control.value.replaceAll(this.transformPipe.separator, '').split(this.transformPipe.decimalSeparator);
@@ -1158,28 +1185,27 @@ class NumberComponent extends AbstractMatFormField {
1158
1185
  if (this.#precision() !== undefined) {
1159
1186
  const prePointDigits = this.#precision() - this.#scale();
1160
1187
  if (split[0]?.length > prePointDigits) {
1161
- this.validationErrors.push({ key: 'precision', params: { prePointDigits } });
1188
+ errors.push({ key: 'precision', params: { prePointDigits } });
1162
1189
  }
1163
1190
  }
1164
1191
  // check scale
1165
1192
  if (this.#scale() && split[1]?.length > this.#scale()) {
1166
- this.validationErrors.push({ key: 'scale', params: { scale: this.#scale() } });
1193
+ errors.push({ key: 'scale', params: { scale: this.#scale() } });
1167
1194
  }
1168
1195
  // min max
1169
1196
  if (!_a.betweenTwoNumbers(val, this.minValue(), this.maxValue())) {
1170
1197
  if (Utils.isEmpty(this.minValue())) {
1171
- this.validationErrors.push({ key: 'maxvalue', params: { maxValue: this.maxValue() } });
1198
+ errors.push({ key: 'maxvalue', params: { maxValue: this.maxValue() } });
1172
1199
  }
1173
1200
  else if (Utils.isEmpty(this.maxValue())) {
1174
- this.validationErrors.push({ key: 'minvalue', params: { minValue: this.minValue() } });
1201
+ errors.push({ key: 'minvalue', params: { minValue: this.minValue() } });
1175
1202
  }
1176
1203
  else {
1177
- this.validationErrors.push({ key: 'minmax', params: { minValue: this.minValue(), maxValue: this.maxValue() } });
1204
+ errors.push({ key: 'minmax', params: { minValue: this.minValue(), maxValue: this.maxValue() } });
1178
1205
  }
1179
1206
  }
1180
1207
  }
1181
- const e = Utils.arrayToObject(this.validationErrors, 'key', (err) => ({ valid: false, ...err }));
1182
- return this.validationErrors.length ? e : null;
1208
+ return errors.length ? Utils.arrayToObject(errors, 'key', (err) => ({ valid: false, ...err })) : null;
1183
1209
  };
1184
1210
  }
1185
1211
  ngOnInit() {
@@ -1188,25 +1214,20 @@ class NumberComponent extends AbstractMatFormField {
1188
1214
  if (this.required)
1189
1215
  validators.push(Validators.required);
1190
1216
  this.innerCtrl.setValidators(validators);
1191
- this.innerCtrl.statusChanges.pipe(takeUntilDestroyed(this.#dRef)).subscribe((v) => {
1192
- this.errorState = FormUtils.getErrorState(this.innerCtrl);
1193
- if (this.ngControl?.control) {
1194
- this.ngControl.control.setErrors(this.innerCtrl.errors);
1195
- }
1196
- });
1217
+ this.innerCtrl.statusChanges.pipe(takeUntilDestroyed(this.#dRef)).subscribe(() => this.#syncErrorState());
1197
1218
  this.innerCtrl.updateValueAndValidity();
1198
1219
  this.innerCtrl.valueChanges.pipe(takeUntilDestroyed(this.#dRef)).subscribe((v) => {
1199
1220
  if (Utils.isEmpty(v)) {
1200
- this.value = null;
1201
- this.propagateChange(this.value);
1221
+ this.#setValue(null);
1202
1222
  }
1203
- else {
1223
+ else if (this.innerCtrl.valid) {
1204
1224
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1205
- const val = this.transformPipe.stringToNumber(v);
1206
- if (this.validationErrors.length === 0 && this.value !== val) {
1207
- this.value = val;
1208
- this.propagateChange(this.value);
1209
- }
1225
+ this.#setValue(this.transformPipe.stringToNumber(v));
1226
+ }
1227
+ else {
1228
+ // Invalid input: do not keep the previous value silently in the background —
1229
+ // clear the model so the form value matches what the user sees (an error).
1230
+ this.#setValue(null);
1210
1231
  }
1211
1232
  });
1212
1233
  }
@@ -2116,6 +2137,271 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2116
2137
  args: [{ selector: 'yuv-range-select-filesize', standalone: true, imports: [MatSelectModule, ReactiveFormsModule], providers: [{ provide: MatFormFieldControl, useExisting: RangeSelectFilesizeComponent }], template: "<mat-select [panelWidth]=\"null\" [hideSingleSelectionIndicator]=\"true\" [formControl]=\"fc\">\n @for (o of options(); track $index) {\n <mat-option [value]=\"o.value\">{{ o.label }}</mat-option>\n }\n</mat-select>\n", styles: [":host{display:flex}:host mat-select{flex:1}\n"] }]
2117
2138
  }], ctorParameters: () => [], propDecorators: { ranges: [{ type: i0.Input, args: [{ isSignal: true, alias: "ranges", required: false }] }] } });
2118
2139
 
2140
+ /** Escape single quotes and backslashes for safe embedding in a CMIS statement. */
2141
+ function escapeCmis(value) {
2142
+ return value.replace(/'/g, "''").replace(/\\/g, '\\\\');
2143
+ }
2144
+ /**
2145
+ * Form input for references to other dms objects. As the user types, a CMIS
2146
+ * fulltext search (narrowed to the allowed target types from the field's
2147
+ * `id:reference[...]` classification) provides suggestions. The selected object
2148
+ * id(s) are stored; on load they are resolved back to display chips.
2149
+ *
2150
+ * ## Label retrieval
2151
+ *
2152
+ * The chip label shown for a referenced object is its resolved object-config
2153
+ * title, not a raw property. Both search hits and objects resolved on load are
2154
+ * turned into a {@link ReferenceEntry} by `#toEntryFromDms`, which asks
2155
+ * {@link ObjectConfigService.getResolvedObjectConfig} for the title of the
2156
+ * object's data. The tricky part is picking the right config: reference targets
2157
+ * are usually secondary object types (SOTs) applied to a generic primary type,
2158
+ * and the title-defining config is keyed by the SOT's virtual config id (colons
2159
+ * replaced by dots, e.g. `appCasem.caseDocument`) inside the owning app's
2160
+ * bucket — not under the primary object type id. `#resolveConfigType` therefore
2161
+ * probes each allowed target type the hit actually carries (as primary type or
2162
+ * SOT), trying the dotted and raw config ids, and uses the first bucket that
2163
+ * defines one; it falls back to the primary object type's default config, and
2164
+ * ultimately to the raw object id when no title can be resolved. Objects that
2165
+ * cannot be fetched at all (e.g. deleted or access-denied) become `notFound`
2166
+ * entries labelled with their id.
2167
+ *
2168
+ * ## Opening a reference entry
2169
+ *
2170
+ * A chip can be opened only when a loaded feature app claims responsibility for
2171
+ * the referenced object's type. `#openAppId` maps the entry's `objectTypeId` to
2172
+ * an owning app id via {@link ObjectConfigService.getAppIdForType}, and
2173
+ * `canOpen` gates the action on {@link ShellService.canOpenObjectFor} (and on the
2174
+ * entry not being `notFound`) — this drives whether the chip is rendered as an
2175
+ * openable link. Invoking `open` delegates to {@link ShellService.openObject},
2176
+ * passing the app id and the entry's cached `data` so the app can route to /
2177
+ * display the object without an extra fetch.
2178
+ *
2179
+ * @example
2180
+ * <yuv-reference [multiselect]="true" [classifications]="['id:reference[appCasem:correctionDocument]']" />
2181
+ */
2182
+ class ReferenceComponent extends AbstractMatFormField {
2183
+ constructor() {
2184
+ super(...arguments);
2185
+ this.#system = inject(SystemService);
2186
+ this.#search = inject(SearchService);
2187
+ this.#dms = inject(DmsService);
2188
+ this.#objectConfig = inject(ObjectConfigService);
2189
+ this.#shell = inject(ShellService);
2190
+ this.#dRef = inject(DestroyRef);
2191
+ this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
2192
+ this.acFormControl = new FormControl(undefined);
2193
+ this.ngControl = injectNgControl(this);
2194
+ this._innerValue = [];
2195
+ this.autocompleteRes = [];
2196
+ /**
2197
+ * Possibles values are `EDIT` (default),`SEARCH`,`CREATE`. In search situation validation of the form element will be turned off, so you are able to enter search terms that do not meet the elements validators.
2198
+ */
2199
+ this.situation = input(undefined, ...(ngDevMode ? [{ debugName: "situation" }] : /* istanbul ignore next */ []));
2200
+ /**
2201
+ * Indicator that multiple references could be selected, they will be rendered as chips (default: false).
2202
+ */
2203
+ this.multiselect = input(false, ...(ngDevMode ? [{ debugName: "multiselect" }] : /* istanbul ignore next */ []));
2204
+ /**
2205
+ * Additional semantics for the form element. The `id:reference[...]` classification
2206
+ * restricts the suggestions to the target types listed in the brackets.
2207
+ */
2208
+ this.classifications = input(undefined, ...(ngDevMode ? [{ debugName: "classifications" }] : /* istanbul ignore next */ []));
2209
+ /**
2210
+ * Will prevent the input from being changed (default: false)
2211
+ */
2212
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
2213
+ /**
2214
+ * Minimal number of characters to trigger the search (default: 2)
2215
+ */
2216
+ this.minChars = input(2, ...(ngDevMode ? [{ debugName: "minChars" }] : /* istanbul ignore next */ []));
2217
+ /**
2218
+ * Maximal number of suggestions for the given search term (default: 10)
2219
+ */
2220
+ this.maxSuggestions = input(10, ...(ngDevMode ? [{ debugName: "maxSuggestions" }] : /* istanbul ignore next */ []));
2221
+ /** Allowed target object/secondary object types parsed from the classification. */
2222
+ this.#allowedTargetTypes = computed(() => {
2223
+ const c = this.classifications();
2224
+ return c?.length ? this.#system.getClassifications(c).get(Classification.STRING_REFERENCE)?.options ?? [] : [];
2225
+ }, ...(ngDevMode ? [{ debugName: "#allowedTargetTypes" }] : /* istanbul ignore next */ []));
2226
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2227
+ this.propagateChange = (_) => { };
2228
+ }
2229
+ #system;
2230
+ #search;
2231
+ #dms;
2232
+ #objectConfig;
2233
+ #shell;
2234
+ #dRef;
2235
+ set innerValue(iv) {
2236
+ this._innerValue = iv || [];
2237
+ }
2238
+ get innerValue() {
2239
+ return this._innerValue;
2240
+ }
2241
+ /** Allowed target object/secondary object types parsed from the classification. */
2242
+ #allowedTargetTypes;
2243
+ writeValue(value) {
2244
+ this.value = value;
2245
+ const ids = value ? (Array.isArray(value) ? value : [value]) : [];
2246
+ if (ids.length) {
2247
+ this.resolveFn(ids);
2248
+ }
2249
+ else {
2250
+ this.value = null;
2251
+ this.innerValue = [];
2252
+ this.acFormControl.setValue([], { emitEvent: false });
2253
+ }
2254
+ }
2255
+ registerOnChange(fn) {
2256
+ this.propagateChange = fn;
2257
+ }
2258
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2259
+ registerOnTouched(fn) { }
2260
+ setDisabledState(isDisabled) {
2261
+ if (isDisabled) {
2262
+ this.acFormControl.disable();
2263
+ }
2264
+ else {
2265
+ this.acFormControl.enable();
2266
+ }
2267
+ this.disabled = isDisabled;
2268
+ }
2269
+ propagate() {
2270
+ this.value = this.multiselect() ? this.innerValue.map((v) => v.id) : this.innerValue[0]?.id;
2271
+ this.propagateChange(this.value);
2272
+ }
2273
+ /** Resolve stored object ids back to display entries. */
2274
+ resolveFn(ids) {
2275
+ this.#dms
2276
+ .getDmsObjects(ids)
2277
+ .pipe(catchError(() => of([])))
2278
+ .subscribe((objs) => {
2279
+ const byId = new Map(objs.map((o) => [o.id, o]));
2280
+ const entries = ids.map((id) => {
2281
+ const o = byId.get(id);
2282
+ return o instanceof DmsObject
2283
+ ? this.#toEntryFromDms(o)
2284
+ : { id, objectTypeId: '', title: id, data: {}, notFound: true };
2285
+ });
2286
+ this.#updateAutocompleteControl(entries);
2287
+ });
2288
+ }
2289
+ #updateAutocompleteControl(data) {
2290
+ this.innerValue = data;
2291
+ const mapped = data.map((n) => ({ label: n.title, value: n }));
2292
+ this.acFormControl.setValue(this.multiselect() ? mapped : mapped.slice(0, 1), { emitEvent: false });
2293
+ this.acFormControl.updateValueAndValidity({ emitEvent: false });
2294
+ }
2295
+ autocompleteFn(query) {
2296
+ if (query.length < this.minChars()) {
2297
+ this.autocompleteRes = [];
2298
+ return;
2299
+ }
2300
+ const types = this.#allowedTargetTypes();
2301
+ // A reference target type may be a hit's primary object type OR be carried in its
2302
+ // `system:secondaryObjectTypeIds` (a floating SOT applied to an object of a different
2303
+ // primary type). Match both columns so hits are not missed in the latter case.
2304
+ const quoted = types.map((type) => `'${escapeCmis(type)}'`).join(', ');
2305
+ const typeCond = types.length
2306
+ ? `objectTypeId IN (${quoted}) OR ${BaseObjectTypeField.SECONDARY_OBJECT_TYPE_IDS} IN (${quoted})`
2307
+ : '';
2308
+ const where = [typeCond ? `(${typeCond})` : '', `CONTAINS('${escapeCmis(query)}*')`].filter(Boolean).join(' AND ');
2309
+ const statement = `SELECT * FROM system:object WHERE ${where}`;
2310
+ this.busy.set(true);
2311
+ this.#search
2312
+ .searchCmis(statement, this.maxSuggestions())
2313
+ .pipe(catchError(() => of({ items: [] })))
2314
+ .subscribe((res) => {
2315
+ this.busy.set(false);
2316
+ this.autocompleteRes = res.items
2317
+ .map((i) => this.#toEntry(i))
2318
+ .filter((e) => !this.innerValue.some((v) => v.id === e.id))
2319
+ .map((e) => ({ label: e.title, value: e }));
2320
+ });
2321
+ }
2322
+ #toEntry(item) {
2323
+ return this.#toEntryFromDms(new DmsObject(item));
2324
+ }
2325
+ #toEntryFromDms(dms) {
2326
+ const { type, bucket } = this.#resolveConfigType(dms);
2327
+ const roc = this.#objectConfig.getResolvedObjectConfig(dms.data, type, bucket);
2328
+ return {
2329
+ id: dms.id,
2330
+ objectTypeId: dms.objectTypeId,
2331
+ title: roc?.title?.value || dms.id,
2332
+ data: dms.data
2333
+ };
2334
+ }
2335
+ /**
2336
+ * Pick the object config type + bucket used to resolve a hit's display label.
2337
+ *
2338
+ * Reference targets are typically secondary object types (SOTs) applied to a
2339
+ * generic primary type (e.g. an `appCasem:caseDocument` SOT carried by a
2340
+ * `system:document`). The config that defines the display title is keyed by the
2341
+ * SOT's virtual config id — conventionally the SOT id with colons replaced by
2342
+ * dots (`appCasem.caseDocument`) — and stored in the owning app's bucket, never
2343
+ * under the primary object type id. Resolving by `dms.objectTypeId` alone
2344
+ * therefore misses it and the label falls back to the raw object id.
2345
+ *
2346
+ * For each allowed target type the hit actually carries (as its primary type or
2347
+ * a SOT) we probe the dotted and raw config ids and use the first that a bucket
2348
+ * defines. Falls back to the primary object type (default config) when nothing
2349
+ * matches.
2350
+ */
2351
+ #resolveConfigType(dms) {
2352
+ const carried = new Set([dms.objectTypeId, ...dms.sots]);
2353
+ const candidateIds = this.#allowedTargetTypes()
2354
+ .filter((t) => carried.has(t))
2355
+ .flatMap((t) => (t.includes(':') ? [t.replace(/:/g, '.'), t] : [t]));
2356
+ for (const id of candidateIds) {
2357
+ const bucket = this.#objectConfig.findBucketForConfigType(id);
2358
+ if (bucket)
2359
+ return { type: { id }, bucket };
2360
+ }
2361
+ return { type: { id: dms.objectTypeId } };
2362
+ }
2363
+ /** App id able to open the referenced object, if any. */
2364
+ #openAppId(entry) {
2365
+ return entry.objectTypeId ? this.#objectConfig.getAppIdForType(entry.objectTypeId) : undefined;
2366
+ }
2367
+ /** Whether the referenced object can be opened by a loaded app. */
2368
+ canOpen(entry) {
2369
+ return !entry.notFound && this.#shell.canOpenObjectFor(this.#openAppId(entry));
2370
+ }
2371
+ /** Open the referenced object via the owning app's registered open handler. */
2372
+ open(entry) {
2373
+ const appId = this.#openAppId(entry);
2374
+ if (appId && this.#shell.canOpenObjectFor(appId)) {
2375
+ this.#shell.openObject(appId, entry.data);
2376
+ }
2377
+ }
2378
+ ngOnInit() {
2379
+ if (this.required)
2380
+ this.acFormControl.setValidators(Validators.required);
2381
+ this.acFormControl.statusChanges.pipe(takeUntilDestroyed(this.#dRef)).subscribe(() => {
2382
+ this.errorState = FormUtils.getErrorState(this.acFormControl);
2383
+ if (this.ngControl?.control) {
2384
+ this.ngControl.control.setErrors(this.acFormControl.errors);
2385
+ }
2386
+ });
2387
+ this.acFormControl.updateValueAndValidity({ emitEvent: false });
2388
+ this.acFormControl.valueChanges.pipe(takeUntilDestroyed(this.#dRef)).subscribe((v) => {
2389
+ const items = Array.isArray(v) ? v : v ? [v] : [];
2390
+ this.innerValue = items.map((i) => i.value);
2391
+ this.propagate();
2392
+ });
2393
+ }
2394
+ ngOnDestroy() {
2395
+ super.onNgOnDestroy();
2396
+ }
2397
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ReferenceComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2398
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.12", type: ReferenceComponent, isStandalone: true, selector: "yuv-reference", inputs: { situation: { classPropertyName: "situation", publicName: "situation", isSignal: true, isRequired: false, transformFunction: null }, multiselect: { classPropertyName: "multiselect", publicName: "multiselect", isSignal: true, isRequired: false, transformFunction: null }, classifications: { classPropertyName: "classifications", publicName: "classifications", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, maxSuggestions: { classPropertyName: "maxSuggestions", publicName: "maxSuggestions", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: MatFormFieldControl, useExisting: ReferenceComponent }], usesInheritance: true, ngImport: i0, template: "<yuv-autocomplete\n [required]=\"required\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n #autocomplete\n [placeholder]=\"placeholder\"\n [disabled]=\"readonly()\"\n [autocompleteValues]=\"autocompleteRes\"\n [minLength]=\"minChars()\"\n [forceSelection]=\"true\"\n [distinctValues]=\"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 @if (item.value) {\n <span class=\"option\">{{ item.value.title }}</span>\n }\n </ng-template>\n\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n @if (item.value) {\n <span class=\"chip\" [class.notFound]=\"item.value.notFound\" [matTooltip]=\"item.value.title\">\n {{ item.value.title || '...' }}\n @if (canOpen(item.value)) {\n <mat-icon\n class=\"open ymt-icon--size-s\"\n (click)=\"open(item.value); $event.stopPropagation()\"\n [matTooltip]=\"'yuv.form.element.reference.open.title' | translate\"\n >\n open_in_new\n </mat-icon>\n }\n </span>\n }\n </ng-template>\n</yuv-autocomplete>\n\n<mat-icon class=\"ymt-icon--size-s\" [matTooltip]=\"'yuv.form.element.reference.classify.icon.title' | translate\">link</mat-icon>\n", styles: [":host{display:flex;align-items:center}:host .chip{display:inline-flex;align-items:center;gap:.25em}:host .chip .open{cursor:pointer;color:var(--ymt-text-color-subtle)}:host .chip .open:hover{color:var(--ymt-primary)}:host .chip.notFound{color:var(--ymt-on-danger-container);text-decoration:line-through}:host .chip.notFound:before{content:\"!\";display:inline-block;background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);border-radius:2px;padding-inline:.3em;text-decoration:none;margin-inline-end:.75em}:host yuv-autocomplete{flex:1}:host mat-icon{color:var(--ymt-text-color-subtle)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: YuvAutocompleteModule }, { kind: "component", type: i2$2.AutocompleteComponent, selector: "yuv-autocomplete", inputs: ["ariaLabel", "busy", "multiple", "distinctValues", "addOnBlur", "minLength", "maxItems", "forceSelection", "autocompleteValues"], outputs: ["autocompleteFnc", "acBlur"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2399
+ }
2400
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ReferenceComponent, decorators: [{
2401
+ type: Component,
2402
+ args: [{ selector: 'yuv-reference', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, YuvAutocompleteModule, MatIconModule, MatTooltipModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: MatFormFieldControl, useExisting: ReferenceComponent }], template: "<yuv-autocomplete\n [required]=\"required\"\n [busy]=\"busy()\"\n [formControl]=\"acFormControl\"\n #autocomplete\n [placeholder]=\"placeholder\"\n [disabled]=\"readonly()\"\n [autocompleteValues]=\"autocompleteRes\"\n [minLength]=\"minChars()\"\n [forceSelection]=\"true\"\n [distinctValues]=\"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 @if (item.value) {\n <span class=\"option\">{{ item.value.title }}</span>\n }\n </ng-template>\n\n <!-- template for chip -->\n <ng-template #chipTemplate let-item>\n @if (item.value) {\n <span class=\"chip\" [class.notFound]=\"item.value.notFound\" [matTooltip]=\"item.value.title\">\n {{ item.value.title || '...' }}\n @if (canOpen(item.value)) {\n <mat-icon\n class=\"open ymt-icon--size-s\"\n (click)=\"open(item.value); $event.stopPropagation()\"\n [matTooltip]=\"'yuv.form.element.reference.open.title' | translate\"\n >\n open_in_new\n </mat-icon>\n }\n </span>\n }\n </ng-template>\n</yuv-autocomplete>\n\n<mat-icon class=\"ymt-icon--size-s\" [matTooltip]=\"'yuv.form.element.reference.classify.icon.title' | translate\">link</mat-icon>\n", styles: [":host{display:flex;align-items:center}:host .chip{display:inline-flex;align-items:center;gap:.25em}:host .chip .open{cursor:pointer;color:var(--ymt-text-color-subtle)}:host .chip .open:hover{color:var(--ymt-primary)}:host .chip.notFound{color:var(--ymt-on-danger-container);text-decoration:line-through}:host .chip.notFound:before{content:\"!\";display:inline-block;background-color:var(--ymt-danger-container);color:var(--ymt-on-danger-container);border-radius:2px;padding-inline:.3em;text-decoration:none;margin-inline-end:.75em}:host yuv-autocomplete{flex:1}:host mat-icon{color:var(--ymt-text-color-subtle)}\n"] }]
2403
+ }], propDecorators: { situation: [{ type: i0.Input, args: [{ isSignal: true, alias: "situation", required: false }] }], multiselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiselect", required: false }] }], classifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "classifications", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], maxSuggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSuggestions", required: false }] }] } });
2404
+
2119
2405
  /**
2120
2406
  * Creates form input for strings. Based on the input values different kinds of inputs will be generated.
2121
2407
  *
@@ -2455,5 +2741,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2455
2741
  * Generated bundle index. Do not edit.
2456
2742
  */
2457
2743
 
2458
- export { CatalogComponent, DataGridComponent, DatetimeComponent, DatetimeRangeComponent, DynamicCatalogComponent, FormUtils, I18nCatalogComponent, NumberComponent, NumberRangeComponent, OrganizationComponent, OrganizationSetComponent, RangeSelectDateComponent, RangeSelectFilesizeComponent, StringComponent, YuvFormsModule };
2744
+ export { CatalogComponent, DataGridComponent, DatetimeComponent, DatetimeRangeComponent, DynamicCatalogComponent, FormUtils, I18nCatalogComponent, NumberComponent, NumberRangeComponent, OrganizationComponent, OrganizationSetComponent, RangeSelectDateComponent, RangeSelectFilesizeComponent, ReferenceComponent, StringComponent, YuvFormsModule };
2459
2745
  //# sourceMappingURL=yuuvis-client-framework-forms.mjs.map