@yuuvis/client-framework 3.20.0 → 3.21.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.
- package/fesm2022/yuuvis-client-framework-badges.mjs +80 -39
- package/fesm2022/yuuvis-client-framework-badges.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-datepicker.mjs +90 -34
- package/fesm2022/yuuvis-client-framework-datepicker.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-forms.mjs +14 -5
- package/fesm2022/yuuvis-client-framework-forms.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-object-preview.mjs +25 -42
- package/fesm2022/yuuvis-client-framework-object-preview.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-smart-search.mjs +22 -3
- package/fesm2022/yuuvis-client-framework-smart-search.mjs.map +1 -1
- package/fesm2022/yuuvis-client-framework-tile-list.mjs +1 -1
- package/fesm2022/yuuvis-client-framework-tile-list.mjs.map +1 -1
- package/lib/assets/i18n/de.json +1 -1
- package/lib/assets/i18n/en.json +5 -5
- package/package.json +6 -6
- package/types/yuuvis-client-framework-badges.d.ts +32 -2
- package/types/yuuvis-client-framework-datepicker.d.ts +16 -1
- package/types/yuuvis-client-framework-smart-search.d.ts +16 -1
|
@@ -3,7 +3,7 @@ import { inject, signal, Injectable, input, linkedSignal, computed, effect, untr
|
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { DmsService, UserService, ObjectLockingService, ContentStreamField, EventService, YuvEventType, TranslatePipe } from '@yuuvis/client-core';
|
|
5
5
|
import { MediaViewerService, defaultViewers, DASHLET_MESSAGE_EVENTS, YuvMediaViewerComponent, MailComponent } from '@yuuvis/media-viewer';
|
|
6
|
-
import { tap as tap$1 } from 'rxjs';
|
|
6
|
+
import { tap as tap$1, map, filter, switchMap, EMPTY } from 'rxjs';
|
|
7
7
|
import { tap } from 'rxjs/operators';
|
|
8
8
|
|
|
9
9
|
/* eslint-disable id-length */
|
|
@@ -49,8 +49,12 @@ class ObjectPreviewService {
|
|
|
49
49
|
? `/api/dms/objects/${id}/versions/${version}/contents/renditions/slide?timeout=50000`
|
|
50
50
|
: `/api/dms/objects/${id}/versions/${version}/contents/file`;
|
|
51
51
|
}
|
|
52
|
-
const base = mimeType === 'image/tiff'
|
|
53
|
-
|
|
52
|
+
const base = mimeType === 'image/tiff'
|
|
53
|
+
? `/api/dms/objects/${id}/contents/renditions/slide`
|
|
54
|
+
: `/api/dms/objects/${id}/contents/file`;
|
|
55
|
+
return contentStreamId
|
|
56
|
+
? `${base}${base.includes('?') ? '&' : '?'}_cs=${encodeURIComponent(contentStreamId)}`
|
|
57
|
+
: base;
|
|
54
58
|
}
|
|
55
59
|
getEmailViewerDetails(object, version, MAIL_APP_ID = 'appSystemmail:') {
|
|
56
60
|
return {
|
|
@@ -167,51 +171,30 @@ class ObjectPreviewComponent {
|
|
|
167
171
|
});
|
|
168
172
|
}, ...(ngDevMode ? [{ debugName: "#objectIdEffect" }] : /* istanbul ignore next */ []));
|
|
169
173
|
this.#event
|
|
170
|
-
.on(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_UPDATE)
|
|
171
|
-
.pipe(takeUntilDestroyed(),
|
|
172
|
-
|
|
173
|
-
//
|
|
174
|
-
|
|
174
|
+
.on(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_UPDATE)
|
|
175
|
+
.pipe(takeUntilDestroyed(), tap$1(() => this.#event.trigger(YuvEventType.DMS_OBJECT_CONTENT_UPDATED, { externalUpdate: true })))
|
|
176
|
+
.subscribe();
|
|
177
|
+
// A conversion produces new content (new content stream, usually a new mime type): reload the DMS object so
|
|
178
|
+
// the viewer details are rebuilt for the converted file.
|
|
179
|
+
this.#event
|
|
180
|
+
.on(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_CONVERTED)
|
|
181
|
+
.pipe(takeUntilDestroyed(), map((event) => event.data?.objectId ?? null),
|
|
182
|
+
// several previews may share the page — only reload the object shown here
|
|
183
|
+
filter((objectId) => !objectId || objectId === this.#currentObjectId()), switchMap(() => {
|
|
184
|
+
const id = this.#currentObjectId();
|
|
185
|
+
return id ? this.#objectPreviewService.getDmsObject(id) : EMPTY;
|
|
186
|
+
}), tap$1(() => this.#event.trigger(YuvEventType.DMS_OBJECT_CONTENT_UPDATED, { externalUpdate: true })))
|
|
175
187
|
.subscribe();
|
|
176
188
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
@let viewerDetailsObj = viewerDetails();
|
|
180
|
-
@if (viewerDetailsObj) {
|
|
181
|
-
<yuv-media-viewer
|
|
182
|
-
class="viewer"
|
|
183
|
-
[id]="viewerDetailsObj.id"
|
|
184
|
-
[type]="viewerDetailsObj.mimeType"
|
|
185
|
-
[src]="viewerDetailsObj.mediaViewerSrc ?? null"
|
|
186
|
-
[metadata]="viewerDetailsObj.metadata"
|
|
187
|
-
/>
|
|
188
|
-
}
|
|
189
|
-
@if (isTiff()) {
|
|
190
|
-
<div class="tiff-warning">
|
|
191
|
-
<div class="tiff-warning-text">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>
|
|
192
|
-
</div>
|
|
189
|
+
#currentObjectId() {
|
|
190
|
+
return this.dmsObject()?.id ?? this.objectId();
|
|
193
191
|
}
|
|
194
|
-
|
|
192
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ObjectPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
193
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ObjectPreviewComponent, isStandalone: true, selector: "yuv-object-preview", inputs: { objectId: { classPropertyName: "objectId", publicName: "objectId", isSignal: true, isRequired: false, transformFunction: null }, dmsObjectInput: { classPropertyName: "dmsObjectInput", publicName: "dmsObject", isSignal: true, isRequired: false, transformFunction: null }, version: { classPropertyName: "version", publicName: "version", isSignal: true, isRequired: false, transformFunction: null }, metadata: { classPropertyName: "metadata", publicName: "metadata", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let viewerDetailsObj = viewerDetails();\n@if (viewerDetailsObj) {\n <yuv-media-viewer\n class=\"viewer\"\n [id]=\"viewerDetailsObj.id\"\n [type]=\"viewerDetailsObj.mimeType\"\n [src]=\"viewerDetailsObj.mediaViewerSrc ?? null\"\n [metadata]=\"viewerDetailsObj.metadata\"\n />\n}\n@if (isTiff()) {\n <div class=\"tiff-warning\">\n <div class=\"tiff-warning-text\">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>\n </div>\n}\n", styles: [":host{--media-viewer-background-color: transparent;height:100%;overflow:hidden;display:grid;grid-auto-columns:1fr;grid-auto-rows:1fr;grid-template-columns:1fr;grid-template-rows:1fr max-content;gap:0px 0px;grid-template-areas:\"viewer\" \"message\"}.viewer{grid-area:viewer;background-color:var(--media-viewer-background-color)}.viewer ::ng-deep pdf-paging-area{display:flex;align-items:center}.viewer ::ng-deep pdf-paging-area .paging-right,.viewer ::ng-deep pdf-paging-area .paging-left{display:flex;align-items:center}.viewer ::ng-deep pdf-zoom-toolbar div{display:flex;align-items:center}.tiff-warning{grid-area:message;text-align:center;padding:var(--ymt-spacing-xs) 0}.tiff-warning-text{margin:var(--ymt-spacing-m)}\n"], dependencies: [{ kind: "component", type: YuvMediaViewerComponent, selector: "yuv-media-viewer", inputs: ["id", "type", "metadata", "src"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
|
|
195
194
|
}
|
|
196
195
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ObjectPreviewComponent, decorators: [{
|
|
197
196
|
type: Component,
|
|
198
|
-
args: [{ selector: 'yuv-object-preview', standalone: true, imports: [YuvMediaViewerComponent, TranslatePipe], template:
|
|
199
|
-
@let viewerDetailsObj = viewerDetails();
|
|
200
|
-
@if (viewerDetailsObj) {
|
|
201
|
-
<yuv-media-viewer
|
|
202
|
-
class="viewer"
|
|
203
|
-
[id]="viewerDetailsObj.id"
|
|
204
|
-
[type]="viewerDetailsObj.mimeType"
|
|
205
|
-
[src]="viewerDetailsObj.mediaViewerSrc ?? null"
|
|
206
|
-
[metadata]="viewerDetailsObj.metadata"
|
|
207
|
-
/>
|
|
208
|
-
}
|
|
209
|
-
@if (isTiff()) {
|
|
210
|
-
<div class="tiff-warning">
|
|
211
|
-
<div class="tiff-warning-text">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>
|
|
212
|
-
</div>
|
|
213
|
-
}
|
|
214
|
-
`, styles: [":host{--media-viewer-background-color: transparent;height:100%;overflow:hidden;display:grid;grid-auto-columns:1fr;grid-auto-rows:1fr;grid-template-columns:1fr;grid-template-rows:1fr max-content;gap:0px 0px;grid-template-areas:\"viewer\" \"message\"}.viewer{grid-area:viewer;background-color:var(--media-viewer-background-color)}.tiff-warning{grid-area:message;text-align:center;padding:var(--ymt-spacing-xs) 0;&-text{margin:var(--ymt-spacing-m)}}\n"] }]
|
|
197
|
+
args: [{ selector: 'yuv-object-preview', standalone: true, imports: [YuvMediaViewerComponent, TranslatePipe], template: "@let viewerDetailsObj = viewerDetails();\n@if (viewerDetailsObj) {\n <yuv-media-viewer\n class=\"viewer\"\n [id]=\"viewerDetailsObj.id\"\n [type]=\"viewerDetailsObj.mimeType\"\n [src]=\"viewerDetailsObj.mediaViewerSrc ?? null\"\n [metadata]=\"viewerDetailsObj.metadata\"\n />\n}\n@if (isTiff()) {\n <div class=\"tiff-warning\">\n <div class=\"tiff-warning-text\">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>\n </div>\n}\n", styles: [":host{--media-viewer-background-color: transparent;height:100%;overflow:hidden;display:grid;grid-auto-columns:1fr;grid-auto-rows:1fr;grid-template-columns:1fr;grid-template-rows:1fr max-content;gap:0px 0px;grid-template-areas:\"viewer\" \"message\"}.viewer{grid-area:viewer;background-color:var(--media-viewer-background-color)}.viewer ::ng-deep pdf-paging-area{display:flex;align-items:center}.viewer ::ng-deep pdf-paging-area .paging-right,.viewer ::ng-deep pdf-paging-area .paging-left{display:flex;align-items:center}.viewer ::ng-deep pdf-zoom-toolbar div{display:flex;align-items:center}.tiff-warning{grid-area:message;text-align:center;padding:var(--ymt-spacing-xs) 0}.tiff-warning-text{margin:var(--ymt-spacing-m)}\n"] }]
|
|
215
198
|
}], ctorParameters: () => [], propDecorators: { objectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectId", required: false }] }], dmsObjectInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dmsObject", required: false }] }], version: [{ type: i0.Input, args: [{ isSignal: true, alias: "version", required: false }] }], metadata: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }] } });
|
|
216
199
|
|
|
217
200
|
class ObjectEmailPreviewComponent {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yuuvis-client-framework-object-preview.mjs","sources":["../../../../../libs/yuuvis/client-framework/object-preview/src/lib/services/object-preview.service.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/lib/components/object-preview/object-preview.component.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/lib/components/object-email-preview/object-email-preview.component.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/yuuvis-client-framework-object-preview.ts"],"sourcesContent":["/* eslint-disable id-length */\nimport { inject, Injectable, signal } from '@angular/core';\nimport { ContentStreamField, DmsObject, DmsService, ObjectLockingService, UserService } from '@yuuvis/client-core';\nimport { defaultViewers, EmailMetadata, MediaViewerService } from '@yuuvis/media-viewer';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { ViewerDataDefault, ViewerDataEmail } from '../models';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ObjectPreviewService {\n readonly #dms = inject(DmsService);\n readonly #userService = inject(UserService);\n readonly #mediaViewerService = inject(MediaViewerService);\n readonly #objectLockingService = inject(ObjectLockingService);\n\n #dmsObject = signal<DmsObject | undefined>(undefined);\n\n dmsObject = this.#dmsObject.asReadonly();\n\n setDmsObject(object: DmsObject): void {\n this.#dmsObject.set(object);\n }\n\n getDmsObject(id: string): Observable<DmsObject> {\n return this.#dms.getDmsObject(id).pipe(tap((dmsObject) => this.#dmsObject.set(dmsObject)));\n }\n\n /**\n * Builds the media viewer source URL for an object's content.\n *\n * The URL of the *current* version is keyed on the content stream (`_cs=<contentStreamId>`) when one is\n * given. The viewers only reload when their `src` input is a *different string*: `DynamicViewerDirective`\n * `setInput`s the same viewer instance for a same-type update, `PdfComponent` skips `#loadPDF` for a src\n * it has already loaded and `ImageComponent` derives its cache-busting URL from `src()` alone. Without\n * the key a content replacement that keeps the mime type kept showing the previous file (the object id,\n * and therefore the URL, never changed). `ObjectPreviewComponent` rebuilds the details exactly when the\n * content stream changes, so the key changes exactly then — never on metadata-only saves.\n *\n * Versioned URLs (`/versions/{n}/…`) address immutable content and are returned unchanged.\n */\n getMediaViewerSrc(id: string, mimeType: string | undefined, version?: number, contentStreamId?: string): string {\n if (!mimeType) {\n return '';\n }\n if (version) {\n return mimeType === 'image/tiff'\n ? `/api/dms/objects/${id}/versions/${version}/contents/renditions/slide?timeout=50000`\n : `/api/dms/objects/${id}/versions/${version}/contents/file`;\n }\n const base = mimeType === 'image/tiff' ? `/api/dms/objects/${id}/contents/renditions/slide` : `/api/dms/objects/${id}/contents/file`;\n return contentStreamId ? `${base}${base.includes('?') ? '&' : '?'}_cs=${encodeURIComponent(contentStreamId)}` : base;\n }\n\n getEmailViewerDetails(object: DmsObject, version?: number, MAIL_APP_ID = 'appSystemmail:'): ViewerDataEmail {\n return {\n mediaViewerSrc: this.getMediaViewerSrc(object.id, object.content?.mimeType, version, object.content?.contentStreamId),\n id: object.id,\n mimeType: object.content?.mimeType,\n metadata: this.getEmailMetadata(object, MAIL_APP_ID)\n };\n }\n\n getEmailMetadata(object: DmsObject, MAIL_APP_ID = 'appSystemmail:'): EmailMetadata {\n return {\n type: 'EMAIL',\n header: {\n subject: object.data[`${MAIL_APP_ID}subject`] as string,\n sent: new Date(object.data[`${MAIL_APP_ID}sent`] as string),\n from: object.data[`${MAIL_APP_ID}from`] as string,\n to: object.data[`${MAIL_APP_ID}to`] as string[],\n cc: object.data[`${MAIL_APP_ID}cc`] as string[],\n bcc: object.data[`${MAIL_APP_ID}bcc`] as string[]\n },\n theme: 'light',\n attachments: object.data[`${MAIL_APP_ID}attachments`] as string[]\n };\n }\n\n getViewerDetails(object: DmsObject, version: number | undefined): ViewerDataEmail | ViewerDataDefault | undefined {\n if (object) {\n const type = object?.content && this.#mediaViewerService.getComponentType(object.content.mimeType);\n\n return this.#isEmailViewer(object)\n ? this.getEmailViewerDetails(object, version)\n : {\n mediaViewerSrc: this.getMediaViewerSrc(object.id, object.content?.mimeType, version, object.content?.contentStreamId),\n id: object.id,\n mimeType: object.content?.mimeType,\n metadata: {\n type,\n name: object.data[ContentStreamField.FILENAME] as string,\n dmsObject: object?.data,\n id: object?.id,\n editable: this.#isEditable(object),\n user: this.#userService.getCurrentUser(),\n theme: 'light',\n version,\n sendEvent:\n !!(\n object.content?.mimeType &&\n defaultViewers\n .filter((viewer) => viewer.type === 'OFFICE')\n .map((viewer) => viewer.mimeType.includes(object.content?.mimeType || '')).length\n ) || false\n }\n };\n }\n return undefined;\n }\n\n #isEditable(object: DmsObject): boolean {\n const state = this.#objectLockingService.getLockState(object);\n return state ? !state.locked || state.lockedByCurrentUser : true;\n }\n\n #isEmailViewer(object: DmsObject): boolean {\n return defaultViewers\n .filter((viewer) => viewer.type === 'EMAIL')[0]\n .mimeType.includes(object.data[ContentStreamField.MIME_TYPE] as string);\n }\n}\n","import { Component, computed, effect, inject, input, linkedSignal, untracked, WritableSignal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { DmsObject, EventService, TranslatePipe, YuvEventType } from '@yuuvis/client-core';\nimport { DASHLET_MESSAGE_EVENTS, Metadata, YuvMediaViewerComponent } from '@yuuvis/media-viewer';\nimport { tap } from 'rxjs';\nimport { ObjectPreviewService } from '../../services/object-preview.service';\n\ninterface ViewerDetails {\n id: string;\n mimeType: string | undefined;\n mediaViewerSrc: string | undefined;\n metadata: Partial<Metadata>;\n}\n\n@Component({\n selector: 'yuv-object-preview',\n standalone: true,\n imports: [YuvMediaViewerComponent, TranslatePipe],\n template: `\n @let viewerDetailsObj = viewerDetails();\n @if (viewerDetailsObj) {\n <yuv-media-viewer\n class=\"viewer\"\n [id]=\"viewerDetailsObj.id\"\n [type]=\"viewerDetailsObj.mimeType\"\n [src]=\"viewerDetailsObj.mediaViewerSrc ?? null\"\n [metadata]=\"viewerDetailsObj.metadata\"\n />\n }\n @if (isTiff()) {\n <div class=\"tiff-warning\">\n <div class=\"tiff-warning-text\">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>\n </div>\n }\n `,\n styles: [\n `\n :host {\n --media-viewer-background-color: transparent;\n height: 100%;\n overflow: hidden;\n display: grid;\n grid-auto-columns: 1fr;\n grid-auto-rows: 1fr;\n grid-template-columns: 1fr;\n grid-template-rows: 1fr max-content;\n gap: 0px 0px;\n grid-template-areas:\n 'viewer'\n 'message';\n }\n .viewer {\n grid-area: viewer;\n background-color: var(--media-viewer-background-color);\n ::ng-deep {\n pdf-paging-area {\n display: flex;\n align-items: center;\n\n .paging-right,\n .paging-left {\n display: flex;\n align-items: center;\n }\n }\n pdf-zoom-toolbar {\n div {\n display: flex;\n align-items: center;\n }\n }\n }\n }\n .tiff-warning {\n grid-area: message;\n text-align: center;\n padding: var(--ymt-spacing-xs) 0;\n\n &-text {\n margin: var(--ymt-spacing-m);\n }\n }\n `\n ]\n})\nexport class ObjectPreviewComponent {\n readonly #objectPreviewService = inject(ObjectPreviewService);\n readonly #event = inject(EventService);\n\n objectId = input<string | undefined>(undefined);\n dmsObjectInput = input<DmsObject | undefined>(undefined, { alias: 'dmsObject' });\n version = input<number | undefined>(undefined);\n dmsObject = this.#objectPreviewService.dmsObject;\n\n metadata = input<Partial<Metadata>>({});\n\n viewerDetails: WritableSignal<ViewerDetails | null> = linkedSignal({\n source: () => ({ object: this.dmsObject(), version: this.version(), metadata: this.metadata() }),\n computation: (source, previous): ViewerDetails | null => {\n const object = Array.isArray(source.object) ? source.object[0] : source.object;\n if (!object) {\n return null;\n }\n\n const prevSource = previous?.source;\n const prevObject = Array.isArray(prevSource?.object) ? prevSource?.object[0] : prevSource?.object;\n\n const contentChanged =\n !previous ||\n object.id !== prevObject?.id ||\n object.content?.contentStreamId !== prevObject?.content?.contentStreamId ||\n source.version !== prevSource?.version;\n\n if (contentChanged) {\n const details = this.#objectPreviewService.getViewerDetails(object, source.version);\n return details\n ? { ...details, metadata: { ...details.metadata, ...source.metadata } as Partial<Metadata> }\n : null;\n }\n\n return previous?.value\n ? ({ ...previous.value, metadata: { ...previous.value.metadata, ...source.metadata } } as ViewerDetails)\n : null;\n }\n });\n\n isTiff = computed(() => this.dmsObject()?.content?.mimeType === 'image/tiff');\n\n #objectIdEffect = effect(() => {\n const objectId = this.objectId();\n const dmsObject = this.dmsObjectInput();\n untracked(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n dmsObject && this.#objectPreviewService.setDmsObject(dmsObject);\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n objectId && !dmsObject && this.#objectPreviewService.getDmsObject(objectId).subscribe();\n });\n });\n\n constructor() {\n this.#event\n .on(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_UPDATE) //, DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_CONVERTED)\n .pipe(\n takeUntilDestroyed(),\n // delay(2000),\n // tap((e) => console.log({ e })),\n tap(() => this.#event.trigger(YuvEventType.DMS_OBJECT_CONTENT_UPDATED, { externalUpdate: true }))\n )\n .subscribe();\n }\n}\n","import { Component, computed, effect, inject, input, untracked } from '@angular/core';\nimport { DmsObject } from '@yuuvis/client-core';\nimport { MailComponent } from '@yuuvis/media-viewer';\nimport { ObjectPreviewService } from '../../services/object-preview.service';\n\n@Component({\n selector: 'yuv-object-email-preview',\n standalone: true,\n imports: [MailComponent],\n template: `\n @let viewerDetailsObj = viewerDetails();\n @if (viewerDetailsObj) {\n <yuv-mail [id]=\"viewerDetailsObj.id\" [src]=\"viewerDetailsObj.mediaViewerSrc\" [metadata]=\"viewerDetailsObj.metadata\"></yuv-mail>\n }\n `,\n styles: `\n :host {\n height: 100%;\n }\n `\n})\nexport class ObjectEmailPreviewComponent {\n #objectPreviwService = inject(ObjectPreviewService);\n\n objectId = input<string | undefined>(undefined);\n dmsObjectInput = input<DmsObject | undefined>(undefined, { alias: 'dmsObject' });\n dmsObject = this.#objectPreviwService.dmsObject;\n\n #objectIdEffect = effect(() => {\n const objectId = this.objectId();\n const dmsObject = this.dmsObjectInput();\n\n untracked(() => {\n dmsObject && this.#objectPreviwService.setDmsObject(dmsObject);\n if (objectId && !dmsObject) {\n this.#objectPreviwService.getDmsObject(objectId).subscribe();\n }\n });\n });\n\n viewerDetails = computed(() => {\n const object = this.dmsObject();\n return object ? this.#objectPreviwService.getEmailViewerDetails(object) : undefined;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["tap"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"yuuvis-client-framework-object-preview.mjs","sources":["../../../../../libs/yuuvis/client-framework/object-preview/src/lib/services/object-preview.service.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/lib/components/object-preview/object-preview.component.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/lib/components/object-preview/object-preview.component.html","../../../../../libs/yuuvis/client-framework/object-preview/src/lib/components/object-email-preview/object-email-preview.component.ts","../../../../../libs/yuuvis/client-framework/object-preview/src/yuuvis-client-framework-object-preview.ts"],"sourcesContent":["/* eslint-disable id-length */\nimport { inject, Injectable, signal } from '@angular/core';\nimport { ContentStreamField, DmsObject, DmsService, ObjectLockingService, UserService } from '@yuuvis/client-core';\nimport { defaultViewers, EmailMetadata, MediaViewerService } from '@yuuvis/media-viewer';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { ViewerDataDefault, ViewerDataEmail } from '../models';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ObjectPreviewService {\n readonly #dms = inject(DmsService);\n readonly #userService = inject(UserService);\n readonly #mediaViewerService = inject(MediaViewerService);\n readonly #objectLockingService = inject(ObjectLockingService);\n\n #dmsObject = signal<DmsObject | undefined>(undefined);\n\n dmsObject = this.#dmsObject.asReadonly();\n\n setDmsObject(object: DmsObject): void {\n this.#dmsObject.set(object);\n }\n\n getDmsObject(id: string): Observable<DmsObject> {\n return this.#dms.getDmsObject(id).pipe(tap((dmsObject) => this.#dmsObject.set(dmsObject)));\n }\n\n /**\n * Builds the media viewer source URL for an object's content.\n *\n * The URL of the *current* version is keyed on the content stream (`_cs=<contentStreamId>`) when one is\n * given. The viewers only reload when their `src` input is a *different string*: `DynamicViewerDirective`\n * `setInput`s the same viewer instance for a same-type update, `PdfComponent` skips `#loadPDF` for a src\n * it has already loaded and `ImageComponent` derives its cache-busting URL from `src()` alone. Without\n * the key a content replacement that keeps the mime type kept showing the previous file (the object id,\n * and therefore the URL, never changed). `ObjectPreviewComponent` rebuilds the details exactly when the\n * content stream changes, so the key changes exactly then — never on metadata-only saves.\n *\n * Versioned URLs (`/versions/{n}/…`) address immutable content and are returned unchanged.\n */\n getMediaViewerSrc(id: string, mimeType: string | undefined, version?: number, contentStreamId?: string): string {\n if (!mimeType) {\n return '';\n }\n if (version) {\n return mimeType === 'image/tiff'\n ? `/api/dms/objects/${id}/versions/${version}/contents/renditions/slide?timeout=50000`\n : `/api/dms/objects/${id}/versions/${version}/contents/file`;\n }\n const base =\n mimeType === 'image/tiff'\n ? `/api/dms/objects/${id}/contents/renditions/slide`\n : `/api/dms/objects/${id}/contents/file`;\n return contentStreamId\n ? `${base}${base.includes('?') ? '&' : '?'}_cs=${encodeURIComponent(contentStreamId)}`\n : base;\n }\n\n getEmailViewerDetails(object: DmsObject, version?: number, MAIL_APP_ID = 'appSystemmail:'): ViewerDataEmail {\n return {\n mediaViewerSrc: this.getMediaViewerSrc(\n object.id,\n object.content?.mimeType,\n version,\n object.content?.contentStreamId\n ),\n id: object.id,\n mimeType: object.content?.mimeType,\n metadata: this.getEmailMetadata(object, MAIL_APP_ID)\n };\n }\n\n getEmailMetadata(object: DmsObject, MAIL_APP_ID = 'appSystemmail:'): EmailMetadata {\n return {\n type: 'EMAIL',\n header: {\n subject: object.data[`${MAIL_APP_ID}subject`] as string,\n sent: new Date(object.data[`${MAIL_APP_ID}sent`] as string),\n from: object.data[`${MAIL_APP_ID}from`] as string,\n to: object.data[`${MAIL_APP_ID}to`] as string[],\n cc: object.data[`${MAIL_APP_ID}cc`] as string[],\n bcc: object.data[`${MAIL_APP_ID}bcc`] as string[]\n },\n theme: 'light',\n attachments: object.data[`${MAIL_APP_ID}attachments`] as string[]\n };\n }\n\n getViewerDetails(object: DmsObject, version: number | undefined): ViewerDataEmail | ViewerDataDefault | undefined {\n if (object) {\n const type = object?.content && this.#mediaViewerService.getComponentType(object.content.mimeType);\n\n return this.#isEmailViewer(object)\n ? this.getEmailViewerDetails(object, version)\n : {\n mediaViewerSrc: this.getMediaViewerSrc(\n object.id,\n object.content?.mimeType,\n version,\n object.content?.contentStreamId\n ),\n id: object.id,\n mimeType: object.content?.mimeType,\n metadata: {\n type,\n name: object.data[ContentStreamField.FILENAME] as string,\n dmsObject: object?.data,\n id: object?.id,\n editable: this.#isEditable(object),\n user: this.#userService.getCurrentUser(),\n theme: 'light',\n version,\n sendEvent:\n !!(\n object.content?.mimeType &&\n defaultViewers\n .filter((viewer) => viewer.type === 'OFFICE')\n .map((viewer) => viewer.mimeType.includes(object.content?.mimeType || '')).length\n ) || false\n }\n };\n }\n return undefined;\n }\n\n #isEditable(object: DmsObject): boolean {\n const state = this.#objectLockingService.getLockState(object);\n return state ? !state.locked || state.lockedByCurrentUser : true;\n }\n\n #isEmailViewer(object: DmsObject): boolean {\n return defaultViewers\n .filter((viewer) => viewer.type === 'EMAIL')[0]\n .mimeType.includes(object.data[ContentStreamField.MIME_TYPE] as string);\n }\n}\n","import { Component, computed, effect, inject, input, linkedSignal, untracked, WritableSignal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { DmsObject, EventService, TranslatePipe, YuvEventType } from '@yuuvis/client-core';\nimport { DASHLET_MESSAGE_EVENTS, Metadata, OfficeDocumentMessage, YuvMediaViewerComponent } from '@yuuvis/media-viewer';\nimport { EMPTY, filter, map, switchMap, tap } from 'rxjs';\nimport { ObjectPreviewService } from '../../services/object-preview.service';\n\ninterface ViewerDetails {\n id: string;\n mimeType: string | undefined;\n mediaViewerSrc: string | undefined;\n metadata: Partial<Metadata>;\n}\n\n@Component({\n selector: 'yuv-object-preview',\n standalone: true,\n imports: [YuvMediaViewerComponent, TranslatePipe],\n templateUrl: `./object-preview.component.html`,\n styleUrls: [`./object-preview.component.scss`]\n})\nexport class ObjectPreviewComponent {\n readonly #objectPreviewService = inject(ObjectPreviewService);\n readonly #event = inject(EventService);\n\n objectId = input<string | undefined>(undefined);\n dmsObjectInput = input<DmsObject | undefined>(undefined, { alias: 'dmsObject' });\n version = input<number | undefined>(undefined);\n dmsObject = this.#objectPreviewService.dmsObject;\n\n metadata = input<Partial<Metadata>>({});\n\n viewerDetails: WritableSignal<ViewerDetails | null> = linkedSignal({\n source: () => ({ object: this.dmsObject(), version: this.version(), metadata: this.metadata() }),\n computation: (source, previous): ViewerDetails | null => {\n const object = Array.isArray(source.object) ? source.object[0] : source.object;\n if (!object) {\n return null;\n }\n\n const prevSource = previous?.source;\n const prevObject = Array.isArray(prevSource?.object) ? prevSource?.object[0] : prevSource?.object;\n\n const contentChanged =\n !previous ||\n object.id !== prevObject?.id ||\n object.content?.contentStreamId !== prevObject?.content?.contentStreamId ||\n source.version !== prevSource?.version;\n\n if (contentChanged) {\n const details = this.#objectPreviewService.getViewerDetails(object, source.version);\n return details\n ? { ...details, metadata: { ...details.metadata, ...source.metadata } as Partial<Metadata> }\n : null;\n }\n\n return previous?.value\n ? ({ ...previous.value, metadata: { ...previous.value.metadata, ...source.metadata } } as ViewerDetails)\n : null;\n }\n });\n\n isTiff = computed(() => this.dmsObject()?.content?.mimeType === 'image/tiff');\n\n #objectIdEffect = effect(() => {\n const objectId = this.objectId();\n const dmsObject = this.dmsObjectInput();\n untracked(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n dmsObject && this.#objectPreviewService.setDmsObject(dmsObject);\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n objectId && !dmsObject && this.#objectPreviewService.getDmsObject(objectId).subscribe();\n });\n });\n\n constructor() {\n this.#event\n .on(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_UPDATE)\n .pipe(\n takeUntilDestroyed(),\n tap(() => this.#event.trigger(YuvEventType.DMS_OBJECT_CONTENT_UPDATED, { externalUpdate: true }))\n )\n .subscribe();\n\n // A conversion produces new content (new content stream, usually a new mime type): reload the DMS object so\n // the viewer details are rebuilt for the converted file.\n this.#event\n .on<string, OfficeDocumentMessage>(DASHLET_MESSAGE_EVENTS.OFFICE_DOCUMENT_CONVERTED)\n .pipe(\n takeUntilDestroyed(),\n map((event) => event.data?.objectId ?? null),\n // several previews may share the page — only reload the object shown here\n filter((objectId) => !objectId || objectId === this.#currentObjectId()),\n switchMap(() => {\n const id = this.#currentObjectId();\n return id ? this.#objectPreviewService.getDmsObject(id) : EMPTY;\n }),\n tap(() => this.#event.trigger(YuvEventType.DMS_OBJECT_CONTENT_UPDATED, { externalUpdate: true }))\n )\n .subscribe();\n }\n\n #currentObjectId(): string | undefined {\n return this.dmsObject()?.id ?? this.objectId();\n }\n}\n","@let viewerDetailsObj = viewerDetails();\n@if (viewerDetailsObj) {\n <yuv-media-viewer\n class=\"viewer\"\n [id]=\"viewerDetailsObj.id\"\n [type]=\"viewerDetailsObj.mimeType\"\n [src]=\"viewerDetailsObj.mediaViewerSrc ?? null\"\n [metadata]=\"viewerDetailsObj.metadata\"\n />\n}\n@if (isTiff()) {\n <div class=\"tiff-warning\">\n <div class=\"tiff-warning-text\">{{ 'yuv.object-preview.tiff-warning' | translate }}</div>\n </div>\n}\n","import { Component, computed, effect, inject, input, untracked } from '@angular/core';\nimport { DmsObject } from '@yuuvis/client-core';\nimport { MailComponent } from '@yuuvis/media-viewer';\nimport { ObjectPreviewService } from '../../services/object-preview.service';\n\n@Component({\n selector: 'yuv-object-email-preview',\n standalone: true,\n imports: [MailComponent],\n template: `\n @let viewerDetailsObj = viewerDetails();\n @if (viewerDetailsObj) {\n <yuv-mail [id]=\"viewerDetailsObj.id\" [src]=\"viewerDetailsObj.mediaViewerSrc\" [metadata]=\"viewerDetailsObj.metadata\"></yuv-mail>\n }\n `,\n styles: `\n :host {\n height: 100%;\n }\n `\n})\nexport class ObjectEmailPreviewComponent {\n #objectPreviwService = inject(ObjectPreviewService);\n\n objectId = input<string | undefined>(undefined);\n dmsObjectInput = input<DmsObject | undefined>(undefined, { alias: 'dmsObject' });\n dmsObject = this.#objectPreviwService.dmsObject;\n\n #objectIdEffect = effect(() => {\n const objectId = this.objectId();\n const dmsObject = this.dmsObjectInput();\n\n untracked(() => {\n dmsObject && this.#objectPreviwService.setDmsObject(dmsObject);\n if (objectId && !dmsObject) {\n this.#objectPreviwService.getDmsObject(objectId).subscribe();\n }\n });\n });\n\n viewerDetails = computed(() => {\n const object = this.dmsObject();\n return object ? this.#objectPreviwService.getEmailViewerDetails(object) : undefined;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["tap"],"mappings":"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"}
|
|
@@ -598,6 +598,12 @@ class SmartSearchEditController {
|
|
|
598
598
|
* searchable type of the schema is a candidate and nothing is restricted.
|
|
599
599
|
*/
|
|
600
600
|
this.allowedTypes = signal([], ...(ngDevMode ? [{ debugName: "allowedTypes" }] : /* istanbul ignore next */ []));
|
|
601
|
+
/**
|
|
602
|
+
* Whether to bypass the user's object-type read permissions when offering types
|
|
603
|
+
* (mirrors the host `ignoreTypePermissions` input). Off by default: only types the
|
|
604
|
+
* user may search are offered.
|
|
605
|
+
*/
|
|
606
|
+
this.ignoreTypePermissions = signal(false, ...(ngDevMode ? [{ debugName: "ignoreTypePermissions" }] : /* istanbul ignore next */ []));
|
|
601
607
|
/** ObjectTypeField IDs to exclude from the field-step autocomplete suggestions. */
|
|
602
608
|
this.skipProperties = signal([], ...(ngDevMode ? [{ debugName: "skipProperties" }] : /* istanbul ignore next */ []));
|
|
603
609
|
// ── Persistent form controls (one per step) ──────────────────────────────
|
|
@@ -674,7 +680,7 @@ class SmartSearchEditController {
|
|
|
674
680
|
// Plain method call, not a signal: a live language switch does not re-run this computed.
|
|
675
681
|
const lang = this.#translate.getCurrentLang();
|
|
676
682
|
return this.#system
|
|
677
|
-
.getObjectTypes(true, 'search')
|
|
683
|
+
.getObjectTypes(true, this.ignoreTypePermissions() ? undefined : 'search')
|
|
678
684
|
.filter((type) => this.allowedTypes().includes(type.id))
|
|
679
685
|
.map((type) => ({ ...type, label: type.label || type.id }))
|
|
680
686
|
.sort((a, b) => a.label.localeCompare(b.label, lang));
|
|
@@ -1720,6 +1726,15 @@ class SmartSearchComponent {
|
|
|
1720
1726
|
* is offered in the picker, so only a full-text search can be built.
|
|
1721
1727
|
*/
|
|
1722
1728
|
this.types = input([], ...(ngDevMode ? [{ debugName: "types" }] : /* istanbul ignore next */ []));
|
|
1729
|
+
/**
|
|
1730
|
+
* Offers every object type of the schema in the type picker, not only those the user
|
|
1731
|
+
* has READ permission for. Off by default.
|
|
1732
|
+
*
|
|
1733
|
+
* Meant for surfaces that *author* a query rather than execute it as the current user
|
|
1734
|
+
* (admin/configuration screens, stored-search templates). {@link types} still applies
|
|
1735
|
+
* on top: this widens what the permissions allow, never what the consumer scoped.
|
|
1736
|
+
*/
|
|
1737
|
+
this.ignoreTypePermissions = input(false, ...(ngDevMode ? [{ debugName: "ignoreTypePermissions" }] : /* istanbul ignore next */ []));
|
|
1723
1738
|
/**
|
|
1724
1739
|
* Field ids to hide from the field-step autocomplete (e.g. internal/system
|
|
1725
1740
|
* properties that should not be user-queryable). Applies to block fields and
|
|
@@ -1861,6 +1876,10 @@ class SmartSearchComponent {
|
|
|
1861
1876
|
effect(() => {
|
|
1862
1877
|
this.ctrl.skipProperties.set(this.skipProperties());
|
|
1863
1878
|
});
|
|
1879
|
+
// Mirror the permission-bypass opt-in; the controller re-computes the offered types.
|
|
1880
|
+
effect(() => {
|
|
1881
|
+
this.ctrl.ignoreTypePermissions.set(this.ignoreTypePermissions());
|
|
1882
|
+
});
|
|
1864
1883
|
// Mirror the full-text-only opt-in into the controller (it also drops the blocks from
|
|
1865
1884
|
// the emitted query). Turning it on discards an in-progress condition edit and form
|
|
1866
1885
|
// mode, so flipping the flag at runtime can't leave hidden UI state active.
|
|
@@ -2487,7 +2506,7 @@ class SmartSearchComponent {
|
|
|
2487
2506
|
this.#host.nativeElement.querySelector(`.block[data-block-id="${blockId}"] .add-condition-btn`)?.focus();
|
|
2488
2507
|
}
|
|
2489
2508
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: SmartSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2490
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: SmartSearchComponent, isStandalone: true, selector: "yuv-smart-search", inputs: { types: { classPropertyName: "types", publicName: "types", isSignal: true, isRequired: false, transformFunction: null }, skipProperties: { classPropertyName: "skipProperties", publicName: "skipProperties", isSignal: true, isRequired: false, transformFunction: null }, fulltextOnly: { classPropertyName: "fulltextOnly", publicName: "fulltextOnly", isSignal: true, isRequired: false, transformFunction: null }, supportDynamicConditions: { classPropertyName: "supportDynamicConditions", publicName: "supportDynamicConditions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { queryChange: "queryChange", querySubmit: "querySubmit" }, host: { listeners: { "keydown.enter": "onHostEnter($event)" } }, providers: [SmartSearchEditController], viewQueries: [{ propertyName: "auto", first: true, predicate: ["auto"], descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"smart-search\" halo-container halo-container-skip=\"true\">\n <!-- Condition chip template \u2014 passed down into the recursive group component. -->\n <ng-template #chipTpl let-condition let-container=\"container\" let-i=\"index\">\n <div\n class=\"condition-chip\"\n [class.condition-chip--unset]=\"isUnset(condition)\"\n [class.condition-chip--dynamic]=\"condition.dynamic\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'yuv.smart-search.condition.edit-aria' | translate: { label: condition.conditionLabel }\"\n (click)=\"editCondition(container, condition, i)\"\n (keydown.space)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n (keydown.enter)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n >\n @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n }\n\n <span class=\"condition-chip__part condition-chip__field\">\n <!-- <mat-icon>tune</mat-icon> -->\n {{ condition.fieldLabel }}\n </span>\n <span class=\"condition-chip__part condition-chip__op\">{{ condition.operatorLabel }}</span>\n <!-- A valueless operator states the whole condition on its own (`is empty`, or a date\n preset whose label already reads \u201CThis year\u201D), so it gets no value segment: the\n stored value is an internal marker (`thisYear`), not something to show the user. -->\n @if (!isValueless(condition.operator)) {\n <span class=\"condition-chip__part condition-chip__value\">\n @if (isUnset(condition)) {\n <span class=\"condition-chip__placeholder\">{{ 'yuv.smart-search.condition.unset-value' | translate }}</span>\n } @else if (valueRendererInput(condition); as rendererInput) {\n <ng-container *yuvRenderer=\"rendererInput\" />\n } @else {\n {{ condition.value }}\n }\n </span>\n }\n <!-- @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n } -->\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'yuv.smart-search.condition.remove' | translate\"\n (click)=\"$event.stopPropagation(); removeCondition(container, condition)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- Inline editor template \u2014 rendered inside whichever container is active. -->\n <ng-template #editorTpl>\n <!-- Enter is bound on the wrapper, not per input, so it also covers the value step's\n real metadata widget and runs after each widget's own key handling. -->\n <div\n class=\"inline-input\"\n tabindex=\"-1\"\n (focusout)=\"onInlineBlur($event)\"\n (keydown.enter)=\"onEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n >\n @if (ctrl.step() !== 'field' && ctrl.pendingField()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'field'\"\n (click)=\"editField()\"\n [matTooltip]=\"'yuv.smart-search.field.change' | translate\"\n >\n {{ ctrl.pendingField()?.label }}\n </button>\n }\n\n <!-- The operator is always a pill, never editable text: at the value step it is the\n way back to the operator picker, and at the operator step it shows what is\n currently set while the (empty) input below filters the replacement list. -->\n @if (ctrl.step() !== 'field' && ctrl.pendingOperatorLabel()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'operator'\"\n (click)=\"editOperator()\"\n [matTooltip]=\"'yuv.smart-search.operator.change' | translate\"\n >\n {{ ctrl.pendingOperatorLabel() }}\n </button>\n }\n\n @if (ctrl.step() === 'field') {\n <input\n [formControl]=\"ctrl.fieldCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.field.pick' | translate\"\n />\n } @else if (ctrl.step() === 'operator') {\n <input\n [formControl]=\"ctrl.operatorCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.operator.select' | translate\"\n />\n } @else if (ctrl.step() === 'value') {\n @let otf = valueFieldDef();\n @if (otf) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"otf\" situation=\"EDIT\" [formControl]=\"ctrl.valueCtrl\" />\n } @else {\n <input\n [formControl]=\"ctrl.valueCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.value.enter' | translate\"\n />\n }\n }\n\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"confirm-btn\"\n [disabled]=\"!ctrl.isConditionComplete()\"\n (click)=\"onEnter()\"\n [matTooltip]=\"'yuv.smart-search.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"cancel-btn\"\n (click)=\"cancelPending()\"\n [matTooltip]=\"'yuv.smart-search.cancel' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Toolbar: Essentials \u21C4 Full Form mode switch (only when the dynamic feature is enabled) \u2500\u2500 -->\n @if (!fulltextOnly() && supportDynamicConditions() && (hasDynamicConditions() || formMode())) {\n <div class=\"toolbar\">\n <div class=\"toolbar__mode\" role=\"group\" [attr.aria-label]=\"'yuv.smart-search.mode.label' | translate\">\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"formMode()\"\n [attr.aria-pressed]=\"formMode()\"\n (click)=\"enterFormMode()\"\n >\n {{ 'yuv.smart-search.mode.essentials' | translate }}\n </button>\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"!formMode()\"\n [attr.aria-pressed]=\"!formMode()\"\n (click)=\"exitFormMode()\"\n >\n {{ 'yuv.smart-search.mode.full' | translate }}\n </button>\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Generated fill-out form of the dynamic conditions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (formMode()) {\n <div class=\"dynamic-form\">\n @for (grp of formBlocks(); track grp.block.id; let last = $last) {\n <div class=\"dynamic-form__block\">\n <!-- Muted header listing the block's target types (a block matches any of them). -->\n <div class=\"dynamic-form__block-header\">\n @for (t of grp.block.types; track t.id) {\n @if (!$first) {\n <span class=\"dynamic-form__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"dynamic-form__type\">{{ t.label }}</span>\n }\n </div>\n\n <div class=\"dynamic-form__rows\">\n @for (f of grp.fields; track f.condition) {\n <div class=\"dynamic-form__row\">\n <span class=\"dynamic-form__label\">{{ f.condition.fieldLabel }}</span>\n <span class=\"dynamic-form__op\">{{ f.condition.operatorLabel }}</span>\n <div class=\"dynamic-form__value\">\n @if (f.def) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"f.def\" situation=\"EDIT\" [formControl]=\"f.control\" />\n } @else {\n <input [formControl]=\"f.control\" [placeholder]=\"'yuv.smart-search.value.enter' | translate\" />\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Blocks are type-scoped and always combine with OR (\"this type as well as that type\"). -->\n @if (!last) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n } @empty {\n <span class=\"dynamic-form__empty\">{{ 'yuv.smart-search.mode.empty' | translate }}</span>\n }\n </div>\n } @else {\n <!-- \u2500\u2500 Full-text search bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Muting hints \"the blocks carry the search, not me\" \u2014 with the blocks hidden there\n is nothing to defer to, so the only visible control never greys out. -->\n <div class=\"fulltext\" [class.muted]=\"!fulltextOnly() && !fulltextActive() && ctrl.blocks().length\">\n <!-- Row 1: full-width term -->\n <div class=\"fulltext__term\">\n <mat-icon class=\"fulltext__icon\">search</mat-icon>\n <input\n class=\"fulltext__input\"\n [formControl]=\"fulltextTermCtrl\"\n [placeholder]=\"'yuv.smart-search.fulltext.placeholder' | translate\"\n />\n </div>\n\n <!-- Row 2: scope (single) + types (multiple) -->\n <div class=\"fulltext__filters\">\n <mat-select\n class=\"fulltext__scope\"\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltext().scope\"\n (selectionChange)=\"setFulltextScope($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.scope.label' | translate\"\n >\n <mat-option value=\"all\">{{ 'yuv.smart-search.fulltext.scope.all' | translate }}</mat-option>\n <mat-option value=\"metadata\">{{ 'yuv.smart-search.fulltext.scope.metadata' | translate }}</mat-option>\n <mat-option value=\"content\">{{ 'yuv.smart-search.fulltext.scope.content' | translate }}</mat-option>\n </mat-select>\n\n <mat-select\n class=\"fulltext__types\"\n multiple\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltextTypeSelection()\"\n (selectionChange)=\"onFulltextTypesChange($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.types.label' | translate\"\n >\n <mat-option [value]=\"ALL_TYPES\">{{ 'yuv.smart-search.fulltext.types.all' | translate }}</mat-option>\n @for (t of objectTypes(); track t.id) {\n <mat-option [value]=\"t.id\">{{ t.label ?? t.id }}</mat-option>\n }\n </mat-select>\n </div>\n </div>\n\n <!-- \u2500\u2500 Condition builder (hidden in full-text-only mode) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!fulltextOnly()) {\n <!-- Joiner between the full-text unit and the condition blocks. Top-level units\n always combine with OR (\"looking for this as well as that\"), so this is a\n static label rather than a toggle. -->\n @if (fulltextActive() && ctrl.blocks().length) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n\n <!-- \u2500\u2500 Step 1: Type blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (block of ctrl.blocks(); track block.id) {\n <div class=\"block\" [attr.data-block-id]=\"block.id\" [class.block--active]=\"ctrl.activeBlock() === block\">\n <div class=\"block__header\">\n <div class=\"block__types\">\n @for (t of block.types; track t.id) {\n @if (!$first) {\n <span class=\"block__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"block__type\">\n <span class=\"block__type-label\">{{ t.label }}</span>\n </span>\n }\n </div>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"block__remove\"\n (click)=\"removeBlock(block)\"\n [matTooltip]=\"'yuv.smart-search.type.remove' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <div class=\"block__conditions\">\n <yuv-smart-search-group [group]=\"block\" [bare]=\"true\" [chipTpl]=\"chipTpl\" [editorTpl]=\"editorTpl\" />\n </div>\n </div>\n\n <!-- Static joiner between blocks \u2014 blocks are type-scoped, so they always\n combine with OR (\"this type as well as that type\"). -->\n @if (!$last && ctrl.showCombinator()) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n }\n\n <!-- \u2500\u2500 Step 1 input: add a type block (multi-select) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (ctrl.step() === 'type') {\n <div class=\"add-type-row\">\n <button\n type=\"button\"\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__icon\"\n (click)=\"typeInput.focus()\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n <mat-icon>add_circle_outline</mat-icon>\n </button>\n\n <!-- Staged draft types as removable Material chips -->\n <mat-chip-grid\n #typeChipGrid\n class=\"add-type-row__chips\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n @for (t of ctrl.draftTypes(); track t.id) {\n <mat-chip-row (removed)=\"ctrl.removeDraftType(t.id)\">\n {{ t.label }}\n <button matChipRemove [attr.aria-label]=\"'yuv.smart-search.type.remove-draft' | translate\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-chip-row>\n }\n <input\n #typeInput\n [formControl]=\"ctrl.fieldCtrl\"\n [placeholder]=\"'yuv.smart-search.add-type' | translate\"\n [matAutocomplete]=\"auto\"\n [matChipInputFor]=\"typeChipGrid\"\n [matChipInputSeparatorKeyCodes]=\"NO_SEPARATOR_KEYS\"\n (keydown.enter)=\"onTypeEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n />\n </mat-chip-grid>\n\n @if (ctrl.draftTypes().length) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__confirm\"\n (click)=\"confirmTypes()\"\n [matTooltip]=\"'yuv.smart-search.type.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n }\n </div>\n }\n }\n }\n\n <!-- \u2500\u2500 Shared autocomplete panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-autocomplete\n #auto\n panelWidth=\"auto\"\n [displayWith]=\"displayFn\"\n (optionSelected)=\"onSuggestionSelected($event)\"\n (closed)=\"onPickerClosed()\"\n >\n @for (s of ctrl.suggestions(); track s.id) {\n <mat-option [value]=\"s\">\n <div class=\"suggestion\">\n @if (s.kind !== 'type') {\n <mat-icon class=\"suggestion__icon\">\n @switch (s.kind) {\n @case ('field') {\n tune\n }\n @case ('date-preset') {\n calendar_today\n }\n @default {\n manage_search\n }\n }\n </mat-icon>\n }\n <span class=\"suggestion__label\">{{ s.label }}</span>\n </div>\n </mat-option>\n }\n </mat-autocomplete>\n</div>\n", styles: [":host{display:block;--outline: rgb(from var(--ymt-text-color) r g b / .5);--focus-visible-border-color: var(--ymt-primary);--focus-visible-border-shadow-color: rgb(from var(--ymt-primary) r g b / .3)}::ng-deep .smart-search-select-panel.mat-mdc-select-panel{min-width:max-content}::ng-deep .smart-search-select-panel.mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{white-space:nowrap}.smart-search{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.fulltext{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px solid var(--outline);border-radius:var(--ymt-corner-s);transition:opacity .15s}.fulltext.muted{opacity:.7}.fulltext.muted:hover,.fulltext.muted:focus-within{opacity:1}.fulltext__term{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.fulltext__icon{color:var(--ymt-text-color-subtle)}.fulltext__input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.fulltext__input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.fulltext__filters{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ymt-spacing-s)}.fulltext__scope,.fulltext__types{width:auto;font-size:.9em;color:var(--ymt-text-color-subtle);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding-inline:var(--ymt-spacing-xs)}.fulltext__scope:focus-visible,.fulltext__types:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.block{border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden;transition:border-color .15s;padding:var(--ymt-spacing-2xs)}.block:focus-within{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 3px var(--focus-visible-border-shadow-color)}.block__header{border-radius:var(--ymt-corner-xs);display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);background:var(--ymt-surface-container-high)}.block__types{flex:1;display:flex;align-items:center;flex-wrap:wrap}.block__type{display:inline-flex;align-items:center;gap:var(--ymt-spacing-2xs)}.block__type-sep{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-xs)}.block__remove{margin-inline-start:auto;opacity:.7}.block__conditions{display:flex;flex-direction:column;align-items:stretch;gap:var(--ymt-spacing-2xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);min-height:44px}.condition-chip{display:flex;flex:1;align-items:center;gap:0;border-radius:var(--ymt-corner-xs);border:1px solid var(--outline);cursor:pointer;overflow:hidden;outline:none;gap:var(--ymt-spacing-2xs)}.condition-chip:hover{border-color:var(--ymt-outline)}.condition-chip:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__part{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);padding:0 var(--ymt-spacing-xs);height:100%;white-space:nowrap;color:var(--ymt-text-color)}.condition-chip__part:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);padding:0}.condition-chip__value{flex:1;overflow:hidden;text-overflow:ellipsis;min-height:24px;--tile-slot-padding: 0;--yuv-renderer-display: inline-flex}.condition-chip__remove{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__remove:hover{color:var(--ymt-text-color)}.condition-chip__placeholder{font:var(--ymt-font-body-subtle);font-style:italic;color:var(--ymt-text-color-subtle);opacity:.8}.condition-chip__dynamic{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__dynamic:hover{color:var(--ymt-text-color)}.condition-chip--dynamic{outline:2px solid var(--ymt-inverse-surface);outline-offset:1px}.condition-chip--dynamic .condition-chip__dynamic{border-radius:0;background-color:var(--ymt-inverse-surface);color:var(--ymt-on-inverse-surface)}.toolbar{display:flex;justify-content:flex-end;align-items:center;background:var(--ymt-surface-container-high);border-radius:var(--ymt-corner-xs);border:1px solid var(--ymt-outline);padding:var(--ymt-spacing-3xs)}.toolbar__mode{display:inline-flex;gap:0}.toolbar__mode-btn{--bg: transparent;--fg: var(--ymt-text-color);--mdc-shape-small: var(--ymt-corner-xs);font-size:.82em;background-color:var(--bg);color:var(--fg);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs);border:1px solid var(--ymt-inverse-surface);cursor:pointer}.toolbar__mode-btn:focus-visible{box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.toolbar__mode-btn:first-child{border-radius:var(--mdc-shape-small) 0 0 var(--mdc-shape-small)}.toolbar__mode-btn:last-child{border-radius:0 var(--mdc-shape-small) var(--mdc-shape-small) 0}.toolbar__mode-btn--active{--bg: var(--ymt-inverse-surface);--fg: var(--ymt-on-inverse-surface)}.dynamic-form{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.dynamic-form__block{container-type:inline-size;border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden}.dynamic-form__block-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-s);font:var(--ymt-font-body-subtle)}.dynamic-form__type-sep{text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-2xs)}.dynamic-form__rows{display:grid;grid-template-columns:max-content max-content 1fr;row-gap:var(--ymt-spacing-s);column-gap:var(--ymt-spacing-xs);overflow:auto;padding:var(--ymt-spacing-s)}.dynamic-form__row{display:grid;grid-template-columns:subgrid;grid-column:1/-1;align-items:center}.dynamic-form__label{color:var(--ymt-text-color)}.dynamic-form__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);border:1px solid var(--outline);padding-inline:var(--ymt-spacing-2xs);border-radius:var(--ymt-corner-xs)}.dynamic-form__value{flex:1;padding:var(--ymt-spacing-xs);border:1px solid var(--outline);border-radius:var(--ymt-corner-xs)}.dynamic-form__value input{width:100%;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.dynamic-form__empty{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}@container (max-width: 499px){.dynamic-form__rows{grid-template-columns:1fr auto}.dynamic-form__row{grid-template-columns:subgrid;grid-column:1/-1;row-gap:var(--ymt-spacing-2xs)}.dynamic-form__value{grid-column:1/-1}}.inline-input{display:flex;align-items:center;gap:var(--ymt-spacing-2xs);flex:1;border:1px dashed var(--outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-2xs);background:var(--ymt-surface)}.inline-input yuv-metadata-form-field{border-radius:var(--ymt-corner-xs)}.inline-input yuv-metadata-form-field:focus-within{outline:1px solid var(--focus-visible-border-color)}.inline-input .part-pill{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);white-space:nowrap;font:inherit;font-size:var(--ymt-font-body-subtle-size);color:var(--ymt-text-color);background:var(--ymt-surface-container);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-4xs) var(--ymt-spacing-xs);cursor:pointer;transition:border-color .1s,background .1s}.inline-input .part-pill:hover{background:var(--ymt-surface-container-high)}.inline-input .part-pill:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill--active{border-color:var(--ymt-primary);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill mat-icon{font-size:var(--ymt-sizing-xs);width:var(--ymt-sizing-xs);height:var(--ymt-sizing-xs)}.inline-input yuv-metadata-form-field{flex:1;min-width:150px}.inline-input input{flex:1;border:none;outline:none;background:transparent}.inline-input .confirm-btn:not(:disabled){opacity:1;color:var(--ymt-color-accent)}.combinator{display:flex;justify-content:center;gap:0}.combinator__label{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs)}.add-type-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px dashed var(--ymt-outline);border-radius:var(--ymt-corner-s);color:var(--ymt-text-color-subtle)}.add-type-row__icon{display:inline-flex;align-items:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}.add-type-row__icon mat-icon{font-size:var(--ymt-sizing-m);width:var(--ymt-sizing-m);height:var(--ymt-sizing-m);opacity:.5}.add-type-row__icon:hover mat-icon{opacity:.8}.add-type-row__confirm{color:var(--ymt-primary)}.add-type-row__chips{flex:1;min-width:120px}.add-type-row input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;font-size:.9em;color:inherit}.add-type-row input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.condition-combinator{display:flex;align-self:center}.suggestion{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.suggestion__icon{font-size:var(--ymt-sizing-s);width:var(--ymt-sizing-s);height:var(--ymt-sizing-s);--icon-size: var(--ymt-sizing-s);opacity:.6}.suggestion__label{flex:1}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i4.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i4.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i4.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i4.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: YmtIconButtonDirective, selector: "button[ymtIconButton],button[ymt-icon-button],a[ymtIconButton],a[ymt-icon-button]", inputs: ["disabled", "disableRipple", "aria-disabled", "disabledInteractive", "icon-button-size"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MetadataFormFieldComponent, selector: "yuv-metadata-form-field", inputs: ["formChangedSubject", "field", "variant", "situation"] }, { kind: "directive", type: RendererDirective, selector: "[yuvRenderer]", inputs: ["yuvRenderer"] }, { kind: "component", type: SmartSearchGroupComponent, selector: "yuv-smart-search-group", inputs: ["group", "bare", "chipTpl", "editorTpl"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2509
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: SmartSearchComponent, isStandalone: true, selector: "yuv-smart-search", inputs: { types: { classPropertyName: "types", publicName: "types", isSignal: true, isRequired: false, transformFunction: null }, ignoreTypePermissions: { classPropertyName: "ignoreTypePermissions", publicName: "ignoreTypePermissions", isSignal: true, isRequired: false, transformFunction: null }, skipProperties: { classPropertyName: "skipProperties", publicName: "skipProperties", isSignal: true, isRequired: false, transformFunction: null }, fulltextOnly: { classPropertyName: "fulltextOnly", publicName: "fulltextOnly", isSignal: true, isRequired: false, transformFunction: null }, supportDynamicConditions: { classPropertyName: "supportDynamicConditions", publicName: "supportDynamicConditions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { queryChange: "queryChange", querySubmit: "querySubmit" }, host: { listeners: { "keydown.enter": "onHostEnter($event)" } }, providers: [SmartSearchEditController], viewQueries: [{ propertyName: "auto", first: true, predicate: ["auto"], descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"smart-search\" halo-container halo-container-skip=\"true\">\n <!-- Condition chip template \u2014 passed down into the recursive group component. -->\n <ng-template #chipTpl let-condition let-container=\"container\" let-i=\"index\">\n <div\n class=\"condition-chip\"\n [class.condition-chip--unset]=\"isUnset(condition)\"\n [class.condition-chip--dynamic]=\"condition.dynamic\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'yuv.smart-search.condition.edit-aria' | translate: { label: condition.conditionLabel }\"\n (click)=\"editCondition(container, condition, i)\"\n (keydown.space)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n (keydown.enter)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n >\n @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n }\n\n <span class=\"condition-chip__part condition-chip__field\">\n <!-- <mat-icon>tune</mat-icon> -->\n {{ condition.fieldLabel }}\n </span>\n <span class=\"condition-chip__part condition-chip__op\">{{ condition.operatorLabel }}</span>\n <!-- A valueless operator states the whole condition on its own (`is empty`, or a date\n preset whose label already reads \u201CThis year\u201D), so it gets no value segment: the\n stored value is an internal marker (`thisYear`), not something to show the user. -->\n @if (!isValueless(condition.operator)) {\n <span class=\"condition-chip__part condition-chip__value\">\n @if (isUnset(condition)) {\n <span class=\"condition-chip__placeholder\">{{ 'yuv.smart-search.condition.unset-value' | translate }}</span>\n } @else if (valueRendererInput(condition); as rendererInput) {\n <ng-container *yuvRenderer=\"rendererInput\" />\n } @else {\n {{ condition.value }}\n }\n </span>\n }\n <!-- @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n } -->\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'yuv.smart-search.condition.remove' | translate\"\n (click)=\"$event.stopPropagation(); removeCondition(container, condition)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- Inline editor template \u2014 rendered inside whichever container is active. -->\n <ng-template #editorTpl>\n <!-- Enter is bound on the wrapper, not per input, so it also covers the value step's\n real metadata widget and runs after each widget's own key handling. -->\n <div\n class=\"inline-input\"\n tabindex=\"-1\"\n (focusout)=\"onInlineBlur($event)\"\n (keydown.enter)=\"onEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n >\n @if (ctrl.step() !== 'field' && ctrl.pendingField()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'field'\"\n (click)=\"editField()\"\n [matTooltip]=\"'yuv.smart-search.field.change' | translate\"\n >\n {{ ctrl.pendingField()?.label }}\n </button>\n }\n\n <!-- The operator is always a pill, never editable text: at the value step it is the\n way back to the operator picker, and at the operator step it shows what is\n currently set while the (empty) input below filters the replacement list. -->\n @if (ctrl.step() !== 'field' && ctrl.pendingOperatorLabel()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'operator'\"\n (click)=\"editOperator()\"\n [matTooltip]=\"'yuv.smart-search.operator.change' | translate\"\n >\n {{ ctrl.pendingOperatorLabel() }}\n </button>\n }\n\n @if (ctrl.step() === 'field') {\n <input\n [formControl]=\"ctrl.fieldCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.field.pick' | translate\"\n />\n } @else if (ctrl.step() === 'operator') {\n <input\n [formControl]=\"ctrl.operatorCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.operator.select' | translate\"\n />\n } @else if (ctrl.step() === 'value') {\n @let otf = valueFieldDef();\n @if (otf) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"otf\" situation=\"EDIT\" [formControl]=\"ctrl.valueCtrl\" />\n } @else {\n <input\n [formControl]=\"ctrl.valueCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.value.enter' | translate\"\n />\n }\n }\n\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"confirm-btn\"\n [disabled]=\"!ctrl.isConditionComplete()\"\n (click)=\"onEnter()\"\n [matTooltip]=\"'yuv.smart-search.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"cancel-btn\"\n (click)=\"cancelPending()\"\n [matTooltip]=\"'yuv.smart-search.cancel' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Toolbar: Essentials \u21C4 Full Form mode switch (only when the dynamic feature is enabled) \u2500\u2500 -->\n @if (!fulltextOnly() && supportDynamicConditions() && (hasDynamicConditions() || formMode())) {\n <div class=\"toolbar\">\n <div class=\"toolbar__mode\" role=\"group\" [attr.aria-label]=\"'yuv.smart-search.mode.label' | translate\">\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"formMode()\"\n [attr.aria-pressed]=\"formMode()\"\n (click)=\"enterFormMode()\"\n >\n {{ 'yuv.smart-search.mode.essentials' | translate }}\n </button>\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"!formMode()\"\n [attr.aria-pressed]=\"!formMode()\"\n (click)=\"exitFormMode()\"\n >\n {{ 'yuv.smart-search.mode.full' | translate }}\n </button>\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Generated fill-out form of the dynamic conditions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (formMode()) {\n <div class=\"dynamic-form\">\n @for (grp of formBlocks(); track grp.block.id; let last = $last) {\n <div class=\"dynamic-form__block\">\n <!-- Muted header listing the block's target types (a block matches any of them). -->\n <div class=\"dynamic-form__block-header\">\n @for (t of grp.block.types; track t.id) {\n @if (!$first) {\n <span class=\"dynamic-form__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"dynamic-form__type\">{{ t.label }}</span>\n }\n </div>\n\n <div class=\"dynamic-form__rows\">\n @for (f of grp.fields; track f.condition) {\n <div class=\"dynamic-form__row\">\n <span class=\"dynamic-form__label\">{{ f.condition.fieldLabel }}</span>\n <span class=\"dynamic-form__op\">{{ f.condition.operatorLabel }}</span>\n <div class=\"dynamic-form__value\">\n @if (f.def) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"f.def\" situation=\"EDIT\" [formControl]=\"f.control\" />\n } @else {\n <input [formControl]=\"f.control\" [placeholder]=\"'yuv.smart-search.value.enter' | translate\" />\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Blocks are type-scoped and always combine with OR (\"this type as well as that type\"). -->\n @if (!last) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n } @empty {\n <span class=\"dynamic-form__empty\">{{ 'yuv.smart-search.mode.empty' | translate }}</span>\n }\n </div>\n } @else {\n <!-- \u2500\u2500 Full-text search bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Muting hints \"the blocks carry the search, not me\" \u2014 with the blocks hidden there\n is nothing to defer to, so the only visible control never greys out. -->\n <div class=\"fulltext\" [class.muted]=\"!fulltextOnly() && !fulltextActive() && ctrl.blocks().length\">\n <!-- Row 1: full-width term -->\n <div class=\"fulltext__term\">\n <mat-icon class=\"fulltext__icon\">search</mat-icon>\n <input\n class=\"fulltext__input\"\n [formControl]=\"fulltextTermCtrl\"\n [placeholder]=\"'yuv.smart-search.fulltext.placeholder' | translate\"\n />\n </div>\n\n <!-- Row 2: scope (single) + types (multiple) -->\n <div class=\"fulltext__filters\">\n <mat-select\n class=\"fulltext__scope\"\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltext().scope\"\n (selectionChange)=\"setFulltextScope($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.scope.label' | translate\"\n >\n <mat-option value=\"all\">{{ 'yuv.smart-search.fulltext.scope.all' | translate }}</mat-option>\n <mat-option value=\"metadata\">{{ 'yuv.smart-search.fulltext.scope.metadata' | translate }}</mat-option>\n <mat-option value=\"content\">{{ 'yuv.smart-search.fulltext.scope.content' | translate }}</mat-option>\n </mat-select>\n\n <mat-select\n class=\"fulltext__types\"\n multiple\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltextTypeSelection()\"\n (selectionChange)=\"onFulltextTypesChange($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.types.label' | translate\"\n >\n <mat-option [value]=\"ALL_TYPES\">{{ 'yuv.smart-search.fulltext.types.all' | translate }}</mat-option>\n @for (t of objectTypes(); track t.id) {\n <mat-option [value]=\"t.id\">{{ t.label ?? t.id }}</mat-option>\n }\n </mat-select>\n </div>\n </div>\n\n <!-- \u2500\u2500 Condition builder (hidden in full-text-only mode) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!fulltextOnly()) {\n <!-- Joiner between the full-text unit and the condition blocks. Top-level units\n always combine with OR (\"looking for this as well as that\"), so this is a\n static label rather than a toggle. -->\n @if (fulltextActive() && ctrl.blocks().length) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n\n <!-- \u2500\u2500 Step 1: Type blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (block of ctrl.blocks(); track block.id) {\n <div class=\"block\" [attr.data-block-id]=\"block.id\" [class.block--active]=\"ctrl.activeBlock() === block\">\n <div class=\"block__header\">\n <div class=\"block__types\">\n @for (t of block.types; track t.id) {\n @if (!$first) {\n <span class=\"block__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"block__type\">\n <span class=\"block__type-label\">{{ t.label }}</span>\n </span>\n }\n </div>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"block__remove\"\n (click)=\"removeBlock(block)\"\n [matTooltip]=\"'yuv.smart-search.type.remove' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <div class=\"block__conditions\">\n <yuv-smart-search-group [group]=\"block\" [bare]=\"true\" [chipTpl]=\"chipTpl\" [editorTpl]=\"editorTpl\" />\n </div>\n </div>\n\n <!-- Static joiner between blocks \u2014 blocks are type-scoped, so they always\n combine with OR (\"this type as well as that type\"). -->\n @if (!$last && ctrl.showCombinator()) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n }\n\n <!-- \u2500\u2500 Step 1 input: add a type block (multi-select) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (ctrl.step() === 'type') {\n <div class=\"add-type-row\">\n <button\n type=\"button\"\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__icon\"\n (click)=\"typeInput.focus()\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n <mat-icon>add_circle_outline</mat-icon>\n </button>\n\n <!-- Staged draft types as removable Material chips -->\n <mat-chip-grid\n #typeChipGrid\n class=\"add-type-row__chips\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n @for (t of ctrl.draftTypes(); track t.id) {\n <mat-chip-row (removed)=\"ctrl.removeDraftType(t.id)\">\n {{ t.label }}\n <button matChipRemove [attr.aria-label]=\"'yuv.smart-search.type.remove-draft' | translate\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-chip-row>\n }\n <input\n #typeInput\n [formControl]=\"ctrl.fieldCtrl\"\n [placeholder]=\"'yuv.smart-search.add-type' | translate\"\n [matAutocomplete]=\"auto\"\n [matChipInputFor]=\"typeChipGrid\"\n [matChipInputSeparatorKeyCodes]=\"NO_SEPARATOR_KEYS\"\n (keydown.enter)=\"onTypeEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n />\n </mat-chip-grid>\n\n @if (ctrl.draftTypes().length) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__confirm\"\n (click)=\"confirmTypes()\"\n [matTooltip]=\"'yuv.smart-search.type.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n }\n </div>\n }\n }\n }\n\n <!-- \u2500\u2500 Shared autocomplete panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-autocomplete\n #auto\n panelWidth=\"auto\"\n [displayWith]=\"displayFn\"\n (optionSelected)=\"onSuggestionSelected($event)\"\n (closed)=\"onPickerClosed()\"\n >\n @for (s of ctrl.suggestions(); track s.id) {\n <mat-option [value]=\"s\">\n <div class=\"suggestion\">\n @if (s.kind !== 'type') {\n <mat-icon class=\"suggestion__icon\">\n @switch (s.kind) {\n @case ('field') {\n tune\n }\n @case ('date-preset') {\n calendar_today\n }\n @default {\n manage_search\n }\n }\n </mat-icon>\n }\n <span class=\"suggestion__label\">{{ s.label }}</span>\n </div>\n </mat-option>\n }\n </mat-autocomplete>\n</div>\n", styles: [":host{display:block;--outline: rgb(from var(--ymt-text-color) r g b / .5);--focus-visible-border-color: var(--ymt-primary);--focus-visible-border-shadow-color: rgb(from var(--ymt-primary) r g b / .3)}::ng-deep .smart-search-select-panel.mat-mdc-select-panel{min-width:max-content}::ng-deep .smart-search-select-panel.mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{white-space:nowrap}.smart-search{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.fulltext{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px solid var(--outline);border-radius:var(--ymt-corner-s);transition:opacity .15s}.fulltext.muted{opacity:.7}.fulltext.muted:hover,.fulltext.muted:focus-within{opacity:1}.fulltext__term{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.fulltext__icon{color:var(--ymt-text-color-subtle)}.fulltext__input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.fulltext__input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.fulltext__filters{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ymt-spacing-s)}.fulltext__scope,.fulltext__types{width:auto;font-size:.9em;color:var(--ymt-text-color-subtle);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding-inline:var(--ymt-spacing-xs)}.fulltext__scope:focus-visible,.fulltext__types:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.block{border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden;transition:border-color .15s;padding:var(--ymt-spacing-2xs)}.block:focus-within{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 3px var(--focus-visible-border-shadow-color)}.block__header{border-radius:var(--ymt-corner-xs);display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);background:var(--ymt-surface-container-high)}.block__types{flex:1;display:flex;align-items:center;flex-wrap:wrap}.block__type{display:inline-flex;align-items:center;gap:var(--ymt-spacing-2xs)}.block__type-sep{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-xs)}.block__remove{margin-inline-start:auto;opacity:.7}.block__conditions{display:flex;flex-direction:column;align-items:stretch;gap:var(--ymt-spacing-2xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);min-height:44px}.condition-chip{display:flex;flex:1;align-items:center;gap:0;border-radius:var(--ymt-corner-xs);border:1px solid var(--outline);cursor:pointer;overflow:hidden;outline:none;gap:var(--ymt-spacing-2xs)}.condition-chip:hover{border-color:var(--ymt-outline)}.condition-chip:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__part{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);padding:0 var(--ymt-spacing-xs);height:100%;white-space:nowrap;color:var(--ymt-text-color)}.condition-chip__part:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);padding:0}.condition-chip__value{flex:1;overflow:hidden;text-overflow:ellipsis;min-height:24px;--tile-slot-padding: 0;--yuv-renderer-display: inline-flex}.condition-chip__remove{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__remove:hover{color:var(--ymt-text-color)}.condition-chip__placeholder{font:var(--ymt-font-body-subtle);font-style:italic;color:var(--ymt-text-color-subtle);opacity:.8}.condition-chip__dynamic{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__dynamic:hover{color:var(--ymt-text-color)}.condition-chip--dynamic{outline:2px solid var(--ymt-inverse-surface);outline-offset:1px}.condition-chip--dynamic .condition-chip__dynamic{border-radius:0;background-color:var(--ymt-inverse-surface);color:var(--ymt-on-inverse-surface)}.toolbar{display:flex;justify-content:flex-end;align-items:center;background:var(--ymt-surface-container-high);border-radius:var(--ymt-corner-xs);border:1px solid var(--ymt-outline);padding:var(--ymt-spacing-3xs)}.toolbar__mode{display:inline-flex;gap:0}.toolbar__mode-btn{--bg: transparent;--fg: var(--ymt-text-color);--mdc-shape-small: var(--ymt-corner-xs);font-size:.82em;background-color:var(--bg);color:var(--fg);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs);border:1px solid var(--ymt-inverse-surface);cursor:pointer}.toolbar__mode-btn:focus-visible{box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.toolbar__mode-btn:first-child{border-radius:var(--mdc-shape-small) 0 0 var(--mdc-shape-small)}.toolbar__mode-btn:last-child{border-radius:0 var(--mdc-shape-small) var(--mdc-shape-small) 0}.toolbar__mode-btn--active{--bg: var(--ymt-inverse-surface);--fg: var(--ymt-on-inverse-surface)}.dynamic-form{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.dynamic-form__block{container-type:inline-size;border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden}.dynamic-form__block-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-s);font:var(--ymt-font-body-subtle)}.dynamic-form__type-sep{text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-2xs)}.dynamic-form__rows{display:grid;grid-template-columns:max-content max-content 1fr;row-gap:var(--ymt-spacing-s);column-gap:var(--ymt-spacing-xs);overflow:auto;padding:var(--ymt-spacing-s)}.dynamic-form__row{display:grid;grid-template-columns:subgrid;grid-column:1/-1;align-items:center}.dynamic-form__label{color:var(--ymt-text-color)}.dynamic-form__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);border:1px solid var(--outline);padding-inline:var(--ymt-spacing-2xs);border-radius:var(--ymt-corner-xs)}.dynamic-form__value{flex:1;padding:var(--ymt-spacing-xs);border:1px solid var(--outline);border-radius:var(--ymt-corner-xs)}.dynamic-form__value input{width:100%;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.dynamic-form__empty{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}@container (max-width: 499px){.dynamic-form__rows{grid-template-columns:1fr auto}.dynamic-form__row{grid-template-columns:subgrid;grid-column:1/-1;row-gap:var(--ymt-spacing-2xs)}.dynamic-form__value{grid-column:1/-1}}.inline-input{display:flex;align-items:center;gap:var(--ymt-spacing-2xs);flex:1;border:1px dashed var(--outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-2xs);background:var(--ymt-surface)}.inline-input yuv-metadata-form-field{border-radius:var(--ymt-corner-xs)}.inline-input yuv-metadata-form-field:focus-within{outline:1px solid var(--focus-visible-border-color)}.inline-input .part-pill{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);white-space:nowrap;font:inherit;font-size:var(--ymt-font-body-subtle-size);color:var(--ymt-text-color);background:var(--ymt-surface-container);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-4xs) var(--ymt-spacing-xs);cursor:pointer;transition:border-color .1s,background .1s}.inline-input .part-pill:hover{background:var(--ymt-surface-container-high)}.inline-input .part-pill:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill--active{border-color:var(--ymt-primary);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill mat-icon{font-size:var(--ymt-sizing-xs);width:var(--ymt-sizing-xs);height:var(--ymt-sizing-xs)}.inline-input yuv-metadata-form-field{flex:1;min-width:150px}.inline-input input{flex:1;border:none;outline:none;background:transparent}.inline-input .confirm-btn:not(:disabled){opacity:1;color:var(--ymt-color-accent)}.combinator{display:flex;justify-content:center;gap:0}.combinator__label{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs)}.add-type-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px dashed var(--ymt-outline);border-radius:var(--ymt-corner-s);color:var(--ymt-text-color-subtle)}.add-type-row__icon{display:inline-flex;align-items:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}.add-type-row__icon mat-icon{font-size:var(--ymt-sizing-m);width:var(--ymt-sizing-m);height:var(--ymt-sizing-m);opacity:.5}.add-type-row__icon:hover mat-icon{opacity:.8}.add-type-row__confirm{color:var(--ymt-primary)}.add-type-row__chips{flex:1;min-width:120px}.add-type-row input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;font-size:.9em;color:inherit}.add-type-row input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.condition-combinator{display:flex;align-self:center}.suggestion{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.suggestion__icon{font-size:var(--ymt-sizing-s);width:var(--ymt-sizing-s);height:var(--ymt-sizing-s);--icon-size: var(--ymt-sizing-s);opacity:.6}.suggestion__label{flex:1}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i4.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i4.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i4.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i4.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: YmtIconButtonDirective, selector: "button[ymtIconButton],button[ymt-icon-button],a[ymtIconButton],a[ymt-icon-button]", inputs: ["disabled", "disableRipple", "aria-disabled", "disabledInteractive", "icon-button-size"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MetadataFormFieldComponent, selector: "yuv-metadata-form-field", inputs: ["formChangedSubject", "field", "variant", "situation"] }, { kind: "directive", type: RendererDirective, selector: "[yuvRenderer]", inputs: ["yuvRenderer"] }, { kind: "component", type: SmartSearchGroupComponent, selector: "yuv-smart-search-group", inputs: ["group", "bare", "chipTpl", "editorTpl"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2491
2510
|
}
|
|
2492
2511
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: SmartSearchComponent, decorators: [{
|
|
2493
2512
|
type: Component,
|
|
@@ -2506,7 +2525,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
|
|
|
2506
2525
|
SmartSearchGroupComponent,
|
|
2507
2526
|
TranslatePipe
|
|
2508
2527
|
], providers: [SmartSearchEditController], host: { '(keydown.enter)': 'onHostEnter($event)' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"smart-search\" halo-container halo-container-skip=\"true\">\n <!-- Condition chip template \u2014 passed down into the recursive group component. -->\n <ng-template #chipTpl let-condition let-container=\"container\" let-i=\"index\">\n <div\n class=\"condition-chip\"\n [class.condition-chip--unset]=\"isUnset(condition)\"\n [class.condition-chip--dynamic]=\"condition.dynamic\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'yuv.smart-search.condition.edit-aria' | translate: { label: condition.conditionLabel }\"\n (click)=\"editCondition(container, condition, i)\"\n (keydown.space)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n (keydown.enter)=\"$event.preventDefault(); editCondition(container, condition, i)\"\n >\n @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n }\n\n <span class=\"condition-chip__part condition-chip__field\">\n <!-- <mat-icon>tune</mat-icon> -->\n {{ condition.fieldLabel }}\n </span>\n <span class=\"condition-chip__part condition-chip__op\">{{ condition.operatorLabel }}</span>\n <!-- A valueless operator states the whole condition on its own (`is empty`, or a date\n preset whose label already reads \u201CThis year\u201D), so it gets no value segment: the\n stored value is an internal marker (`thisYear`), not something to show the user. -->\n @if (!isValueless(condition.operator)) {\n <span class=\"condition-chip__part condition-chip__value\">\n @if (isUnset(condition)) {\n <span class=\"condition-chip__placeholder\">{{ 'yuv.smart-search.condition.unset-value' | translate }}</span>\n } @else if (valueRendererInput(condition); as rendererInput) {\n <ng-container *yuvRenderer=\"rendererInput\" />\n } @else {\n {{ condition.value }}\n }\n </span>\n }\n <!-- @if (supportDynamicConditions() && !isValueless(condition.operator)) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__dynamic\"\n tabindex=\"-1\"\n [attr.aria-pressed]=\"!!condition.dynamic\"\n [matTooltip]=\"\n (condition.dynamic\n ? 'yuv.smart-search.condition.unmark-dynamic'\n : 'yuv.smart-search.condition.mark-dynamic'\n ) | translate\n \"\n (click)=\"$event.stopPropagation(); toggleDynamic(container, condition)\"\n >\n <mat-icon>bolt</mat-icon>\n </button>\n } -->\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"condition-chip__remove\"\n tabindex=\"-1\"\n [attr.aria-label]=\"'yuv.smart-search.condition.remove' | translate\"\n (click)=\"$event.stopPropagation(); removeCondition(container, condition)\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- Inline editor template \u2014 rendered inside whichever container is active. -->\n <ng-template #editorTpl>\n <!-- Enter is bound on the wrapper, not per input, so it also covers the value step's\n real metadata widget and runs after each widget's own key handling. -->\n <div\n class=\"inline-input\"\n tabindex=\"-1\"\n (focusout)=\"onInlineBlur($event)\"\n (keydown.enter)=\"onEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n >\n @if (ctrl.step() !== 'field' && ctrl.pendingField()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'field'\"\n (click)=\"editField()\"\n [matTooltip]=\"'yuv.smart-search.field.change' | translate\"\n >\n {{ ctrl.pendingField()?.label }}\n </button>\n }\n\n <!-- The operator is always a pill, never editable text: at the value step it is the\n way back to the operator picker, and at the operator step it shows what is\n currently set while the (empty) input below filters the replacement list. -->\n @if (ctrl.step() !== 'field' && ctrl.pendingOperatorLabel()) {\n <button\n class=\"part-pill\"\n [class.part-pill--active]=\"ctrl.step() === 'operator'\"\n (click)=\"editOperator()\"\n [matTooltip]=\"'yuv.smart-search.operator.change' | translate\"\n >\n {{ ctrl.pendingOperatorLabel() }}\n </button>\n }\n\n @if (ctrl.step() === 'field') {\n <input\n [formControl]=\"ctrl.fieldCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.field.pick' | translate\"\n />\n } @else if (ctrl.step() === 'operator') {\n <input\n [formControl]=\"ctrl.operatorCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.operator.select' | translate\"\n />\n } @else if (ctrl.step() === 'value') {\n @let otf = valueFieldDef();\n @if (otf) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"otf\" situation=\"EDIT\" [formControl]=\"ctrl.valueCtrl\" />\n } @else {\n <input\n [formControl]=\"ctrl.valueCtrl\"\n [matAutocomplete]=\"auto\"\n [placeholder]=\"'yuv.smart-search.value.enter' | translate\"\n />\n }\n }\n\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"confirm-btn\"\n [disabled]=\"!ctrl.isConditionComplete()\"\n (click)=\"onEnter()\"\n [matTooltip]=\"'yuv.smart-search.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"cancel-btn\"\n (click)=\"cancelPending()\"\n [matTooltip]=\"'yuv.smart-search.cancel' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </ng-template>\n\n <!-- \u2500\u2500 Toolbar: Essentials \u21C4 Full Form mode switch (only when the dynamic feature is enabled) \u2500\u2500 -->\n @if (!fulltextOnly() && supportDynamicConditions() && (hasDynamicConditions() || formMode())) {\n <div class=\"toolbar\">\n <div class=\"toolbar__mode\" role=\"group\" [attr.aria-label]=\"'yuv.smart-search.mode.label' | translate\">\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"formMode()\"\n [attr.aria-pressed]=\"formMode()\"\n (click)=\"enterFormMode()\"\n >\n {{ 'yuv.smart-search.mode.essentials' | translate }}\n </button>\n <button\n type=\"button\"\n class=\"toolbar__mode-btn\"\n [class.toolbar__mode-btn--active]=\"!formMode()\"\n [attr.aria-pressed]=\"!formMode()\"\n (click)=\"exitFormMode()\"\n >\n {{ 'yuv.smart-search.mode.full' | translate }}\n </button>\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Generated fill-out form of the dynamic conditions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (formMode()) {\n <div class=\"dynamic-form\">\n @for (grp of formBlocks(); track grp.block.id; let last = $last) {\n <div class=\"dynamic-form__block\">\n <!-- Muted header listing the block's target types (a block matches any of them). -->\n <div class=\"dynamic-form__block-header\">\n @for (t of grp.block.types; track t.id) {\n @if (!$first) {\n <span class=\"dynamic-form__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"dynamic-form__type\">{{ t.label }}</span>\n }\n </div>\n\n <div class=\"dynamic-form__rows\">\n @for (f of grp.fields; track f.condition) {\n <div class=\"dynamic-form__row\">\n <span class=\"dynamic-form__label\">{{ f.condition.fieldLabel }}</span>\n <span class=\"dynamic-form__op\">{{ f.condition.operatorLabel }}</span>\n <div class=\"dynamic-form__value\">\n @if (f.def) {\n <yuv-metadata-form-field variant=\"raw\" [field]=\"f.def\" situation=\"EDIT\" [formControl]=\"f.control\" />\n } @else {\n <input [formControl]=\"f.control\" [placeholder]=\"'yuv.smart-search.value.enter' | translate\" />\n }\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Blocks are type-scoped and always combine with OR (\"this type as well as that type\"). -->\n @if (!last) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n } @empty {\n <span class=\"dynamic-form__empty\">{{ 'yuv.smart-search.mode.empty' | translate }}</span>\n }\n </div>\n } @else {\n <!-- \u2500\u2500 Full-text search bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- Muting hints \"the blocks carry the search, not me\" \u2014 with the blocks hidden there\n is nothing to defer to, so the only visible control never greys out. -->\n <div class=\"fulltext\" [class.muted]=\"!fulltextOnly() && !fulltextActive() && ctrl.blocks().length\">\n <!-- Row 1: full-width term -->\n <div class=\"fulltext__term\">\n <mat-icon class=\"fulltext__icon\">search</mat-icon>\n <input\n class=\"fulltext__input\"\n [formControl]=\"fulltextTermCtrl\"\n [placeholder]=\"'yuv.smart-search.fulltext.placeholder' | translate\"\n />\n </div>\n\n <!-- Row 2: scope (single) + types (multiple) -->\n <div class=\"fulltext__filters\">\n <mat-select\n class=\"fulltext__scope\"\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltext().scope\"\n (selectionChange)=\"setFulltextScope($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.scope.label' | translate\"\n >\n <mat-option value=\"all\">{{ 'yuv.smart-search.fulltext.scope.all' | translate }}</mat-option>\n <mat-option value=\"metadata\">{{ 'yuv.smart-search.fulltext.scope.metadata' | translate }}</mat-option>\n <mat-option value=\"content\">{{ 'yuv.smart-search.fulltext.scope.content' | translate }}</mat-option>\n </mat-select>\n\n <mat-select\n class=\"fulltext__types\"\n multiple\n [panelWidth]=\"null\"\n panelClass=\"smart-search-select-panel\"\n [value]=\"fulltextTypeSelection()\"\n (selectionChange)=\"onFulltextTypesChange($event.value)\"\n [aria-label]=\"'yuv.smart-search.fulltext.types.label' | translate\"\n >\n <mat-option [value]=\"ALL_TYPES\">{{ 'yuv.smart-search.fulltext.types.all' | translate }}</mat-option>\n @for (t of objectTypes(); track t.id) {\n <mat-option [value]=\"t.id\">{{ t.label ?? t.id }}</mat-option>\n }\n </mat-select>\n </div>\n </div>\n\n <!-- \u2500\u2500 Condition builder (hidden in full-text-only mode) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (!fulltextOnly()) {\n <!-- Joiner between the full-text unit and the condition blocks. Top-level units\n always combine with OR (\"looking for this as well as that\"), so this is a\n static label rather than a toggle. -->\n @if (fulltextActive() && ctrl.blocks().length) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n\n <!-- \u2500\u2500 Step 1: Type blocks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (block of ctrl.blocks(); track block.id) {\n <div class=\"block\" [attr.data-block-id]=\"block.id\" [class.block--active]=\"ctrl.activeBlock() === block\">\n <div class=\"block__header\">\n <div class=\"block__types\">\n @for (t of block.types; track t.id) {\n @if (!$first) {\n <span class=\"block__type-sep\">{{ 'yuv.smart-search.combinator.or' | translate }}</span>\n }\n <span class=\"block__type\">\n <span class=\"block__type-label\">{{ t.label }}</span>\n </span>\n }\n </div>\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"block__remove\"\n (click)=\"removeBlock(block)\"\n [matTooltip]=\"'yuv.smart-search.type.remove' | translate\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <div class=\"block__conditions\">\n <yuv-smart-search-group [group]=\"block\" [bare]=\"true\" [chipTpl]=\"chipTpl\" [editorTpl]=\"editorTpl\" />\n </div>\n </div>\n\n <!-- Static joiner between blocks \u2014 blocks are type-scoped, so they always\n combine with OR (\"this type as well as that type\"). -->\n @if (!$last && ctrl.showCombinator()) {\n <div class=\"combinator\">\n <span class=\"combinator__label\">{{ 'yuv.smart-search.combinator.as-well-as' | translate }}</span>\n </div>\n }\n }\n\n <!-- \u2500\u2500 Step 1 input: add a type block (multi-select) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (ctrl.step() === 'type') {\n <div class=\"add-type-row\">\n <button\n type=\"button\"\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__icon\"\n (click)=\"typeInput.focus()\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n <mat-icon>add_circle_outline</mat-icon>\n </button>\n\n <!-- Staged draft types as removable Material chips -->\n <mat-chip-grid\n #typeChipGrid\n class=\"add-type-row__chips\"\n [attr.aria-label]=\"'yuv.smart-search.add-type' | translate\"\n >\n @for (t of ctrl.draftTypes(); track t.id) {\n <mat-chip-row (removed)=\"ctrl.removeDraftType(t.id)\">\n {{ t.label }}\n <button matChipRemove [attr.aria-label]=\"'yuv.smart-search.type.remove-draft' | translate\">\n <mat-icon>close</mat-icon>\n </button>\n </mat-chip-row>\n }\n <input\n #typeInput\n [formControl]=\"ctrl.fieldCtrl\"\n [placeholder]=\"'yuv.smart-search.add-type' | translate\"\n [matAutocomplete]=\"auto\"\n [matChipInputFor]=\"typeChipGrid\"\n [matChipInputSeparatorKeyCodes]=\"NO_SEPARATOR_KEYS\"\n (keydown.enter)=\"onTypeEnter($event)\"\n (keydown.escape)=\"cancelPending()\"\n />\n </mat-chip-grid>\n\n @if (ctrl.draftTypes().length) {\n <button\n ymtIconButton\n icon-button-size=\"extra-small\"\n class=\"add-type-row__confirm\"\n (click)=\"confirmTypes()\"\n [matTooltip]=\"'yuv.smart-search.type.confirm' | translate\"\n >\n <mat-icon>check</mat-icon>\n </button>\n }\n </div>\n }\n }\n }\n\n <!-- \u2500\u2500 Shared autocomplete panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <mat-autocomplete\n #auto\n panelWidth=\"auto\"\n [displayWith]=\"displayFn\"\n (optionSelected)=\"onSuggestionSelected($event)\"\n (closed)=\"onPickerClosed()\"\n >\n @for (s of ctrl.suggestions(); track s.id) {\n <mat-option [value]=\"s\">\n <div class=\"suggestion\">\n @if (s.kind !== 'type') {\n <mat-icon class=\"suggestion__icon\">\n @switch (s.kind) {\n @case ('field') {\n tune\n }\n @case ('date-preset') {\n calendar_today\n }\n @default {\n manage_search\n }\n }\n </mat-icon>\n }\n <span class=\"suggestion__label\">{{ s.label }}</span>\n </div>\n </mat-option>\n }\n </mat-autocomplete>\n</div>\n", styles: [":host{display:block;--outline: rgb(from var(--ymt-text-color) r g b / .5);--focus-visible-border-color: var(--ymt-primary);--focus-visible-border-shadow-color: rgb(from var(--ymt-primary) r g b / .3)}::ng-deep .smart-search-select-panel.mat-mdc-select-panel{min-width:max-content}::ng-deep .smart-search-select-panel.mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{white-space:nowrap}.smart-search{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.fulltext{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px solid var(--outline);border-radius:var(--ymt-corner-s);transition:opacity .15s}.fulltext.muted{opacity:.7}.fulltext.muted:hover,.fulltext.muted:focus-within{opacity:1}.fulltext__term{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.fulltext__icon{color:var(--ymt-text-color-subtle)}.fulltext__input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.fulltext__input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.fulltext__filters{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--ymt-spacing-s)}.fulltext__scope,.fulltext__types{width:auto;font-size:.9em;color:var(--ymt-text-color-subtle);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding-inline:var(--ymt-spacing-xs)}.fulltext__scope:focus-visible,.fulltext__types:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.block{border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden;transition:border-color .15s;padding:var(--ymt-spacing-2xs)}.block:focus-within{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 3px var(--focus-visible-border-shadow-color)}.block__header{border-radius:var(--ymt-corner-xs);display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);background:var(--ymt-surface-container-high)}.block__types{flex:1;display:flex;align-items:center;flex-wrap:wrap}.block__type{display:inline-flex;align-items:center;gap:var(--ymt-spacing-2xs)}.block__type-sep{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-xs)}.block__remove{margin-inline-start:auto;opacity:.7}.block__conditions{display:flex;flex-direction:column;align-items:stretch;gap:var(--ymt-spacing-2xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);min-height:44px}.condition-chip{display:flex;flex:1;align-items:center;gap:0;border-radius:var(--ymt-corner-xs);border:1px solid var(--outline);cursor:pointer;overflow:hidden;outline:none;gap:var(--ymt-spacing-2xs)}.condition-chip:hover{border-color:var(--ymt-outline)}.condition-chip:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__part{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);padding:0 var(--ymt-spacing-xs);height:100%;white-space:nowrap;color:var(--ymt-text-color)}.condition-chip__part:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.condition-chip__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);padding:0}.condition-chip__value{flex:1;overflow:hidden;text-overflow:ellipsis;min-height:24px;--tile-slot-padding: 0;--yuv-renderer-display: inline-flex}.condition-chip__remove{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__remove:hover{color:var(--ymt-text-color)}.condition-chip__placeholder{font:var(--ymt-font-body-subtle);font-style:italic;color:var(--ymt-text-color-subtle);opacity:.8}.condition-chip__dynamic{--mat-icon-button-container-shape: 0;color:var(--ymt-text-color-subtle)}.condition-chip__dynamic:hover{color:var(--ymt-text-color)}.condition-chip--dynamic{outline:2px solid var(--ymt-inverse-surface);outline-offset:1px}.condition-chip--dynamic .condition-chip__dynamic{border-radius:0;background-color:var(--ymt-inverse-surface);color:var(--ymt-on-inverse-surface)}.toolbar{display:flex;justify-content:flex-end;align-items:center;background:var(--ymt-surface-container-high);border-radius:var(--ymt-corner-xs);border:1px solid var(--ymt-outline);padding:var(--ymt-spacing-3xs)}.toolbar__mode{display:inline-flex;gap:0}.toolbar__mode-btn{--bg: transparent;--fg: var(--ymt-text-color);--mdc-shape-small: var(--ymt-corner-xs);font-size:.82em;background-color:var(--bg);color:var(--fg);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs);border:1px solid var(--ymt-inverse-surface);cursor:pointer}.toolbar__mode-btn:focus-visible{box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.toolbar__mode-btn:first-child{border-radius:var(--mdc-shape-small) 0 0 var(--mdc-shape-small)}.toolbar__mode-btn:last-child{border-radius:0 var(--mdc-shape-small) var(--mdc-shape-small) 0}.toolbar__mode-btn--active{--bg: var(--ymt-inverse-surface);--fg: var(--ymt-on-inverse-surface)}.dynamic-form{display:flex;flex-direction:column;gap:var(--ymt-spacing-xs)}.dynamic-form__block{container-type:inline-size;border:1px solid var(--outline);border-radius:var(--ymt-corner-s);overflow:hidden}.dynamic-form__block-header{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-s);font:var(--ymt-font-body-subtle)}.dynamic-form__type-sep{text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:0 var(--ymt-spacing-2xs)}.dynamic-form__rows{display:grid;grid-template-columns:max-content max-content 1fr;row-gap:var(--ymt-spacing-s);column-gap:var(--ymt-spacing-xs);overflow:auto;padding:var(--ymt-spacing-s)}.dynamic-form__row{display:grid;grid-template-columns:subgrid;grid-column:1/-1;align-items:center}.dynamic-form__label{color:var(--ymt-text-color)}.dynamic-form__op{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle);border:1px solid var(--outline);padding-inline:var(--ymt-spacing-2xs);border-radius:var(--ymt-corner-xs)}.dynamic-form__value{flex:1;padding:var(--ymt-spacing-xs);border:1px solid var(--outline);border-radius:var(--ymt-corner-xs)}.dynamic-form__value input{width:100%;border:none;outline:none;background:transparent;font:inherit;color:var(--ymt-text-color)}.dynamic-form__empty{font:var(--ymt-font-body-subtle);color:var(--ymt-text-color-subtle)}@container (max-width: 499px){.dynamic-form__rows{grid-template-columns:1fr auto}.dynamic-form__row{grid-template-columns:subgrid;grid-column:1/-1;row-gap:var(--ymt-spacing-2xs)}.dynamic-form__value{grid-column:1/-1}}.inline-input{display:flex;align-items:center;gap:var(--ymt-spacing-2xs);flex:1;border:1px dashed var(--outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-2xs);background:var(--ymt-surface)}.inline-input yuv-metadata-form-field{border-radius:var(--ymt-corner-xs)}.inline-input yuv-metadata-form-field:focus-within{outline:1px solid var(--focus-visible-border-color)}.inline-input .part-pill{display:inline-flex;align-items:center;gap:var(--ymt-spacing-4xs);white-space:nowrap;font:inherit;font-size:var(--ymt-font-body-subtle-size);color:var(--ymt-text-color);background:var(--ymt-surface-container);border:1px solid var(--ymt-outline);border-radius:var(--ymt-corner-xs);padding:var(--ymt-spacing-4xs) var(--ymt-spacing-xs);cursor:pointer;transition:border-color .1s,background .1s}.inline-input .part-pill:hover{background:var(--ymt-surface-container-high)}.inline-input .part-pill:focus-visible{border-color:var(--focus-visible-border-color);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill--active{border-color:var(--ymt-primary);box-shadow:0 0 0 2px var(--focus-visible-border-shadow-color)}.inline-input .part-pill mat-icon{font-size:var(--ymt-sizing-xs);width:var(--ymt-sizing-xs);height:var(--ymt-sizing-xs)}.inline-input yuv-metadata-form-field{flex:1;min-width:150px}.inline-input input{flex:1;border:none;outline:none;background:transparent}.inline-input .confirm-btn:not(:disabled){opacity:1;color:var(--ymt-color-accent)}.combinator{display:flex;justify-content:center;gap:0}.combinator__label{font:var(--ymt-font-body-subtle);text-transform:lowercase;color:var(--ymt-text-color-subtle);padding:var(--ymt-spacing-2xs) var(--ymt-spacing-xs)}.add-type-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ymt-spacing-xs);padding:var(--ymt-spacing-xs) var(--ymt-spacing-s);border:1px dashed var(--ymt-outline);border-radius:var(--ymt-corner-s);color:var(--ymt-text-color-subtle)}.add-type-row__icon{display:inline-flex;align-items:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer}.add-type-row__icon mat-icon{font-size:var(--ymt-sizing-m);width:var(--ymt-sizing-m);height:var(--ymt-sizing-m);opacity:.5}.add-type-row__icon:hover mat-icon{opacity:.8}.add-type-row__confirm{color:var(--ymt-primary)}.add-type-row__chips{flex:1;min-width:120px}.add-type-row input{flex:1;min-width:120px;border:none;outline:none;background:transparent;font:inherit;font-size:.9em;color:inherit}.add-type-row input::placeholder{color:var(--ymt-text-color-subtle);opacity:.7}.condition-combinator{display:flex;align-self:center}.suggestion{display:flex;align-items:center;gap:var(--ymt-spacing-xs)}.suggestion__icon{font-size:var(--ymt-sizing-s);width:var(--ymt-sizing-s);height:var(--ymt-sizing-s);--icon-size: var(--ymt-sizing-s);opacity:.6}.suggestion__label{flex:1}\n"] }]
|
|
2509
|
-
}], ctorParameters: () => [], propDecorators: { types: [{ type: i0.Input, args: [{ isSignal: true, alias: "types", required: false }] }], skipProperties: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipProperties", required: false }] }], fulltextOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "fulltextOnly", required: false }] }], supportDynamicConditions: [{ type: i0.Input, args: [{ isSignal: true, alias: "supportDynamicConditions", required: false }] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], querySubmit: [{ type: i0.Output, args: ["querySubmit"] }], auto: [{ type: i0.ViewChild, args: ['auto', { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocompleteTrigger), { isSignal: true }] }] } });
|
|
2528
|
+
}], ctorParameters: () => [], propDecorators: { types: [{ type: i0.Input, args: [{ isSignal: true, alias: "types", required: false }] }], ignoreTypePermissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreTypePermissions", required: false }] }], skipProperties: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipProperties", required: false }] }], fulltextOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "fulltextOnly", required: false }] }], supportDynamicConditions: [{ type: i0.Input, args: [{ isSignal: true, alias: "supportDynamicConditions", required: false }] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], querySubmit: [{ type: i0.Output, args: ["querySubmit"] }], auto: [{ type: i0.ViewChild, args: ['auto', { isSignal: true }] }], trigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocompleteTrigger), { isSignal: true }] }] } });
|
|
2510
2529
|
|
|
2511
2530
|
/**
|
|
2512
2531
|
* Convenience NgModule that imports and re-exports {@link SmartSearchComponent}.
|