@teipublisher/pb-components 2.26.1-next.2 → 3.0.0-next-4.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.
Files changed (262) hide show
  1. package/.github/workflows/docker-cypress.yml +54 -0
  2. package/.github/workflows/main.yml +6 -4
  3. package/.github/workflows/node.js.yml +56 -21
  4. package/.github/workflows/release.js.yml +19 -17
  5. package/.releaserc.json +1 -1
  6. package/CHANGELOG.md +351 -9
  7. package/Dockerfile +78 -70
  8. package/README.md +112 -4
  9. package/css/components.css +5 -5
  10. package/css/gridjs/mermaid.min.css +1 -1
  11. package/css/leaflet/Control.Geocoder.css +1 -126
  12. package/css/leaflet/images/layers.png +0 -0
  13. package/css/tify/tify.css +6 -5
  14. package/css/tom-select/tom-select.bootstrap4.min.css +1 -1
  15. package/css/tom-select/tom-select.bootstrap5.min.css +1 -1
  16. package/css/tom-select/tom-select.default.min.css +1 -1
  17. package/css/tom-select/tom-select.default.min.css.map +1 -0
  18. package/css/tom-select/tom-select.min.css +1 -1
  19. package/cypress.config.js +84 -0
  20. package/dist/api.html +1 -1
  21. package/dist/css/design-system.css +607 -0
  22. package/dist/demo/bundle-test.html +4 -3
  23. package/dist/demo/components.css +46 -1
  24. package/dist/demo/design-system.html +710 -0
  25. package/dist/demo/dts-client.html +2 -2
  26. package/dist/demo/pb-autocomplete.html +23 -11
  27. package/dist/demo/pb-autocomplete2.html +66 -55
  28. package/dist/demo/pb-autocomplete3.html +17 -8
  29. package/dist/demo/pb-blacklab-highlight.html +28 -11
  30. package/dist/demo/pb-blacklab-results.html +3 -2
  31. package/dist/demo/pb-browse-docs.html +24 -24
  32. package/dist/demo/pb-browse-docs2.html +3 -3
  33. package/dist/demo/pb-clipboard.html +32 -28
  34. package/dist/demo/pb-code-editor.html +6 -6
  35. package/dist/demo/pb-code-highlight.html +63 -63
  36. package/dist/demo/pb-codepen.html +1 -1
  37. package/dist/demo/pb-collapse.html +1 -1
  38. package/dist/demo/pb-collapse2.html +2 -2
  39. package/dist/demo/pb-combo-box.html +135 -130
  40. package/dist/demo/pb-custom-form.html +64 -55
  41. package/dist/demo/pb-dialog.html +12 -6
  42. package/dist/demo/pb-document.html +1 -1
  43. package/dist/demo/pb-download.html +68 -59
  44. package/dist/demo/pb-drawer.html +67 -46
  45. package/dist/demo/pb-drawer2.html +65 -58
  46. package/dist/demo/pb-edit-app.html +2 -2
  47. package/dist/demo/pb-edit-xml.html +1 -1
  48. package/dist/demo/pb-facsimile-2.html +26 -11
  49. package/dist/demo/pb-facsimile-3.html +25 -10
  50. package/dist/demo/pb-facsimile-dedup-test-2.html +48 -0
  51. package/dist/demo/pb-facsimile-dedup-test.html +48 -0
  52. package/dist/demo/pb-facsimile.html +4 -4
  53. package/dist/demo/pb-formula.html +1 -1
  54. package/dist/demo/pb-grid.html +22 -8
  55. package/dist/demo/pb-highlight.html +2 -2
  56. package/dist/demo/pb-i18n-simple.html +1 -0
  57. package/dist/demo/pb-i18n.html +15 -5
  58. package/dist/demo/pb-image-strip-standalone.html +2 -2
  59. package/dist/demo/pb-image-strip-view.html +2 -2
  60. package/dist/demo/pb-leaflet-map.html +3 -3
  61. package/dist/demo/pb-leaflet-map2.html +2 -2
  62. package/dist/demo/pb-leaflet-map3.html +3 -3
  63. package/dist/demo/pb-link.html +1 -1
  64. package/dist/demo/pb-load.html +2 -6
  65. package/dist/demo/pb-login.html +1 -3
  66. package/dist/demo/pb-manage-odds.html +9 -4
  67. package/dist/demo/pb-markdown.html +1 -1
  68. package/dist/demo/pb-media-query.html +2 -2
  69. package/dist/demo/pb-mei.html +2 -2
  70. package/dist/demo/pb-mei2.html +2 -2
  71. package/dist/demo/pb-message.html +2 -3
  72. package/dist/demo/pb-odd-editor.html +54 -52
  73. package/dist/demo/pb-page-header.html +27 -0
  74. package/dist/demo/pb-popover.html +1 -1
  75. package/dist/demo/pb-print-preview.html +2 -2
  76. package/dist/demo/pb-progress.html +4 -4
  77. package/dist/demo/pb-repeat.html +32 -36
  78. package/dist/demo/pb-search.html +16 -5
  79. package/dist/demo/pb-search2.html +4 -4
  80. package/dist/demo/pb-search3.html +3 -3
  81. package/dist/demo/pb-search4.html +3 -3
  82. package/dist/demo/pb-select-feature.html +4 -4
  83. package/dist/demo/pb-select-feature2.html +4 -4
  84. package/dist/demo/pb-select-feature3.html +2 -2
  85. package/dist/demo/pb-select-i18n.html +58 -53
  86. package/dist/demo/pb-select-odd.html +1 -1
  87. package/dist/demo/pb-select.html +190 -75
  88. package/dist/demo/pb-select2.html +91 -37
  89. package/dist/demo/pb-select3.html +109 -41
  90. package/dist/demo/pb-svg.html +1 -1
  91. package/dist/demo/pb-table-grid.html +26 -15
  92. package/dist/demo/pb-tabs.html +15 -7
  93. package/dist/demo/pb-tify.html +7 -7
  94. package/dist/demo/pb-timeline.html +1 -1
  95. package/dist/demo/pb-timeline2.html +1 -1
  96. package/dist/demo/pb-toggle-feature.html +26 -23
  97. package/dist/demo/pb-toggle-feature2.html +4 -4
  98. package/dist/demo/pb-toggle-feature3.html +2 -2
  99. package/dist/demo/pb-toggle-feature4.html +56 -54
  100. package/dist/demo/pb-version.html +2 -2
  101. package/dist/demo/pb-view.html +78 -40
  102. package/dist/demo/pb-view2.html +69 -46
  103. package/dist/demo/pb-view3.html +53 -48
  104. package/dist/demo/pb-view4.html +70 -49
  105. package/dist/demo/pb-zoom.html +2 -2
  106. package/dist/{es-global-bridge-d8ce175d.js → es-global-bridge-D8ZcUcx_.js} +0 -4
  107. package/dist/focus-mixin-VCsFap6b.js +768 -0
  108. package/dist/images/icons.svg +217 -0
  109. package/dist/jinn-codemirror-DETLdm08.js +1 -0
  110. package/dist/lib/openseadragon.min.js +80 -0
  111. package/dist/lib/openseadragon.min.js.map +1 -0
  112. package/dist/pb-code-editor.js +25 -20
  113. package/dist/pb-component-docs.js +414 -3225
  114. package/dist/pb-components-bundle.js +3046 -4402
  115. package/dist/pb-dialog-tklYGWfc.js +121 -0
  116. package/dist/pb-edit-app.js +208 -107
  117. package/dist/pb-elements.json +716 -249
  118. package/dist/pb-facsimile.js +46 -0
  119. package/dist/pb-i18n-C0NDma4h.js +1 -0
  120. package/dist/pb-leaflet-map.js +23 -23
  121. package/dist/pb-mei.js +152 -134
  122. package/dist/pb-mixin-DHoWQheB.js +1 -0
  123. package/dist/pb-odd-editor.js +1671 -1231
  124. package/dist/pb-tify.js +1 -27
  125. package/dist/unsafe-html-D5VGo9Oq.js +1 -0
  126. package/dist/urls-BEONu_g4.js +1 -0
  127. package/eslint.config.mjs +92 -0
  128. package/gh-pages.js +5 -3
  129. package/i18n/common/en.json +6 -0
  130. package/i18n/common/pl.json +2 -2
  131. package/images/icons.svg +217 -0
  132. package/index.html +0 -5
  133. package/lib/leaflet-src.js.map +1 -0
  134. package/lib/leaflet.markercluster-src.js.map +1 -0
  135. package/lib/openseadragon.min.js +6 -6
  136. package/package.json +56 -81
  137. package/pb-elements.json +716 -249
  138. package/rollup.config.mjs +312 -0
  139. package/src/assets/components.css +5 -5
  140. package/src/assets/design-system.css +607 -0
  141. package/src/authority/airtable.js +20 -21
  142. package/src/authority/anton.js +129 -129
  143. package/src/authority/custom.js +70 -27
  144. package/src/authority/geonames.js +38 -32
  145. package/src/authority/gnd.js +50 -42
  146. package/src/authority/kbga.js +136 -134
  147. package/src/authority/metagrid.js +44 -46
  148. package/src/authority/reconciliation.js +66 -68
  149. package/src/authority/registry.js +4 -4
  150. package/src/docs/demo-utils.js +91 -0
  151. package/src/docs/pb-component-docs.js +287 -147
  152. package/src/docs/pb-component-view.js +380 -273
  153. package/src/docs/pb-components-list.js +115 -51
  154. package/src/docs/pb-demo-snippet.js +199 -174
  155. package/src/dts-client.js +306 -303
  156. package/src/dts-select-endpoint.js +125 -85
  157. package/src/parse-date-service.js +184 -135
  158. package/src/pb-ajax.js +175 -173
  159. package/src/pb-authority-lookup.js +198 -158
  160. package/src/pb-autocomplete.js +731 -313
  161. package/src/pb-blacklab-highlight.js +266 -260
  162. package/src/pb-blacklab-results.js +230 -225
  163. package/src/pb-browse-docs.js +601 -484
  164. package/src/pb-browse.js +68 -65
  165. package/src/pb-clipboard.js +97 -76
  166. package/src/pb-code-editor.js +111 -103
  167. package/src/pb-code-highlight.js +234 -204
  168. package/src/pb-codepen.js +81 -73
  169. package/src/pb-collapse.js +265 -152
  170. package/src/pb-combo-box.js +191 -191
  171. package/src/pb-components-bundle.js +1 -7
  172. package/src/pb-components.js +2 -6
  173. package/src/pb-custom-form.js +230 -141
  174. package/src/pb-dialog.js +99 -63
  175. package/src/pb-document.js +118 -91
  176. package/src/pb-download.js +214 -198
  177. package/src/pb-drawer.js +146 -149
  178. package/src/pb-edit-app.js +471 -240
  179. package/src/pb-edit-xml.js +101 -98
  180. package/src/pb-events.js +126 -107
  181. package/src/pb-facs-link.js +130 -101
  182. package/src/pb-facsimile.js +494 -410
  183. package/src/pb-fetch.js +389 -0
  184. package/src/pb-formula.js +152 -154
  185. package/src/pb-geolocation.js +130 -132
  186. package/src/pb-grid-action.js +59 -56
  187. package/src/pb-grid.js +388 -228
  188. package/src/pb-highlight.js +142 -142
  189. package/src/pb-hotkeys.js +40 -42
  190. package/src/pb-i18n.js +115 -127
  191. package/src/pb-icon-button.js +108 -0
  192. package/src/pb-icon.js +283 -0
  193. package/src/pb-image-strip.js +85 -79
  194. package/src/pb-lang.js +142 -57
  195. package/src/pb-leaflet-map.js +551 -483
  196. package/src/pb-link.js +132 -126
  197. package/src/pb-load.js +495 -428
  198. package/src/pb-login.js +303 -248
  199. package/src/pb-manage-odds.js +384 -338
  200. package/src/pb-map-icon.js +90 -90
  201. package/src/pb-map-layer.js +86 -86
  202. package/src/pb-markdown.js +107 -110
  203. package/src/pb-media-query.js +75 -73
  204. package/src/pb-mei.js +523 -303
  205. package/src/pb-message.js +144 -98
  206. package/src/pb-mixin.js +268 -265
  207. package/src/pb-navigation.js +83 -96
  208. package/src/pb-observable.js +39 -39
  209. package/src/pb-odd-editor.js +1209 -948
  210. package/src/pb-odd-elementspec-editor.js +375 -310
  211. package/src/pb-odd-model-editor.js +1189 -941
  212. package/src/pb-odd-parameter-editor.js +269 -170
  213. package/src/pb-odd-rendition-editor.js +184 -131
  214. package/src/pb-page.js +451 -422
  215. package/src/pb-paginate.js +260 -178
  216. package/src/pb-panel.js +217 -183
  217. package/src/pb-popover-themes.js +16 -9
  218. package/src/pb-popover.js +297 -288
  219. package/src/pb-print-preview.js +128 -128
  220. package/src/pb-progress.js +52 -52
  221. package/src/pb-repeat.js +141 -108
  222. package/src/pb-restricted.js +85 -78
  223. package/src/pb-search.js +258 -230
  224. package/src/pb-select-feature.js +210 -126
  225. package/src/pb-select-odd.js +184 -118
  226. package/src/pb-select-template.js +113 -78
  227. package/src/pb-select.js +330 -229
  228. package/src/pb-split-list.js +181 -176
  229. package/src/pb-svg.js +81 -80
  230. package/src/pb-table-column.js +55 -55
  231. package/src/pb-table-grid.js +334 -205
  232. package/src/pb-tabs.js +238 -61
  233. package/src/pb-tify.js +3331 -126
  234. package/src/pb-timeline.js +394 -255
  235. package/src/pb-toggle-feature.js +196 -188
  236. package/src/pb-upload.js +201 -176
  237. package/src/pb-version.js +22 -34
  238. package/src/pb-view-annotate.js +138 -102
  239. package/src/pb-view.js +1722 -1272
  240. package/src/pb-zoom.js +144 -46
  241. package/src/search-result-service.js +256 -223
  242. package/src/seed-element.js +14 -22
  243. package/src/settings.js +4 -4
  244. package/src/theming.js +98 -91
  245. package/src/urls.js +403 -289
  246. package/src/utils.js +53 -51
  247. package/vite.config.js +86 -0
  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-6e4cee3a.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 -40
  262. package/src/polymer-hack.js +0 -6
