@teipublisher/pb-components 1.43.0 → 1.43.1

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