@teipublisher/pb-components 1.43.4 → 1.44.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,521 +1,521 @@
1
- import { html, css } from 'lit-element';
2
- import { PbLoad } from './pb-load.js';
3
- import { translate } from "./pb-i18n.js";
4
- import { themableMixin } from "./theming.js";
5
- import '@polymer/paper-input/paper-input.js';
6
- import '@polymer/paper-button';
7
- import '@polymer/paper-dropdown-menu/paper-dropdown-menu.js';
8
- import '@polymer/paper-listbox';
9
- import '@polymer/paper-dialog';
10
- import '@polymer/paper-dialog-scrollable';
11
- import '@polymer/iron-ajax';
12
- import '@cwmr/paper-autocomplete/paper-autocomplete-suggestions.js';
13
- import { cmpVersion } from './utils.js';
14
-
15
- /**
16
- * Component to browse through a collection of documents with sorting, filtering and facets.
17
- *
18
- * @slot toolbar - toolbar area
19
- * @slot - unnamed default slot
20
- * @slot footer - footer area
21
- *
22
- * @fires pb-collection - Sent to inform e.g. pb-upload about current collection
23
- * @fires pb-search-resubmit - When received, set facet values as received from the event
24
- * @fires pb-login - When received, refresh the view if the user changed
25
- *
26
- * @cssprop --pb-search-suggestions-background - Background for the autocomplete suggestions for the filter field
27
- * @cssprop --pb-search-suggestions-color - Text color for the autocomplete suggestion for the filter field
28
- * @cssprop --pb-search-label-color - Determines the color of small label above the sort by/filter by/filter fields
29
- * @cssprop --pb-search-input-color - Determines the color of the text in the sort by/filter by/filter fields
30
- * @cssprop --pb-search-focus-color - Color of the field labels and underline when in focus
31
- * @cssprop --pb-browse-toolbar-justify-content - How to justify the browse toolbar content, following flexbox justify-content property e.g. center, space-evenly, start...
32
- *
33
- * @csspart delete-button - the delete button
34
- * @csspart sort-dropdown - dropdown for sorting
35
- * @csspart filter-dropdown - dropdown for filtering
36
- * @csspart filter-input - input for filtering
37
- */
38
- export class PbBrowseDocs extends themableMixin(PbLoad) {
39
-
40
- static get properties() {
41
- return {
42
- ...super.properties,
43
- sortBy: {
44
- type: String,
45
- attribute: 'sort-by'
46
- },
47
- sortOptions: {
48
- type: Array,
49
- attribute: 'sort-options'
50
- },
51
- sortLabel: {
52
- type: String
53
- },
54
- filter: {
55
- type: String
56
- },
57
- filterBy: {
58
- type: String,
59
- attribute: 'filter-by'
60
- },
61
- filterOptions: {
62
- type: Array,
63
- attribute: 'filter-options'
64
- },
65
- filterByLabel: {
66
- type: String
67
- },
68
- filterPlaceholderLabel: {
69
- type: String
70
- },
71
- collection: {
72
- type: String
73
- },
74
- facets: {
75
- type: Object
76
- },
77
- /** Id of the pb-login element to connect to */
78
- login: {
79
- type: String
80
- },
81
- /**
82
- * If set, requires the logged in user to be member of
83
- * the given group.
84
- */
85
- group: {
86
- type: String
87
- },
88
- subforms: {
89
- type: String
90
- },
91
- /**
92
- * If set, rewrite URLs to load pages as static HTML files,
93
- * so no TEI Publisher instance is required
94
- */
95
- static: {
96
- type: Boolean
97
- },
98
- _file: {
99
- type: String
100
- },
101
- _selected: {
102
- type: Array
103
- },
104
- _allowModification: {
105
- type: Boolean,
106
- },
107
- _suggestions: {
108
- type: Array
109
- }
110
- };
111
- }
112
-
113
- constructor() {
114
- super();
115
- this.sortOptions = [];
116
- this.sortLabel = 'browse.sort';
117
- this.sortBy = 'default';
118
- this.filter = '';
119
- this.filterOptions = [
120
- {
121
- label: 'Title',
122
- value: 'title'
123
- }
124
- ];
125
- this.filterByLabel = 'browse.filter';
126
- this.filterPlaceholderLabel = 'browse.filterPlaceholder';
127
-
128
- this.filterBy = 'title';
129
- this._allowModification = false;
130
- this._suggestions = [];
131
-
132
- this.static = false;
133
- }
134
-
135
- connectedCallback() {
136
- super.connectedCallback();
137
-
138
- const sortParam = this.getParameter('sort');
139
- if (sortParam) {
140
- this.sortBy = sortParam;
141
- }
142
-
143
- const filterParam = this.getParameter('filter');
144
- if (filterParam) {
145
- this.filter = filterParam;
146
- this.filterBy = this.getParameter('filterBy', this.filterBy);
147
- }
148
-
149
- this.facets = this.getParametersMatching(/^facet-.*$/);
150
-
151
- this.collection = this.getParameter('collection');
152
-
153
- this.subscribeTo('pb-search-resubmit', this._facets.bind(this));
154
- this.subscribeTo('pb-login', (ev) => {
155
- if (ev.detail.userChanged) {
156
- this._facets(ev);
157
- }
158
- }, []);
159
- document.addEventListener('pb-i18n-update', () => {
160
- // clear paper-listbox selection after language updates
161
- const lb = this.shadowRoot.getElementById('sort-list');
162
- let old = lb.selected;
163
- lb.selected = undefined;
164
- lb.selected = old;
165
-
166
- const fl = this.shadowRoot.getElementById('filter-list');
167
- old = fl.selected;
168
- fl.selected = undefined;
169
- fl.selected = old;
170
- });
171
- }
172
-
173
- firstUpdated() {
174
- PbBrowseDocs.waitOnce('pb-page-ready', (options) => {
175
- const loader = this.shadowRoot.getElementById('autocompleteLoader');
176
- if (cmpVersion(options.apiVersion, '1.0.0') >= 0) {
177
- loader.url = `${options.endpoint}/api/search/autocomplete`;
178
- if (!this.url) {
179
- this.url = 'api/collection';
180
- }
181
- } else {
182
- loader.url = `${options.endpoint}/modules/autocomplete.xql`;
183
- if (!this.url) {
184
- this.url = 'collection/';
185
- }
186
- }
187
- });
188
- this.shadowRoot.getElementById('autocomplete').addEventListener('autocomplete-change', this._autocomplete.bind(this));
189
-
190
- if (this.login) {
191
- const login = document.getElementById(this.login);
192
- if (!login) {
193
- console.error('<pb-browse-docs> connected pb-login element not found!');
194
- } else {
195
- this.subscribeTo('pb-login', (ev) => {
196
- this._allowModification = this._loggedIn(ev.detail.user, ev.detail.group);
197
- }, []);
198
- this._allowModification = login.loggedIn && this._loggedIn(login.user, login.groups);
199
- }
200
- }
201
-
202
- this.shadowRoot.getElementById('sort-list').addEventListener('selected-item-changed', this._sort.bind(this));
203
- this.shadowRoot.getElementById('delete').addEventListener('click', this._handleDelete.bind(this));
204
- super.firstUpdated();
205
- }
206
-
207
- render() {
208
- return html`
209
- <custom-style>
210
- <style>
211
- :host {
212
- --suggestions-item: {
213
- color: var(--pb-search-suggestions-color, black);
214
- };
215
- --suggestions-wrapper: {
216
- background: var(--pb-search-suggestions-background, white);
217
- }
218
- }
219
- </style>
220
- </custom-style>
221
- <slot name="header"></slot>
222
- <div class="toolbar">
223
- <paper-dropdown-menu id="sort" label="${translate(this.sortLabel)}" part="sort-dropdown">
224
- <paper-listbox id="sort-list" selected="${this.sortBy}" slot="dropdown-content" class="dropdown-content" attr-for-selected="value">
225
- ${this.sortOptions.map(option =>
226
- html`<paper-item value="${option.value}">${translate(option.label)}</paper-item>`
227
- )}
228
- </paper-listbox>
229
- </paper-dropdown-menu>
230
- <div>
231
- <paper-dropdown-menu id="filterSelect" label="${translate(this.filterByLabel)}" part="filter-dropdown">
232
- <paper-listbox id="filter-list" selected="${this.filterBy}" slot="dropdown-content" class="dropdown-content" attr-for-selected="value" @selected-item-changed="${this._filterChanged}">
233
- ${this.filterOptions.map(option =>
234
- html`<paper-item value="${option.value}">${translate(option.label)}</paper-item>`
235
- )}
236
- </paper-listbox>
237
- </paper-dropdown-menu>
238
- <paper-input id="filterString" type="search" name="filter" label="${translate(this.filterPlaceholderLabel)}" value="${this.filter}"
239
- @keyup="${this._handleEnter}" part="filter-input">
240
- <iron-icon icon="search" @click="${this._filter}" slot="prefix"></iron-icon>
241
- </paper-input>
242
- <paper-autocomplete-suggestions id="autocomplete" for="filterString" source="${this._suggestions}" remote-source></paper-autocomplete-suggestions>
243
- </div>
244
- </div>
245
- <div class="toolbar">
246
- <slot name="toolbar"></slot>
247
- <paper-button id="delete" part="delete-button" title="${translate('browse.delete')}" class="${this._canModify(this._allowModification)}">
248
- <iron-icon icon="delete"></iron-icon>
249
- <span class="label">${translate('browse.delete')}</span>
250
- </paper-button>
251
- </div>
252
- <slot></slot>
253
- <slot name="footer"></slot>
254
-
255
- <iron-ajax
256
- id="loadContent"
257
- verbose
258
- handle-as="text"
259
- method="get"
260
- with-credentials
261
- @response="${this._handleContent}"
262
- @error="${this._handleError}"></iron-ajax>
263
- <iron-ajax
264
- id="autocompleteLoader"
265
- verbose
266
- handle-as="json"
267
- method="get"
268
- with-credentials
269
- @response="${this._updateSuggestions}"></iron-ajax>
270
-
271
- <paper-dialog id="deleteDialog">
272
- <h2>${translate('browse.delete')}</h2>
273
- <paper-dialog-scrollable>
274
- <p>${translate('browse.confirmDeletion', { count: (this._selected ? this._selected.length : 0) })}</p>
275
- </paper-dialog-scrollable>
276
- <div class="buttons">
277
- <paper-button dialog-confirm="dialog-confirm" autofocus @click="${this._confirmDelete}">${translate('dialogs.yes')}</paper-button>
278
- <paper-button dialog-confirm="dialog-cancel">${translate('dialogs.no')}</paper-button>
279
- </div>
280
- </paper-dialog>
281
- <paper-dialog id="errorDialog">
282
- <h2>${translate('dialogs.error')}</h2>
283
- <paper-dialog-scrollable></paper-dialog-scrollable>
284
- <div class="buttons">
285
- <paper-button dialog-confirm="dialog-confirm" autofocus="autofocus">
286
- ${translate('dialogs.close')}
287
- </paper-button>
288
- </div>
289
- </paper-dialog>
290
- `;
291
- }
292
-
293
- static get styles() {
294
- return css`
295
- :host {
296
- display: block;
297
- --paper-input-container-color: var(--pb-search-label-color, var(--paper-grey-500, #303030));
298
- --paper-input-container-input-color: var(--pb-search-input-color, var(--pb-color-primary, #000000));
299
- --paper-input-container-focus-color: var(--pb-search-focus-color, var(--paper-grey-500, #303030));
300
- }
301
-
302
- .toolbar {
303
- display: flex;
304
- justify-content: var(--pb-browse-toolbar-justify-content);
305
- }
306
-
307
- [name="toolbar"] {
308
- flex: 1 0;
309
- }
310
-
311
- #sort {
312
- display: block;
313
- }
314
-
315
- #filterString {
316
- position: relative;
317
- display: inline-block;
318
- vertical-align: bottom;
319
- }
320
-
321
- .hidden {
322
- display: none;
323
- }
324
- `;
325
- }
326
-
327
- getURL(params) {
328
- if (this.static) {
329
- // use a static URL
330
- return `collections/${this.collection ? this.collection + '/' : ''}${params.start || '1'}.html`;
331
- }
332
- const url = super.getURL(params);
333
- return this.collection ? `${url}/${this.collection}` : url;
334
- }
335
-
336
- prepareParameters(params) {
337
- params = this._paramsFromSubforms(params);
338
- params.sort = this.sortBy;
339
- if (this.filter) {
340
- params.filter = this.filter;
341
- params.browse = this.filterBy;
342
- }
343
- if (this.facets) {
344
- params = Object.assign(params, this.facets);
345
- }
346
- return params;
347
- }
348
-
349
- _paramsFromSubforms(params) {
350
- if (this.subforms) {
351
- document.querySelectorAll(this.subforms).forEach((form) => {
352
- if (form.serializeForm) {
353
- Object.assign(params, form.serializeForm());
354
- }
355
- });
356
- }
357
- return params;
358
- }
359
-
360
- /**
361
- * returns selected documents.
362
- *
363
- * @returns {Array}
364
- */
365
- getSelected() {
366
- const selected = [];
367
- if (this.container) {
368
- document.querySelectorAll(this.container).forEach((container) =>
369
- container.querySelectorAll('.document-select paper-checkbox[checked]').forEach((checkbox) => {
370
- selected.push(checkbox.value);
371
- })
372
- );
373
- } else {
374
- this.querySelectorAll('.document-select paper-checkbox[checked]').forEach((checkbox) => {
375
- selected.push(checkbox.value);
376
- });
377
- }
378
- return selected;
379
- }
380
-
381
- _filter() {
382
- const filter = this.shadowRoot.getElementById('filterString').value;
383
- const filterBy = this.shadowRoot.getElementById('filter-list').selected;
384
- if (typeof filter !== 'undefined') {
385
- console.log('<pb-browse-docs> Filter by %s', filter);
386
- this.filter = filter;
387
- this.setParameter('filter', filter);
388
- this.setParameter('filterBy', filterBy);
389
- this.pushHistory('filter docs');
390
-
391
- this.load();
392
- }
393
- }
394
-
395
- _filterChanged() {
396
- const filterBy = this.shadowRoot.getElementById('filter-list').selected;
397
- if (filterBy && filterBy !== this.filterBy) {
398
- console.log('<pb-browse-docs> Filtering on %s', filterBy);
399
- this.filterBy = filterBy;
400
- }
401
- }
402
-
403
- _sort() {
404
- const sortBy = this.shadowRoot.getElementById('sort-list').selected;
405
- if (sortBy && sortBy !== this.sortBy) {
406
- console.log('<pb-browse-docs> Sorting by %s', sortBy);
407
- this.sortBy = sortBy;
408
- this.setParameter('sort', sortBy);
409
- this.pushHistory('sort docs');
410
-
411
- this.load();
412
- }
413
- }
414
-
415
- _facets(ev) {
416
- if (ev.detail && ev.detail.params) {
417
- this.clearParametersMatching(/^(all-|facet-).*/);
418
- for (let param in ev.detail.params) {
419
- this.setParameter(param, ev.detail.params[param]);
420
- }
421
- this.facets = ev.detail.params;
422
- this.start = 1;
423
- this.pushHistory('facets');
424
- }
425
- this.load();
426
- }
427
-
428
- _onLoad(content) {
429
- window.scrollTo(0, 0);
430
- const div = content.querySelector('[data-root]');
431
- const collection = div && div.getAttribute('data-root');
432
- const writable = div && div.classList.contains('writable');
433
- this.emitTo('pb-collection', {
434
- writable,
435
- collection
436
- });
437
- document.querySelectorAll('[can-write]').forEach((elem) => {
438
- elem.disabled = !writable;
439
- });
440
- content.querySelectorAll('[data-collection]').forEach(link => {
441
- link.addEventListener('click', (ev) => {
442
- ev.preventDefault();
443
- this.collection = link.getAttribute('data-collection');
444
- this.start = 1;
445
- this.setParameter('collection', this.collection);
446
- this.pushHistory('browse collection');
447
- console.log('<pb-browse-docs> loading collection %s', this.collection);
448
- this.load();
449
- });
450
- });
451
- }
452
-
453
- _handleDelete(target, ev) {
454
- const deleteDialog = this.shadowRoot.getElementById('deleteDialog');
455
- const selected = this.getSelected();
456
- if (selected.length > 0) {
457
- this._selected = selected;
458
- deleteDialog.open();
459
- }
460
- }
461
-
462
- _confirmDelete() {
463
- if (!(this._file || this._selected)) {
464
- return;
465
- }
466
-
467
- let files;
468
- if (this._selected) {
469
- files = this._selected;
470
- } else {
471
- files = [this._file];
472
- }
473
- console.log('<pb-browse-docs> Deleting %o', this._file);
474
- const params = {
475
- action: 'delete',
476
- 'docs[]': files
477
- };
478
- this._file = null;
479
- this._selected = null;
480
- this.load(params);
481
- }
482
-
483
- _loggedIn(user, groups) {
484
- if (user == null) {
485
- return false;
486
- }
487
- if (this.group) {
488
- if (!groups) {
489
- return false;
490
- }
491
- return groups.indexOf(this.group) > -1;
492
- }
493
- return true;
494
- }
495
-
496
- _canModify(allowModification) {
497
- return allowModification ? '' : 'hidden';
498
- }
499
-
500
- _autocomplete(ev) {
501
- const autocompleteLoader = this.shadowRoot.getElementById('autocompleteLoader');
502
- autocompleteLoader.params = {
503
- query: ev.detail.option.text,
504
- field: this.filterBy
505
- };
506
- autocompleteLoader.generateRequest();
507
- }
508
-
509
- _updateSuggestions() {
510
- const autocomplete = this.shadowRoot.getElementById('autocomplete');
511
- const autocompleteLoader = this.shadowRoot.getElementById('autocompleteLoader');
512
- autocomplete.suggestions(autocompleteLoader.lastResponse);
513
- }
514
-
515
- _handleEnter(e) {
516
- if (e.keyCode == 13) {
517
- this._filter();
518
- }
519
- }
520
- }
1
+ import { html, css } from 'lit-element';
2
+ import { PbLoad } from './pb-load.js';
3
+ import { translate } from "./pb-i18n.js";
4
+ import { themableMixin } from "./theming.js";
5
+ import '@polymer/paper-input/paper-input.js';
6
+ import '@polymer/paper-button';
7
+ import '@polymer/paper-dropdown-menu/paper-dropdown-menu.js';
8
+ import '@polymer/paper-listbox';
9
+ import '@polymer/paper-dialog';
10
+ import '@polymer/paper-dialog-scrollable';
11
+ import '@polymer/iron-ajax';
12
+ import '@cwmr/paper-autocomplete/paper-autocomplete-suggestions.js';
13
+ import { cmpVersion } from './utils.js';
14
+
15
+ /**
16
+ * Component to browse through a collection of documents with sorting, filtering and facets.
17
+ *
18
+ * @slot toolbar - toolbar area
19
+ * @slot - unnamed default slot
20
+ * @slot footer - footer area
21
+ *
22
+ * @fires pb-collection - Sent to inform e.g. pb-upload about current collection
23
+ * @fires pb-search-resubmit - When received, set facet values as received from the event
24
+ * @fires pb-login - When received, refresh the view if the user changed
25
+ *
26
+ * @cssprop --pb-search-suggestions-background - Background for the autocomplete suggestions for the filter field
27
+ * @cssprop --pb-search-suggestions-color - Text color for the autocomplete suggestion for the filter field
28
+ * @cssprop --pb-search-label-color - Determines the color of small label above the sort by/filter by/filter fields
29
+ * @cssprop --pb-search-input-color - Determines the color of the text in the sort by/filter by/filter fields
30
+ * @cssprop --pb-search-focus-color - Color of the field labels and underline when in focus
31
+ * @cssprop --pb-browse-toolbar-justify-content - How to justify the browse toolbar content, following flexbox justify-content property e.g. center, space-evenly, start...
32
+ *
33
+ * @csspart delete-button - the delete button
34
+ * @csspart sort-dropdown - dropdown for sorting
35
+ * @csspart filter-dropdown - dropdown for filtering
36
+ * @csspart filter-input - input for filtering
37
+ */
38
+ export class PbBrowseDocs extends themableMixin(PbLoad) {
39
+
40
+ static get properties() {
41
+ return {
42
+ ...super.properties,
43
+ sortBy: {
44
+ type: String,
45
+ attribute: 'sort-by'
46
+ },
47
+ sortOptions: {
48
+ type: Array,
49
+ attribute: 'sort-options'
50
+ },
51
+ sortLabel: {
52
+ type: String
53
+ },
54
+ filter: {
55
+ type: String
56
+ },
57
+ filterBy: {
58
+ type: String,
59
+ attribute: 'filter-by'
60
+ },
61
+ filterOptions: {
62
+ type: Array,
63
+ attribute: 'filter-options'
64
+ },
65
+ filterByLabel: {
66
+ type: String
67
+ },
68
+ filterPlaceholderLabel: {
69
+ type: String
70
+ },
71
+ collection: {
72
+ type: String
73
+ },
74
+ facets: {
75
+ type: Object
76
+ },
77
+ /** Id of the pb-login element to connect to */
78
+ login: {
79
+ type: String
80
+ },
81
+ /**
82
+ * If set, requires the logged in user to be member of
83
+ * the given group.
84
+ */
85
+ group: {
86
+ type: String
87
+ },
88
+ subforms: {
89
+ type: String
90
+ },
91
+ /**
92
+ * If set, rewrite URLs to load pages as static HTML files,
93
+ * so no TEI Publisher instance is required
94
+ */
95
+ static: {
96
+ type: Boolean
97
+ },
98
+ _file: {
99
+ type: String
100
+ },
101
+ _selected: {
102
+ type: Array
103
+ },
104
+ _allowModification: {
105
+ type: Boolean,
106
+ },
107
+ _suggestions: {
108
+ type: Array
109
+ }
110
+ };
111
+ }
112
+
113
+ constructor() {
114
+ super();
115
+ this.sortOptions = [];
116
+ this.sortLabel = 'browse.sort';
117
+ this.sortBy = 'default';
118
+ this.filter = '';
119
+ this.filterOptions = [
120
+ {
121
+ label: 'Title',
122
+ value: 'title'
123
+ }
124
+ ];
125
+ this.filterByLabel = 'browse.filter';
126
+ this.filterPlaceholderLabel = 'browse.filterPlaceholder';
127
+
128
+ this.filterBy = 'title';
129
+ this._allowModification = false;
130
+ this._suggestions = [];
131
+
132
+ this.static = false;
133
+ }
134
+
135
+ connectedCallback() {
136
+ super.connectedCallback();
137
+
138
+ const sortParam = this.getParameter('sort');
139
+ if (sortParam) {
140
+ this.sortBy = sortParam;
141
+ }
142
+
143
+ const filterParam = this.getParameter('filter');
144
+ if (filterParam) {
145
+ this.filter = filterParam;
146
+ this.filterBy = this.getParameter('filterBy', this.filterBy);
147
+ }
148
+
149
+ this.facets = this.getParametersMatching(/^facet-.*$/);
150
+
151
+ this.collection = this.getParameter('collection');
152
+
153
+ this.subscribeTo('pb-search-resubmit', this._facets.bind(this));
154
+ this.subscribeTo('pb-login', (ev) => {
155
+ if (ev.detail.userChanged) {
156
+ this._facets(ev);
157
+ }
158
+ }, []);
159
+ document.addEventListener('pb-i18n-update', () => {
160
+ // clear paper-listbox selection after language updates
161
+ const lb = this.shadowRoot.getElementById('sort-list');
162
+ let old = lb.selected;
163
+ lb.selected = undefined;
164
+ lb.selected = old;
165
+
166
+ const fl = this.shadowRoot.getElementById('filter-list');
167
+ old = fl.selected;
168
+ fl.selected = undefined;
169
+ fl.selected = old;
170
+ });
171
+ }
172
+
173
+ firstUpdated() {
174
+ PbBrowseDocs.waitOnce('pb-page-ready', (options) => {
175
+ const loader = this.shadowRoot.getElementById('autocompleteLoader');
176
+ if (cmpVersion(options.apiVersion, '1.0.0') >= 0) {
177
+ loader.url = `${options.endpoint}/api/search/autocomplete`;
178
+ if (!this.url) {
179
+ this.url = 'api/collection';
180
+ }
181
+ } else {
182
+ loader.url = `${options.endpoint}/modules/autocomplete.xql`;
183
+ if (!this.url) {
184
+ this.url = 'collection/';
185
+ }
186
+ }
187
+ });
188
+ this.shadowRoot.getElementById('autocomplete').addEventListener('autocomplete-change', this._autocomplete.bind(this));
189
+
190
+ if (this.login) {
191
+ const login = document.getElementById(this.login);
192
+ if (!login) {
193
+ console.error('<pb-browse-docs> connected pb-login element not found!');
194
+ } else {
195
+ this.subscribeTo('pb-login', (ev) => {
196
+ this._allowModification = this._loggedIn(ev.detail.user, ev.detail.group);
197
+ }, []);
198
+ this._allowModification = login.loggedIn && this._loggedIn(login.user, login.groups);
199
+ }
200
+ }
201
+
202
+ this.shadowRoot.getElementById('sort-list').addEventListener('selected-item-changed', this._sort.bind(this));
203
+ this.shadowRoot.getElementById('delete').addEventListener('click', this._handleDelete.bind(this));
204
+ super.firstUpdated();
205
+ }
206
+
207
+ render() {
208
+ return html`
209
+ <custom-style>
210
+ <style>
211
+ :host {
212
+ --suggestions-item: {
213
+ color: var(--pb-search-suggestions-color, black);
214
+ };
215
+ --suggestions-wrapper: {
216
+ background: var(--pb-search-suggestions-background, white);
217
+ }
218
+ }
219
+ </style>
220
+ </custom-style>
221
+ <slot name="header"></slot>
222
+ <div class="toolbar">
223
+ <paper-dropdown-menu id="sort" label="${translate(this.sortLabel)}" part="sort-dropdown">
224
+ <paper-listbox id="sort-list" selected="${this.sortBy}" slot="dropdown-content" class="dropdown-content" attr-for-selected="value">
225
+ ${this.sortOptions.map(option =>
226
+ html`<paper-item value="${option.value}">${translate(option.label)}</paper-item>`
227
+ )}
228
+ </paper-listbox>
229
+ </paper-dropdown-menu>
230
+ <div>
231
+ <paper-dropdown-menu id="filterSelect" label="${translate(this.filterByLabel)}" part="filter-dropdown">
232
+ <paper-listbox id="filter-list" selected="${this.filterBy}" slot="dropdown-content" class="dropdown-content" attr-for-selected="value" @selected-item-changed="${this._filterChanged}">
233
+ ${this.filterOptions.map(option =>
234
+ html`<paper-item value="${option.value}">${translate(option.label)}</paper-item>`
235
+ )}
236
+ </paper-listbox>
237
+ </paper-dropdown-menu>
238
+ <paper-input id="filterString" type="search" name="filter" label="${translate(this.filterPlaceholderLabel)}" value="${this.filter}"
239
+ @keyup="${this._handleEnter}" part="filter-input">
240
+ <iron-icon icon="search" @click="${this._filter}" slot="prefix"></iron-icon>
241
+ </paper-input>
242
+ <paper-autocomplete-suggestions id="autocomplete" for="filterString" source="${this._suggestions}" remote-source></paper-autocomplete-suggestions>
243
+ </div>
244
+ </div>
245
+ <div class="toolbar">
246
+ <slot name="toolbar"></slot>
247
+ <paper-button id="delete" part="delete-button" title="${translate('browse.delete')}" class="${this._canModify(this._allowModification)}">
248
+ <iron-icon icon="delete"></iron-icon>
249
+ <span class="label">${translate('browse.delete')}</span>
250
+ </paper-button>
251
+ </div>
252
+ <slot></slot>
253
+ <slot name="footer"></slot>
254
+
255
+ <iron-ajax
256
+ id="loadContent"
257
+ verbose
258
+ handle-as="text"
259
+ method="get"
260
+ with-credentials
261
+ @response="${this._handleContent}"
262
+ @error="${this._handleError}"></iron-ajax>
263
+ <iron-ajax
264
+ id="autocompleteLoader"
265
+ verbose
266
+ handle-as="json"
267
+ method="get"
268
+ with-credentials
269
+ @response="${this._updateSuggestions}"></iron-ajax>
270
+
271
+ <paper-dialog id="deleteDialog">
272
+ <h2>${translate('browse.delete')}</h2>
273
+ <paper-dialog-scrollable>
274
+ <p>${translate('browse.confirmDeletion', { count: (this._selected ? this._selected.length : 0) })}</p>
275
+ </paper-dialog-scrollable>
276
+ <div class="buttons">
277
+ <paper-button dialog-confirm="dialog-confirm" autofocus @click="${this._confirmDelete}">${translate('dialogs.yes')}</paper-button>
278
+ <paper-button dialog-confirm="dialog-cancel">${translate('dialogs.no')}</paper-button>
279
+ </div>
280
+ </paper-dialog>
281
+ <paper-dialog id="errorDialog">
282
+ <h2>${translate('dialogs.error')}</h2>
283
+ <paper-dialog-scrollable></paper-dialog-scrollable>
284
+ <div class="buttons">
285
+ <paper-button dialog-confirm="dialog-confirm" autofocus="autofocus">
286
+ ${translate('dialogs.close')}
287
+ </paper-button>
288
+ </div>
289
+ </paper-dialog>
290
+ `;
291
+ }
292
+
293
+ static get styles() {
294
+ return css`
295
+ :host {
296
+ display: block;
297
+ --paper-input-container-color: var(--pb-search-label-color, var(--paper-grey-500, #303030));
298
+ --paper-input-container-input-color: var(--pb-search-input-color, var(--pb-color-primary, #000000));
299
+ --paper-input-container-focus-color: var(--pb-search-focus-color, var(--paper-grey-500, #303030));
300
+ }
301
+
302
+ .toolbar {
303
+ display: flex;
304
+ justify-content: var(--pb-browse-toolbar-justify-content);
305
+ }
306
+
307
+ [name="toolbar"] {
308
+ flex: 1 0;
309
+ }
310
+
311
+ #sort {
312
+ display: block;
313
+ }
314
+
315
+ #filterString {
316
+ position: relative;
317
+ display: inline-block;
318
+ vertical-align: bottom;
319
+ }
320
+
321
+ .hidden {
322
+ display: none;
323
+ }
324
+ `;
325
+ }
326
+
327
+ getURL(params) {
328
+ if (this.static) {
329
+ // use a static URL
330
+ return `collections/${this.collection ? this.collection + '/' : ''}${params.start || '1'}.html`;
331
+ }
332
+ const url = super.getURL(params);
333
+ return this.collection ? `${url}/${this.collection}` : url;
334
+ }
335
+
336
+ prepareParameters(params) {
337
+ params = this._paramsFromSubforms(params);
338
+ params.sort = this.sortBy;
339
+ if (this.filter) {
340
+ params.filter = this.filter;
341
+ params.browse = this.filterBy;
342
+ }
343
+ if (this.facets) {
344
+ params = Object.assign(params, this.facets);
345
+ }
346
+ return params;
347
+ }
348
+
349
+ _paramsFromSubforms(params) {
350
+ if (this.subforms) {
351
+ document.querySelectorAll(this.subforms).forEach((form) => {
352
+ if (form.serializeForm) {
353
+ Object.assign(params, form.serializeForm());
354
+ }
355
+ });
356
+ }
357
+ return params;
358
+ }
359
+
360
+ /**
361
+ * returns selected documents.
362
+ *
363
+ * @returns {Array}
364
+ */
365
+ getSelected() {
366
+ const selected = [];
367
+ if (this.container) {
368
+ document.querySelectorAll(this.container).forEach((container) =>
369
+ container.querySelectorAll('.document-select paper-checkbox[checked]').forEach((checkbox) => {
370
+ selected.push(checkbox.value);
371
+ })
372
+ );
373
+ } else {
374
+ this.querySelectorAll('.document-select paper-checkbox[checked]').forEach((checkbox) => {
375
+ selected.push(checkbox.value);
376
+ });
377
+ }
378
+ return selected;
379
+ }
380
+
381
+ _filter() {
382
+ const filter = this.shadowRoot.getElementById('filterString').value;
383
+ const filterBy = this.shadowRoot.getElementById('filter-list').selected;
384
+ if (typeof filter !== 'undefined') {
385
+ console.log('<pb-browse-docs> Filter by %s', filter);
386
+ this.filter = filter;
387
+ this.setParameter('filter', filter);
388
+ this.setParameter('filterBy', filterBy);
389
+ this.pushHistory('filter docs');
390
+
391
+ this.load();
392
+ }
393
+ }
394
+
395
+ _filterChanged() {
396
+ const filterBy = this.shadowRoot.getElementById('filter-list').selected;
397
+ if (filterBy && filterBy !== this.filterBy) {
398
+ console.log('<pb-browse-docs> Filtering on %s', filterBy);
399
+ this.filterBy = filterBy;
400
+ }
401
+ }
402
+
403
+ _sort() {
404
+ const sortBy = this.shadowRoot.getElementById('sort-list').selected;
405
+ if (sortBy && sortBy !== this.sortBy) {
406
+ console.log('<pb-browse-docs> Sorting by %s', sortBy);
407
+ this.sortBy = sortBy;
408
+ this.setParameter('sort', sortBy);
409
+ this.pushHistory('sort docs');
410
+
411
+ this.load();
412
+ }
413
+ }
414
+
415
+ _facets(ev) {
416
+ if (ev.detail && ev.detail.params) {
417
+ this.clearParametersMatching(/^(all-|facet-).*/);
418
+ for (let param in ev.detail.params) {
419
+ this.setParameter(param, ev.detail.params[param]);
420
+ }
421
+ this.facets = ev.detail.params;
422
+ this.start = 1;
423
+ this.pushHistory('facets');
424
+ }
425
+ this.load();
426
+ }
427
+
428
+ _onLoad(content) {
429
+ window.scrollTo(0, 0);
430
+ const div = content.querySelector('[data-root]');
431
+ const collection = div && div.getAttribute('data-root');
432
+ const writable = div && div.classList.contains('writable');
433
+ this.emitTo('pb-collection', {
434
+ writable,
435
+ collection
436
+ });
437
+ document.querySelectorAll('[can-write]').forEach((elem) => {
438
+ elem.disabled = !writable;
439
+ });
440
+ content.querySelectorAll('[data-collection]').forEach(link => {
441
+ link.addEventListener('click', (ev) => {
442
+ ev.preventDefault();
443
+ this.collection = link.getAttribute('data-collection');
444
+ this.start = 1;
445
+ this.setParameter('collection', this.collection);
446
+ this.pushHistory('browse collection');
447
+ console.log('<pb-browse-docs> loading collection %s', this.collection);
448
+ this.load();
449
+ });
450
+ });
451
+ }
452
+
453
+ _handleDelete(target, ev) {
454
+ const deleteDialog = this.shadowRoot.getElementById('deleteDialog');
455
+ const selected = this.getSelected();
456
+ if (selected.length > 0) {
457
+ this._selected = selected;
458
+ deleteDialog.open();
459
+ }
460
+ }
461
+
462
+ _confirmDelete() {
463
+ if (!(this._file || this._selected)) {
464
+ return;
465
+ }
466
+
467
+ let files;
468
+ if (this._selected) {
469
+ files = this._selected;
470
+ } else {
471
+ files = [this._file];
472
+ }
473
+ console.log('<pb-browse-docs> Deleting %o', this._file);
474
+ const params = {
475
+ action: 'delete',
476
+ 'docs[]': files
477
+ };
478
+ this._file = null;
479
+ this._selected = null;
480
+ this.load(params);
481
+ }
482
+
483
+ _loggedIn(user, groups) {
484
+ if (user == null) {
485
+ return false;
486
+ }
487
+ if (this.group) {
488
+ if (!groups) {
489
+ return false;
490
+ }
491
+ return groups.indexOf(this.group) > -1;
492
+ }
493
+ return true;
494
+ }
495
+
496
+ _canModify(allowModification) {
497
+ return allowModification ? '' : 'hidden';
498
+ }
499
+
500
+ _autocomplete(ev) {
501
+ const autocompleteLoader = this.shadowRoot.getElementById('autocompleteLoader');
502
+ autocompleteLoader.params = {
503
+ query: ev.detail.option.text,
504
+ field: this.filterBy
505
+ };
506
+ autocompleteLoader.generateRequest();
507
+ }
508
+
509
+ _updateSuggestions() {
510
+ const autocomplete = this.shadowRoot.getElementById('autocomplete');
511
+ const autocompleteLoader = this.shadowRoot.getElementById('autocompleteLoader');
512
+ autocomplete.suggestions(autocompleteLoader.lastResponse);
513
+ }
514
+
515
+ _handleEnter(e) {
516
+ if (e.keyCode == 13) {
517
+ this._filter();
518
+ }
519
+ }
520
+ }
521
521
  customElements.define('pb-browse-docs', PbBrowseDocs);