@teipublisher/pb-components 2.26.1-next.3 → 3.0.0-next-4.2

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.
Files changed (262) hide show
  1. package/.github/workflows/docker-cypress.yml +53 -0
  2. package/.github/workflows/node.js.yml +70 -21
  3. package/.releaserc.json +7 -2
  4. package/CHANGELOG.md +363 -11
  5. package/Dockerfile +78 -70
  6. package/README.md +112 -4
  7. package/css/components.css +5 -5
  8. package/css/gridjs/mermaid.min.css +1 -1
  9. package/css/leaflet/Control.Geocoder.css +1 -126
  10. package/css/leaflet/images/layers.png +0 -0
  11. package/css/tify/tify.css +6 -5
  12. package/css/tom-select/tom-select.bootstrap4.min.css +1 -1
  13. package/css/tom-select/tom-select.bootstrap5.min.css +1 -1
  14. package/css/tom-select/tom-select.default.min.css +1 -1
  15. package/css/tom-select/tom-select.default.min.css.map +1 -0
  16. package/css/tom-select/tom-select.min.css +1 -1
  17. package/cypress.config.js +84 -0
  18. package/dist/api.html +1 -1
  19. package/dist/css/design-system.css +607 -0
  20. package/dist/demo/bundle-test.html +4 -3
  21. package/dist/demo/components.css +46 -1
  22. package/dist/demo/design-system.html +710 -0
  23. package/dist/demo/dts-client.html +2 -2
  24. package/dist/demo/pb-autocomplete.html +23 -11
  25. package/dist/demo/pb-autocomplete2.html +66 -55
  26. package/dist/demo/pb-autocomplete3.html +17 -8
  27. package/dist/demo/pb-blacklab-highlight.html +28 -11
  28. package/dist/demo/pb-blacklab-results.html +3 -2
  29. package/dist/demo/pb-browse-docs.html +24 -24
  30. package/dist/demo/pb-browse-docs2.html +3 -3
  31. package/dist/demo/pb-clipboard.html +32 -28
  32. package/dist/demo/pb-code-editor.html +6 -6
  33. package/dist/demo/pb-code-highlight.html +63 -63
  34. package/dist/demo/pb-codepen.html +1 -1
  35. package/dist/demo/pb-collapse.html +1 -1
  36. package/dist/demo/pb-collapse2.html +2 -2
  37. package/dist/demo/pb-combo-box.html +135 -130
  38. package/dist/demo/pb-custom-form.html +64 -55
  39. package/dist/demo/pb-dialog.html +12 -6
  40. package/dist/demo/pb-document.html +1 -1
  41. package/dist/demo/pb-download.html +68 -59
  42. package/dist/demo/pb-drawer.html +67 -46
  43. package/dist/demo/pb-drawer2.html +65 -58
  44. package/dist/demo/pb-edit-app.html +2 -2
  45. package/dist/demo/pb-edit-xml.html +1 -1
  46. package/dist/demo/pb-facsimile-2.html +26 -11
  47. package/dist/demo/pb-facsimile-3.html +25 -10
  48. package/dist/demo/pb-facsimile-dedup-test-2.html +48 -0
  49. package/dist/demo/pb-facsimile-dedup-test.html +48 -0
  50. package/dist/demo/pb-facsimile.html +4 -4
  51. package/dist/demo/pb-formula.html +1 -1
  52. package/dist/demo/pb-grid.html +22 -8
  53. package/dist/demo/pb-highlight.html +2 -2
  54. package/dist/demo/pb-i18n-simple.html +1 -0
  55. package/dist/demo/pb-i18n.html +15 -5
  56. package/dist/demo/pb-image-strip-standalone.html +2 -2
  57. package/dist/demo/pb-image-strip-view.html +2 -2
  58. package/dist/demo/pb-leaflet-map.html +3 -3
  59. package/dist/demo/pb-leaflet-map2.html +2 -2
  60. package/dist/demo/pb-leaflet-map3.html +3 -3
  61. package/dist/demo/pb-link.html +1 -1
  62. package/dist/demo/pb-load.html +2 -6
  63. package/dist/demo/pb-login.html +1 -3
  64. package/dist/demo/pb-manage-odds.html +9 -4
  65. package/dist/demo/pb-markdown.html +1 -1
  66. package/dist/demo/pb-media-query.html +2 -2
  67. package/dist/demo/pb-mei.html +2 -2
  68. package/dist/demo/pb-mei2.html +2 -2
  69. package/dist/demo/pb-message.html +2 -3
  70. package/dist/demo/pb-odd-editor.html +54 -52
  71. package/dist/demo/pb-page-header.html +27 -0
  72. package/dist/demo/pb-popover.html +1 -1
  73. package/dist/demo/pb-print-preview.html +2 -2
  74. package/dist/demo/pb-progress.html +4 -4
  75. package/dist/demo/pb-repeat.html +32 -36
  76. package/dist/demo/pb-search.html +16 -5
  77. package/dist/demo/pb-search2.html +4 -4
  78. package/dist/demo/pb-search3.html +3 -3
  79. package/dist/demo/pb-search4.html +3 -3
  80. package/dist/demo/pb-select-feature.html +4 -4
  81. package/dist/demo/pb-select-feature2.html +4 -4
  82. package/dist/demo/pb-select-feature3.html +2 -2
  83. package/dist/demo/pb-select-i18n.html +58 -53
  84. package/dist/demo/pb-select-odd.html +1 -1
  85. package/dist/demo/pb-select.html +190 -75
  86. package/dist/demo/pb-select2.html +91 -37
  87. package/dist/demo/pb-select3.html +109 -41
  88. package/dist/demo/pb-svg.html +1 -1
  89. package/dist/demo/pb-table-grid.html +26 -15
  90. package/dist/demo/pb-tabs.html +15 -7
  91. package/dist/demo/pb-tify.html +7 -7
  92. package/dist/demo/pb-timeline.html +1 -1
  93. package/dist/demo/pb-timeline2.html +1 -1
  94. package/dist/demo/pb-toggle-feature.html +26 -23
  95. package/dist/demo/pb-toggle-feature2.html +4 -4
  96. package/dist/demo/pb-toggle-feature3.html +2 -2
  97. package/dist/demo/pb-toggle-feature4.html +56 -54
  98. package/dist/demo/pb-version.html +2 -2
  99. package/dist/demo/pb-view.html +78 -40
  100. package/dist/demo/pb-view2.html +69 -46
  101. package/dist/demo/pb-view3.html +53 -48
  102. package/dist/demo/pb-view4.html +70 -49
  103. package/dist/demo/pb-zoom.html +2 -2
  104. package/dist/{es-global-bridge-d8ce175d.js → es-global-bridge-D8ZcUcx_.js} +0 -4
  105. package/dist/focus-mixin-VCsFap6b.js +768 -0
  106. package/dist/images/icons.svg +217 -0
  107. package/dist/jinn-codemirror-DETLdm08.js +1 -0
  108. package/dist/lib/openseadragon.min.js +80 -0
  109. package/dist/lib/openseadragon.min.js.map +1 -0
  110. package/dist/pb-code-editor.js +25 -20
  111. package/dist/pb-component-docs.js +414 -3225
  112. package/dist/pb-components-bundle.js +3046 -4402
  113. package/dist/pb-dialog-tklYGWfc.js +121 -0
  114. package/dist/pb-edit-app.js +208 -107
  115. package/dist/pb-elements.json +716 -249
  116. package/dist/pb-facsimile.js +46 -0
  117. package/dist/pb-i18n-C0NDma4h.js +1 -0
  118. package/dist/pb-leaflet-map.js +23 -23
  119. package/dist/pb-mei.js +152 -134
  120. package/dist/pb-mixin-DHoWQheB.js +1 -0
  121. package/dist/pb-odd-editor.js +1671 -1231
  122. package/dist/pb-tify.js +1 -27
  123. package/dist/unsafe-html-D5VGo9Oq.js +1 -0
  124. package/dist/urls-BEONu_g4.js +1 -0
  125. package/eslint.config.mjs +92 -0
  126. package/gh-pages.js +5 -3
  127. package/i18n/common/en.json +6 -0
  128. package/i18n/common/pl.json +2 -2
  129. package/images/icons.svg +217 -0
  130. package/index.html +0 -5
  131. package/lib/leaflet-src.js.map +1 -0
  132. package/lib/leaflet.markercluster-src.js.map +1 -0
  133. package/lib/openseadragon.min.js +6 -6
  134. package/package.json +56 -81
  135. package/pb-elements.json +716 -249
  136. package/rollup.config.mjs +312 -0
  137. package/src/assets/components.css +5 -5
  138. package/src/assets/design-system.css +607 -0
  139. package/src/authority/airtable.js +20 -21
  140. package/src/authority/anton.js +129 -129
  141. package/src/authority/custom.js +70 -27
  142. package/src/authority/geonames.js +38 -32
  143. package/src/authority/gnd.js +50 -42
  144. package/src/authority/kbga.js +136 -134
  145. package/src/authority/metagrid.js +44 -46
  146. package/src/authority/reconciliation.js +66 -68
  147. package/src/authority/registry.js +4 -4
  148. package/src/docs/demo-utils.js +91 -0
  149. package/src/docs/pb-component-docs.js +287 -147
  150. package/src/docs/pb-component-view.js +380 -273
  151. package/src/docs/pb-components-list.js +115 -51
  152. package/src/docs/pb-demo-snippet.js +199 -174
  153. package/src/dts-client.js +306 -303
  154. package/src/dts-select-endpoint.js +125 -85
  155. package/src/parse-date-service.js +184 -135
  156. package/src/pb-ajax.js +175 -173
  157. package/src/pb-authority-lookup.js +198 -158
  158. package/src/pb-autocomplete.js +731 -313
  159. package/src/pb-blacklab-highlight.js +266 -260
  160. package/src/pb-blacklab-results.js +230 -225
  161. package/src/pb-browse-docs.js +601 -484
  162. package/src/pb-browse.js +68 -65
  163. package/src/pb-clipboard.js +97 -76
  164. package/src/pb-code-editor.js +111 -103
  165. package/src/pb-code-highlight.js +234 -204
  166. package/src/pb-codepen.js +81 -73
  167. package/src/pb-collapse.js +265 -152
  168. package/src/pb-combo-box.js +191 -191
  169. package/src/pb-components-bundle.js +1 -7
  170. package/src/pb-components.js +2 -6
  171. package/src/pb-custom-form.js +230 -141
  172. package/src/pb-dialog.js +99 -63
  173. package/src/pb-document.js +118 -91
  174. package/src/pb-download.js +214 -198
  175. package/src/pb-drawer.js +146 -149
  176. package/src/pb-edit-app.js +471 -240
  177. package/src/pb-edit-xml.js +101 -98
  178. package/src/pb-events.js +126 -107
  179. package/src/pb-facs-link.js +130 -101
  180. package/src/pb-facsimile.js +494 -410
  181. package/src/pb-fetch.js +389 -0
  182. package/src/pb-formula.js +152 -154
  183. package/src/pb-geolocation.js +130 -132
  184. package/src/pb-grid-action.js +59 -56
  185. package/src/pb-grid.js +388 -228
  186. package/src/pb-highlight.js +142 -142
  187. package/src/pb-hotkeys.js +40 -42
  188. package/src/pb-i18n.js +115 -127
  189. package/src/pb-icon-button.js +108 -0
  190. package/src/pb-icon.js +283 -0
  191. package/src/pb-image-strip.js +85 -79
  192. package/src/pb-lang.js +142 -57
  193. package/src/pb-leaflet-map.js +551 -483
  194. package/src/pb-link.js +132 -126
  195. package/src/pb-load.js +495 -428
  196. package/src/pb-login.js +303 -248
  197. package/src/pb-manage-odds.js +384 -338
  198. package/src/pb-map-icon.js +90 -90
  199. package/src/pb-map-layer.js +86 -86
  200. package/src/pb-markdown.js +107 -110
  201. package/src/pb-media-query.js +75 -73
  202. package/src/pb-mei.js +523 -303
  203. package/src/pb-message.js +144 -98
  204. package/src/pb-mixin.js +268 -265
  205. package/src/pb-navigation.js +83 -96
  206. package/src/pb-observable.js +39 -39
  207. package/src/pb-odd-editor.js +1209 -948
  208. package/src/pb-odd-elementspec-editor.js +375 -310
  209. package/src/pb-odd-model-editor.js +1189 -941
  210. package/src/pb-odd-parameter-editor.js +269 -170
  211. package/src/pb-odd-rendition-editor.js +184 -131
  212. package/src/pb-page.js +451 -422
  213. package/src/pb-paginate.js +260 -178
  214. package/src/pb-panel.js +217 -183
  215. package/src/pb-popover-themes.js +16 -9
  216. package/src/pb-popover.js +297 -288
  217. package/src/pb-print-preview.js +128 -128
  218. package/src/pb-progress.js +52 -52
  219. package/src/pb-repeat.js +141 -108
  220. package/src/pb-restricted.js +85 -78
  221. package/src/pb-search.js +258 -230
  222. package/src/pb-select-feature.js +210 -126
  223. package/src/pb-select-odd.js +184 -118
  224. package/src/pb-select-template.js +113 -78
  225. package/src/pb-select.js +330 -229
  226. package/src/pb-split-list.js +181 -176
  227. package/src/pb-svg.js +81 -80
  228. package/src/pb-table-column.js +55 -55
  229. package/src/pb-table-grid.js +334 -205
  230. package/src/pb-tabs.js +238 -61
  231. package/src/pb-tify.js +3331 -126
  232. package/src/pb-timeline.js +394 -255
  233. package/src/pb-toggle-feature.js +196 -188
  234. package/src/pb-upload.js +201 -176
  235. package/src/pb-version.js +22 -34
  236. package/src/pb-view-annotate.js +138 -102
  237. package/src/pb-view.js +1722 -1272
  238. package/src/pb-zoom.js +144 -46
  239. package/src/search-result-service.js +256 -223
  240. package/src/seed-element.js +14 -22
  241. package/src/settings.js +4 -4
  242. package/src/theming.js +98 -91
  243. package/src/urls.js +403 -289
  244. package/src/utils.js +53 -51
  245. package/vite.config.js +86 -0
  246. package/.github/workflows/main.yml +0 -24
  247. package/.github/workflows/release.js.yml +0 -34
  248. package/css/pb-styles.css +0 -51
  249. package/dist/iron-form-3b8dcaa7.js +0 -210
  250. package/dist/jinn-codemirror-da0e2d1f.js +0 -1
  251. package/dist/paper-checkbox-515a5284.js +0 -1597
  252. package/dist/paper-icon-button-b1d31571.js +0 -398
  253. package/dist/paper-listbox-a3b7175c.js +0 -1265
  254. package/dist/pb-i18n-0611135a.js +0 -1
  255. package/dist/pb-mixin-b1caa22e.js +0 -158
  256. package/dist/polymer-hack.js +0 -1
  257. package/dist/vaadin-element-mixin-fe4a4883.js +0 -527
  258. package/lib/Control.Geocoder.min.js +0 -2
  259. package/lib/Control.Geocoder.min.js.map +0 -1
  260. package/src/assets/pb-styles.css +0 -51
  261. package/src/pb-light-dom.js +0 -41
  262. package/src/polymer-hack.js +0 -6
