@teipublisher/pb-components 2.26.0-next-3.4 → 2.26.0-next-3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/pb-ajax.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import { LitElement, html, css } from 'lit-element';
2
2
  import '@polymer/iron-ajax';
3
- import '@polymer/paper-dialog';
4
- import '@polymer/paper-dialog-scrollable';
5
3
  import { pbMixin } from './pb-mixin.js';
6
4
  import { get as i18n } from "./pb-i18n.js";
7
5
  import './pb-message.js';
@@ -75,6 +73,7 @@ export class PbAjax extends pbMixin(LitElement) {
75
73
  this.method = 'get';
76
74
  this.confirm = null;
77
75
  this.quiet = false;
76
+ this._running = false;
78
77
  }
79
78
 
80
79
  connectedCallback() {
@@ -95,7 +94,6 @@ export class PbAjax extends pbMixin(LitElement) {
95
94
  @response="${this._handleResponse}"></iron-ajax>
96
95
  <pb-message id="confirmDialog"></pb-message>
97
96
  <slot name="title" style="display: none"></slot>
98
- <progress id="progress" max="100"></progress>
99
97
  `;
100
98
  }
101
99
 
@@ -104,8 +102,7 @@ export class PbAjax extends pbMixin(LitElement) {
104
102
  const slot = this.shadowRoot.querySelector('slot[name=title]');
105
103
  this._dialogTitle = '';
106
104
  slot.assignedNodes().forEach(node => {this._dialogTitle += node.innerHTML});
107
- this.button = this.querySelector('paper-button');
108
- this.progress = this.shadowRoot.querySelector('progress');
105
+ this.button = this.renderRoot.getElementById('button');
109
106
  }
110
107
 
111
108
  static get styles() {
@@ -116,18 +113,21 @@ export class PbAjax extends pbMixin(LitElement) {
116
113
  slot[name="title"] {
117
114
  margin: 0;
118
115
  }
119
- progress{
120
- width: 100%;
121
- display: none;
122
- }
123
- progress.running{
116
+ pb-message[open] {
124
117
  display: block;
125
118
  }
119
+ pb-message:not([open]) {
120
+ display: none;
121
+ }
126
122
  `;
127
123
  }
128
124
 
129
125
  _handleClick(ev) {
130
126
  ev.preventDefault();
127
+ if (this._running) {
128
+ return;
129
+ }
130
+ this._running = true;
131
131
  if (this.confirm) {
132
132
  const dialog = this.shadowRoot.getElementById('confirmDialog');
133
133
  dialog.confirm(this._dialogTitle, i18n(this.confirm))
@@ -139,31 +139,14 @@ export class PbAjax extends pbMixin(LitElement) {
139
139
  }
140
140
 
141
141
  async trigger() {
142
- this._disable();
143
142
  const loader = this.shadowRoot.getElementById('loadContent');
144
143
  loader.url = `${this.getEndpoint()}/${this.url}`;
145
144
  this.emitTo('pb-start-update');
146
145
  await this.shadowRoot.getElementById('loadContent').generateRequest();
147
146
  }
148
147
 
149
- _enable(){
150
- if(this.button){
151
- this.button.removeAttribute('disabled');
152
- this.button.removeAttribute('readonly');
153
- this.progress.classList.remove('running');
154
- }
155
- }
156
- _disable(){
157
- if(this.button){
158
- this.button.setAttribute('disabled','disabled');
159
- this.button.setAttribute('readonly','readonly');
160
- this.progress.classList.add('running');
161
- }
162
- }
163
-
164
-
165
148
  _handleResponse() {
166
- this._enable();
149
+ this._running = false;
167
150
  const resp = this.shadowRoot.getElementById('loadContent').lastResponse;
168
151
  this._message = resp;
169
152
  if (!this.quiet) {
@@ -179,7 +162,7 @@ export class PbAjax extends pbMixin(LitElement) {
179
162
  }
180
163
 
181
164
  _handleError() {
182
- this._enable();
165
+ this._running = false;
183
166
  const loader = this.shadowRoot.getElementById('loadContent');
184
167
  const msg = loader.lastError.response;
185
168
  const parser = new DOMParser();
@@ -147,7 +147,6 @@ export class PbDownload extends pbMixin(LitElement) {
147
147
 
148
148
  a {
149
149
  text-decoration: var(--pb-download-text-decoration, none);
150
- color: var(--pb-download-color);
151
150
  }
152
151
  `;
153
152
  }
package/src/pb-message.js CHANGED
@@ -13,7 +13,7 @@ export class PbMessage extends themableMixin(LitElement) {
13
13
  static get styles() {
14
14
  return css`
15
15
  :host {
16
- display:block;
16
+ display: block;
17
17
  }
18
18
  `;
19
19
  }
@@ -40,6 +40,10 @@ export class PbMessage extends themableMixin(LitElement) {
40
40
  message: {
41
41
  type: String,
42
42
  reflect: true
43
+ },
44
+ open: {
45
+ type: Boolean,
46
+ reflect: true
43
47
  }
44
48
  };
45
49
  }
@@ -49,6 +53,7 @@ export class PbMessage extends themableMixin(LitElement) {
49
53
  this.title = '';
50
54
  this.message = '';
51
55
  this.type = 'message'; // defaults to 'message'
56
+ this.open = false;
52
57
  }
53
58
 
54
59
  render() {
@@ -84,6 +89,7 @@ export class PbMessage extends themableMixin(LitElement) {
84
89
  show(title, message) {
85
90
  this.type = 'message';
86
91
  this.set(title, message);
92
+ this.open = true;
87
93
  this.modal.openDialog();
88
94
 
89
95
  return new Promise((resolve, reject) => {
@@ -91,6 +97,7 @@ export class PbMessage extends themableMixin(LitElement) {
91
97
  requestAnimationFrame(() => {
92
98
  const close = this.renderRoot.querySelector('.close');
93
99
  close.addEventListener('click', () => {
100
+ this.open = false;
94
101
  this.modal.closeDialog();
95
102
  resolve({ once: true });
96
103
  });
@@ -110,6 +117,7 @@ export class PbMessage extends themableMixin(LitElement) {
110
117
  confirm(title, message) {
111
118
  this.type = 'confirm';
112
119
  this.set(title, message);
120
+ this.open = true;
113
121
  this.modal.openDialog();
114
122
 
115
123
  return new Promise((resolve, reject) => {
@@ -118,10 +126,12 @@ export class PbMessage extends themableMixin(LitElement) {
118
126
  const confirm = this.renderRoot.querySelector('.confirm');
119
127
  const cancel = this.renderRoot.querySelector('.reject');
120
128
  confirm.addEventListener('click', () => {
129
+ this.open = false;
121
130
  this.modal.closeDialog();
122
131
  resolve({ once: true });
123
132
  });
124
133
  cancel.addEventListener('click', () => {
134
+ this.open = false;
125
135
  this.modal.closeDialog();
126
136
  reject({ once: true });
127
137
  });
package/src/pb-search.js CHANGED
@@ -2,13 +2,8 @@ import { LitElement, html, css } from 'lit-element';
2
2
  import { pbMixin, waitOnce } from './pb-mixin.js';
3
3
  import { registry } from "./urls.js";
4
4
  import { translate } from "./pb-i18n.js";
5
- import '@polymer/paper-input/paper-input.js';
6
- import '@polymer/paper-checkbox';
7
5
  import '@polymer/iron-ajax';
8
- import '@polymer/iron-form';
9
- import '@polymer/paper-button';
10
- import '@polymer/iron-icon';
11
- import '@cwmr/paper-autocomplete';
6
+ import {themableMixin} from "./theming.js";
12
7
 
13
8
  /**
14
9
  * Implements a basic search form, which can be extended with additional inputs.
@@ -26,7 +21,7 @@ import '@cwmr/paper-autocomplete';
26
21
  * @fires pb-paginate - When received, triggers the search again with the new value of the start property
27
22
  * @fires pb-search-resubmit - When received, triggers the search again
28
23
  */
29
- export class PbSearch extends pbMixin(LitElement) {
24
+ export class PbSearch extends themableMixin(pbMixin(LitElement)) {
30
25
  static get properties() {
31
26
  return {
32
27
  ...super.properties,
@@ -67,7 +62,7 @@ export class PbSearch extends pbMixin(LitElement) {
67
62
  /**
68
63
  * Optional URL to query for suggestions. If relative, it is interpreted
69
64
  * relative to the endpoint defined on a surrounding `pb-page`.
70
- *
65
+ *
71
66
  * Upon autocomplete, the current input by the user will be sent with a query parameter
72
67
  * `query`. The name/values of form controls nested within `pb-search` or subforms will also be
73
68
  * appended to the request as parameters. This allows the server side code to distinguish
@@ -112,14 +107,6 @@ export class PbSearch extends pbMixin(LitElement) {
112
107
  }
113
108
 
114
109
  firstUpdated() {
115
- if (!this.disableAutocomplete) {
116
- const autocomplete = this.shadowRoot.getElementById('autocomplete');
117
- autocomplete.addEventListener('autocomplete-change', this._autocomplete.bind(this));
118
- }
119
- const ironform = this.shadowRoot.getElementById('ironform');
120
- ironform.addEventListener('iron-form-response', (event) =>
121
- event.detail.completes.then((r) => this.emitTo('pb-search', r.parseResponse()))
122
- );
123
110
  waitOnce('pb-page-ready', (options) => {
124
111
  const loader = this.shadowRoot.getElementById('autocompleteLoader');
125
112
  const url = this.source || "api/search/autocomplete";
@@ -152,36 +139,28 @@ export class PbSearch extends pbMixin(LitElement) {
152
139
  }
153
140
  });
154
141
  }
155
-
142
+
156
143
  render() {
157
144
  return html`
158
- <custom-style>
159
- <style>
160
- :host {
161
- --suggestions-item: {
162
- color: var(--pb-search-suggestions-color, black);
163
- };
164
- --suggestions-wrapper: {
165
- background: var(--pb-search-suggestions-background, white);
166
- }
167
- }
168
- </style>
169
- </custom-style>
170
- <iron-form id="ironform" allow-redirect="${this.redirect}">
171
- <form id="searchPageForm" method="get" action="${this.action}" accept="text/html">
172
- <slot name="beforeInput"></slot>
173
- <paper-input id="search" type="search" name="query" @keyup="${this._handleEnter}" label="${translate(this.placeHolder)}"
174
- value="${this.value}" always-float-label>
175
- <iron-icon icon="search" @click="${this._doSearch}" slot="prefix"></iron-icon>
176
- </paper-input>
177
- <paper-autocomplete-suggestions id="autocomplete" for="search" source="${this._suggestions}" remote-source></paper-autocomplete-suggestions>
178
- <slot></slot>
179
-
180
- <slot name="searchButton"></slot>
145
+ <form id="searchPageForm" method="get" action="${this.action}" accept="text/html" @submit="${this._handleSubmit}">
146
+ <slot name="beforeInput"></slot>
147
+ <div class="input-wrapper">
148
+ <input
149
+ id="search"
150
+ name="query"
151
+ type="search"
152
+ placeholder="${translate(this.placeHolder)}"
153
+ .value="${this.value}"
154
+ @keyup="${this._handleEnter}"
155
+ list="suggestions"
156
+ part="input"
157
+ />
158
+ <datalist id="suggestions"></datalist>
159
+ <slot name="searchButton" part="search-button"></slot>
181
160
  <slot name="resetButton"></slot>
182
-
183
- </form>
184
- </iron-form>
161
+ </div>
162
+ <slot></slot>
163
+ </form>
185
164
  <iron-ajax
186
165
  id="autocompleteLoader"
187
166
  verbose
@@ -195,32 +174,54 @@ export class PbSearch extends pbMixin(LitElement) {
195
174
  static get styles() {
196
175
  return css`
197
176
  :host {
198
- --paper-input-container-color: var(--pb-search-label-color, var(--paper-grey-500, #303030));
199
- --paper-input-container-input-color: var(--pb-search-input-color, var(--pb-color-primary, #000000));
200
- --paper-input-container-focus-color: var(--pb-search-focus-color, var(--paper-grey-500, #303030));
177
+ display: inline-block;
201
178
  }
202
- a{
203
- padding:1rem;
204
- color:var(--pb-reset-color);
179
+ .input-wrapper {
180
+ display: flex;
181
+ align-items: center;
205
182
  }
206
- .buttons{
207
- margin-top:1rem;
208
- }
209
- form {
210
- margin: 0;
183
+ #search {
184
+ flex: 2;
211
185
  }
212
186
  `;
213
187
  }
214
188
 
189
+ _serializeForm() {
190
+ const form = this.shadowRoot.getElementById('searchPageForm');
191
+ const formData = new FormData(form);
192
+ const json = {};
193
+ // @ts-ignore - FormData.entries() is supported in modern browsers
194
+ for (let [key, value] of formData.entries()) {
195
+ json[key] = value;
196
+ }
197
+
198
+ // Also collect form controls from slots
199
+ const formControls = this.querySelectorAll('input, select, textarea');
200
+ formControls.forEach(control => {
201
+ if (control.name && control.type !== 'button' && control.type !== 'submit' && control.type !== 'reset') {
202
+ if (control.type === 'checkbox' || control.type === 'radio') {
203
+ if (control.checked) {
204
+ json[control.name] = control.value;
205
+ }
206
+ } else {
207
+ json[control.name] = control.value;
208
+ }
209
+ }
210
+ });
211
+
212
+ return json;
213
+ }
214
+
215
215
  _doSearch(pagination = false) {
216
- let json = this.shadowRoot.getElementById('ironform').serializeForm();
216
+ let json = this._serializeForm();
217
217
  json = this._paramsFromSubforms(json);
218
- // remove unnecessary param added by autocomplete
219
- delete json['autocomplete-custom-template'];
220
- // always start on first result after submitting new search
221
218
  json.start = pagination ? this.start : 1;
222
219
  if (this.redirect) {
223
- window.location.href = `${this.action}?${new URLSearchParams(json)}`;
220
+ const params = new URLSearchParams();
221
+ Object.keys(json).forEach(key => {
222
+ params.append(key, json[key]);
223
+ });
224
+ window.location.href = `${this.action}?${params}`;
224
225
  } else {
225
226
  registry.commit(this, json);
226
227
  this.emitTo('pb-load', {
@@ -230,6 +231,11 @@ export class PbSearch extends pbMixin(LitElement) {
230
231
  }
231
232
  }
232
233
 
234
+ _handleSubmit(e) {
235
+ e.preventDefault();
236
+ this._doSearch();
237
+ }
238
+
233
239
  _paramsFromSubforms(params) {
234
240
  if (this.subforms) {
235
241
  document.querySelectorAll(this.subforms).forEach((form) => {
@@ -243,32 +249,43 @@ export class PbSearch extends pbMixin(LitElement) {
243
249
 
244
250
  _handleEnter(e) {
245
251
  if (e.keyCode === 13) {
246
- this._doSearch();
252
+ return this._doSearch();
253
+ }
254
+ const value = this.shadowRoot.getElementById('search').value;
255
+ if (value.length > 2) {
256
+ const loader = this.shadowRoot.getElementById('autocompleteLoader');
257
+ loader.params = {
258
+ query: value
259
+ };
260
+ loader.generateRequest();
247
261
  }
248
262
  }
249
263
 
250
264
  _doSubmit() {
251
- this.shadowRoot.getElementById('ironform').submit();
265
+ this._doSearch();
252
266
  }
253
267
 
254
- _reset(){
255
- this.shadowRoot.getElementById('ironform').reset();
256
- }
257
-
258
- _autocomplete(ev) {
259
- const params = this.shadowRoot.getElementById('ironform').serializeForm();
260
- const loader = this.shadowRoot.getElementById('autocompleteLoader');
261
- loader.params = params;
262
- loader.generateRequest();
268
+ _reset() {
269
+ const form = this.shadowRoot.getElementById('searchPageForm');
270
+ form.reset();
271
+ registry.commit(this, {}, true);
263
272
  }
264
273
 
265
274
  _updateSuggestions() {
266
- const autocomplete = this.shadowRoot.getElementById('autocomplete');
267
275
  const loader = this.shadowRoot.getElementById('autocompleteLoader');
276
+ const datalist = this.shadowRoot.getElementById('suggestions');
268
277
  if (loader.lastResponse) {
269
- autocomplete.suggestions(loader.lastResponse);
278
+ datalist.innerHTML = '';
279
+ loader.lastResponse.forEach(({text, value}) => {
280
+ const option = document.createElement('option');
281
+ option.value = value;
282
+ option.innerText = text;
283
+ datalist.appendChild(option);
284
+ });
270
285
  }
271
286
  }
272
-
273
287
  }
274
- customElements.define('pb-search', PbSearch);
288
+ if (!customElements.get('pb-search')) {
289
+ customElements.define('pb-search', PbSearch);
290
+ }
291
+
@@ -2,12 +2,13 @@ import { LitElement, html } from 'lit-element';
2
2
  import '@polymer/paper-checkbox';
3
3
  import { pbMixin, waitOnce } from './pb-mixin.js';
4
4
  import { registry } from "./urls.js";
5
+ import {themableMixin} from "./theming.js";
5
6
 
6
7
  /**
7
8
  * @param {{ selector: any; command: string; state: boolean; }} newConfig
8
9
  * @param {any[]} configs
9
10
  */
10
- export function addSelector(newConfig, configs) {
11
+ export function addSelector(newConfig, configs) {
11
12
  const idx = configs.findIndex((item) => item.selector === newConfig.selector);
12
13
  if (idx > -1) {
13
14
  configs[idx] = newConfig;
@@ -19,11 +20,11 @@ import { registry } from "./urls.js";
19
20
  /**
20
21
  * Enable or disable a particular display feature by setting a predefined or custom parameter.
21
22
  * Toggling display features can be done server-side or client-side.
22
- *
23
+ *
23
24
  * It is important that `pb-toggle-feature` emits and subscribes to the same channel as the target `pb-view`.
24
- *
25
+ *
25
26
  * # Server side toggling
26
- *
27
+ *
27
28
  * You may set the following view parameters which correspond to the properties supported by `pb-view`:
28
29
  *
29
30
  * | Parameter | Description |
@@ -64,22 +65,22 @@ import { registry } from "./urls.js";
64
65
  * ```
65
66
  *
66
67
  * # Client side toggling
67
- *
68
+ *
68
69
  * The component can also be used to toggle features client-side, i.e. without requiring a server-roundtrip.
69
70
  * To enable this, the `selector` property should be set to a CSS3 selector targetting the HTML elements
70
71
  * to toggle. In `on` state, the selected elements will be assigned a class `.toggled`.
71
- *
72
+ *
72
73
  * ```html
73
74
  * <pb-toggle-feature name="normalized" selector=".choice,.choice-alternate,br">Normalized View</pb-toggle-feature>
74
75
  * ```
75
- *
76
+ *
76
77
  * Note that the name attribute is still required: it is used to determine if the feature is in on or off state by
77
78
  * looking at request parameters.
78
- *
79
+ *
79
80
  * Instead of toggling the class, you can also completely disable the elements selected - provided that they are
80
81
  * publisher components implementing the corresponding `command` method of `pb-mixin`. To disable elements instead of
81
82
  * toggling, set the `action` property to *disable*.
82
- *
83
+ *
83
84
  * ```html
84
85
  * <pb-toggle-feature name="plain" selector=".tei-foreign,pb-highlight,pb-popover" action="disable" default="off">Plain Reading View</pb-toggle-feature>
85
86
  * ```
@@ -88,7 +89,7 @@ import { registry } from "./urls.js";
88
89
  * @fires pb-global-toggle - Fired if property `global` is set. Will be caught by the surrounding `pb-page`
89
90
  * @fires pb-update - When received, sets the features passed from the event
90
91
  */
91
- export class PbToggleFeature extends pbMixin(LitElement) {
92
+ export class PbToggleFeature extends themableMixin(pbMixin(LitElement)) {
92
93
 
93
94
  static get properties() {
94
95
  return {
@@ -101,7 +102,7 @@ export class PbToggleFeature extends pbMixin(LitElement) {
101
102
  type: String
102
103
  },
103
104
  /**
104
- * (optional) CSS selector: selects the elements to toggle client side (sets or unsets a
105
+ * (optional) CSS selector: selects the elements to toggle client side (sets or unsets a
105
106
  * CSS class `.toggled`). Setting this property will not trigger a reload as everything is
106
107
  * handled by javascript.
107
108
  */
@@ -164,7 +165,7 @@ export class PbToggleFeature extends pbMixin(LitElement) {
164
165
  },
165
166
  /**
166
167
  * If set, toggle the state of elements which reside
167
- * in the surrounding HTML context below `pb-page`
168
+ * in the surrounding HTML context below `pb-page`
168
169
  * (means: elements which are not inside a `pb-view` or `pb-load`).
169
170
  */
170
171
  global: {
@@ -187,7 +188,8 @@ export class PbToggleFeature extends pbMixin(LitElement) {
187
188
 
188
189
  render() {
189
190
  return html`
190
- <paper-checkbox id="checkbox" @change="${this._changed}" .checked="${this.checked}" .disabled="${this.disabled}"><slot></slot></paper-checkbox>
191
+ <input type="checkbox" id="checkbox" @change="${this._changed}" .checked="${this.checked}" .disabled="${this.disabled}"></input>
192
+ <label for="checkbox"><slot></slot></label>
191
193
  `;
192
194
  }
193
195
 
@@ -201,7 +203,7 @@ export class PbToggleFeature extends pbMixin(LitElement) {
201
203
 
202
204
  const param = registry.state[this.name];
203
205
  this._setChecked(param);
204
-
206
+
205
207
  const newState = {};
206
208
  newState[this.name] = this.checked ? this.on : this.off;
207
209
  registry.replace(this, newState);
@@ -212,10 +214,10 @@ export class PbToggleFeature extends pbMixin(LitElement) {
212
214
  waitOnce('pb-page-ready', () => {
213
215
  if (this.global) {
214
216
  this.dispatchEvent(new CustomEvent('pb-global-toggle', { detail: {
215
- selector: this.selector,
216
- command: this.action,
217
- state: this.checked
218
- }, bubbles: true, composed: true }));
217
+ selector: this.selector,
218
+ command: this.action,
219
+ state: this.checked
220
+ }, bubbles: true, composed: true }));
219
221
  } else if (this.selector) {
220
222
  this.emitTo('pb-toggle', {refresh: false});
221
223
  }