@smartsoft001-mobilems/objects-shell-angular 2.127.0 → 2.129.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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, contentChild, ChangeDetectionStrategy, Component, NgModule, PLATFORM_ID, isDevMode, Directive, signal, effect, viewChild, computed, untracked } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, contentChild, ChangeDetectionStrategy, Component, NgModule, PLATFORM_ID, isDevMode, Directive, signal, effect, viewChild, computed, untracked, input, output, linkedSignal } from '@angular/core';
|
|
3
3
|
import { FILE_SERVICE_CONFIG, PaginationMode, ListMode } from '@smartsoft001/angular';
|
|
4
4
|
import { CrudFacade, initialState as initialState$1, getReducer as getReducer$1, CrudService, CrudConfig, CrudFullConfig, CrudCoreModule, CrudModule, CrudListPageBaseComponent, CrudItemPageBaseComponent } from '@smartsoft001/crud-shell-angular';
|
|
5
|
-
import { CrudBaseService, FiltersBaseComponent, DICTIONARY_PAGE_SIZE, FileUrlService, provideInheritedSharedConfig, SharedModule, SeoService, TranslationService, MuseumSeoService, WcagChangesService, createSafeTimeouts, MasonryGridComponent, createCatalog, createCatalogLoadMore, toPlainText, namedEntryParts } from '@smartsoft001-mobilems/angular';
|
|
5
|
+
import { CrudBaseService, FiltersBaseComponent, DICTIONARY_PAGE_SIZE, FileUrlService, provideInheritedSharedConfig, SharedModule, SeoService, TranslationService, MuseumSeoService, WcagChangesService, createSafeTimeouts, MasonryGridComponent, createCatalog, createCatalogLoadMore, toPlainText, namedEntryParts, serializeCatalogFilter, createCycleTagSearch, mergeSelectedTags, dictionaryFilterItems, dictionaryEntryKey, dictionaryEntries, SHARE_LINK_COPIED_TIMEOUT, ShareLinkBaseComponent } from '@smartsoft001-mobilems/angular';
|
|
6
6
|
import { NgTemplateOutlet, CommonModule, isPlatformServer, isPlatformBrowser, DOCUMENT } from '@angular/common';
|
|
7
7
|
import { firstValueFrom, retry, timer, throwError, map, tap, catchError, of, expand, reduce, switchMap, from, mergeMap, toArray, filter, takeUntil } from 'rxjs';
|
|
8
8
|
import { createFeatureSelector } from '@ngrx/store';
|
|
@@ -1100,9 +1100,650 @@ function askAboutObjectHref(translate, object) {
|
|
|
1100
1100
|
return `mailto:${email}${query.length ? `?${query.join('&')}` : ''}`;
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
|
+
/** Translation keys of the "Pokaż tylko" multimedia entries, by `FormFeatureType`. */
|
|
1104
|
+
const OBJECTS_ADVANCED_FILTERS_MULTIMEDIA_LABELS = {
|
|
1105
|
+
[FormFeatureType.IMAGES]: 'OBJECTS.filters.multimedia.images',
|
|
1106
|
+
[FormFeatureType.VIDEO]: 'OBJECTS.filters.multimedia.video',
|
|
1107
|
+
};
|
|
1108
|
+
/**
|
|
1109
|
+
* Logic of the "Filtry zaawansowane" panel (Muzeum Podlasia's
|
|
1110
|
+
* `objects-advanced-filters`, MOB-1596, layout per the MOB-1802 mockup).
|
|
1111
|
+
* Presentational: reads `filter()`, keeps every edit in a local draft and only
|
|
1112
|
+
* writes through `apply` — the parent (the filters bar) performs the single
|
|
1113
|
+
* `context.filters` assignment. The template and the field config stay in the app.
|
|
1114
|
+
*/
|
|
1115
|
+
class ObjectsAdvancedFiltersBaseComponent {
|
|
1116
|
+
constructor(options) {
|
|
1117
|
+
this.objectsAdvancedFiltersTranslate = inject(TranslateService);
|
|
1118
|
+
this.objectsAdvancedFiltersService = inject(ObjectsDataService);
|
|
1119
|
+
/**
|
|
1120
|
+
* "Kolekcja tematyczna" (MOB-1844): pages of the `public-collections` list
|
|
1121
|
+
* (with the phrase in `filter[phrase]`) instead of a `dictionary/*` dictionary.
|
|
1122
|
+
*/
|
|
1123
|
+
this.collectionsSource = (page, query) => this.objectsAdvancedFiltersService.loadCollectionsDictionary(page, query);
|
|
1124
|
+
/**
|
|
1125
|
+
* "Filtry archeologiczne" (MOB-1844): the `ArchaeologicalData` dictionary
|
|
1126
|
+
* with a segregation — names planned by MobileMS (Kanbanize 50040).
|
|
1127
|
+
*/
|
|
1128
|
+
this.archeoTownsSource = (page, query) => this.objectsAdvancedFiltersService.loadArchaeologicalDictionary('ArcheoTown', page, query);
|
|
1129
|
+
this.archeoPeriodsSource = (page, query) => this.objectsAdvancedFiltersService.loadArchaeologicalDictionary('ArcheoPeriod', page, query);
|
|
1130
|
+
this.archeoCulturesSource = (page, query) => this.objectsAdvancedFiltersService.loadArchaeologicalDictionary('ArcheoCulture', page, query);
|
|
1131
|
+
this.filter = input.required(/* @ts-ignore */
|
|
1132
|
+
...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
|
|
1133
|
+
/** Toggled by the parent — reopening re-derives the draft, discarding it. */
|
|
1134
|
+
this.isOpen = input(false, /* @ts-ignore */
|
|
1135
|
+
...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
1136
|
+
/** The `/obiekty` list — a full-width band with the content in the page container. */
|
|
1137
|
+
this.fullBleed = input(false, /* @ts-ignore */
|
|
1138
|
+
...(ngDevMode ? [{ debugName: "fullBleed" }] : /* istanbul ignore next */ []));
|
|
1139
|
+
this.apply = output();
|
|
1140
|
+
/** Mobile: the "Filtry archeologiczne" section collapses with a chevron (2276:7709). */
|
|
1141
|
+
this.archaeologicalOpen = signal(true, /* @ts-ignore */
|
|
1142
|
+
...(ngDevMode ? [{ debugName: "archaeologicalOpen" }] : /* istanbul ignore next */ []));
|
|
1143
|
+
this.draftQuery = linkedSignal(() => {
|
|
1144
|
+
this.isOpen();
|
|
1145
|
+
return this.filter().query ? [...(this.filter().query ?? [])] : [];
|
|
1146
|
+
}, /* @ts-ignore */
|
|
1147
|
+
...(ngDevMode ? [{ debugName: "draftQuery" }] : /* istanbul ignore next */ []));
|
|
1148
|
+
/** Synthetic filter fed to the reused `app-filter-*` inputs so they read the draft. */
|
|
1149
|
+
this.draftFilter = computed(() => ({
|
|
1150
|
+
...this.filter(),
|
|
1151
|
+
query: this.draftQuery(),
|
|
1152
|
+
}), /* @ts-ignore */
|
|
1153
|
+
...(ngDevMode ? [{ debugName: "draftFilter" }] : /* istanbul ignore next */ []));
|
|
1154
|
+
this.objectsAdvancedFiltersOptions = {
|
|
1155
|
+
fields: options.fields,
|
|
1156
|
+
managedKeys: options.managedKeys,
|
|
1157
|
+
retiredKeys: options.retiredKeys ?? [],
|
|
1158
|
+
onExhibitionValue: options.onExhibitionValue ?? 'Ekspozycja',
|
|
1159
|
+
};
|
|
1160
|
+
this.fields = options.fields;
|
|
1161
|
+
}
|
|
1162
|
+
readDictionaryValue(key) {
|
|
1163
|
+
const value = this.draftQuery().find((q) => q.key === key)?.value;
|
|
1164
|
+
return Array.isArray(value) ? value : [];
|
|
1165
|
+
}
|
|
1166
|
+
onDictionaryToggle(key, item) {
|
|
1167
|
+
const current = this.readDictionaryValue(key);
|
|
1168
|
+
const isPresent = current.some((i) => i.id === item.id);
|
|
1169
|
+
const next = isPresent
|
|
1170
|
+
? current.filter((i) => i.id !== item.id)
|
|
1171
|
+
: [...current, item];
|
|
1172
|
+
this.objectsAdvancedFiltersWriteDictionary(key, next);
|
|
1173
|
+
// Picking a department means "Na ekspozycji: Tak" (MOB-1802) — the field
|
|
1174
|
+
// is enabled from the start and the checkbox follows the choice.
|
|
1175
|
+
const field = this.fields.find((f) => f.key === key);
|
|
1176
|
+
if (field?.dependsOn === 'onExhibition' && next.length) {
|
|
1177
|
+
this.objectsAdvancedFiltersSetDraftEntry('locationType', this.objectsAdvancedFiltersOptions.onExhibitionValue);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
onDictionaryClearAll(key) {
|
|
1181
|
+
this.objectsAdvancedFiltersWriteDictionary(key, []);
|
|
1182
|
+
}
|
|
1183
|
+
toggleArchaeological() {
|
|
1184
|
+
this.archaeologicalOpen.update((open) => !open);
|
|
1185
|
+
}
|
|
1186
|
+
onInventoryNumberChange(value) {
|
|
1187
|
+
this.objectsAdvancedFiltersSetDraftEntry('inventoryNumber', value.length ? value : undefined);
|
|
1188
|
+
}
|
|
1189
|
+
onBooleanToggle(key, checked) {
|
|
1190
|
+
if (key === 'onExhibition') {
|
|
1191
|
+
this.objectsAdvancedFiltersSetDraftEntry('locationType', checked
|
|
1192
|
+
? this.objectsAdvancedFiltersOptions.onExhibitionValue
|
|
1193
|
+
: undefined);
|
|
1194
|
+
if (!checked) {
|
|
1195
|
+
this.onDictionaryClearAll('department');
|
|
1196
|
+
}
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const featureId = key === 'hasImage' ? FormFeatureType.IMAGES : FormFeatureType.VIDEO;
|
|
1200
|
+
this.objectsAdvancedFiltersToggleFormFeature(featureId);
|
|
1201
|
+
}
|
|
1202
|
+
isBooleanChecked(key) {
|
|
1203
|
+
if (key === 'onExhibition') {
|
|
1204
|
+
return this.draftQuery().some((q) => q.key === 'locationType' &&
|
|
1205
|
+
q.value === this.objectsAdvancedFiltersOptions.onExhibitionValue);
|
|
1206
|
+
}
|
|
1207
|
+
const featureId = key === 'hasImage' ? FormFeatureType.IMAGES : FormFeatureType.VIDEO;
|
|
1208
|
+
return this.readDictionaryValue('formFeature').some((item) => item.id === featureId);
|
|
1209
|
+
}
|
|
1210
|
+
applyDraft() {
|
|
1211
|
+
this.apply.emit(this.objectsAdvancedFiltersBuildPayload());
|
|
1212
|
+
}
|
|
1213
|
+
reset() {
|
|
1214
|
+
this.draftQuery.set([]);
|
|
1215
|
+
this.apply.emit(this.objectsAdvancedFiltersBuildPayload());
|
|
1216
|
+
}
|
|
1217
|
+
objectsAdvancedFiltersToggleFormFeature(featureId) {
|
|
1218
|
+
const current = this.readDictionaryValue('formFeature');
|
|
1219
|
+
const isPresent = current.some((item) => item.id === featureId);
|
|
1220
|
+
if (isPresent) {
|
|
1221
|
+
this.objectsAdvancedFiltersWriteDictionary('formFeature', current.filter((item) => item.id !== featureId));
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const labelKey = OBJECTS_ADVANCED_FILTERS_MULTIMEDIA_LABELS[featureId];
|
|
1225
|
+
const name = labelKey
|
|
1226
|
+
? this.objectsAdvancedFiltersTranslate.instant(labelKey)
|
|
1227
|
+
: '';
|
|
1228
|
+
this.objectsAdvancedFiltersWriteDictionary('formFeature', [
|
|
1229
|
+
...current,
|
|
1230
|
+
{ id: featureId, name },
|
|
1231
|
+
]);
|
|
1232
|
+
}
|
|
1233
|
+
objectsAdvancedFiltersWriteDictionary(key, items) {
|
|
1234
|
+
this.objectsAdvancedFiltersSetDraftEntry(key, (items.length ? items : undefined));
|
|
1235
|
+
}
|
|
1236
|
+
objectsAdvancedFiltersSetDraftEntry(key, value) {
|
|
1237
|
+
this.draftQuery.update((current) => {
|
|
1238
|
+
const filtered = current.filter((q) => q.key !== key);
|
|
1239
|
+
return value === undefined
|
|
1240
|
+
? filtered
|
|
1241
|
+
: [...filtered, { key, type: '=', value }];
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
objectsAdvancedFiltersBuildPayload() {
|
|
1245
|
+
const { managedKeys, retiredKeys } = this.objectsAdvancedFiltersOptions;
|
|
1246
|
+
return [
|
|
1247
|
+
...managedKeys.map((key) => ({
|
|
1248
|
+
key,
|
|
1249
|
+
type: '=',
|
|
1250
|
+
val: this.draftQuery().find((q) => q.key === key)?.value,
|
|
1251
|
+
})),
|
|
1252
|
+
...retiredKeys
|
|
1253
|
+
.filter((key) => !managedKeys.includes(key))
|
|
1254
|
+
.map((key) => ({ key, type: '=', val: undefined })),
|
|
1255
|
+
];
|
|
1256
|
+
}
|
|
1257
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsAdvancedFiltersBaseComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1258
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: ObjectsAdvancedFiltersBaseComponent, isStandalone: true, inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, fullBleed: { classPropertyName: "fullBleed", publicName: "fullBleed", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply" }, ngImport: i0 }); }
|
|
1259
|
+
}
|
|
1260
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsAdvancedFiltersBaseComponent, decorators: [{
|
|
1261
|
+
type: Directive
|
|
1262
|
+
}], ctorParameters: () => [{ type: undefined }], propDecorators: { filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }], fullBleed: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullBleed", required: false }] }], apply: [{ type: i0.Output, args: ["apply"] }] } });
|
|
1263
|
+
|
|
1264
|
+
/** Sort options of the objects list (the default order). */
|
|
1265
|
+
const OBJECTS_SORT_OPTIONS = Object.freeze([
|
|
1266
|
+
{ key: 'random', value: 'OBJECTS.filters.random' },
|
|
1267
|
+
{ key: 'publication-date-desc', value: 'OBJECTS.filters.date-desc' },
|
|
1268
|
+
{ key: 'publication-date-asc', value: 'OBJECTS.filters.date-asc' },
|
|
1269
|
+
{ key: 'name-asc', value: 'OBJECTS.filters.name-asc' },
|
|
1270
|
+
{ key: 'name-desc', value: 'OBJECTS.filters.name-desc' },
|
|
1271
|
+
{ key: 'visited-desc', value: 'OBJECTS.filters.most-popular' },
|
|
1272
|
+
]);
|
|
1273
|
+
/** Sort order shown for `?type=new` — newest first. */
|
|
1274
|
+
const OBJECTS_NEW_TYPE_SORT_OPTIONS = Object.freeze([
|
|
1275
|
+
{ key: 'publication-date-desc', value: 'OBJECTS.filters.date-desc' },
|
|
1276
|
+
{ key: 'publication-date-asc', value: 'OBJECTS.filters.date-asc' },
|
|
1277
|
+
{ key: 'random', value: 'OBJECTS.filters.random' },
|
|
1278
|
+
{ key: 'name-asc', value: 'OBJECTS.filters.name-asc' },
|
|
1279
|
+
{ key: 'name-desc', value: 'OBJECTS.filters.name-desc' },
|
|
1280
|
+
{ key: 'visited-desc', value: 'OBJECTS.filters.most-popular' },
|
|
1281
|
+
]);
|
|
1282
|
+
/** Page sizes of the objects list. */
|
|
1283
|
+
const OBJECTS_LIMIT_OPTIONS = Object.freeze([
|
|
1284
|
+
{ key: '20' },
|
|
1285
|
+
{ key: '40' },
|
|
1286
|
+
{ key: '60' },
|
|
1287
|
+
{ key: '80' },
|
|
1288
|
+
{ key: '100' },
|
|
1289
|
+
]);
|
|
1290
|
+
/** API query key of the theme tags (cycles) filter. */
|
|
1291
|
+
const OBJECTS_THEME_TAGS_FILTER_KEY = 'cycles';
|
|
1292
|
+
/** Chip labels of the yes/no filters from the advanced filters panel. */
|
|
1293
|
+
const OBJECTS_FILTER_SINGLE_VALUE_LABELS = {
|
|
1294
|
+
onExhibition: 'SEARCH.advancedFiltersPanel.onExhibition',
|
|
1295
|
+
hasImage: 'SEARCH.advancedFiltersPanel.hasImage',
|
|
1296
|
+
hasVideo: 'SEARCH.advancedFiltersPanel.hasVideo',
|
|
1297
|
+
};
|
|
1298
|
+
/** The sort dropdown key (e.g. `publication-date-desc`) of a sort; no `by` → `random`. */
|
|
1299
|
+
function objectsSortKey(sort) {
|
|
1300
|
+
return !sort?.by ? 'random' : `${sort.by}-${sort.desc ? 'desc' : 'asc'}`;
|
|
1301
|
+
}
|
|
1302
|
+
/** The sort of a sort dropdown key; `random` → an empty `by`. */
|
|
1303
|
+
function objectsSortFromKey(key) {
|
|
1304
|
+
const desc = key.endsWith('-desc');
|
|
1305
|
+
let by = key.replace(/-asc$/, '').replace(/-desc$/, '');
|
|
1306
|
+
if (by === 'random') {
|
|
1307
|
+
by = '';
|
|
1308
|
+
}
|
|
1309
|
+
return { by, desc };
|
|
1310
|
+
}
|
|
1311
|
+
/** The list URL (`/obiekty` by default) restoring the current filter (phrase + query items). */
|
|
1312
|
+
function buildObjectsResultsUrl(origin, filter, listPath = '/obiekty') {
|
|
1313
|
+
const serialized = serializeCatalogFilter(filter);
|
|
1314
|
+
return `${origin}${listPath}${serialized ? `?filter=${encodeURIComponent(serialized)}` : ''}`;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/** API query key of the "Nr inwentarzowy" filter — `filter[inventoryNumber]`. */
|
|
1318
|
+
const OBJECTS_FILTERS_BAR_INVENTORY_NUMBER_KEY = 'inventoryNumber';
|
|
1319
|
+
const OBJECTS_FILTERS_BAR_SINGLE_VALUE_KEYS = [
|
|
1320
|
+
'categories',
|
|
1321
|
+
OBJECTS_FILTERS_BAR_INVENTORY_NUMBER_KEY,
|
|
1322
|
+
'locationType',
|
|
1323
|
+
];
|
|
1324
|
+
/** `val[0]` upper-cased; `''` for an empty value. */
|
|
1325
|
+
function objectsFiltersBarCapitalize(val) {
|
|
1326
|
+
if (!val?.length)
|
|
1327
|
+
return '';
|
|
1328
|
+
return val[0].toUpperCase() + val.substring(1);
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Stand-in `FiltersContext` of the search results tab (MOB-1818): the same
|
|
1332
|
+
* setters as the framework context (`filters`, `sort`, `limit`), but instead of
|
|
1333
|
+
* a read through the facade it hands over the complete filter — the list moves
|
|
1334
|
+
* to `/obiekty` with it. Query items are composed like
|
|
1335
|
+
* `FiltersContext.multipleFilterSet`: a write replaces the item with the same
|
|
1336
|
+
* key and type, `undefined` removes it.
|
|
1337
|
+
*/
|
|
1338
|
+
class ObjectsRedirectFiltersContext {
|
|
1339
|
+
constructor(current, redirect) {
|
|
1340
|
+
this.current = current;
|
|
1341
|
+
this.redirect = redirect;
|
|
1342
|
+
}
|
|
1343
|
+
get query() {
|
|
1344
|
+
return this.current().query;
|
|
1345
|
+
}
|
|
1346
|
+
set filters(writes) {
|
|
1347
|
+
let query = [...(this.current().query ?? [])];
|
|
1348
|
+
for (const { key, type, val } of writes) {
|
|
1349
|
+
query = query.filter((item) => !(item.key === key && item.type === type));
|
|
1350
|
+
if (val) {
|
|
1351
|
+
query.push({
|
|
1352
|
+
key,
|
|
1353
|
+
type,
|
|
1354
|
+
value: val,
|
|
1355
|
+
hidden: true,
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
this.emit({ query, offset: 0 });
|
|
1360
|
+
}
|
|
1361
|
+
set sort(value) {
|
|
1362
|
+
this.emit({ sortBy: value.by, sortDesc: value.desc, offset: 0 });
|
|
1363
|
+
}
|
|
1364
|
+
set limit(value) {
|
|
1365
|
+
this.emit({ limit: value, offset: 0 });
|
|
1366
|
+
}
|
|
1367
|
+
emit(patch) {
|
|
1368
|
+
this.redirect({ ...this.current(), ...patch });
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Logic of the objects list top filter bar (Muzeum Podlasia's `filters-top`):
|
|
1373
|
+
* theme tags, applied-filter chips, sort/limit dropdowns, pagination and the
|
|
1374
|
+
* grid/list view toggle. The template stays in the app.
|
|
1375
|
+
*
|
|
1376
|
+
* Every mutation goes through the framework `FiltersContext` provided by
|
|
1377
|
+
* `<smart-mobilems-objects-filters>` (passed to each method by the template),
|
|
1378
|
+
* which dispatches to the objects facade. With `redirectChanges` on (the search
|
|
1379
|
+
* results tab, MOB-1818) filter, sort and page size changes are emitted as one
|
|
1380
|
+
* complete filter through `filterRedirect` instead.
|
|
1381
|
+
*/
|
|
1382
|
+
class ObjectsFiltersBarBaseComponent {
|
|
1383
|
+
constructor(options = {}) {
|
|
1384
|
+
this.objectsFiltersBarTranslate = inject(TranslateService);
|
|
1385
|
+
/** Query key of the panel's theme tags — `filter[cycles][]`. */
|
|
1386
|
+
this.themeTagsFilterKey = OBJECTS_THEME_TAGS_FILTER_KEY;
|
|
1387
|
+
/**
|
|
1388
|
+
* The page's current filter, bound straight through to the filter fields.
|
|
1389
|
+
* Reads come from here; writes go through the FiltersContext — both sides are
|
|
1390
|
+
* the same facade state.
|
|
1391
|
+
*/
|
|
1392
|
+
this.filter = input.required(/* @ts-ignore */
|
|
1393
|
+
...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
|
|
1394
|
+
this.listMode = input(ListMode.masonryGrid, /* @ts-ignore */
|
|
1395
|
+
...(ngDevMode ? [{ debugName: "listMode" }] : /* istanbul ignore next */ []));
|
|
1396
|
+
/**
|
|
1397
|
+
* The result count from the list page — only for the response to the
|
|
1398
|
+
* current filter (the shared slice guard). Without it the count comes from
|
|
1399
|
+
* the framework context.
|
|
1400
|
+
*/
|
|
1401
|
+
this.totalCount = input(null, /* @ts-ignore */
|
|
1402
|
+
...(ngDevMode ? [{ debugName: "totalCount" }] : /* istanbul ignore next */ []));
|
|
1403
|
+
this.sortOptions = input(OBJECTS_SORT_OPTIONS, /* @ts-ignore */
|
|
1404
|
+
...(ngDevMode ? [{ debugName: "sortOptions" }] : /* istanbul ignore next */ []));
|
|
1405
|
+
/**
|
|
1406
|
+
* The search results tab (MOB-1818): filter, sort and page size changes do
|
|
1407
|
+
* not narrow the list in place, they go to `filterRedirect` with the
|
|
1408
|
+
* complete filter. Paging stays in the tab.
|
|
1409
|
+
*/
|
|
1410
|
+
this.redirectChanges = input(false, /* @ts-ignore */
|
|
1411
|
+
...(ngDevMode ? [{ debugName: "redirectChanges" }] : /* istanbul ignore next */ []));
|
|
1412
|
+
/** The complete filter after a change in the results tab — the list moves to `/obiekty` with it. */
|
|
1413
|
+
this.filterRedirect = output();
|
|
1414
|
+
this.listModeChange = output();
|
|
1415
|
+
/** Free-text search — the list page owns the read so it can persist it. */
|
|
1416
|
+
this.searchEvent = output();
|
|
1417
|
+
this.isAdvancedFiltersOn = signal(false, /* @ts-ignore */
|
|
1418
|
+
...(ngDevMode ? [{ debugName: "isAdvancedFiltersOn" }] : /* istanbul ignore next */ []));
|
|
1419
|
+
this.limitOptions = OBJECTS_LIMIT_OPTIONS;
|
|
1420
|
+
this.objectsFiltersBarSingleValueLabels =
|
|
1421
|
+
options.singleValueLabels ?? OBJECTS_FILTER_SINGLE_VALUE_LABELS;
|
|
1422
|
+
this.objectsFiltersBarSingleValueKeys =
|
|
1423
|
+
options.singleValueKeys ?? OBJECTS_FILTERS_BAR_SINGLE_VALUE_KEYS;
|
|
1424
|
+
// The whole `Cycle` dictionary (every page), with the hit phrase `q`
|
|
1425
|
+
// (MOB-1826, like the collections and articles lists). How many show at
|
|
1426
|
+
// once is the panel's call (6 + "Pokaż wszystkie").
|
|
1427
|
+
this.objectsFiltersBarCycleTags = options.cycleTags
|
|
1428
|
+
? createCycleTagSearch()
|
|
1429
|
+
: { query: signal(''), tags: signal([]) };
|
|
1430
|
+
this.tagsQuery = this.objectsFiltersBarCycleTags.query;
|
|
1431
|
+
const cycleTags = this.objectsFiltersBarCycleTags.tags;
|
|
1432
|
+
this.themeTags = computed(() => mergeSelectedTags(dictionaryFilterItems(this.filter()?.query?.find((q) => q.key === OBJECTS_THEME_TAGS_FILTER_KEY && q.type === '=')?.value), cycleTags()), /* @ts-ignore */
|
|
1433
|
+
...(ngDevMode ? [{ debugName: "themeTags" }] : /* istanbul ignore next */ []));
|
|
1434
|
+
}
|
|
1435
|
+
toggleAdvancedFilters(force) {
|
|
1436
|
+
this.isAdvancedFiltersOn.update((val) => force ?? !val);
|
|
1437
|
+
}
|
|
1438
|
+
onListMode(value) {
|
|
1439
|
+
this.listModeChange.emit(value);
|
|
1440
|
+
}
|
|
1441
|
+
/** Map a sort dropdown key (e.g. `publication-date-desc`) to the matching option. */
|
|
1442
|
+
selectedSort(sort) {
|
|
1443
|
+
const options = this.sortOptions();
|
|
1444
|
+
const key = objectsSortKey(sort);
|
|
1445
|
+
return options.find((option) => option.key === key) ?? options[0];
|
|
1446
|
+
}
|
|
1447
|
+
selectedLimit(limit) {
|
|
1448
|
+
return (this.limitOptions.find((option) => option.key === String(limit ?? 20)) ??
|
|
1449
|
+
this.limitOptions[0]);
|
|
1450
|
+
}
|
|
1451
|
+
sortChange(context, value) {
|
|
1452
|
+
this.objectsFiltersBarWriteTarget(context).sort = objectsSortFromKey(value);
|
|
1453
|
+
}
|
|
1454
|
+
limitChange(context, value) {
|
|
1455
|
+
this.objectsFiltersBarWriteTarget(context).limit = Number(value);
|
|
1456
|
+
}
|
|
1457
|
+
pageChange(context, value) {
|
|
1458
|
+
context.page = value;
|
|
1459
|
+
}
|
|
1460
|
+
/** Toggle a single dictionary entry on/off for `key`. */
|
|
1461
|
+
advancedFilterChange(context, entry, key) {
|
|
1462
|
+
// `"3"` from a deep link and `3` from the dictionary are the same entry —
|
|
1463
|
+
// a click removes it instead of adding a duplicate; an event without an id
|
|
1464
|
+
// changes nothing (MOB-1826 review).
|
|
1465
|
+
const eventKey = dictionaryEntryKey(entry?.id);
|
|
1466
|
+
if (eventKey === null) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
const current = this.objectsFiltersBarDictionaryValue(context, key);
|
|
1470
|
+
const isToggled = (item) => dictionaryEntryKey(item.id) === eventKey;
|
|
1471
|
+
const newValue = current.some(isToggled)
|
|
1472
|
+
? current.filter((item) => !isToggled(item))
|
|
1473
|
+
: [...current, entry];
|
|
1474
|
+
this.objectsFiltersBarSetDictionary(context, key, newValue);
|
|
1475
|
+
}
|
|
1476
|
+
/** The year field already debounces typing (500 ms, MOB-1866). */
|
|
1477
|
+
onCreateDatesFilterChange(context, range) {
|
|
1478
|
+
// Set both bounds in one go so the list is re-read once, not twice.
|
|
1479
|
+
this.objectsFiltersBarWriteTarget(context).filters = [
|
|
1480
|
+
{
|
|
1481
|
+
val: (range.from?.length ? range.from : undefined),
|
|
1482
|
+
key: 'createDates',
|
|
1483
|
+
type: '>=',
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
val: (range.to?.length ? range.to : undefined),
|
|
1487
|
+
key: 'createDates',
|
|
1488
|
+
type: '<=',
|
|
1489
|
+
},
|
|
1490
|
+
];
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Batched write from the "Filtry zaawansowane" panel (MOB-1596) — the panel
|
|
1494
|
+
* keeps its own draft and hands over the full snapshot on Zastosuj/Resetuj,
|
|
1495
|
+
* so this is the single `context.filters` assignment for that whole edit.
|
|
1496
|
+
*/
|
|
1497
|
+
onAdvancedFiltersApply(context, payload) {
|
|
1498
|
+
this.objectsFiltersBarWriteTarget(context).filters =
|
|
1499
|
+
payload;
|
|
1500
|
+
}
|
|
1501
|
+
/** A yes/no filter — unchecking sends `undefined`, which removes the query item. */
|
|
1502
|
+
onBooleanFilterChange(context, event) {
|
|
1503
|
+
this.objectsFiltersBarWriteTarget(context).filters = [
|
|
1504
|
+
{
|
|
1505
|
+
// `FilterType` has no boolean; a yes/no filter writes `true`.
|
|
1506
|
+
val: (event.checked ? true : undefined),
|
|
1507
|
+
key: event.key,
|
|
1508
|
+
type: '=',
|
|
1509
|
+
},
|
|
1510
|
+
];
|
|
1511
|
+
}
|
|
1512
|
+
clearFilter(context, item, value) {
|
|
1513
|
+
if (!item) {
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
// The date range and single-value filters (also the panel's yes/no ones,
|
|
1517
|
+
// e.g. "Z opisem") have no list of values — the chip clears the whole item.
|
|
1518
|
+
if ((item.key === 'createDates' &&
|
|
1519
|
+
(item.type === '>=' || item.type === '<=')) ||
|
|
1520
|
+
this.objectsFiltersBarSingleValueKeys.includes(item.key) ||
|
|
1521
|
+
!Array.isArray(item.value)) {
|
|
1522
|
+
this.objectsFiltersBarWriteTarget(context).filters = [
|
|
1523
|
+
{ val: undefined, key: item.key, type: item.type },
|
|
1524
|
+
];
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
// Removing the last entry of a dictionary filter drops the whole query item.
|
|
1528
|
+
if (value) {
|
|
1529
|
+
this.advancedFilterChange(context, value, item.key);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
isListValue(value) {
|
|
1533
|
+
return Array.isArray(value);
|
|
1534
|
+
}
|
|
1535
|
+
/** List entries for the chips — no `null` and no entries without an id (MOB-1826 review). */
|
|
1536
|
+
listEntries(value) {
|
|
1537
|
+
return dictionaryEntries(value);
|
|
1538
|
+
}
|
|
1539
|
+
/** Chip label of a filter without a list of values — a yes/no one has no value after the colon. */
|
|
1540
|
+
singleValueLabel(item) {
|
|
1541
|
+
const label = objectsFiltersBarCapitalize(this.objectsFiltersBarTranslate.instant(this.objectsFiltersBarSingleValueLabels[item.key] ??
|
|
1542
|
+
`OBJECTS.filters.applied.${item.key}`));
|
|
1543
|
+
return item.value === true ? label : `${label}: ${item.value}`;
|
|
1544
|
+
}
|
|
1545
|
+
$context(context) {
|
|
1546
|
+
return context;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Write a dictionary filter through the generic `filters` setter. An empty
|
|
1550
|
+
* selection must be sent as `undefined` — that is what removes the query item.
|
|
1551
|
+
*/
|
|
1552
|
+
objectsFiltersBarSetDictionary(context, key, value) {
|
|
1553
|
+
this.objectsFiltersBarWriteTarget(context).filters = [
|
|
1554
|
+
{ val: (value.length ? value : undefined), key, type: '=' },
|
|
1555
|
+
];
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Where a filter write goes: the framework context (read through the facade)
|
|
1559
|
+
* or — in the results tab — the redirect context to `/obiekty` (MOB-1818).
|
|
1560
|
+
*/
|
|
1561
|
+
objectsFiltersBarWriteTarget(context) {
|
|
1562
|
+
if (!this.redirectChanges()) {
|
|
1563
|
+
return context;
|
|
1564
|
+
}
|
|
1565
|
+
return new ObjectsRedirectFiltersContext(() => this.filter(), (filter) => this.filterRedirect.emit(filter));
|
|
1566
|
+
}
|
|
1567
|
+
objectsFiltersBarDictionaryValue(context, key) {
|
|
1568
|
+
const value = (context.query ?? []).find((q) => q.key === key && q.type === '=')?.value;
|
|
1569
|
+
return dictionaryEntries(value);
|
|
1570
|
+
}
|
|
1571
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsFiltersBarBaseComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1572
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: ObjectsFiltersBarBaseComponent, isStandalone: true, inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, listMode: { classPropertyName: "listMode", publicName: "listMode", isSignal: true, isRequired: false, transformFunction: null }, totalCount: { classPropertyName: "totalCount", publicName: "totalCount", isSignal: true, isRequired: false, transformFunction: null }, sortOptions: { classPropertyName: "sortOptions", publicName: "sortOptions", isSignal: true, isRequired: false, transformFunction: null }, redirectChanges: { classPropertyName: "redirectChanges", publicName: "redirectChanges", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterRedirect: "filterRedirect", listModeChange: "listModeChange", searchEvent: "searchEvent" }, ngImport: i0 }); }
|
|
1573
|
+
}
|
|
1574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsFiltersBarBaseComponent, decorators: [{
|
|
1575
|
+
type: Directive
|
|
1576
|
+
}], ctorParameters: () => [{ type: undefined }], propDecorators: { filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], listMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "listMode", required: false }] }], totalCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalCount", required: false }] }], sortOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortOptions", required: false }] }], redirectChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "redirectChanges", required: false }] }], filterRedirect: [{ type: i0.Output, args: ["filterRedirect"] }], listModeChange: [{ type: i0.Output, args: ["listModeChange"] }], searchEvent: [{ type: i0.Output, args: ["searchEvent"] }] } });
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* Logic of the grey objects search panel (Muzeum Podlasia's
|
|
1580
|
+
* `objects-search-panel`, mockup "search tagi filtry" 458:7898, desktop
|
|
1581
|
+
* "Frame 502" 2300:51176, mobile "Panel wyszukiwarki" 2300:51195): phrase →
|
|
1582
|
+
* theme tags + "Wyszukaj więcej tagów" → date range, "Pokaż" and "Szukaj" →
|
|
1583
|
+
* advanced filters. On mobile a "Filtry ↓" button under the phrase field
|
|
1584
|
+
* expands the rest of the panel.
|
|
1585
|
+
*
|
|
1586
|
+
* Presentational — it reads its state from `filter()` and emits events; the
|
|
1587
|
+
* parent (the filters bar) writes to `FiltersContext`. No template: each
|
|
1588
|
+
* museum extends it and keeps its own HTML, with the phrase input marked
|
|
1589
|
+
* `#phraseField`.
|
|
1590
|
+
*/
|
|
1591
|
+
class ObjectsSearchPanelBaseComponent {
|
|
1592
|
+
constructor() {
|
|
1593
|
+
this.phrase = input('', /* @ts-ignore */
|
|
1594
|
+
...(ngDevMode ? [{ debugName: "phrase" }] : /* istanbul ignore next */ []));
|
|
1595
|
+
this.filter = input.required(/* @ts-ignore */
|
|
1596
|
+
...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
|
|
1597
|
+
/**
|
|
1598
|
+
* Theme tags — the whole `Cycle` dictionary or the "Wyszukaj więcej tagów"
|
|
1599
|
+
* result; the template decides how many show at once (MOB-1826).
|
|
1600
|
+
*/
|
|
1601
|
+
this.tags = input([], /* @ts-ignore */
|
|
1602
|
+
...(ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []));
|
|
1603
|
+
/** The parent controls the advanced filters panel state. */
|
|
1604
|
+
this.isAdvancedFiltersOn = input(false, /* @ts-ignore */
|
|
1605
|
+
...(ngDevMode ? [{ debugName: "isAdvancedFiltersOn" }] : /* istanbul ignore next */ []));
|
|
1606
|
+
/**
|
|
1607
|
+
* The `/obiekty` list also searches by filters alone — "Szukaj" with an
|
|
1608
|
+
* empty phrase is allowed there. In the search results the phrase is part
|
|
1609
|
+
* of the address, so an empty phrase has nowhere to go.
|
|
1610
|
+
*/
|
|
1611
|
+
this.allowEmptyPhrase = input(false, /* @ts-ignore */
|
|
1612
|
+
...(ngDevMode ? [{ debugName: "allowEmptyPhrase" }] : /* istanbul ignore next */ []));
|
|
1613
|
+
/** A full-bleed band (the `/obiekty` list) instead of a grey box (search results). */
|
|
1614
|
+
this.fullBleed = input(false, /* @ts-ignore */
|
|
1615
|
+
...(ngDevMode ? [{ debugName: "fullBleed" }] : /* istanbul ignore next */ []));
|
|
1616
|
+
/**
|
|
1617
|
+
* Selected filter chips (state "Selected" 2193:3301 / mobile 2276:7726) —
|
|
1618
|
+
* rendered by the parent and inserted once by the panel; only CSS places
|
|
1619
|
+
* them, so the DOM is the same after SSR and in the browser.
|
|
1620
|
+
*/
|
|
1621
|
+
this.selectedFiltersTpl = input(null, /* @ts-ignore */
|
|
1622
|
+
...(ngDevMode ? [{ debugName: "selectedFiltersTpl" }] : /* istanbul ignore next */ []));
|
|
1623
|
+
this.searchPhrase = output();
|
|
1624
|
+
this.tagToggle = output();
|
|
1625
|
+
/**
|
|
1626
|
+
* Passed through unchanged — the date field debounces the year typing
|
|
1627
|
+
* (MOB-1866), the panel delays nothing here.
|
|
1628
|
+
*/
|
|
1629
|
+
this.dateRangeChange = output();
|
|
1630
|
+
this.booleanFilterChange = output();
|
|
1631
|
+
/** "Wyszukaj więcej tagów" — an empty phrase goes back to the default tags. */
|
|
1632
|
+
this.tagsSearch = output();
|
|
1633
|
+
this.advancedFiltersToggle = output();
|
|
1634
|
+
/** One batched payload from the "Filtry zaawansowane" panel's Zastosuj/Resetuj. */
|
|
1635
|
+
this.advancedFiltersApply = output();
|
|
1636
|
+
/** The selected theme tags read from the current filter. */
|
|
1637
|
+
this.selectedTagIds = computed(() => {
|
|
1638
|
+
const value = (this.filter()?.query ?? []).find((q) => q.key === OBJECTS_THEME_TAGS_FILTER_KEY)?.value;
|
|
1639
|
+
// `null`, `[null]` and entries without an id (a restored filter) are
|
|
1640
|
+
// skipped, `"3"` from the address = `3` from the dictionary (MOB-1826 review).
|
|
1641
|
+
return dictionaryFilterItems(value).map((item) => item.id);
|
|
1642
|
+
}, /* @ts-ignore */
|
|
1643
|
+
...(ngDevMode ? [{ debugName: "selectedTagIds" }] : /* istanbul ignore next */ []));
|
|
1644
|
+
/** The field text — starts from `phrase()` and follows a change from the URL. */
|
|
1645
|
+
this.searchText = linkedSignal(() => this.phrase(), /* @ts-ignore */
|
|
1646
|
+
...(ngDevMode ? [{ debugName: "searchText" }] : /* istanbul ignore next */ []));
|
|
1647
|
+
/** The "Wyszukaj więcej tagów" phrase and the last query sent. */
|
|
1648
|
+
this.tagQuery = signal('', /* @ts-ignore */
|
|
1649
|
+
...(ngDevMode ? [{ debugName: "tagQuery" }] : /* istanbul ignore next */ []));
|
|
1650
|
+
this.submittedTagQuery = signal('', /* @ts-ignore */
|
|
1651
|
+
...(ngDevMode ? [{ debugName: "submittedTagQuery" }] : /* istanbul ignore next */ []));
|
|
1652
|
+
/** "Pokaż wszystkie tagi (N)" expanded — a new search collapses it. */
|
|
1653
|
+
this.tagsExpanded = signal(false, /* @ts-ignore */
|
|
1654
|
+
...(ngDevMode ? [{ debugName: "tagsExpanded" }] : /* istanbul ignore next */ []));
|
|
1655
|
+
/** Mobile: "Filtry ↓" expands the tags, dates and buttons (always visible from lg). */
|
|
1656
|
+
this.filtersOpen = signal(false, /* @ts-ignore */
|
|
1657
|
+
...(ngDevMode ? [{ debugName: "filtersOpen" }] : /* istanbul ignore next */ []));
|
|
1658
|
+
/** Mobile: "Więcej tagów" reveals the tag search field (2276:7913). */
|
|
1659
|
+
this.moreTagsOpen = signal(false, /* @ts-ignore */
|
|
1660
|
+
...(ngDevMode ? [{ debugName: "moreTagsOpen" }] : /* istanbul ignore next */ []));
|
|
1661
|
+
this.objectsSearchPanelPhraseField = viewChild('phraseField', /* @ts-ignore */
|
|
1662
|
+
...(ngDevMode ? [{ debugName: "objectsSearchPanelPhraseField" }] : /* istanbul ignore next */ []));
|
|
1663
|
+
}
|
|
1664
|
+
/** The "Pokaż" boxes' state read from the filter — e.g. after a link with filters. */
|
|
1665
|
+
isBooleanFilterOn(key) {
|
|
1666
|
+
return (this.filter()?.query ?? []).some((q) => q.key === key && q.value === true);
|
|
1667
|
+
}
|
|
1668
|
+
toggleMoreTags() {
|
|
1669
|
+
this.moreTagsOpen.update((open) => !open);
|
|
1670
|
+
}
|
|
1671
|
+
toggleFilters() {
|
|
1672
|
+
this.filtersOpen.update((open) => !open);
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* "×" in the phrase field. On the list it also clears the search; in the
|
|
1676
|
+
* search results it only empties the field (a new phrase is a new address).
|
|
1677
|
+
*/
|
|
1678
|
+
clearPhrase() {
|
|
1679
|
+
this.searchText.set('');
|
|
1680
|
+
this.objectsSearchPanelPhraseField()?.nativeElement.focus();
|
|
1681
|
+
if (this.allowEmptyPhrase()) {
|
|
1682
|
+
this.searchPhrase.emit('');
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
searchTags() {
|
|
1686
|
+
const query = this.tagQuery().trim();
|
|
1687
|
+
this.submittedTagQuery.set(query);
|
|
1688
|
+
this.tagsExpanded.set(false);
|
|
1689
|
+
this.tagsSearch.emit(query);
|
|
1690
|
+
}
|
|
1691
|
+
onBooleanFilterChange(key, checked) {
|
|
1692
|
+
this.booleanFilterChange.emit({ key, checked });
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Only an explicit action (button / Enter) searches — the panel has no
|
|
1696
|
+
* live search, so there is no debounce here either.
|
|
1697
|
+
*/
|
|
1698
|
+
search() {
|
|
1699
|
+
const phrase = this.searchText().trim();
|
|
1700
|
+
if (!phrase && !this.allowEmptyPhrase()) {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
this.searchPhrase.emit(phrase);
|
|
1704
|
+
}
|
|
1705
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsSearchPanelBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1706
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.1.3", type: ObjectsSearchPanelBaseComponent, isStandalone: true, inputs: { phrase: { classPropertyName: "phrase", publicName: "phrase", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null }, isAdvancedFiltersOn: { classPropertyName: "isAdvancedFiltersOn", publicName: "isAdvancedFiltersOn", isSignal: true, isRequired: false, transformFunction: null }, allowEmptyPhrase: { classPropertyName: "allowEmptyPhrase", publicName: "allowEmptyPhrase", isSignal: true, isRequired: false, transformFunction: null }, fullBleed: { classPropertyName: "fullBleed", publicName: "fullBleed", isSignal: true, isRequired: false, transformFunction: null }, selectedFiltersTpl: { classPropertyName: "selectedFiltersTpl", publicName: "selectedFiltersTpl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchPhrase: "searchPhrase", tagToggle: "tagToggle", dateRangeChange: "dateRangeChange", booleanFilterChange: "booleanFilterChange", tagsSearch: "tagsSearch", advancedFiltersToggle: "advancedFiltersToggle", advancedFiltersApply: "advancedFiltersApply" }, viewQueries: [{ propertyName: "objectsSearchPanelPhraseField", first: true, predicate: ["phraseField"], descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
1707
|
+
}
|
|
1708
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsSearchPanelBaseComponent, decorators: [{
|
|
1709
|
+
type: Directive
|
|
1710
|
+
}], propDecorators: { phrase: [{ type: i0.Input, args: [{ isSignal: true, alias: "phrase", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], isAdvancedFiltersOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdvancedFiltersOn", required: false }] }], allowEmptyPhrase: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowEmptyPhrase", required: false }] }], fullBleed: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullBleed", required: false }] }], selectedFiltersTpl: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedFiltersTpl", required: false }] }], searchPhrase: [{ type: i0.Output, args: ["searchPhrase"] }], tagToggle: [{ type: i0.Output, args: ["tagToggle"] }], dateRangeChange: [{ type: i0.Output, args: ["dateRangeChange"] }], booleanFilterChange: [{ type: i0.Output, args: ["booleanFilterChange"] }], tagsSearch: [{ type: i0.Output, args: ["tagsSearch"] }], advancedFiltersToggle: [{ type: i0.Output, args: ["advancedFiltersToggle"] }], advancedFiltersApply: [{ type: i0.Output, args: ["advancedFiltersApply"] }], objectsSearchPanelPhraseField: [{ type: i0.ViewChild, args: ['phraseField', { isSignal: true }] }] } });
|
|
1711
|
+
|
|
1712
|
+
/** How long (ms) the "link copied" message stays after a click. */
|
|
1713
|
+
const OBJECTS_SHARE_COPIED_TIMEOUT = SHARE_LINK_COPIED_TIMEOUT;
|
|
1714
|
+
/**
|
|
1715
|
+
* Logic of "Share search results" (mock-up "Action panel" 2300:51184): a link
|
|
1716
|
+
* to the objects list with the current filters, shared through
|
|
1717
|
+
* `ShareLinkBaseComponent` — the native share sheet, or a clipboard copy with a
|
|
1718
|
+
* "link copied" flag when the browser has no share sheet. The template stays in
|
|
1719
|
+
* the app.
|
|
1720
|
+
*/
|
|
1721
|
+
class ObjectsShareResultsBaseComponent extends ShareLinkBaseComponent {
|
|
1722
|
+
constructor() {
|
|
1723
|
+
super(...arguments);
|
|
1724
|
+
this.filter = input(undefined, /* @ts-ignore */
|
|
1725
|
+
...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
|
|
1726
|
+
/** The list path the link opens; overridable, read inside `share`. */
|
|
1727
|
+
this.objectsShareResultsListPath = '/obiekty';
|
|
1728
|
+
/** How long (ms) "copied" stays on; overridable, read inside `share`. */
|
|
1729
|
+
this.objectsShareResultsCopiedTimeout = OBJECTS_SHARE_COPIED_TIMEOUT;
|
|
1730
|
+
}
|
|
1731
|
+
get shareLinkCopiedTimeout() {
|
|
1732
|
+
return this.objectsShareResultsCopiedTimeout;
|
|
1733
|
+
}
|
|
1734
|
+
shareLinkUrl() {
|
|
1735
|
+
return buildObjectsResultsUrl(this.shareLinkDocument.defaultView?.location.origin ?? '', this.filter(), this.objectsShareResultsListPath);
|
|
1736
|
+
}
|
|
1737
|
+
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsShareResultsBaseComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1738
|
+
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: ObjectsShareResultsBaseComponent, isStandalone: true, inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
1739
|
+
}
|
|
1740
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ObjectsShareResultsBaseComponent, decorators: [{
|
|
1741
|
+
type: Directive
|
|
1742
|
+
}], propDecorators: { filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }] } });
|
|
1743
|
+
|
|
1103
1744
|
/**
|
|
1104
1745
|
* Generated bundle index. Do not edit.
|
|
1105
1746
|
*/
|
|
1106
1747
|
|
|
1107
|
-
export { ASK_ABOUT_OBJECT_KEYS, AdvancedFilterType, ComponentsModule, FiltersComponent, FormFeatureType, ListComponent, MULTIMEDIA_DICTIONARY, OBJECTS_CATALOG_DEFAULTS, OBJECTS_CATALOG_SEO, ObjectDetailsBaseComponent, ObjectMediaService, ObjectsCatalogBaseComponent, ObjectsDataService, ObjectsFacade, ObjectsItemComponent, ObjectsListComponent, ObjectsModule, ObjectsService, ObjectsSharedModule, SERVICES, askAboutObjectHref, bibliography, descriptionHtml, getObjectsState, getReducer, initialState, inventoryValues, objectTitle, relatedArticles, tagValues };
|
|
1748
|
+
export { ASK_ABOUT_OBJECT_KEYS, AdvancedFilterType, ComponentsModule, FiltersComponent, FormFeatureType, ListComponent, MULTIMEDIA_DICTIONARY, OBJECTS_CATALOG_DEFAULTS, OBJECTS_CATALOG_SEO, OBJECTS_FILTER_SINGLE_VALUE_LABELS, OBJECTS_LIMIT_OPTIONS, OBJECTS_NEW_TYPE_SORT_OPTIONS, OBJECTS_SHARE_COPIED_TIMEOUT, OBJECTS_SORT_OPTIONS, OBJECTS_THEME_TAGS_FILTER_KEY, ObjectDetailsBaseComponent, ObjectMediaService, ObjectsAdvancedFiltersBaseComponent, ObjectsCatalogBaseComponent, ObjectsDataService, ObjectsFacade, ObjectsFiltersBarBaseComponent, ObjectsItemComponent, ObjectsListComponent, ObjectsModule, ObjectsSearchPanelBaseComponent, ObjectsService, ObjectsShareResultsBaseComponent, ObjectsSharedModule, SERVICES, askAboutObjectHref, bibliography, buildObjectsResultsUrl, descriptionHtml, getObjectsState, getReducer, initialState, inventoryValues, objectTitle, objectsSortFromKey, objectsSortKey, relatedArticles, tagValues };
|
|
1108
1749
|
//# sourceMappingURL=smartsoft001-mobilems-objects-shell-angular.mjs.map
|