@@ -1,9 +1,7 @@
1
- import { html, css } from 'lit-element';
2
- import '@polymer/iron-form';
3
- import '@polymer/iron-ajax';
4
- import '@polymer/paper-dialog';
5
- import '@polymer/paper-dialog-scrollable';
1
+ import { html, css } from 'lit';
2
+ import './pb-fetch.js';
6
3
  import { PbLoad } from './pb-load.js';
4
+ import './pb-dialog.js';
7
5
 
8
6
  /**
9
7
  * A custom form element which loads the actual form from a server-side script using AJAX.
@@ -19,159 +17,250 @@ import { PbLoad } from './pb-load.js';
19
17
  * @fires pb-submit - Fired when the form is submitted
20
18
  */
21
19
  export class PbCustomForm extends PbLoad {
20
+ constructor() {
21
+ super();
22
+ this._autoSubmitTargets = new WeakSet();
23
+ }
22
24
 
23
- firstUpdated() {
24
- this.shadowRoot.getElementById('ironform').addEventListener('iron-form-presubmit', (ev) => {
25
- ev.preventDefault();
26
- this._submit();
27
- });
28
- this.addEventListener('click', (e) => {
29
- if (e.target.slot === 'searchButtonTop'){
30
- this.submit();
31
- }
32
- if (e.target.slot === 'searchButtonBottom'){
33
- this.submit();
34
- }
35
- if (e.target.slot === 'resetButton'){
36
- this._reset();
37
- }
38
- });
39
-
40
- this._submissionHandlers();
25
+ firstUpdated() {
26
+ const form = this.shadowRoot && this.shadowRoot.getElementById('form');
27
+ if (form) {
28
+ form.addEventListener('submit', ev => {
29
+ ev.preventDefault();
30
+ this._submit();
31
+ });
41
32
  }
33
+ this.addEventListener('click', e => {
34
+ if (e.target.slot === 'searchButtonTop') {
35
+ this.submit();
36
+ }
37
+ if (e.target.slot === 'searchButtonBottom') {
38
+ this.submit();
39
+ }
40
+ if (e.target.slot === 'resetButton') {
41
+ this._reset();
42
+ }
43
+ });
42
44
 
43
- render() {
44
- return html`
45
- <iron-form id="ironform">
46
- <form action="" accept="text/html" method="GET">
47
- <slot name="searchButtonTop"></slot>
48
- <slot></slot>
49
- <slot name="searchButtonBottom"></slot>
50
- <slot name="resetButton"></slot>
51
- </form>
52
- </iron-form>
53
-
54
- <iron-ajax
55
- id="loadContent"
56
- verbose
57
- handle-as="text"
58
- method="get"
59
- with-credentials
60
- @response="${this._handleContent}"
61
- @error="${this._handleError}"></iron-ajax>
62
- <paper-dialog id="errorDialog">
63
- <h2>Error</h2>
64
- <paper-dialog-scrollable></paper-dialog-scrollable>
65
- <div class="buttons">
66
- <paper-button dialog-confirm="dialog-confirm" autofocus="autofocus">
67
- Close
68
- </paper-button>
69
- </div>
70
- </paper-dialog>
71
- `;
72
- }
45
+ this._submissionHandlers();
46
+ }
73
47
 
74
- static get styles() {
75
- return css`
76
- :host {
77
- display: block;
78
- }
79
- `;
80
- }
48
+ render() {
49
+ return html`
50
+ <form id="form" action="" accept="text/html" method="GET" novalidate>
51
+ <slot name="searchButtonTop"></slot>
52
+ <slot></slot>
53
+ <slot name="searchButtonBottom"></slot>
54
+ <slot name="resetButton"></slot>
55
+ </form>
81
56
 
82
- submit() {
83
- this.shadowRoot.getElementById('ironform').submit();
84
- }
57
+ <pb-fetch
58
+ id="loadContent"
59
+ verbose
60
+ handle-as="text"
61
+ method="get"
62
+ with-credentials
63
+ @response="${this._handleContent}"
64
+ @error="${this._handleError}"
65
+ ></pb-fetch>
66
+ <pb-dialog id="errorDialog" title="Error">
67
+ <p id="errorMessage"></p>
68
+ <div slot="footer">
69
+ <button rel="prev" type="button">Close</button>
70
+ </div>
71
+ </pb-dialog>
72
+ `;
73
+ }
85
74
 
86
- _submit() {
87
- const json = this.serializeForm();
88
- this.emitTo('pb-search-resubmit', { params: json });
89
- this.emitTo('pb-submit', { params: json});
90
- }
75
+ static get styles() {
76
+ return css`
77
+ :host {
78
+ display: block;
79
+ }
80
+ `;
81
+ }
91
82
 
92
- _reset(){
93
- this.shadowRoot.getElementById('ironform').reset();
83
+ submit() {
84
+ const form = this.shadowRoot && this.shadowRoot.getElementById('form');
85
+ if (!form) {
86
+ return;
94
87
  }
95
-
96
- /**
97
- * serialize custom form to object with name value pairs
98
- * empty, unselected and undifined inputs will be returned
99
- * as null while disabled elements will still be omitted
100
- * this allows url parameters to be reset in the URL
101
- * as IronForm.serializeform will omit names without a value
102
- * @returns {Object} name value pairs
103
- */
104
- serializeForm() {
105
- const elements = this.shadowRoot.getElementById('ironform')._getSubmittableElements()
106
- const initial = {}
107
- for (const element of elements) {
108
- initial[element.name] = null;
109
- }
110
- return Object.assign(
111
- initial,
112
- this.shadowRoot.getElementById('ironform').serializeForm()
113
- );
88
+ if (form.requestSubmit) {
89
+ form.requestSubmit();
90
+ } else if (typeof form.submit === 'function') {
91
+ form.submit();
114
92
  }
93
+ }
115
94
 
116
- _parseHeaders(xhr) {
117
- // overwrite to avoid `pb-results-received` event being sent
95
+ _submit() {
96
+ const json = this.serializeForm();
97
+ this.emitTo('pb-search-resubmit', { params: json });
98
+ this.emitTo('pb-submit', { params: json });
99
+ }
100
+
101
+ _reset() {
102
+ const form = this.shadowRoot && this.shadowRoot.getElementById('form');
103
+ if (form && typeof form.reset === 'function') {
104
+ form.reset();
118
105
  }
106
+ }
119
107
 
120
- _onLoad(content) {
121
- super._onLoad(content);
108
+ /**
109
+ * serialize custom form to object with name value pairs
110
+ * empty, unselected and undifined inputs will be returned
111
+ * as null while disabled elements will still be omitted
112
+ * this allows url parameters to be reset in the URL
113
+ * as IronForm.serializeform will omit names without a value
114
+ * @returns {Object} name value pairs
115
+ */
116
+ serializeForm() {
117
+ const form = this.shadowRoot && this.shadowRoot.getElementById('form');
118
+ if (!form) return {};
122
119
 
123
- this.dispatchEvent(new CustomEvent('pb-custom-form-loaded', { detail: content }));
124
- }
120
+ // Get elements from both form.elements AND slotted/light DOM content
121
+ // form.elements doesn't include slotted content in all browsers
122
+ const formElements = Array.from(form.elements || []);
123
+ const slottedElements = Array.from(this.querySelectorAll('input, select, textarea'));
124
+ const allElements = [...new Set([...formElements, ...slottedElements])];
125
125
 
126
- _submissionHandlers() {
127
- if (!this.autoSubmit) {
128
- return;
126
+ const elements = allElements.filter(
127
+ el => el.name && !el.disabled && !el.closest('[disabled]'),
128
+ );
129
+ const initial = {};
130
+ elements.forEach(element => {
131
+ if (!(element.name in initial)) {
132
+ initial[element.name] = null;
133
+ }
134
+ });
135
+
136
+ // Build form data manually to include slotted content
137
+ const data = {};
138
+ elements.forEach(el => {
139
+ if (el.type === 'checkbox' || el.type === 'radio') {
140
+ if (el.checked) {
141
+ if (data[el.name] == null) {
142
+ data[el.name] = el.value;
143
+ } else if (Array.isArray(data[el.name])) {
144
+ data[el.name].push(el.value);
145
+ } else {
146
+ data[el.name] = [data[el.name], el.value];
147
+ }
148
+ }
149
+ } else if (el.type === 'select-multiple') {
150
+ const values = Array.from(el.selectedOptions).map(opt => opt.value);
151
+ if (values.length > 0) {
152
+ data[el.name] = values.length === 1 ? values[0] : values;
129
153
  }
130
- this.querySelectorAll(this.autoSubmit).forEach((control) => {
131
- const name = control.nodeName.toLowerCase();
132
- let event = 'change';
133
- if (control instanceof HTMLButtonElement ||
134
- name === 'paper-icon-button' || name === 'paper-button' ||
135
- (name === 'input' && (control.type === 'button' || control.type === 'submit' || control.type === 'reset'))
136
- ) {
137
- event = 'click';
138
- } else if (name === 'paper-input' || (control instanceof HTMLInputElement && control.type === 'text')) {
139
- event = 'keyup';
140
- } else if (name === 'paper-dropdown-menu') {
141
- event = 'value-changed';
142
- }
143
- control.addEventListener(event, this._submit.bind(this));
144
- });
154
+ } else {
155
+ data[el.name] = el.value;
156
+ }
157
+ });
158
+
159
+ // Merge with initial (to preserve null for unset values)
160
+ Object.keys(data).forEach(key => {
161
+ if (data[key] != null) {
162
+ initial[key] = data[key];
163
+ }
164
+ });
165
+
166
+ return initial;
167
+ }
168
+
169
+ _parseHeaders(xhr) {
170
+ // overwrite to avoid `pb-results-received` event being sent
171
+ }
172
+
173
+ _onLoad(content) {
174
+ super._onLoad(content);
175
+
176
+ this.dispatchEvent(new CustomEvent('pb-custom-form-loaded', { detail: content }));
177
+ this._submissionHandlers();
178
+ }
179
+
180
+ _handleError() {
181
+ this.emitTo('pb-end-update');
182
+ const loader = this.shadowRoot.getElementById('loadContent');
183
+ const { response } = loader.lastError;
184
+ if (this.silent) {
185
+ console.error('Request failed: %s', response ? response.description : '');
186
+ return;
187
+ }
188
+ let message;
189
+ if (response) {
190
+ message = response.description;
191
+ } else {
192
+ message = 'Server error occurred';
145
193
  }
194
+ const dialog = this.shadowRoot.getElementById('errorDialog');
195
+ const messageElement = this.shadowRoot.getElementById('errorMessage');
196
+ messageElement.textContent = `Server error: ${message}`;
197
+ dialog.openDialog();
198
+ }
146
199
 
147
- static get properties() {
148
- return {
149
- /**
150
- * Register event handlers on all descendant elements matching the given CSS selector and submit the form
151
- * automatically if any of those changes. For button-like controls,
152
- * a submit is triggered on click, for text input on keyUp, and for
153
- * all other form components on change.
154
- */
155
- autoSubmit: {
156
- type: String,
157
- attribute: 'auto-submit'
158
- },
159
- ...super.properties
160
- };
200
+ _submissionHandlers() {
201
+ if (!this.autoSubmit) {
202
+ return;
161
203
  }
204
+ this.querySelectorAll(this.autoSubmit).forEach(control => {
205
+ if (this._autoSubmitTargets.has(control)) {
206
+ return;
207
+ }
208
+ this._autoSubmitTargets.add(control);
209
+ const name = (control.nodeName || '').toLowerCase();
210
+ let event = 'change';
211
+ if (
212
+ control instanceof HTMLButtonElement ||
213
+ name === 'pb-icon-button' ||
214
+ name === 'paper-button' ||
215
+ name === 'button' ||
216
+ (name === 'input' &&
217
+ (control.type === 'button' || control.type === 'submit' || control.type === 'reset'))
218
+ ) {
219
+ event = 'click';
220
+ } else if (
221
+ name === 'paper-input' ||
222
+ (control instanceof HTMLInputElement &&
223
+ (control.type === 'text' || control.type === 'search'))
224
+ ) {
225
+ event = 'keyup';
226
+ } else if (name === 'paper-dropdown-menu') {
227
+ event = 'value-changed';
228
+ }
229
+ if (control instanceof HTMLSelectElement) {
230
+ event = 'change';
231
+ }
232
+ control.addEventListener(event, this._submit.bind(this));
233
+ });
234
+ }
235
+
236
+ static get properties() {
237
+ return {
238
+ /**
239
+ * Register event handlers on all descendant elements matching the given CSS selector and submit the form
240
+ * automatically if any of those changes. For button-like controls,
241
+ * a submit is triggered on click, for text input on keyUp, and for
242
+ * all other form components on change.
243
+ */
244
+ autoSubmit: {
245
+ type: String,
246
+ attribute: 'auto-submit',
247
+ },
248
+ ...super.properties,
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Fired before the element updates its content
254
+ *
255
+ * @event pb-custom-form-loaded
256
+ * @param {string} the loaded content
257
+ */
162
258
 
163
- /**
164
- * Fired before the element updates its content
165
- *
166
- * @event pb-custom-form-loaded
167
- * @param {string} the loaded content
168
- */
169
-
170
- /**
171
- * Fired when form is submitted
172
- *
173
- * @event pb-search-resubmit
174
- * @param {object} params: serialized form parameters as json object
175
- */
259
+ /**
260
+ * Fired when form is submitted
261
+ *
262
+ * @event pb-search-resubmit
263
+ * @param {object} params: serialized form parameters as json object
264
+ */
176
265
  }
177
- customElements.define('pb-custom-form', PbCustomForm);
266
+ customElements.define('pb-custom-form', PbCustomForm);
package/src/pb-dialog.js CHANGED
@@ -1,83 +1,119 @@
1
- import { LitElement, html } from 'lit-element';
2
- import { pbMixin } from './pb-mixin';
3
- import { pbLightDom } from './pb-light-dom';
4
- import { unsafeHTML } from 'lit-html/directives/unsafe-html';
1
+ import { LitElement, html, css } from 'lit';
2
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3
+ import { pbMixin } from './pb-mixin.js';
4
+ import { themableMixin } from './theming.js';
5
5
 
6
6
  /**
7
- * A simple dialog component using the HTML5 <dialog> element (no shadow DOM).
7
+ * A simple dialog component using the HTML5 <dialog> element.
8
+ *
9
+ * Any button with the attribute `rel="prev"` will close the dialog when clicked.
8
10
  *
9
11
  * @slot - Content of the dialog
10
12
  * @slot title - Title of the dialog
11
13
  * @fires pb-dialog-opened - Fired when the dialog is opened
12
14
  * @fires pb-dialog-closed - Fired when the dialog is closed
13
15
  */
14
- export class PbDialog extends pbLightDom(pbMixin(LitElement)) {
16
+ export class PbDialog extends themableMixin(pbMixin(LitElement)) {
17
+ static get properties() {
18
+ return {
19
+ ...super.properties,
20
+ open: { type: Boolean, reflect: true },
21
+ modal: { type: Boolean, reflect: true },
22
+ title: { type: String, reflect: true },
23
+ message: { type: String, reflect: true },
24
+ };
25
+ }
15
26
 
16
- static get properties() {
17
- return {
18
- ...super.properties,
19
- open: { type: Boolean, reflect: true },
20
- modal: { type: Boolean, reflect: true },
21
- title: { type: String, reflect: true },
22
- message: { type: String, reflect: true }
23
- };
24
- }
27
+ constructor() {
28
+ super();
29
+ this.open = false;
30
+ this.modal = true;
31
+ this._escListener = this._onEsc.bind(this);
32
+ this.title = null;
33
+ this.message = null;
34
+ }
25
35
 
26
- constructor() {
27
- super();
28
- this.open = false;
29
- this.modal = true;
30
- this._escListener = this._onEsc.bind(this);
31
- this.title = null;
32
- this.message = null;
36
+ _onEsc(e) {
37
+ if (e.key === 'Escape' && this.open) {
38
+ this.closeDialog();
33
39
  }
40
+ }
34
41
 
35
- _onEsc(e) {
36
- if (e.key === 'Escape' && this.open) {
37
- this.closeDialog();
38
- }
42
+ openDialog() {
43
+ if (!this.open) {
44
+ if (this.modal) {
45
+ this._dialog.showModal();
46
+ } else {
47
+ this._dialog.show();
48
+ }
49
+ this.dispatchEvent(new CustomEvent('pb-dialog-opened', { bubbles: true, composed: true }));
50
+ this.open = true;
39
51
  }
52
+ }
40
53
 
41
- openDialog() {
42
- if (!this.open) {
43
- if (this.modal) {
44
- this._dialog.showModal();
45
- } else {
46
- this._dialog.show();
47
- }
48
- this.dispatchEvent(new CustomEvent('pb-dialog-opened', { bubbles: true, composed: true }));
49
- this.open = true;
50
- }
54
+ closeDialog() {
55
+ if (this.open) {
56
+ this._dialog.close();
57
+ this.dispatchEvent(new CustomEvent('pb-dialog-closed', { bubbles: true, composed: true }));
58
+ this.open = false;
51
59
  }
60
+ }
52
61
 
53
- closeDialog() {
54
- if (this.open) {
55
- this._dialog.close();
56
- this.dispatchEvent(new CustomEvent('pb-dialog-closed', { bubbles: true, composed: true }));
57
- this.open = false;
58
- }
59
- }
60
-
61
- render() {
62
- return html`
63
- <dialog @click="${(e) => e.target === this._dialog && this.modal && this.closeDialog()}" ?modal="${this.modal}">
64
- <article>
65
- <header>
66
- ${ this.title ? unsafeHTML(this.title) : this.fillSlot('title') }
67
- <button rel="prev" aria-label="Close" @click="${this.closeDialog}">&times;</button>
68
- </header>
69
- ${ this.message ? unsafeHTML(this.message) : this.fillSlot() }
70
- <footer>
71
- ${ this.fillSlot('footer') }
72
- </footer>
73
- </article>
74
- </dialog>
75
- `;
76
- }
62
+ render() {
63
+ return html`
64
+ <dialog @click="${e => e.target === this._dialog && this.modal && this.closeDialog()}">
65
+ <article>
66
+ <header>
67
+ ${this.title ? unsafeHTML(this.title) : html`<slot name="title"></slot>`}
68
+ <button rel="prev" aria-label="Close" type="button"></button>
69
+ </header>
70
+ ${this.message ? unsafeHTML(this.message) : html`<slot></slot>`}
71
+ <footer>
72
+ <slot name="footer"></slot>
73
+ </footer>
74
+ </article>
75
+ </dialog>
76
+ `;
77
+ }
77
78
 
78
- firstUpdated() {
79
- this._dialog = this.querySelector('dialog');
80
- }
79
+ firstUpdated() {
80
+ this._dialog = this.renderRoot.querySelector('dialog');
81
+ // Add click listeners to close buttons in both shadow and light DOM
82
+ [
83
+ ...this._dialog.querySelectorAll('button[rel="prev"]'),
84
+ ...this.querySelectorAll('button[rel="prev"]'),
85
+ ].forEach(button => button.addEventListener('click', this.closeDialog.bind(this)));
86
+ }
87
+
88
+ static get styles() {
89
+ return css`
90
+ header {
91
+ display: flex;
92
+ justify-content: space-between;
93
+ align-items: center;
94
+ }
95
+ button[rel='prev'] {
96
+ display: block;
97
+ height: 1rem;
98
+ width: 1rem;
99
+ background-image: var(
100
+ --pb-dialog-background-image,
101
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E")
102
+ );
103
+ background-position: center;
104
+ background-size: auto 1rem;
105
+ background-repeat: no-repeat;
106
+ background-color: transparent;
107
+ border: none;
108
+ }
109
+ footer {
110
+ display: flex;
111
+ justify-content: flex-end;
112
+ align-items: center;
113
+ margin-top: 16px;
114
+ }
115
+ `;
116
+ }
81
117
  }
82
118
 
83
119
  customElements.define('pb-dialog', PbDialog);