package/src/pb-grid.js CHANGED
@@ -1,6 +1,7 @@
1
- import { LitElement, html, css } from 'lit-element';
1
+ import { LitElement, html, css } from 'lit';
2
+ import { animate } from 'animejs';
2
3
  import { pbMixin } from './pb-mixin.js';
3
- import { registry } from "./urls.js";
4
+ import { registry } from './urls.js';
4
5
  import './pb-panel.js';
5
6
 
6
7
  /**
@@ -15,265 +16,424 @@ import './pb-panel.js';
15
16
  * @cssprop --pb-grid-column-gap - Width of the gap between columns
16
17
  */
17
18
  export class PbGrid extends pbMixin(LitElement) {
18
- static get properties() {
19
- return {
20
- ...super.properties,
21
- /**
22
- * an array of panel items to display when the component is loaded. It should contain a
23
- * number for each panel to show, indicating the ordinal position of the template within the `<pb-panel>`
24
- * to initialize. For example, if you have two templates in `<pb-panel>`: "transcription" and "translation",
25
- * setting `panels="[0, 1]"` will show two columns, one with the transcription, the other with the translation.
26
- *
27
- * Passing in a browser parameter `panels` with a comma-separated list will set this property as well.
28
- */
29
- panels: {
30
- type: Array
31
- },
32
- direction: {
33
- type: String
34
- },
35
- /**
36
- * the number of columns
37
- */
38
- _columns: {
39
- type: Number
40
- },
41
- /**
42
- * CSS Selektor to choose elements to animate. If not specified all 'pb-view' elements will be animated by default.
43
- */
44
- animated: {
45
- type: String
46
- },
47
- /**
48
- * wether to animate the view when new page is loaded. Defaults to 'false' meaning that no
49
- * animation takes place.
50
- */
51
- animation: {
52
- type: Boolean
53
- }
54
- };
55
- }
19
+ static get properties() {
20
+ return {
21
+ ...super.properties,
22
+ /**
23
+ * an array of panel items to display when the component is loaded. It should contain a
24
+ * number for each panel to show, indicating the ordinal position of the template within the `<pb-panel>`
25
+ * to initialize. For example, if you have two templates in `<pb-panel>`: "transcription" and "translation",
26
+ * setting `panels="[0, 1]"` will show two columns, one with the transcription, the other with the translation.
27
+ *
28
+ * Passing in a browser parameter `panels` with a comma-separated list will set this property as well.
29
+ */
30
+ panels: {
31
+ type: Array,
32
+ },
33
+ direction: {
34
+ type: String,
35
+ },
36
+ /**
37
+ * the number of columns
38
+ */
39
+ _columns: {
40
+ type: Number,
41
+ },
42
+ /**
43
+ * CSS Selektor to choose elements to animate. If not specified all 'pb-view' elements will be animated by default.
44
+ */
45
+ animated: {
46
+ type: String,
47
+ },
48
+ /**
49
+ * wether to animate the view when new page is loaded. Defaults to 'false' meaning that no
50
+ * animation takes place.
51
+ */
52
+ animation: {
53
+ type: Boolean,
54
+ },
55
+ };
56
+ }
56
57
 
57
- constructor() {
58
- super();
59
- this.panels = [];
60
- this.direction = 'ltr';
61
- this.animated = 'pb-view';
62
- this.animation = false;
63
- }
58
+ constructor() {
59
+ super();
60
+ this.panels = [];
61
+ this.direction = 'ltr';
62
+ this.animated = 'pb-view';
63
+ this.animation = false;
64
+ this._panelsInitialized = false; // Track if panels have been initialized from registry
65
+ }
64
66
 
65
- connectedCallback() {
66
- super.connectedCallback();
67
+ connectedCallback() {
68
+ super.connectedCallback();
67
69
 
68
- this.subscribeTo('pb-panel', ev => {
69
- const idx = this._getPanelIndex(ev.detail.panel);
70
- if (idx < 0) {
71
- return // panel not found
72
- }
73
- console.log('<pb-grid> Updating panel %d to show %s', idx, ev.detail.active);
74
- this.panels[this.direction === 'rtl' ? this.panels.length - idx - 1 : idx] = ev.detail.active;
70
+ this.subscribeTo('pb-panel', ev => {
71
+ const idx = this._getPanelIndex(ev.detail.panel);
72
+ if (idx < 0) {
73
+ return; // panel not found
74
+ }
75
+ console.log('<pb-grid> Updating panel %d to show %s', idx, ev.detail.active);
76
+ // Update the panel's active template index (this is for pb-panel's internal state)
77
+ // BUT: Do NOT commit panels to registry - panels array should only change when panels are added/removed,
78
+ // not when a panel switches its active view. The panels parameter in the URL should remain stable.
79
+ // Only update the internal state, don't commit to registry
80
+ const panelIdx = this.direction === 'rtl' ? this.panels.length - idx - 1 : idx;
81
+ // Store the active template index for this panel, but don't change the panels array structure
82
+ // The panels array represents which panels are visible, not which template each panel is showing
83
+ // We'll track active templates separately if needed, but for now just log it
84
+ console.log('<pb-grid> Panel %d switched to template %s (not committing to registry)', panelIdx, ev.detail.active);
85
+ // DO NOT commit - panels parameter should remain stable
86
+ });
75
87
 
76
- registry.commit(this, this._getState())
77
- });
88
+ this.subscribeTo('pb-zoom', ev => {
89
+ this.zoom(ev.detail.direction);
90
+ });
78
91
 
79
- this.subscribeTo('pb-zoom', ev => {
80
- this.zoom(ev.detail.direction);
81
- });
92
+ // CRITICAL: Always prioritize registry value over template attribute
93
+ // The template may have panels="[0,1,2]" hardcoded, but the URL parameter should override it
94
+ const panelsParam = registry.get('panels');
95
+ if (panelsParam) {
96
+ // Parse panels from registry, but ensure we don't concatenate
97
+ // Split by '.' and parse each segment as integer
98
+ const parsed = panelsParam.split('.').map(param => parseInt(param, 10));
99
+ // Only use if all segments are valid numbers and reasonable (max 10 panels)
100
+ if (parsed.length > 0 && parsed.length <= 10 && parsed.every(p => !isNaN(p) && p >= 0 && p < 10)) {
101
+ console.log('<pb-grid> connectedCallback: Using panels from registry:', parsed, 'overriding template attribute');
102
+ this.panels = parsed;
103
+ this._panelsInitialized = true;
104
+ }
105
+ } else {
106
+ // No registry value - check if template attribute was set (LitElement may have already parsed it)
107
+ // If template has panels="[0,1,2]" but we want to use that only if no registry value exists
108
+ // But we should still mark as initialized to prevent duplicates
109
+ if (this.panels && this.panels.length > 0) {
110
+ console.log('<pb-grid> connectedCallback: Using panels from template attribute:', this.panels, '(no registry value)');
111
+ this._panelsInitialized = true;
112
+ }
113
+ }
82
114
 
83
- const panelsParam = registry.get('panels');
84
- if (panelsParam) {
85
- this.panels = panelsParam.split('.').map(param => parseInt(param));
86
- }
115
+ this._isUpdatingFromRegistry = false;
116
+ this._lastPanelsState = null; // Track last panels state to prevent duplicate rebuilds
117
+ registry.subscribe(this, state => {
118
+ console.log(
119
+ '<pb-grid> Registry subscribe callback triggered, _isUpdatingFromRegistry:',
120
+ this._isUpdatingFromRegistry,
121
+ 'state.panels:',
122
+ state.panels,
123
+ );
124
+ // Only rebuild DOM if state change came from external source (e.g., browser navigation)
125
+ // not from our own registry.commit() calls
126
+ if (this._isUpdatingFromRegistry) {
127
+ console.log(
128
+ '<pb-grid> Skipping registry subscribe callback due to _isUpdatingFromRegistry flag',
129
+ );
130
+ return;
131
+ }
132
+ const newState = state.panels ? state.panels.split('.').map(p => parseInt(p, 10)).filter(p => !isNaN(p)) : [];
133
+ const newStateStr = newState.join('.');
134
+
135
+ // Prevent duplicate rebuilds if panels haven't actually changed
136
+ if (this._lastPanelsState === newStateStr) {
137
+ console.log(
138
+ '<pb-grid> Skipping registry subscribe callback - panels state unchanged:',
139
+ newStateStr,
140
+ );
141
+ return;
142
+ }
143
+
144
+ // Prevent rebuild if we haven't initialized yet (firstUpdated hasn't run)
145
+ // This prevents the registry callback from running before firstUpdated and creating duplicates
146
+ if (!this._panelsInitialized && !this.template) {
147
+ console.log(
148
+ '<pb-grid> Skipping registry subscribe callback - not yet initialized (template not ready)',
149
+ );
150
+ return;
151
+ }
152
+
153
+ console.log('<pb-grid> Registry subscribe callback rebuilding DOM with panels:', newState, 'current panels:', this.panels);
154
+ this._lastPanelsState = newStateStr;
155
+ this.panels = newState;
156
+ this._panelsInitialized = true;
157
+ this.innerHTML = ''; // hard reset of child DOM
158
+ this.panels.forEach(panelNum => this._insertPanel(panelNum));
159
+ this._update();
160
+ });
161
+ this._columns = this.panels.length;
162
+ this.template = this.querySelector('template');
163
+
164
+ // If template is ready and we have panels but haven't initialized, mark as initialized
165
+ // This handles the case where template attribute was parsed before registry value was read
166
+ if (this.template && this.panels && this.panels.length > 0 && !this._panelsInitialized) {
167
+ console.log('<pb-grid> connectedCallback: Template ready, panels from attribute:', this.panels);
168
+ this._panelsInitialized = true;
169
+ }
170
+ }
87
171
 
88
- registry.subscribe(this, (state) => {
89
- const newState = state.panels ? state.panels.split('.') : [];
90
- this.panels = newState;
91
- this.innerHTML=''; // hard reset of child DOM
92
- this.panels.forEach(panelNum => this._insertPanel(panelNum));
93
- this._update();
94
- });
95
- this._columns = this.panels.length;
96
- this.template = this.querySelector('template');
172
+ firstUpdated() {
173
+ // CRITICAL: Double-check registry value one more time before creating panels
174
+ // LitElement may have set this.panels from the template attribute before connectedCallback ran
175
+ // We need to ensure we use the registry value, not the template attribute
176
+ const panelsParam = registry.get('panels');
177
+ if (panelsParam) {
178
+ const parsed = panelsParam.split('.').map(param => parseInt(param, 10)).filter(p => !isNaN(p) && p >= 0 && p < 10);
179
+ if (parsed.length > 0 && parsed.length <= 10) {
180
+ const parsedStr = parsed.join('.');
181
+ const currentStr = this.panels.join('.');
182
+ if (parsedStr !== currentStr) {
183
+ console.log('<pb-grid> firstUpdated: Overriding template panels', this.panels, 'with registry value', parsed);
184
+ this.panels = parsed;
185
+ this._panelsInitialized = true;
186
+ }
187
+ }
188
+ }
189
+
190
+ // Only insert panels if they haven't been inserted yet
191
+ // This prevents duplicates if registry subscribe callback already created them
192
+ const existingPanels = this.querySelectorAll('._grid_panel').length;
193
+ if (existingPanels === 0 && this.panels && this.panels.length > 0) {
194
+ console.log('<pb-grid> firstUpdated: Inserting panels:', this.panels, 'existing panels:', existingPanels);
195
+ this.panels.forEach(panelNum => this._insertPanel(panelNum));
196
+ } else {
197
+ console.log('<pb-grid> firstUpdated: Skipping panel insertion - already have', existingPanels, 'panels');
198
+ }
199
+
200
+ // Track initial state to prevent duplicate rebuilds
201
+ this._lastPanelsState = this._getState().panels;
202
+ this._isUpdatingFromRegistry = true;
203
+ // Only commit if panels state differs from what's already in the registry
204
+ // This prevents unnecessary URL updates during initialization
205
+ const currentPanels = registry.get('panels');
206
+ const newPanels = this._getState().panels;
207
+ if (currentPanels !== newPanels) {
208
+ registry.commit(this, this._getState());
97
209
  }
210
+ this._isUpdatingFromRegistry = false;
211
+ this._animate();
212
+ this._update();
98
213
 
99
- firstUpdated() {
214
+ this.addEventListener('pb-drop', ev => {
215
+ const draggedPanelIdx = parseInt(ev.detail.panel);
216
+ const targetPanelIdx = this._getPanelIndex(ev.detail.target);
100
217
 
101
- this.panels.forEach(panelNum => this._insertPanel(panelNum));
102
- registry.commit(this, this._getState())
103
- this._animate();
104
- this._update();
218
+ console.log(
219
+ '<pb-grid> Insert panel %d at %d in %s',
220
+ draggedPanelIdx,
221
+ targetPanelIdx,
222
+ this.panels,
223
+ );
224
+ this.querySelectorAll('._grid_panel').forEach(panel => {
225
+ panel.classList.remove('dragover');
226
+ });
105
227
 
106
- this.addEventListener('pb-drop', (ev) => {
107
- const draggedPanelIdx = parseInt(ev.detail.panel);
108
- const targetPanelIdx = this._getPanelIndex(ev.detail.target);
228
+ this.panels.splice(targetPanelIdx, 0, this.panels.splice(draggedPanelIdx, 1)[0]);
229
+ this.innerHTML = ''; // hard reset of child DOM
230
+ this.panels.forEach(panelNum => this._insertPanel(panelNum));
231
+ this._isUpdatingFromRegistry = true;
232
+ registry.commit(this, this._getState());
233
+ this._isUpdatingFromRegistry = false;
234
+ this._update();
235
+ });
236
+ }
109
237
 
110
- console.log('<pb-grid> Insert panel %d at %d in %s', draggedPanelIdx, targetPanelIdx, this.panels);
111
- this.querySelectorAll('._grid_panel').forEach((panel) => {
112
- panel.classList.remove('dragover');
113
- });
114
-
115
- this.panels.splice(targetPanelIdx, 0, this.panels.splice(draggedPanelIdx, 1)[0]);
116
- this.innerHTML=''; // hard reset of child DOM
117
- this.panels.forEach(panelNum => this._insertPanel(panelNum));
118
- registry.commit(this, this._getState());
119
- this._update();
238
+ /**
239
+ * slides in all panels from left to right with a slight delay between the panels. If animejs is not
240
+ * loaded nothing happens and content is displayed as usual.
241
+ */
242
+ _animate() {
243
+ if (this.animation) {
244
+ // console.log('animated elements', document.querySelectorAll('pb-panel'));
245
+ const animated = document.querySelectorAll(this.animated);
246
+
247
+ // Animate each element with a staggered delay
248
+ animated.forEach((element, index) => {
249
+ const animation = animate(element, {
250
+ opacity: [0, 0.6],
251
+ translateX: [2000, 0],
252
+ duration: 400,
253
+ delay: 100 + index * 100,
254
+ ease: 'linear',
120
255
  });
121
- }
122
256
 
123
- /**
124
- * slides in all panels from left to right with a slight delay between the panels. If animejs is not
125
- * loaded nothing happens and content is displayed as usual.
126
- */
127
- _animate() {
128
- if (this.animation) {
129
- if (typeof anime && "anime" in window) {
130
- // console.log('animated elements', document.querySelectorAll('pb-panel'));
131
- const animated = document.querySelectorAll(this.animated);
132
- const anim = anime.timeline({
133
- easing: 'linear',
134
- duration: 400
135
- });
136
- anim.add({
137
- targets: animated,
138
- opacity: {
139
- value: [0, 0.6],
140
- duration: 200,
141
- delay: 100,
142
- easing: 'linear'
143
- },
144
- translateX: [2000, 0],
145
- duration: 400,
146
- delay: anime.stagger(100, { start: 100 })
147
- });
148
- anim.add({
149
- targets: animated,
150
- opacity: [0.6, 1],
151
- duration: 200,
152
- delay: anime.stagger(50)
153
- });
154
- anim.play();
155
- }
257
+ // Check if animation has a finished promise
258
+ if (animation && animation.finished) {
259
+ animation.finished.then(() => {
260
+ // Second phase: fade to full opacity
261
+ animate(element, {
262
+ opacity: [0.6, 1],
263
+ duration: 200,
264
+ delay: index * 50,
265
+ ease: 'linear',
266
+ });
267
+ });
268
+ } else {
269
+ // Fallback: use setTimeout if no promise available
270
+ setTimeout(() => {
271
+ animate(element, {
272
+ opacity: [0.6, 1],
273
+ duration: 200,
274
+ delay: index * 50,
275
+ ease: 'linear',
276
+ });
277
+ }, 400 + index * 100);
156
278
  }
279
+ });
157
280
  }
281
+ }
158
282
 
159
- /**
160
- * Add a panel. Defaults to opening 'the next' panel if the `initial` parameter is omitted: if
161
- * panels 1,6,3 are open, panel 7 will be added
162
- *
163
- * @param {number} [initial] The panel number of the panel to add.
164
- */
165
- addPanel(initial) {
166
- let value = initial;
167
- if (initial === undefined && !this.panels.length) {
168
- value = 0;
169
- }
170
- if (initial === undefined && this.panels.length) {
171
- const max = this.panels.reduce((result, next) => Math.max(result, next), 0);
172
- value = max + 1;
173
- }
283
+ /**
284
+ * Add a panel. Defaults to opening 'the next' panel if the `initial` parameter is omitted: if
285
+ * panels 1,6,3 are open, panel 7 will be added
286
+ *
287
+ * @param {number} [initial] The panel number of the panel to add.
288
+ */
289
+ addPanel(initial) {
290
+ let value = initial;
291
+ if (initial === undefined && !this.panels.length) {
292
+ value = 0;
293
+ }
294
+ if (initial === undefined && this.panels.length) {
295
+ const max = this.panels.reduce((result, next) => Math.max(result, next), 0);
296
+ value = max + 1;
297
+ }
174
298
 
175
- this._columns += 1;
176
- this.panels.push(value);
299
+ console.log('<pb-grid> Adding panel with value:', value);
300
+ console.log('<pb-grid> Current panels before add:', this.panels);
301
+ console.log(
302
+ '<pb-grid> Current panel count before add:',
303
+ this.querySelectorAll('._grid_panel').length,
304
+ );
177
305
 
178
- this._insertPanel(value);
179
- registry.commit(this, this._getState());
180
- this._update();
181
- this.emitTo('pb-refresh');
182
- }
306
+ this._columns += 1;
307
+ this.panels.push(value);
183
308
 
184
- /**
185
- * Remove a panel from the grid
186
- *
187
- * @param {HTMLElement|number} panel the pb-panel element or the panel number
188
- */
189
- removePanel(panel) {
190
- let idx;
191
- let container;
192
- if (typeof panel === 'number') {
193
- idx = this.panels.indexOf(panel);
194
- container = this.querySelector(`[active="${panel}"]`);
195
- } else {
196
- container = panel;
197
- idx = this._getPanelIndex(panel);
198
- }
199
- console.log('<pb-grid> Removing panel %d', idx);
200
- this.panels.splice(this.direction === 'rtl' ? this.panels.length - idx - 1 : idx, 1);
201
-
202
- container.parentNode.removeChild(container);
203
- this._columns -= 1;
204
- registry.commit(this, this._getState());
205
- this._assignPanelIds();
206
- this._update();
207
- }
309
+ this._insertPanel(value);
310
+ this._isUpdatingFromRegistry = true;
311
+ registry.commit(this, this._getState());
312
+ this._isUpdatingFromRegistry = false;
313
+ this._update();
314
+ this.emitTo('pb-refresh');
208
315
 
209
- _insertPanel(active) {
210
- const clone = document.importNode(this.template.content.firstElementChild, true);
211
- clone.setAttribute('active', active);
212
- if (this.direction === 'ltr' || this.querySelectorAll('._grid_panel').length === 0) {
213
- this.appendChild(clone);
214
- } else {
215
- this.insertBefore(clone, this.firstElementChild);
216
- }
217
- clone.classList.add('_grid_panel');
218
- this._assignPanelIds();
219
- }
316
+ console.log('<pb-grid> After adding panel - panels:', this.panels);
317
+ console.log(
318
+ '<pb-grid> After adding panel - panel count:',
319
+ this.querySelectorAll('._grid_panel').length,
320
+ );
321
+ }
220
322
 
221
- _update() {
222
- const widths = Array.from(this.children)
223
- .filter(child => !(child instanceof HTMLTemplateElement))
224
- .map(child => {
225
- const styles = window.getComputedStyle(child);
226
- const width = styles.getPropertyValue('max-width');
227
- if (width && width !== 'none') {
228
- return width;
229
- }
230
- return '1fr';
231
- });
232
- this.style.setProperty('--pb-computed-column-widths', widths.join(' '));
323
+ /**
324
+ * Remove a panel from the grid
325
+ *
326
+ * @param {HTMLElement|number} panel the pb-panel element or the panel number
327
+ */
328
+ removePanel(panel) {
329
+ let idx;
330
+ let container;
331
+ if (typeof panel === 'number') {
332
+ idx = this.panels.indexOf(panel);
333
+ container = this.querySelector(`[active="${panel}"]`);
334
+ } else {
335
+ container = panel;
336
+ idx = this._getPanelIndex(panel);
233
337
  }
338
+ console.log('<pb-grid> Removing panel %d', idx);
339
+ console.log('<pb-grid> Container:', container);
340
+ console.log('<pb-grid> Current panels:', [...this.panels]);
341
+ console.log('<pb-grid> Current panel count:', this.querySelectorAll('._grid_panel').length);
234
342
 
235
- _getPanelIndex(panel) {
236
- const panels = Array.from(this.querySelectorAll('._grid_panel'));
237
- return panels.indexOf(panel);
238
- }
343
+ this.panels.splice(this.direction === 'rtl' ? this.panels.length - idx - 1 : idx, 1);
239
344
 
240
- _assignPanelIds() {
241
- this.querySelectorAll('._grid_panel').forEach((panel, idx) => {
242
- panel.position = idx;
243
- });
244
- }
345
+ container.parentNode.removeChild(container);
346
+ this._columns -= 1;
347
+ this._isUpdatingFromRegistry = true;
348
+ registry.commit(this, this._getState());
349
+ this._isUpdatingFromRegistry = false;
350
+ this._assignPanelIds();
351
+ this._update();
245
352
 
246
- _getState() {
247
- return { panels: this.panels.join('.') };
248
- }
353
+ console.log('<pb-grid> After removal - panels:', [...this.panels]);
354
+ console.log(
355
+ '<pb-grid> After removal - panel count:',
356
+ this.querySelectorAll('._grid_panel').length,
357
+ );
358
+ }
249
359
 
250
- render() {
251
- return html`<slot></slot>`;
252
- }
360
+ _insertPanel(active) {
361
+ console.log('<pb-grid> _insertPanel called with active:', active);
362
+ console.log('<pb-grid> Template content:', this.template.content);
363
+ console.log('<pb-grid> Template firstElementChild:', this.template.content.firstElementChild);
364
+
365
+ const clone = document.importNode(this.template.content.firstElementChild, true);
366
+ console.log('<pb-grid> Cloned element:', clone);
253
367
 
254
- static get styles() {
255
- return css`
256
- :host {
257
- display: grid;
258
- grid-template-columns: var(--pb-grid-column-widths, var(--pb-computed-column-widths));
259
- grid-column-gap: var(--pb-grid-column-gap, 20px);
260
- justify-content: space-between;
261
- }
262
- `;
368
+ clone.setAttribute('active', active);
369
+ if (this.direction === 'ltr' || this.querySelectorAll('._grid_panel').length === 0) {
370
+ this.appendChild(clone);
371
+ } else {
372
+ this.insertBefore(clone, this.firstElementChild);
263
373
  }
374
+ clone.classList.add('_grid_panel');
375
+ this._assignPanelIds();
264
376
 
265
- zoom(direction) {
266
- const fontSize = window.getComputedStyle(this).getPropertyValue('font-size');
267
- const size = parseInt(fontSize.replace(/^(\d+)px/, "$1"));
377
+ console.log(
378
+ '<pb-grid> After _insertPanel - DOM panels:',
379
+ this.querySelectorAll('._grid_panel').length,
380
+ );
381
+ }
268
382
 
269
- if (direction === 'in') {
270
- this.style.fontSize = (size + 1) + 'px';
271
- } else {
272
- this.style.fontSize = (size - 1) + 'px';
383
+ _update() {
384
+ const widths = Array.from(this.children)
385
+ .filter(child => !(child instanceof HTMLTemplateElement))
386
+ .map(child => {
387
+ const styles = window.getComputedStyle(child);
388
+ const width = styles.getPropertyValue('max-width');
389
+ if (width && width !== 'none') {
390
+ return width;
273
391
  }
274
- }
392
+ return '1fr';
393
+ });
394
+ this.style.setProperty('--pb-computed-column-widths', widths.join(' '));
395
+ }
396
+
397
+ _getPanelIndex(panel) {
398
+ const panels = Array.from(this.querySelectorAll('._grid_panel'));
399
+ return panels.indexOf(panel);
400
+ }
401
+
402
+ _assignPanelIds() {
403
+ this.querySelectorAll('._grid_panel').forEach((panel, idx) => {
404
+ panel.position = idx;
405
+ });
406
+ }
407
+
408
+ _getState() {
409
+ // Ensure panels array is valid before joining
410
+ // Filter out any invalid values (NaN, undefined, null)
411
+ const validPanels = this.panels.filter(p => typeof p === 'number' && !isNaN(p) && p >= 0 && p < 10);
412
+ return { panels: validPanels.join('.') };
413
+ }
414
+
415
+ render() {
416
+ return html`<slot></slot>`;
417
+ }
418
+
419
+ static get styles() {
420
+ return css`
421
+ :host {
422
+ display: grid;
423
+ grid-template-columns: var(--pb-grid-column-widths, var(--pb-computed-column-widths));
424
+ grid-column-gap: var(--pb-grid-column-gap, 20px);
425
+ justify-content: space-between;
426
+ font-size: calc(var(--pb-content-font-size, 1rem) * var(--pb-zoom-factor, 1));
427
+ }
428
+ `;
429
+ }
275
430
 
431
+ zoom(direction) {
432
+ // Zoom is now handled globally by pb-zoom component using CSS custom properties
433
+ // This method is kept for compatibility but does nothing
434
+ // The component should rely on CSS: font-size: calc(var(--pb-content-font-size, 1rem) * var(--pb-zoom-factor, 1));
435
+ }
276
436
  }
277
437
  if (!customElements.get('pb-grid')) {
278
- customElements.define('pb-grid', PbGrid);
438
+ customElements.define('pb-grid', PbGrid);
279
439
  }