@yuuvis/client-framework 3.17.1 → 3.19.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.
@@ -92,6 +92,7 @@
92
92
  "yuv.object-metadata.tabs.indexdata.title": "Metadaten",
93
93
  "yuv.object-flavor.apply-flavor.button.apply": "Anwenden",
94
94
  "yuv.object-flavor.apply-flavor.button.cancel": "Abbrechen",
95
+ "yuv.object-flavor.apply-flavor.error": "Der Typ konnte nicht hinzugefügt werden.",
95
96
  "yuv.object-flavor.apply-flavor.headline": "Typ '{{name}}' hinzufügen",
96
97
  "yuv.object-flavor.button.add": "Typ hinzufügen",
97
98
  "yuv.object-flavor.button.clear-selection": "Auswahl aufheben",
@@ -92,6 +92,7 @@
92
92
  "yuv.object-metadata.tabs.indexdata.title": "Metadata",
93
93
  "yuv.object-flavor.apply-flavor.button.apply": "Apply",
94
94
  "yuv.object-flavor.apply-flavor.button.cancel": "Cancel",
95
+ "yuv.object-flavor.apply-flavor.error": "The type could not be added.",
95
96
  "yuv.object-flavor.apply-flavor.headline": "Add type '{{name}}'",
96
97
  "yuv.object-flavor.button.add": "Add type",
97
98
  "yuv.object-flavor.button.clear-selection": "Unselect",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuuvis/client-framework",
3
- "version": "3.17.1",
3
+ "version": "3.19.0",
4
4
  "author": "OPTIMAL SYSTEMS GmbH <npm@optimal-systems.de>",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {
@@ -8,16 +8,16 @@
8
8
  "@angular/common": "^21.2.19",
9
9
  "@angular/core": "^21.2.19",
10
10
  "angular-gridster2": "^21.0.1",
11
- "@yuuvis/client-core": "^3.17.1",
12
- "@yuuvis/client-shell-core": "^3.17.1",
13
- "@yuuvis/client-components": "^3.17.1",
11
+ "@yuuvis/client-core": "^3.19.0",
12
+ "@yuuvis/client-shell-core": "^3.19.0",
13
+ "@yuuvis/client-components": "^3.19.0",
14
14
  "ng-dynamic-component": "^10.8.2",
15
15
  "modern-normalize": "^3.0.1"
16
16
  },
