@yuuvis/client-framework 3.18.0 → 3.20.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.
Files changed (27) hide show
  1. package/fesm2022/yuuvis-client-framework-forms.mjs +4 -2
  2. package/fesm2022/yuuvis-client-framework-forms.mjs.map +1 -1
  3. package/fesm2022/yuuvis-client-framework-object-details.mjs +2 -1
  4. package/fesm2022/yuuvis-client-framework-object-details.mjs.map +1 -1
  5. package/fesm2022/yuuvis-client-framework-object-flavor.mjs +2 -1
  6. package/fesm2022/yuuvis-client-framework-object-flavor.mjs.map +1 -1
  7. package/fesm2022/yuuvis-client-framework-object-form.mjs +3 -1
  8. package/fesm2022/yuuvis-client-framework-object-form.mjs.map +1 -1
  9. package/fesm2022/yuuvis-client-framework-object-preview.mjs +25 -13
  10. package/fesm2022/yuuvis-client-framework-object-preview.mjs.map +1 -1
  11. package/fesm2022/yuuvis-client-framework-object-relationship.mjs +9 -3
  12. package/fesm2022/yuuvis-client-framework-object-relationship.mjs.map +1 -1
  13. package/fesm2022/yuuvis-client-framework-object-summary.mjs +4 -1
  14. package/fesm2022/yuuvis-client-framework-object-summary.mjs.map +1 -1
  15. package/fesm2022/yuuvis-client-framework-renderer.mjs +100 -16
  16. package/fesm2022/yuuvis-client-framework-renderer.mjs.map +1 -1
  17. package/fesm2022/yuuvis-client-framework-smart-search.mjs +433 -69
  18. package/fesm2022/yuuvis-client-framework-smart-search.mjs.map +1 -1
  19. package/fesm2022/yuuvis-client-framework-tile-list.mjs +2 -2
  20. package/fesm2022/yuuvis-client-framework-tile-list.mjs.map +1 -1
  21. package/package.json +5 -5
  22. package/smart-search/README.md +57 -7
  23. package/types/yuuvis-client-framework-object-form.d.ts +6 -0
  24. package/types/yuuvis-client-framework-object-preview.d.ts +14 -1
  25. package/types/yuuvis-client-framework-renderer.d.ts +40 -6
  26. package/types/yuuvis-client-framework-smart-search.d.ts +171 -22
  27. package/types/yuuvis-client-framework-tile-list.d.ts +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuuvis/client-framework",
3
- "version": "3.18.0",
3
+ "version": "3.20.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.18.0",
12
- "@yuuvis/client-shell-core": "^3.18.0",
13
- "@yuuvis/client-components": "^3.18.0",
11
+ "@yuuvis/client-core": "^3.20.0",
12
+ "@yuuvis/client-shell-core": "^3.20.0",
13
+ "@yuuvis/client-components": "^3.20.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.18.0",
20
+ "@yuuvis/material": "^3.20.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
 