17
17
  "dependencies": {
18
18
  "@angular/material": "^21.2.14",
19
19
  "@ngrx/signals": "^21.1.0",
20
- "@yuuvis/material": "^3.17.1",
20
+ "@yuuvis/material": "^3.19.0",
21
21
  "@yuuvis/media-viewer": "^4.0.0",
22
22
  "angular-split": "^20.0.0",
23
23
  "vis-network": "^10.0.2",
@@ -46,16 +46,66 @@ export class SearchComponent {
46
46
 
47
47
  ### Inputs
48
48
 
49
- | Input | Type | Default | Description |
50
- | ---------------- | ---------- | ------- | --------------------------------------------------------------------------------------------- |
51
- | `types` | `string[]` | `[]` | Object-type ids that may be searched. Set at least one to enable building blocks. |
52
- | `skipProperties` | `string[]` | `[]` | Field ids to hide from the field picker (e.g. internal/system properties). |
49
+ | Input | Type | Default | Description |
50
+ | -------------------------- | ---------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
51
+ | `types` | `string[]` | `[]` | Object-type ids that may be searched. Set at least one to enable building blocks. |
52
+ | `skipProperties` | `string[]` | `[]` | Field ids to hide from the field picker (e.g. internal/system properties). |
53
+ | `fulltextOnly` | `boolean` | `false` | Renders only the full-text bar — no condition builder. See [Full-text only](#full-text-only). |
54
+ | `supportDynamicConditions` | `boolean` | `false` | Enables marking conditions as *dynamic* and the form-mode toggle that swaps the builder for a fill-out form of those fields. |
53
55
 
54
56
  ### Outputs
55
57
 
56
- | Output | Type | Description |
57
- | ------------- | -------- | ------------------------------------------------------------------------------------------- |
58
- | `queryChange` | `string` | The current CMIS query. Emitted on every change; `''` means an empty search ("no query"). |
58
+ | Output | Type | Description |
59
+ | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
60
+ | `queryChange` | `string` | The current CMIS query. Emitted on **every** change; `''` means an empty search ("no query"). |
61
+ | `querySubmit` | `string` | The current CMIS query, emitted when the user presses <kbd>Enter</kbd> to submit. Same `''` convention. |
62
+
63
+ ### Full-text only
64
+
65
+ `fulltextOnly` reduces the component to a plain full-text search: the term box, the scope
66
+ select and the object-type filter stay, everything else — type blocks, the add-type row, the
67
+ inline condition editor and the form-mode toggle — is not rendered.
68
+
69
+ ```html
70
+ <yuv-smart-search [types]="['document', 'invoice']" [fulltextOnly]="true" (querySubmit)="runSearch($event)" />
71
+ ```
72
+
73
+ `types` keeps its meaning, so the emitted query is still scoped to the allowed types:
74
+
75
+ ```sql
76
+ -- types=['document','invoice'], full-text "acme", type filter left on "All types"
77
+ SELECT * FROM system:object WHERE (objectTypeId IN ('document','invoice') AND CONTAINS('acme'))
78
+ ```
79
+
80
+ Because the blocks are not rendered, they are not queried either: a state restored via
81
+ `loadState()` keeps its blocks (they come back when the flag is cleared) but only its full-text
82
+ unit contributes to the emitted query while the flag is set.
83
+
84
+ ### Submitting with Enter
85
+
86
+ `queryChange` is a live preview — it fires on every keystroke and every edit. To run a
87
+ search only when the user is *done*, bind `querySubmit` instead: it behaves like an HTML
88
+ form, emitting when <kbd>Enter</kbd> reaches the component without an inner widget having
89
+ claimed the key.
90
+
91
+ Widgets that own <kbd>Enter</kbd> for their own purpose keep it, so a submit never
92
+ happens by surprise:
93
+
94
+ | Where <kbd>Enter</kbd> is pressed | What happens |
95
+ | ------------------------------------------------ | ------------------------------------------------ |
96
+ | Suggestion list open (type / field / operator) | Picks the highlighted suggestion |
97
+ | Catalog, date or other value widget with a panel | Opens the panel / picks the value |
98
+ | Inline editor with a condition in progress | Commits the condition — the *next* Enter submits |
99
+ | Add-type input with staged types | Confirms the type block |
100
+ | Focus on a button (remove, confirm, …) | Activates the button |
101
+ | Anywhere else (full-text bar, fill-out form, …) | **Submits** — `querySubmit` emits |
102
+
103
+ <kbd>Shift</kbd>+<kbd>Enter</kbd> and <kbd>Enter</kbd> inside a multi-line value never
104
+ submit, so long text entry keeps working.
105
+
106
+ ```html
107
+ <yuv-smart-search [types]="types" (queryChange)="preview.set($event)" (querySubmit)="runSearch($event)" />
108
+ ```
59
109
 
60
110
  ### Saving and restoring state
61
111
 
@@ -1,6 +1,6 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { Signal } from '@angular/core';
3
- import { DmsObject, YuvUser, LockState } from '@yuuvis/client-core';
3
+ import { DmsObject, YuvUser, LockState, VirtualObjectType } from '@yuuvis/client-core';
4
4
  import { Severity } from '@yuuvis/material/badge';
5
5
 
6
6
  type BadgeSeverity = Severity;
@@ -14,6 +14,23 @@ interface BadgeRule {
14
14
  op: 'isSet' | 'equals' | 'tagPresent';
15
15
  value?: unknown;
16
16
  }
17
+ /**
18
+ * Restricts the object types a badge may be configured for. Mirrors
19
+ * `ActionSupport`: an entry matches when the object type id is listed in
20
+ * `types` or `sots`, or matches `pattern`.
21
+ *
22
+ * This scopes *configurability* (which types offer the badge in selection UIs
23
+ * such as the tile config), not rendering — whether a badge actually shows on
24
+ * a given object stays governed by {@link Badge.applies} / {@link Badge.rule}.
25
+ */
26
+ interface BadgeSupport {
27
+ /** Object types the badge can be configured for (e.g. `system:document`). */
28
+ types?: string[];
29
+ /** Secondary object types the badge can be configured for. */
30
+ sots?: string[];
31
+ /** Pattern matched against the object type id. */
32
+ pattern?: string;
33
+ }
17
34
  interface Badge {
18
35
  id: string;
19
36
  labelKey: string;
@@ -29,6 +46,12 @@ interface Badge {
29
46
  * the id filter. Variants are hidden from selection UIs.
30
47
  */
31
48
  variantOf?: string;
49
+ /**
50
+ * Object types this badge may be configured for. When omitted, the badge is
51
+ * offered for every type. Variants inherit the support of their parent badge
52
+ * and are hidden from selection UIs anyway.
53
+ */
54
+ supports?: BadgeSupport;
32
55
  applies?: (dmsObject: DmsObject, ctx: BadgeContext) => boolean;
33
56
  rule?: BadgeRule;
34
57
  }
@@ -53,10 +76,15 @@ declare class BadgeSelectComponent {
53
76
  #private;
54
77
  selectedBadgeIds: _angular_core.InputSignal<string[]>;
55
78
  bucket: _angular_core.InputSignal<string | undefined>;
79
+ /**
80
+ * Type currently being configured. Badges that declare `supports` are only
81
+ * offered for the types they support. Omitting it offers every badge.
82
+ */
83
+ objectType: _angular_core.InputSignal<VirtualObjectType | undefined>;
56
84
  badgeSelect: _angular_core.OutputEmitterRef<Badge>;
57
85
  readonly badges: Signal<Badge[]>;
58
86
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BadgeSelectComponent, never>;
59
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BadgeSelectComponent, "yuv-tile-badge-select", never, { "selectedBadgeIds": { "alias": "selectedBadgeIds"; "required": false; "isSignal": true; }; "bucket": { "alias": "bucket"; "required": false; "isSignal": true; }; }, { "badgeSelect": "badgeSelect"; }, never, never, true, never>;
87
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BadgeSelectComponent, "yuv-tile-badge-select", never, { "selectedBadgeIds": { "alias": "selectedBadgeIds"; "required": false; "isSignal": true; }; "bucket": { "alias": "bucket"; "required": false; "isSignal": true; }; "objectType": { "alias": "objectType"; "required": false; "isSignal": true; }; }, { "badgeSelect": "badgeSelect"; }, never, never, true, never>;
60
88
  }
61
89
 
62
90
  /**
@@ -84,6 +112,10 @@ declare const LOCK_SELF_BADGE: Badge;
84
112
  * @remarks
85
113
  * - `severity` is set to `'danger'` to visually emphasize the retention constraint.
86
114
  * - `priority` is `20`, which controls the display order relative to other badges.
115
+ * - `supports` limits the badge to documents, so selection UIs such as the tile
116
+ * config do not offer it for folder types. It only scopes configurability -
117
+ * `applies` still governs rendering, so an object under retention keeps its
118
+ * badge regardless.
87
119
  */ declare const RETENTION_BADGE: Badge;
88
120
 
89
121
  declare class BadgeRegistryService {
@@ -196,6 +228,21 @@ declare class TileBadgesComponent {
196
228
  * @returns `true` if the rule matches; otherwise `false`.
197
229
  */
198
230
  declare function evaluateRule(rule: BadgeRule, dmsObject: DmsObject): boolean;
231
+ /**
232
+ * Checks whether a badge may be configured for the given object type.
233
+ *
234
+ * A badge without {@link Badge.supports} is unrestricted and matches every
235
+ * type. Otherwise the type's `objectType` and its `sots` are matched against
236
+ * the badge's `types` / `sots` lists and its `pattern`; a single hit is enough.
237
+ *
238
+ * Called without an object type (no type context available) the badge is kept,
239
+ * so callers that do not know the type never lose badges.
240
+ *
241
+ * @param badge Badge to check.
242
+ * @param objectType Type the selection UI is currently configuring.
243
+ * @returns `true` if the badge may be offered for that type; otherwise `false`.
244
+ */
245
+ declare function badgeSupportsType(badge: Badge, objectType?: VirtualObjectType): boolean;
199
246
 
200
- export { BadgeChipComponent, BadgeRegistryService, BadgeSelectComponent, LOCK_BADGE, LOCK_SELF_BADGE, RETENTION_BADGE, TileBadgesComponent, evaluateRule };
201
- export type { Badge, BadgeContext, BadgeRule, BadgeSeverity };
247
+ export { BadgeChipComponent, BadgeRegistryService, BadgeSelectComponent, LOCK_BADGE, LOCK_SELF_BADGE, RETENTION_BADGE, TileBadgesComponent, badgeSupportsType, evaluateRule };
248
+ export type { Badge, BadgeContext, BadgeRule, BadgeSeverity, BadgeSupport };
@@ -61,7 +61,20 @@ declare class ObjectPreviewService {
61
61
  dmsObject: _angular_core.Signal<DmsObject | undefined>;
62
62
  setDmsObject(object: DmsObject): void;
63
63
  getDmsObject(id: string): Observable<DmsObject>;
64
- getMediaViewerSrc(id: string, mimeType: string | undefined, version?: number): string;
64
+ /**
65
+ * Builds the media viewer source URL for an object's content.
66
+ *
67
+ * The URL of the *current* version is keyed on the content stream (`_cs=<contentStreamId>`) when one is
68
+ * given. The viewers only reload when their `src` input is a *different string*: `DynamicViewerDirective`
69
+ * `setInput`s the same viewer instance for a same-type update, `PdfComponent` skips `#loadPDF` for a src
70
+ * it has already loaded and `ImageComponent` derives its cache-busting URL from `src()` alone. Without
71
+ * the key a content replacement that keeps the mime type kept showing the previous file (the object id,
72
+ * and therefore the URL, never changed). `ObjectPreviewComponent` rebuilds the details exactly when the
73
+ * content stream changes, so the key changes exactly then — never on metadata-only saves.
74
+ *
75
+ * Versioned URLs (`/versions/{n}/…`) address immutable content and are returned unchanged.
76
+ */
77
+ getMediaViewerSrc(id: string, mimeType: string | undefined, version?: number, contentStreamId?: string): string;
65
78
  getEmailViewerDetails(object: DmsObject, version?: number, MAIL_APP_ID?: string): ViewerDataEmail;
66
79
  getEmailMetadata(object: DmsObject, MAIL_APP_ID?: string): EmailMetadata;
67
80
  getViewerDetails(object: DmsObject, version: number | undefined): ViewerDataEmail | ViewerDataDefault | undefined;
@@ -82,6 +82,11 @@ declare class BooleanRendererComponent extends AbstractRendererComponent {
82
82
  }
83
83
 
84
84
  declare class DateTimeRendererComponent extends AbstractRendererComponent<Date> {
85
+ /**
86
+ * Fields declared with `resolution: 'date'` hold a calendar day, so rendering them with the
87
+ * default `short` format would invent a time that is not stored anywhere.
88
+ */
89
+ protected dateOnly: i0.Signal<boolean>;
85
90
  static ɵfac: i0.ɵɵFactoryDeclaration<DateTimeRendererComponent, never>;
86
91
  static ɵcmp: i0.ɵɵComponentDeclaration<DateTimeRendererComponent, "yuv-datetime-renderer", never, {}, {}, never, never, true, never>;
87
92
  }
@@ -91,6 +96,22 @@ declare class DecimalRendererComponent extends AbstractRendererComponent<number>
91
96
  static ɵcmp: i0.ɵɵComponentDeclaration<DecimalRendererComponent, "yuv-decimal-renderer", never, {}, {}, never, never, true, never>;
92
97
  }
93
98
 
99
+ /**
100
+ * Renders the value(s) of a dynamic catalog field (schema field carrying a `catalog` reference).
101
+ *
102
+ * Those fields store the technical entry name, so the label has to be resolved from the catalog
103
+ * entry's localizations — the same resolution `yuv-dynamic-catalog` does for the form element.
104
+ * The entries are fetched and cached by `CatalogLabelService` (once per catalog, not once per
105
+ * renderer instance — a summary or tile list mounts many rows of the same catalog).
106
+ * Values without a matching entry fall back to the technical value.
107
+ */
108
+ declare class DynamicCatalogRendererComponent extends AbstractRendererComponent<string | string[]> {
109
+ #private;
110
+ readonly label: i0.Signal<string>;
111
+ static ɵfac: i0.ɵɵFactoryDeclaration<DynamicCatalogRendererComponent, never>;
112
+ static ɵcmp: i0.ɵɵComponentDeclaration<DynamicCatalogRendererComponent, "yuv-dynamic-catalog-renderer", never, {}, {}, never, never, true, never>;
113
+ }
114
+
94
115
  declare class FileSizeRendererComponent extends AbstractRendererComponent {
95
116
  parsedValue: i0.Signal<number>;
96
117
  static ɵfac: i0.ɵɵFactoryDeclaration<FileSizeRendererComponent, never>;
@@ -162,7 +183,7 @@ declare class UnknownRendererComponent extends AbstractRendererComponent<any> {
162
183
  static ɵcmp: i0.ɵɵComponentDeclaration<UnknownRendererComponent, "yuv-unknown-renderer", never, {}, {}, never, never, true, never>;
163
184
  }
164
185
 
165
- type RendererComponent = IntegerRendererComponent | DecimalRendererComponent | DateTimeRendererComponent | IconRendererComponent | StringRendererComponent | BooleanRendererComponent | TableRendererComponent | I18nCatalogRendererComponent;
186
+ type RendererComponent = IntegerRendererComponent | DecimalRendererComponent | DateTimeRendererComponent | IconRendererComponent | StringRendererComponent | BooleanRendererComponent | TableRendererComponent | I18nCatalogRendererComponent | DynamicCatalogRendererComponent;
166
187
  type RendererDirectiveInput = ResolvedObjectConfigItem;
167
188
 
168
189
  /**
@@ -207,5 +228,5 @@ declare class RendererDirective {
207
228
  static ɵdir: i0.ɵɵDirectiveDeclaration<RendererDirective, "[yuvRenderer]", never, { "yuvRenderer": { "alias": "yuvRenderer"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
208
229
  }
209
230
 
210
- export { AbstractAuditRendererComponent, AbstractRendererComponent, AuditRendererDirective, AuditRendererService, BooleanRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, DefaultAuditRendererComponent, FileSizeRendererComponent, I18nCatalogRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, TableRendererComponent, UnknownRendererComponent };
231
+ export { AbstractAuditRendererComponent, AbstractRendererComponent, AuditRendererDirective, AuditRendererService, BooleanRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, DefaultAuditRendererComponent, DynamicCatalogRendererComponent, FileSizeRendererComponent, I18nCatalogRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, TableRendererComponent, UnknownRendererComponent };
211
232
  export type { RendererComponent, RendererDirectiveInput };
@@ -116,6 +116,12 @@ interface SuggestionItem {
116
116
  * Capture via {@link SmartSearchComponent.getState} and restore via
117
117
  * {@link SmartSearchComponent.loadState}. Safe for `JSON.stringify` /
118
118
  * `JSON.parse` roundtrips (plain data, no class instances or signals).
119
+ *
120
+ * The display labels it carries (`SearchBlockType.label`, `FieldCondition.fieldLabel` /
121
+ * `operatorLabel` / `conditionLabel` and `TableCondition.fieldLabel`) are a snapshot of the
122
+ * language the state was captured in. `loadState` re-resolves them from the live schema and the
123
+ * current language, so a state built by hand only needs the ids — the labels are a fallback for
124
+ * ids that can no longer be resolved.
119
125
  */
120
126
  interface SmartSearchState {
121
127
  blocks: SearchBlock[];
@@ -154,7 +160,10 @@ interface DynamicFormBlock {
154
160
  */
155
161
  declare class SmartSearchEditController {
156
162
  #private;
157
- /** The set of object type IDs that can be used as search blocks. */
163
+ /**
164
+ * The set of object type IDs the search is scoped to. Empty = unscoped: every
165
+ * searchable type of the schema is a candidate and nothing is restricted.
166
+ */
158
167
  allowedTypes: _angular_core.WritableSignal<string[]>;
159
168
  /** ObjectTypeField IDs to exclude from the field-step autocomplete suggestions. */
160
169
  skipProperties: _angular_core.WritableSignal<string[]>;
@@ -189,6 +198,12 @@ declare class SmartSearchEditController {
189
198
  fulltext: _angular_core.WritableSignal<FulltextSearch>;
190
199
  /** Whether the dynamic-conditions feature is enabled (mirrors the host `supportDynamicConditions` input). */
191
200
  supportDynamic: _angular_core.WritableSignal<boolean>;
201
+ /**
202
+ * Whether the host renders a plain full-text search (mirrors its `fulltextOnly` input).
203
+ * The condition builder is hidden and {@link cmisQuery} is built from {@link fulltext}
204
+ * alone, so blocks a loaded state carries can't contribute invisible clauses.
205
+ */
206
+ fulltextOnly: _angular_core.WritableSignal<boolean>;
192
207
  /**
193
208
  * Form mode: replace the builder with a generated form of the user-marked dynamic
194
209
  * conditions. View state only — the template ({@link blocks}) is never mutated; the
@@ -207,6 +222,17 @@ declare class SmartSearchEditController {
207
222
  /** Whether any condition is marked dynamic (gates the form-mode toggle). */
208
223
  hasDynamicConditions: _angular_core.Signal<boolean>;
209
224
  objectTypes: _angular_core.Signal<GenericObjectType[]>;
225
+ /**
226
+ * The types an "all types" pick resolves to. With a configured allow-list, "all types"
227
+ * means *any of the allowed types* — not every type of the system — so the restriction
228
+ * is still emitted. Without one the list is empty and "all types" restricts nothing.
229
+ *
230
+ * Resolved id-by-id against the schema (rather than from {@link objectTypes}) so an
231
+ * allowed id the current schema doesn't know still narrows the query instead of silently
232
+ * widening it; `isSot` decides whether the id lands in the primary or the secondary
233
+ * object-type clause.
234
+ */
235
+ scopeTypes: _angular_core.Signal<SearchBlockType[]>;
210
236
  /** Fields for the active top-level block, used by the field-step suggestions. */
211
237
  activeBlockFields: _angular_core.Signal<SuggestionItem[]>;
212
238
  /**
@@ -229,7 +255,16 @@ declare class SmartSearchEditController {
229
255
  activeCtrl: _angular_core.Signal<FormControl<unknown>>;
230
256
  /** Deep-clone the current blocks, combinator and full-text unit into a serializable {@link SmartSearchState}. */
231
257
  getState(): SmartSearchState;
232
- /** Replace the current state with a saved one, normalizing legacy blocks and cancelling any in-progress edit. */
258
+ /**
259
+ * Replace the current state with a saved one, normalizing legacy blocks and cancelling any
260
+ * in-progress edit.
261
+ *
262
+ * Everything a persisted state carries for display only — type, field and operator labels and
263
+ * the composed condition label — is re-resolved against the live schema and the *current* UI
264
+ * language (see {@link #resolveType} / {@link #relabelNodes}), so a query saved in one language
265
+ * reads in the language it is restored in. The persisted strings act as the fallback for ids the
266
+ * schema or the translations can no longer resolve.
267
+ */
233
268
  loadState(state: SmartSearchState): void;
234
269
  /** Reset all search state back to its initial empty values. */
235
270
  reset(): void;
@@ -245,9 +280,19 @@ declare class SmartSearchEditController {
245
280
  exitFormMode(): void;
246
281
  /** Toggle form mode (rebuilds the form rows on each entry). */
247
282
  toggleFormMode(): void;
248
- /** Record a value entered in the form for a dynamic condition (drives the overlaid {@link cmisQuery}). */
283
+ /**
284
+ * Record a value entered in the form for a dynamic condition (drives the overlaid
285
+ * {@link cmisQuery}). No-op when unchanged, so re-applying a row's current value —
286
+ * e.g. flushing the debounced controls on submit — can't churn the query.
287
+ */
249
288
  setFormValue(condition: FieldCondition, raw: unknown): void;
250
- /** Set the full-text search term. */
289
+ /**
290
+ * Re-apply every generated form row's current control value. The row controls are
291
+ * debounced, so a submit fired immediately after typing would otherwise read a stale
292
+ * overlay. Rows whose value already matches are skipped by {@link setFormValue}.
293
+ */
294
+ flushFormValues(): void;
295
+ /** Set the full-text search term. No-op when unchanged, so re-setting it can't churn {@link cmisQuery}. */
251
296
  setFulltextTerm(term: string): void;
252
297
  /** Set the full-text search scope (`all` / `metadata` / `content`). */
253
298
  setFulltextScope(scope: FulltextScope): void;
@@ -312,7 +357,10 @@ declare class SmartSearchEditController {
312
357
  isConditionComplete(): boolean;
313
358
  /** The operators available for a field's internal type, as autocomplete items. */
314
359
  operatorsForField(field: SuggestionItem): SuggestionItem[];
315
- /** Human-readable label for an operator id: a math symbol (`=`, `≠`, …), a translated key, or the id itself. */
360
+ /**
361
+ * Human-readable label for an operator id: a math symbol (`=`, `≠`, …), a translated
362
+ * key, a translated date-preset label (`date:thisMonth` → “This month”) or the id itself.
363
+ */
316
364
  operatorLabel(operator: string): string;
317
365
  /** Build a condition record from a date preset / boolean operator / value. */
318
366
  buildCommitCondition(field: SuggestionItem, operatorId: string, operatorLabelText: string, value: string | string[], internalTypeOverride?: string): FieldCondition;
@@ -350,8 +398,13 @@ declare function buildNodeClause(node: ConditionNode): string;
350
398
  /**
351
399
  * Build the whole-object full-text clause: a single `CONTAINS('term')` predicate, optionally scoped
352
400
  * to a column and AND-ed with a type restriction. Returns `''` when the term is blank.
401
+ *
402
+ * @param fulltext The full-text unit. An empty `types` list means "all types".
403
+ * @param scopeTypes The types the search is scoped to (the host's `types` allow-list). When
404
+ * the unit picks no concrete type, "all types" means *any of these* — so the restriction is
405
+ * still emitted. Pass an empty list for an unscoped search: then "all types" restricts nothing.
353
406
  */
354
- declare function buildFulltextClause(fulltext: FulltextSearch): string;
407
+ declare function buildFulltextClause(fulltext: FulltextSearch, scopeTypes?: SearchBlockType[]): string;
355
408
  /**
356
409
  * Assemble the full CMIS statement from the search state. Each unit — the
357
410
  * full-text clause plus every type block (type restriction `AND`-ed with its
@@ -361,9 +414,12 @@ declare function buildFulltextClause(fulltext: FulltextSearch): string;
361
414
  * @param blocks The type blocks with their conditions.
362
415
  * @param combinator How the top-level units combine (`AND` / `OR`).
363
416
  * @param fulltext Optional whole-object full-text unit.
417
+ * @param scopeTypes The types the whole search is scoped to (the host's `types` allow-list).
418
+ * Applied to the full-text unit when it targets "all types"; the blocks always carry their own
419
+ * (already scoped) types. Empty = unscoped, i.e. "all types" restricts nothing.
364
420
  * @returns A `SELECT * FROM system:object WHERE …` statement, or `''`.
365
421
  */
366
- declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, fulltext?: FulltextSearch): string;
422
+ declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, fulltext?: FulltextSearch, scopeTypes?: SearchBlockType[]): string;
367
423
 
368
424
  /**
369
425
  * Visual query builder that turns guided, chip-based user input into a CMIS query.
@@ -374,15 +430,26 @@ declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, f
374
430
  * - one or more **type blocks**, each targeting one or more object types and holding
375
431
  * field conditions, nested groups and table-column conditions.
376
432
  *
433
+ * Set {@link fulltextOnly} to drop the second part entirely and render a plain full-text
434
+ * search bar.
435
+ *
377
436
  * Conditions are built step by step (type → field → operator → value) with an inline
378
437
  * autocomplete editor; the value step renders the field's real metadata widget
379
438
  * (datepicker, catalog select, organization picker, …). The resulting CMIS query is
380
439
  * emitted through {@link queryChange} on every change and can be saved/restored as a
381
- * plain-data {@link SmartSearchState} via {@link getState} / {@link loadState}.
440
+ * plain-data {@link SmartSearchState} via {@link getState} / {@link loadState}. A restored
441
+ * state is re-labelled in the current UI language (see {@link loadState}).
382
442
  *
383
443
  * State and mutators live in {@link SmartSearchEditController} (provided per instance);
384
444
  * this component owns only the UI concerns (focus, autocomplete plumbing, blur handling).
385
445
  *
446
+ * **Submitting.** {@link queryChange} is a live preview — it fires on every change. To run
447
+ * a search only when the user is done, bind {@link querySubmit}: it emits when ENTER
448
+ * reaches the component without an inner widget having claimed it, the way a plain HTML
449
+ * form submits on ENTER. Widgets that own ENTER for their own purpose keep it: the
450
+ * suggestion autocomplete picks an option, a catalog select picks a value, and the inline
451
+ * editor commits the condition it is building. Only the *next* ENTER then submits.
452
+ *
386
453
  * @example
387
454
  * ```html
388
455
  * <!-- Restrict the picker to two object types and skip a noisy property -->
@@ -417,9 +484,14 @@ declare class SmartSearchComponent {
417
484
  #private;
418
485
  ctrl: SmartSearchEditController;
419
486
  /**
420
- * Object-type ids that may be searched. Restricts the type picker (both the
421
- * full-text type filter and the type-block multi-select) to these types. When
422
- * empty, no type is offered set at least one id to enable building blocks.
487
+ * Object-type ids that may be searched the scope of the whole search. Restricts
488
+ * the type picker (both the full-text type filter and the type-block multi-select)
489
+ * to these types, and narrows the emitted query: picking *All types* in the
490
+ * full-text filter then means "any of these types" (`objectTypeId IN (…)`), not
491
+ * every type the system knows.
492
+ *
493
+ * When empty the search is unscoped — *All types* restricts nothing — and no type
494
+ * is offered in the picker, so only a full-text search can be built.
423
495
  */
424
496
  types: _angular_core.InputSignal<string[]>;
425
497
  /**
@@ -428,6 +500,14 @@ declare class SmartSearchComponent {
428
500
  * table columns alike.
429
501
  */
430
502
  skipProperties: _angular_core.InputSignal<string[]>;
503
+ /**
504
+ * Renders the component as a plain full-text search: only the full-text bar (term, scope
505
+ * and the object-type filter) is shown — no type blocks, no condition builder, no
506
+ * form-mode toggle. The emitted query is built from the full-text unit alone, so a
507
+ * previously {@link loadState loaded} state's blocks do not contribute while this is set.
508
+ * {@link types} keeps its meaning: the search stays scoped to those types.
509
+ */
510
+ fulltextOnly: _angular_core.InputSignal<boolean>;
431
511
  /**
432
512
  * Enables the **dynamic conditions** feature. When `true`, each committed condition can
433
513
  * be marked dynamic and a form-mode toggle appears that swaps the builder for a generated
@@ -442,6 +522,14 @@ declare class SmartSearchComponent {
442
522
  * non-empty values.
443
523
  */
444
524
  queryChange: _angular_core.OutputEmitterRef<string>;
525
+ /**
526
+ * Emits the current CMIS query when the user presses ENTER and no inner widget
527
+ * claimed that key — the smart-search equivalent of submitting a form. Bind this
528
+ * (rather than {@link queryChange}) to run the search only once the user is done
529
+ * building it. Like {@link queryChange}, `''` means "no query"; an empty search
530
+ * still submits.
531
+ */
532
+ querySubmit: _angular_core.OutputEmitterRef<string>;
445
533
  auto: _angular_core.Signal<MatAutocomplete>;
446
534
  /** Trigger of the currently-focused autocomplete input — used to re-open the
447
535
  * panel after a type pick so the multi-select stays open. */
@@ -450,6 +538,14 @@ declare class SmartSearchComponent {
450
538
  fulltextTermCtrl: FormControl<string | null>;
451
539
  /** Sentinel option value representing "no type restriction" in the type multi-select. */
452
540
  readonly ALL_TYPES = "__all__";
541
+ /**
542
+ * Empty `matChipInputSeparatorKeyCodes` for the add-type input. The directive defaults
543
+ * to `[ENTER]` and then calls `preventDefault()` on *every* Enter to end a chip — but
544
+ * we never bind `matChipInputTokenEnd`, so that only served to hide the key from
545
+ * {@link onHostEnter}. A stable reference: a `[]` literal in the template would be a
546
+ * new array on each change-detection run.
547
+ */
548
+ readonly NO_SEPARATOR_KEYS: number[];
453
549
  /**
454
550
  * Guard that prevents `onInlineBlur` from cancelling the pending condition
455
551
  * when we programmatically open the inline editor.
@@ -523,7 +619,15 @@ declare class SmartSearchComponent {
523
619
  * later restore it with {@link loadState}.
524
620
  */
525
621
  getState(): SmartSearchState;
526
- /** Restore a previously {@link getState saved} search, replacing the current one. */
622
+ /**
623
+ * Restore a previously {@link getState saved} search, replacing the current one.
624
+ *
625
+ * The display labels a state carries (type, field and operator labels and the chip's composed
626
+ * condition label) are re-resolved against the live schema and the *current* UI language, so a
627
+ * search saved in one language reads in the language it is restored in. Ids the schema or the
628
+ * translations can no longer resolve keep the label the state carried. Nothing that drives the
629
+ * emitted query is affected.
630
+ */
527
631
  loadState(state: SmartSearchState): void;
528
632
  /** Clear the whole search: discard all blocks, conditions and the full-text term. */
529
633
  clear(): void;
@@ -579,14 +683,32 @@ declare class SmartSearchComponent {
579
683
  * pick, so Enter commits the staged types — even while the autocomplete panel
580
684
  * is open. While the user is typing a filter term, Enter is left to the
581
685
  * autocomplete so it can select the highlighted option.
686
+ *
687
+ * Unlike {@link onEnter} this deliberately lets an unusable Enter fall through:
688
+ * the add-type input is the resting state of an empty search, so Enter with
689
+ * nothing staged has to reach {@link onHostEnter} and submit.
582
690
  */
583
- onTypeEnter(): void;
691
+ onTypeEnter(event: Event): void;
584
692
  /**
585
- * Enter / confirm-button handler for the value step. Commits the in-progress
586
- * condition when a value is present. No-op while the autocomplete panel is open
587
- * (Enter selects the highlighted option there) or before the value step.
693
+ * Enter / confirm-button handler for the inline editor. Commits the in-progress
694
+ * condition once it has a field and an operator. No-op while the autocomplete
695
+ * panel is open (Enter selects the highlighted option there).
696
+ *
697
+ * Bound on the editor wrapper, so it covers every step — including the value
698
+ * step's real metadata widget — and runs after the widgets' own key handling.
699
+ * The `event` is optional because the confirm button calls this from a click.
588
700
  */
589
- onEnter(): void;
701
+ onEnter(event?: Event): void;
702
+ /**
703
+ * Enter that bubbled all the way up to the component host without being claimed:
704
+ * submit the search, the way a plain HTML form does.
705
+ *
706
+ * Everything inside that owns Enter marks the event handled via `preventDefault()`
707
+ * — Material's autocomplete and select do, and so do {@link onEnter} /
708
+ * {@link onTypeEnter} — so this only sees the "nothing else wanted it" case. The
709
+ * remaining guards cover keys the browser itself is still acting on.
710
+ */
711
+ onHostEnter(event: KeyboardEvent | Event): void;
590
712
  /**
591
713
  * Called when the inline-input wrapper loses focus.
592
714
  * Commits the condition if complete, discards it if incomplete.
@@ -609,7 +731,11 @@ declare class SmartSearchComponent {
609
731
  * condition, mirroring the blur path.
610
732
  */
611
733
  onPickerClosed(): void;
612
- /** Abandon the in-progress condition edit, discarding any partial input. */
734
+ /**
735
+ * Abandon the in-progress condition edit, discarding any partial input. When an
736
+ * existing condition was opened for editing it is put back verbatim rather than
737
+ * lost — cancelling an edit means "leave it as it was", the same as the blur path.
738
+ */
613
739
  cancelPending(): void;
614
740
  /** Jump the inline editor back to the field step so the user can re-pick the field. */
615
741
  editField(): void;
@@ -639,7 +765,7 @@ declare class SmartSearchComponent {
639
765
  */
640
766
  getObjectTypeField(item: SuggestionItem | null): ObjectTypeField | null;
641
767
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SmartSearchComponent, never>;
642
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SmartSearchComponent, "yuv-smart-search", never, { "types": { "alias": "types"; "required": false; "isSignal": true; }; "skipProperties": { "alias": "skipProperties"; "required": false; "isSignal": true; }; "supportDynamicConditions": { "alias": "supportDynamicConditions"; "required": false; "isSignal": true; }; }, { "queryChange": "queryChange"; }, never, never, true, never>;
768
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SmartSearchComponent, "yuv-smart-search", never, { "types": { "alias": "types"; "required": false; "isSignal": true; }; "skipProperties": { "alias": "skipProperties"; "required": false; "isSignal": true; }; "fulltextOnly": { "alias": "fulltextOnly"; "required": false; "isSignal": true; }; "supportDynamicConditions": { "alias": "supportDynamicConditions"; "required": false; "isSignal": true; }; }, { "queryChange": "queryChange"; "querySubmit": "querySubmit"; }, never, never, true, never>;
643
769
  }
644
770
 
645
771
  declare class YuvSmartSearchModule {
@@ -29,6 +29,11 @@ interface TileListConfigOptions {
29
29
  */
30
30
  configTypes: VirtualObjectType[];
31
31
  configFlavors?: ObjectFlavor[];
32
+ /**
33
+ * Property IDs that should not be offered for selection in the property
34
+ * slots. Added on top of the always-excluded system/base fields.
35
+ */
36
+ excludeProperties?: string[];
32
37
  actionContext?: ActionContext;
33
38
  }
34
39
  interface TileListHighlight {