@@ -25,6 +25,12 @@ interface ObjectFormOptions {
25
25
  */
26
26
  data: Record<string, unknown>;
27
27
  objectId?: string;
28
+ /**
29
+ * ID of the object type (or secondary object type) the form model belongs to. Used to
30
+ * enrich form elements from the schema in the context of that type — a type may bind a
31
+ * shared property to its own dynamic catalog. Falls back to the form model's own id.
32
+ */
33
+ objectTypeId?: string;
28
34
  /**
29
35
  * whether or not to disable the complete form
30
36
  */
@@ -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;
@@ -1,7 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { ComponentRef, Type } from '@angular/core';
3
3
  import { AuditEntry, SchemaResponseFieldDefinition, TranslateService, ResolvedObjectConfigItem, RendererType } from '@yuuvis/client-core';
4
- import * as dist_libs_yuuvis_client_core_types_yuuvis_client_core from 'dist/libs/yuuvis/client-core/types/yuuvis-client-core';
5
4
 
6
5
  /**
7
6
  * Abstract class to be extended by audit-entry renderers. The renderer controls the
@@ -71,9 +70,15 @@ declare abstract class AbstractRendererComponent<T = string, U = null> {
71
70
  propertyName: i0.InputSignal<string>;
72
71
  value: i0.InputSignal<T | null>;
73
72
  meta: i0.InputSignal<U | Record<string, unknown> | undefined>;
73
+ /**
74
+ * ID of the object type (or secondary object type) the property is rendered for, when known.
75
+ * Some schema attributes — most notably the dynamic `catalog` a property is bound to — may be
76
+ * overridden per type, and can only be resolved with that context.
77
+ */
78
+ objectTypeId: i0.InputSignal<string | undefined>;
74
79
  protected getProperty(): SchemaResponseFieldDefinition | undefined;
75
80
  static ɵfac: i0.ɵɵFactoryDeclaration<AbstractRendererComponent<any, any>, never>;
76
- static ɵcmp: i0.ɵɵComponentDeclaration<AbstractRendererComponent<any, any>, "yuv-abstract-renderer", never, { "propertyName": { "alias": "propertyName"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
81
+ static ɵcmp: i0.ɵɵComponentDeclaration<AbstractRendererComponent<any, any>, "yuv-abstract-renderer", never, { "propertyName": { "alias": "propertyName"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "objectTypeId": { "alias": "objectTypeId"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
77
82
  }
78
83
 
79
84
  declare class BooleanRendererComponent extends AbstractRendererComponent {
@@ -82,6 +87,11 @@ declare class BooleanRendererComponent extends AbstractRendererComponent {
82
87
  }
83
88
 
84
89
  declare class DateTimeRendererComponent extends AbstractRendererComponent<Date> {
90
+ /**
91
+ * Fields declared with `resolution: 'date'` hold a calendar day, so rendering them with the
92
+ * default `short` format would invent a time that is not stored anywhere.
93
+ */
94
+ protected dateOnly: i0.Signal<boolean>;
85
95
  static ɵfac: i0.ɵɵFactoryDeclaration<DateTimeRendererComponent, never>;
86
96
  static ɵcmp: i0.ɵɵComponentDeclaration<DateTimeRendererComponent, "yuv-datetime-renderer", never, {}, {}, never, never, true, never>;
87
97
  }
@@ -91,6 +101,22 @@ declare class DecimalRendererComponent extends AbstractRendererComponent<number>
91
101
  static ɵcmp: i0.ɵɵComponentDeclaration<DecimalRendererComponent, "yuv-decimal-renderer", never, {}, {}, never, never, true, never>;
92
102
  }
93
103
 
104
+ /**
105
+ * Renders the value(s) of a dynamic catalog field (schema field carrying a `catalog` reference).
106
+ *
107
+ * Those fields store the technical entry name, so the label has to be resolved from the catalog
108
+ * entry's localizations — the same resolution `yuv-dynamic-catalog` does for the form element.
109
+ * The entries are fetched and cached by `CatalogLabelService` (once per catalog, not once per
110
+ * renderer instance — a summary or tile list mounts many rows of the same catalog).
111
+ * Values without a matching entry fall back to the technical value.
112
+ */
113
+ declare class DynamicCatalogRendererComponent extends AbstractRendererComponent<string | string[]> {
114
+ #private;
115
+ readonly label: i0.Signal<string>;
116
+ static ɵfac: i0.ɵɵFactoryDeclaration<DynamicCatalogRendererComponent, never>;
117
+ static ɵcmp: i0.ɵɵComponentDeclaration<DynamicCatalogRendererComponent, "yuv-dynamic-catalog-renderer", never, {}, {}, never, never, true, never>;
118
+ }
119
+
94
120
  declare class FileSizeRendererComponent extends AbstractRendererComponent {
95
121
  parsedValue: i0.Signal<number>;
96
122
  static ɵfac: i0.ɵɵFactoryDeclaration<FileSizeRendererComponent, never>;
@@ -162,8 +188,16 @@ declare class UnknownRendererComponent extends AbstractRendererComponent<any> {
162
188
  static ɵcmp: i0.ɵɵComponentDeclaration<UnknownRendererComponent, "yuv-unknown-renderer", never, {}, {}, never, never, true, never>;
163
189
  }
164
190
 
165
- type RendererComponent = IntegerRendererComponent | DecimalRendererComponent | DateTimeRendererComponent | IconRendererComponent | StringRendererComponent | BooleanRendererComponent | TableRendererComponent | I18nCatalogRendererComponent;
166
- type RendererDirectiveInput = ResolvedObjectConfigItem;
191
+ type RendererComponent = IntegerRendererComponent | DecimalRendererComponent | DateTimeRendererComponent | IconRendererComponent | StringRendererComponent | BooleanRendererComponent | TableRendererComponent | I18nCatalogRendererComponent | DynamicCatalogRendererComponent;
192
+ /**
193
+ * Input of the `yuvRenderer` directive. `objectTypeId` is optional type context: a type may bind
194
+ * a shared property to its own dynamic catalog, and that override can only be resolved when the
195
+ * type (or secondary object type) the property is rendered for is known. Without it the renderer
196
+ * falls back to the global property definition.
197
+ */
198
+ type RendererDirectiveInput = ResolvedObjectConfigItem & {
199
+ objectTypeId?: string;
200
+ };
167
201
 
168
202
  /**
169
203
  * Service for managing property type renderers. Renderers are components that will render certain
@@ -202,10 +236,10 @@ declare class RendererService {
202
236
  declare class RendererDirective {
203
237
  #private;
204
238
  component: ComponentRef<RendererComponent>;
205
- yuvRenderer: i0.InputSignal<dist_libs_yuuvis_client_core_types_yuuvis_client_core.ResolvedObjectConfigItem | undefined>;
239
+ yuvRenderer: i0.InputSignal<RendererDirectiveInput | undefined>;
206
240
  static ɵfac: i0.ɵɵFactoryDeclaration<RendererDirective, never>;
207
241
  static ɵdir: i0.ɵɵDirectiveDeclaration<RendererDirective, "[yuvRenderer]", never, { "yuvRenderer": { "alias": "yuvRenderer"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
208
242
  }
209
243
 
210
- export { AbstractAuditRendererComponent, AbstractRendererComponent, AuditRendererDirective, AuditRendererService, BooleanRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, DefaultAuditRendererComponent, FileSizeRendererComponent, I18nCatalogRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, TableRendererComponent, UnknownRendererComponent };
244
+ export { AbstractAuditRendererComponent, AbstractRendererComponent, AuditRendererDirective, AuditRendererService, BooleanRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, DefaultAuditRendererComponent, DynamicCatalogRendererComponent, FileSizeRendererComponent, I18nCatalogRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, TableRendererComponent, UnknownRendererComponent };
211
245
  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
  /**
@@ -221,7 +247,13 @@ declare class SmartSearchEditController {
221
247
  */
222
248
  activeFields: _angular_core.Signal<SuggestionItem[]>;
223
249
  suggestions: _angular_core.Signal<SuggestionItem[]>;
224
- /** Number of top-level query units: the full-text unit (when it has a term) plus each block. */
250
+ /**
251
+ * Whether the full-text unit contributes to the query — a term, a type restriction, or both.
252
+ * Drives its own muted styling and the joiner below it, so what reads as active matches what
253
+ * is actually queried (see {@link isFulltextActive}).
254
+ */
255
+ fulltextActive: _angular_core.Signal<boolean>;
256
+ /** Number of top-level query units: the full-text unit (when it contributes one) plus each block. */
225
257
  unitCount: _angular_core.Signal<number>;
226
258
  showCombinator: _angular_core.Signal<boolean>;
227
259
  cmisQuery: _angular_core.Signal<string>;
@@ -229,7 +261,16 @@ declare class SmartSearchEditController {
229
261
  activeCtrl: _angular_core.Signal<FormControl<unknown>>;
230
262
  /** Deep-clone the current blocks, combinator and full-text unit into a serializable {@link SmartSearchState}. */
231
263
  getState(): SmartSearchState;
232
- /** Replace the current state with a saved one, normalizing legacy blocks and cancelling any in-progress edit. */
264
+ /**
265
+ * Replace the current state with a saved one, normalizing legacy blocks and cancelling any
266
+ * in-progress edit.
267
+ *
268
+ * Everything a persisted state carries for display only — type, field and operator labels and
269
+ * the composed condition label — is re-resolved against the live schema and the *current* UI
270
+ * language (see {@link #resolveType} / {@link #relabelNodes}), so a query saved in one language
271
+ * reads in the language it is restored in. The persisted strings act as the fallback for ids the
272
+ * schema or the translations can no longer resolve.
273
+ */
233
274
  loadState(state: SmartSearchState): void;
234
275
  /** Reset all search state back to its initial empty values. */
235
276
  reset(): void;
@@ -245,9 +286,19 @@ declare class SmartSearchEditController {
245
286
  exitFormMode(): void;
246
287
  /** Toggle form mode (rebuilds the form rows on each entry). */
247
288
  toggleFormMode(): void;
248
- /** Record a value entered in the form for a dynamic condition (drives the overlaid {@link cmisQuery}). */
289
+ /**
290
+ * Record a value entered in the form for a dynamic condition (drives the overlaid
291
+ * {@link cmisQuery}). No-op when unchanged, so re-applying a row's current value —
292
+ * e.g. flushing the debounced controls on submit — can't churn the query.
293
+ */
249
294
  setFormValue(condition: FieldCondition, raw: unknown): void;
250
- /** Set the full-text search term. */
295
+ /**
296
+ * Re-apply every generated form row's current control value. The row controls are
297
+ * debounced, so a submit fired immediately after typing would otherwise read a stale
298
+ * overlay. Rows whose value already matches are skipped by {@link setFormValue}.
299
+ */
300
+ flushFormValues(): void;
301
+ /** Set the full-text search term. No-op when unchanged, so re-setting it can't churn {@link cmisQuery}. */
251
302
  setFulltextTerm(term: string): void;
252
303
  /** Set the full-text search scope (`all` / `metadata` / `content`). */
253
304
  setFulltextScope(scope: FulltextScope): void;
@@ -312,7 +363,10 @@ declare class SmartSearchEditController {
312
363
  isConditionComplete(): boolean;
313
364
  /** The operators available for a field's internal type, as autocomplete items. */
314
365
  operatorsForField(field: SuggestionItem): SuggestionItem[];
315
- /** Human-readable label for an operator id: a math symbol (`=`, `≠`, …), a translated key, or the id itself. */
366
+ /**
367
+ * Human-readable label for an operator id: a math symbol (`=`, `≠`, …), a translated
368
+ * key, a translated date-preset label (`date:thisMonth` → “This month”) or the id itself.
369
+ */
316
370
  operatorLabel(operator: string): string;
317
371
  /** Build a condition record from a date preset / boolean operator / value. */
318
372
  buildCommitCondition(field: SuggestionItem, operatorId: string, operatorLabelText: string, value: string | string[], internalTypeOverride?: string): FieldCondition;
@@ -349,9 +403,30 @@ declare function isConditionUnset(cond: FieldCondition): boolean;
349
403
  declare function buildNodeClause(node: ConditionNode): string;
350
404
  /**
351
405
  * Build the whole-object full-text clause: a single `CONTAINS('term')` predicate, optionally scoped
352
- * to a column and AND-ed with a type restriction. Returns `''` when the term is blank.
406
+ * to a column and AND-ed with a type restriction.
407
+ *
408
+ * A type restriction on its own — types picked in the full-text bar's type filter with nothing
409
+ * typed — is a query in its own right ("show me everything of this type"), exactly like a type
410
+ * block carrying no conditions. So the picked types are returned alone in that case; only a unit
411
+ * with neither a term nor a picked type contributes nothing and yields `''`.
412
+ *
413
+ * @param fulltext The full-text unit. An empty `types` list means "all types".
414
+ * @param scopeTypes The types the search is scoped to (the host's `types` allow-list). When the
415
+ * unit picks no concrete type, "all types" means *any of these* — so the restriction is still
416
+ * emitted. Pass an empty list for an unscoped search: then "all types" restricts nothing.
417
+ *
418
+ * The scope deliberately only narrows a *term* search. With nothing typed and no type picked the
419
+ * panel is simply untouched, and falling back to the scope there would turn it into a standing
420
+ * "everything in scope" query the moment it opens.
353
421
  */
354
- declare function buildFulltextClause(fulltext: FulltextSearch): string;
422
+ declare function buildFulltextClause(fulltext: FulltextSearch, scopeTypes?: SearchBlockType[]): string;
423
+ /**
424
+ * Whether the full-text unit contributes a clause to the query — a term, a type restriction, or
425
+ * both. Lives next to {@link buildFulltextClause} so the UI's notion of "this unit is active"
426
+ * (its muted styling, the joiner to the blocks below it, the unit count behind the AND/OR toggle)
427
+ * cannot drift from what actually ends up in the query.
428
+ */
429
+ declare function isFulltextActive(fulltext: FulltextSearch): boolean;
355
430
  /**
356
431
  * Assemble the full CMIS statement from the search state. Each unit — the
357
432
  * full-text clause plus every type block (type restriction `AND`-ed with its
@@ -361,9 +436,12 @@ declare function buildFulltextClause(fulltext: FulltextSearch): string;
361
436
  * @param blocks The type blocks with their conditions.
362
437
  * @param combinator How the top-level units combine (`AND` / `OR`).
363
438
  * @param fulltext Optional whole-object full-text unit.
439
+ * @param scopeTypes The types the whole search is scoped to (the host's `types` allow-list).
440
+ * Applied to the full-text unit when it targets "all types"; the blocks always carry their own
441
+ * (already scoped) types. Empty = unscoped, i.e. "all types" restricts nothing.
364
442
  * @returns A `SELECT * FROM system:object WHERE …` statement, or `''`.
365
443
  */
366
- declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, fulltext?: FulltextSearch): string;
444
+ declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, fulltext?: FulltextSearch, scopeTypes?: SearchBlockType[]): string;
367
445
 
368
446
  /**
369
447
  * Visual query builder that turns guided, chip-based user input into a CMIS query.
@@ -374,15 +452,26 @@ declare function buildCmisQuery(blocks: SearchBlock[], combinator: Combinator, f
374
452
  * - one or more **type blocks**, each targeting one or more object types and holding
375
453
  * field conditions, nested groups and table-column conditions.
376
454
  *
455
+ * Set {@link fulltextOnly} to drop the second part entirely and render a plain full-text
456
+ * search bar.
457
+ *
377
458
  * Conditions are built step by step (type → field → operator → value) with an inline
378
459
  * autocomplete editor; the value step renders the field's real metadata widget
379
460
  * (datepicker, catalog select, organization picker, …). The resulting CMIS query is
380
461
  * emitted through {@link queryChange} on every change and can be saved/restored as a
381
- * plain-data {@link SmartSearchState} via {@link getState} / {@link loadState}.
462
+ * plain-data {@link SmartSearchState} via {@link getState} / {@link loadState}. A restored
463
+ * state is re-labelled in the current UI language (see {@link loadState}).
382
464
  *
383
465
  * State and mutators live in {@link SmartSearchEditController} (provided per instance);
384
466
  * this component owns only the UI concerns (focus, autocomplete plumbing, blur handling).
385
467
  *
468
+ * **Submitting.** {@link queryChange} is a live preview — it fires on every change. To run
469
+ * a search only when the user is done, bind {@link querySubmit}: it emits when ENTER
470
+ * reaches the component without an inner widget having claimed it, the way a plain HTML
471
+ * form submits on ENTER. Widgets that own ENTER for their own purpose keep it: the
472
+ * suggestion autocomplete picks an option, a catalog select picks a value, and the inline
473
+ * editor commits the condition it is building. Only the *next* ENTER then submits.
474
+ *
386
475
  * @example
387
476
  * ```html
388
477
  * <!-- Restrict the picker to two object types and skip a noisy property -->
@@ -417,9 +506,14 @@ declare class SmartSearchComponent {
417
506
  #private;
418
507
  ctrl: SmartSearchEditController;
419
508
  /**
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.
509
+ * Object-type ids that may be searched the scope of the whole search. Restricts
510
+ * the type picker (both the full-text type filter and the type-block multi-select)
511
+ * to these types, and narrows the emitted query: picking *All types* in the
512
+ * full-text filter then means "any of these types" (`objectTypeId IN (…)`), not
513
+ * every type the system knows.
514
+ *
515
+ * When empty the search is unscoped — *All types* restricts nothing — and no type
516
+ * is offered in the picker, so only a full-text search can be built.
423
517
  */
424
518
  types: _angular_core.InputSignal<string[]>;
425
519
  /**
@@ -428,6 +522,14 @@ declare class SmartSearchComponent {
428
522
  * table columns alike.
429
523
  */
430
524
  skipProperties: _angular_core.InputSignal<string[]>;
525
+ /**
526
+ * Renders the component as a plain full-text search: only the full-text bar (term, scope
527
+ * and the object-type filter) is shown — no type blocks, no condition builder, no
528
+ * form-mode toggle. The emitted query is built from the full-text unit alone, so a
529
+ * previously {@link loadState loaded} state's blocks do not contribute while this is set.
530
+ * {@link types} keeps its meaning: the search stays scoped to those types.
531
+ */
532
+ fulltextOnly: _angular_core.InputSignal<boolean>;
431
533
  /**
432
534
  * Enables the **dynamic conditions** feature. When `true`, each committed condition can
433
535
  * be marked dynamic and a form-mode toggle appears that swaps the builder for a generated
@@ -442,6 +544,14 @@ declare class SmartSearchComponent {
442
544
  * non-empty values.
443
545
  */
444
546
  queryChange: _angular_core.OutputEmitterRef<string>;
547
+ /**
548
+ * Emits the current CMIS query when the user presses ENTER and no inner widget
549
+ * claimed that key — the smart-search equivalent of submitting a form. Bind this
550
+ * (rather than {@link queryChange}) to run the search only once the user is done
551
+ * building it. Like {@link queryChange}, `''` means "no query"; an empty search
552
+ * still submits.
553
+ */
554
+ querySubmit: _angular_core.OutputEmitterRef<string>;
445
555
  auto: _angular_core.Signal<MatAutocomplete>;
446
556
  /** Trigger of the currently-focused autocomplete input — used to re-open the
447
557
  * panel after a type pick so the multi-select stays open. */
@@ -450,6 +560,14 @@ declare class SmartSearchComponent {
450
560
  fulltextTermCtrl: FormControl<string | null>;
451
561
  /** Sentinel option value representing "no type restriction" in the type multi-select. */
452
562
  readonly ALL_TYPES = "__all__";
563
+ /**
564
+ * Empty `matChipInputSeparatorKeyCodes` for the add-type input. The directive defaults
565
+ * to `[ENTER]` and then calls `preventDefault()` on *every* Enter to end a chip — but
566
+ * we never bind `matChipInputTokenEnd`, so that only served to hide the key from
567
+ * {@link onHostEnter}. A stable reference: a `[]` literal in the template would be a
568
+ * new array on each change-detection run.
569
+ */
570
+ readonly NO_SEPARATOR_KEYS: number[];
453
571
  /**
454
572
  * Guard that prevents `onInlineBlur` from cancelling the pending condition
455
573
  * when we programmatically open the inline editor.
@@ -485,6 +603,7 @@ declare class SmartSearchComponent {
485
603
  readonly showCombinator: _angular_core.Signal<boolean>;
486
604
  readonly suggestions: _angular_core.Signal<SuggestionItem[]>;
487
605
  readonly fulltext: _angular_core.WritableSignal<_yuuvis_client_framework_smart_search.FulltextSearch>;
606
+ readonly fulltextActive: _angular_core.Signal<boolean>;
488
607
  readonly objectTypes: _angular_core.Signal<_yuuvis_client_core.GenericObjectType[]>;
489
608
  readonly activeBlockFields: _angular_core.Signal<SuggestionItem[]>;
490
609
  readonly formMode: _angular_core.WritableSignal<boolean>;
@@ -523,7 +642,15 @@ declare class SmartSearchComponent {
523
642
  * later restore it with {@link loadState}.
524
643
  */
525
644
  getState(): SmartSearchState;
526
- /** Restore a previously {@link getState saved} search, replacing the current one. */
645
+ /**
646
+ * Restore a previously {@link getState saved} search, replacing the current one.
647
+ *
648
+ * The display labels a state carries (type, field and operator labels and the chip's composed
649
+ * condition label) are re-resolved against the live schema and the *current* UI language, so a
650
+ * search saved in one language reads in the language it is restored in. Ids the schema or the
651
+ * translations can no longer resolve keep the label the state carried. Nothing that drives the
652
+ * emitted query is affected.
653
+ */
527
654
  loadState(state: SmartSearchState): void;
528
655
  /** Clear the whole search: discard all blocks, conditions and the full-text term. */
529
656
  clear(): void;
@@ -579,14 +706,32 @@ declare class SmartSearchComponent {
579
706
  * pick, so Enter commits the staged types — even while the autocomplete panel
580
707
  * is open. While the user is typing a filter term, Enter is left to the
581
708
  * autocomplete so it can select the highlighted option.
709
+ *
710
+ * Unlike {@link onEnter} this deliberately lets an unusable Enter fall through:
711
+ * the add-type input is the resting state of an empty search, so Enter with
712
+ * nothing staged has to reach {@link onHostEnter} and submit.
713
+ */
714
+ onTypeEnter(event: Event): void;
715
+ /**
716
+ * Enter / confirm-button handler for the inline editor. Commits the in-progress
717
+ * condition once it has a field and an operator. No-op while the autocomplete
718
+ * panel is open (Enter selects the highlighted option there).
719
+ *
720
+ * Bound on the editor wrapper, so it covers every step — including the value
721
+ * step's real metadata widget — and runs after the widgets' own key handling.
722
+ * The `event` is optional because the confirm button calls this from a click.
582
723
  */
583
- onTypeEnter(): void;
724
+ onEnter(event?: Event): void;
584
725
  /**
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.
726
+ * Enter that bubbled all the way up to the component host without being claimed:
727
+ * submit the search, the way a plain HTML form does.
728
+ *
729
+ * Everything inside that owns Enter marks the event handled via `preventDefault()`
730
+ * — Material's autocomplete and select do, and so do {@link onEnter} /
731
+ * {@link onTypeEnter} — so this only sees the "nothing else wanted it" case. The
732
+ * remaining guards cover keys the browser itself is still acting on.
588
733
  */
589
- onEnter(): void;
734
+ onHostEnter(event: KeyboardEvent | Event): void;
590
735
  /**
591
736
  * Called when the inline-input wrapper loses focus.
592
737
  * Commits the condition if complete, discards it if incomplete.
@@ -609,7 +754,11 @@ declare class SmartSearchComponent {
609
754
  * condition, mirroring the blur path.
610
755
  */
611
756
  onPickerClosed(): void;
612
- /** Abandon the in-progress condition edit, discarding any partial input. */
757
+ /**
758
+ * Abandon the in-progress condition edit, discarding any partial input. When an
759
+ * existing condition was opened for editing it is put back verbatim rather than
760
+ * lost — cancelling an edit means "leave it as it was", the same as the blur path.
761
+ */
613
762
  cancelPending(): void;
614
763
  /** Jump the inline editor back to the field step so the user can re-pick the field. */
615
764
  editField(): void;
@@ -639,7 +788,7 @@ declare class SmartSearchComponent {
639
788
  */
640
789
  getObjectTypeField(item: SuggestionItem | null): ObjectTypeField | null;
641
790
  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>;
791
+ 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
792
  }
644
793
 
645
794
  declare class YuvSmartSearchModule {
@@ -648,5 +797,5 @@ declare class YuvSmartSearchModule {
648
797
  static ɵinj: _angular_core.ɵɵInjectorDeclaration<YuvSmartSearchModule>;
649
798
  }
650
799
 
651
- export { SmartSearchComponent, YuvSmartSearchModule, buildCmisQuery, buildFulltextClause, buildNodeClause, isConditionGroup, isFieldCondition, isTableCondition };
800
+ export { SmartSearchComponent, YuvSmartSearchModule, buildCmisQuery, buildFulltextClause, buildNodeClause, isConditionGroup, isFieldCondition, isFulltextActive, isTableCondition };
652
801
  export type { BuildStep, Combinator, ConditionContainer, ConditionGroup, ConditionNode, FieldCondition, FulltextScope, FulltextSearch, SearchBlock, SearchBlockType, SmartSearchState, SuggestionItem, TableCondition };
@@ -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 {