@mintplayer/ng-bootstrap 20.6.1 → 20.6.3

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 (168) hide show
  1. package/dock/index.d.ts +19 -0
  2. package/fesm2022/mintplayer-ng-bootstrap-accordion.mjs +13 -13
  3. package/fesm2022/mintplayer-ng-bootstrap-accordion.mjs.map +1 -1
  4. package/fesm2022/mintplayer-ng-bootstrap-alert.mjs +10 -10
  5. package/fesm2022/mintplayer-ng-bootstrap-alert.mjs.map +1 -1
  6. package/fesm2022/mintplayer-ng-bootstrap-async-host-binding.mjs.map +1 -1
  7. package/fesm2022/mintplayer-ng-bootstrap-badge.mjs +3 -3
  8. package/fesm2022/mintplayer-ng-bootstrap-badge.mjs.map +1 -1
  9. package/fesm2022/mintplayer-ng-bootstrap-breadcrumb.mjs +10 -10
  10. package/fesm2022/mintplayer-ng-bootstrap-breadcrumb.mjs.map +1 -1
  11. package/fesm2022/mintplayer-ng-bootstrap-button-group.mjs +3 -3
  12. package/fesm2022/mintplayer-ng-bootstrap-button-group.mjs.map +1 -1
  13. package/fesm2022/mintplayer-ng-bootstrap-button-type.mjs +3 -3
  14. package/fesm2022/mintplayer-ng-bootstrap-button-type.mjs.map +1 -1
  15. package/fesm2022/mintplayer-ng-bootstrap-calendar-month.mjs +9 -9
  16. package/fesm2022/mintplayer-ng-bootstrap-calendar-month.mjs.map +1 -1
  17. package/fesm2022/mintplayer-ng-bootstrap-calendar.mjs +3 -3
  18. package/fesm2022/mintplayer-ng-bootstrap-calendar.mjs.map +1 -1
  19. package/fesm2022/mintplayer-ng-bootstrap-card.mjs +10 -10
  20. package/fesm2022/mintplayer-ng-bootstrap-card.mjs.map +1 -1
  21. package/fesm2022/mintplayer-ng-bootstrap-carousel.mjs +13 -13
  22. package/fesm2022/mintplayer-ng-bootstrap-carousel.mjs.map +1 -1
  23. package/fesm2022/mintplayer-ng-bootstrap-close.mjs +3 -3
  24. package/fesm2022/mintplayer-ng-bootstrap-close.mjs.map +1 -1
  25. package/fesm2022/mintplayer-ng-bootstrap-code-snippet.mjs +4 -4
  26. package/fesm2022/mintplayer-ng-bootstrap-code-snippet.mjs.map +1 -1
  27. package/fesm2022/mintplayer-ng-bootstrap-color-picker.mjs +28 -28
  28. package/fesm2022/mintplayer-ng-bootstrap-color-picker.mjs.map +1 -1
  29. package/fesm2022/mintplayer-ng-bootstrap-container.mjs +3 -3
  30. package/fesm2022/mintplayer-ng-bootstrap-container.mjs.map +1 -1
  31. package/fesm2022/mintplayer-ng-bootstrap-context-menu.mjs +7 -7
  32. package/fesm2022/mintplayer-ng-bootstrap-context-menu.mjs.map +1 -1
  33. package/fesm2022/mintplayer-ng-bootstrap-copy.mjs +3 -3
  34. package/fesm2022/mintplayer-ng-bootstrap-copy.mjs.map +1 -1
  35. package/fesm2022/mintplayer-ng-bootstrap-datatable.mjs +13 -13
  36. package/fesm2022/mintplayer-ng-bootstrap-datatable.mjs.map +1 -1
  37. package/fesm2022/mintplayer-ng-bootstrap-datepicker.mjs +3 -3
  38. package/fesm2022/mintplayer-ng-bootstrap-datepicker.mjs.map +1 -1
  39. package/fesm2022/mintplayer-ng-bootstrap-dock.mjs +769 -15
  40. package/fesm2022/mintplayer-ng-bootstrap-dock.mjs.map +1 -1
  41. package/fesm2022/mintplayer-ng-bootstrap-dropdown-divider.mjs +3 -3
  42. package/fesm2022/mintplayer-ng-bootstrap-dropdown-divider.mjs.map +1 -1
  43. package/fesm2022/mintplayer-ng-bootstrap-dropdown-menu.mjs +10 -10
  44. package/fesm2022/mintplayer-ng-bootstrap-dropdown-menu.mjs.map +1 -1
  45. package/fesm2022/mintplayer-ng-bootstrap-dropdown.mjs +13 -13
  46. package/fesm2022/mintplayer-ng-bootstrap-dropdown.mjs.map +1 -1
  47. package/fesm2022/mintplayer-ng-bootstrap-enhanced-paste.mjs +3 -3
  48. package/fesm2022/mintplayer-ng-bootstrap-enhanced-paste.mjs.map +1 -1
  49. package/fesm2022/mintplayer-ng-bootstrap-enum.mjs +3 -3
  50. package/fesm2022/mintplayer-ng-bootstrap-enum.mjs.map +1 -1
  51. package/fesm2022/mintplayer-ng-bootstrap-file-upload.mjs +13 -13
  52. package/fesm2022/mintplayer-ng-bootstrap-file-upload.mjs.map +1 -1
  53. package/fesm2022/mintplayer-ng-bootstrap-floating-labels.mjs +3 -3
  54. package/fesm2022/mintplayer-ng-bootstrap-floating-labels.mjs.map +1 -1
  55. package/fesm2022/mintplayer-ng-bootstrap-font-color.mjs +3 -3
  56. package/fesm2022/mintplayer-ng-bootstrap-font-color.mjs.map +1 -1
  57. package/fesm2022/mintplayer-ng-bootstrap-for.mjs +3 -3
  58. package/fesm2022/mintplayer-ng-bootstrap-for.mjs.map +1 -1
  59. package/fesm2022/mintplayer-ng-bootstrap-form.mjs +13 -13
  60. package/fesm2022/mintplayer-ng-bootstrap-form.mjs.map +1 -1
  61. package/fesm2022/mintplayer-ng-bootstrap-grid.mjs +19 -19
  62. package/fesm2022/mintplayer-ng-bootstrap-grid.mjs.map +1 -1
  63. package/fesm2022/mintplayer-ng-bootstrap-has-id.mjs.map +1 -1
  64. package/fesm2022/mintplayer-ng-bootstrap-has-overlay.mjs +3 -3
  65. package/fesm2022/mintplayer-ng-bootstrap-has-overlay.mjs.map +1 -1
  66. package/fesm2022/mintplayer-ng-bootstrap-has-property.mjs +3 -3
  67. package/fesm2022/mintplayer-ng-bootstrap-has-property.mjs.map +1 -1
  68. package/fesm2022/mintplayer-ng-bootstrap-in-list.mjs +3 -3
  69. package/fesm2022/mintplayer-ng-bootstrap-in-list.mjs.map +1 -1
  70. package/fesm2022/mintplayer-ng-bootstrap-input-group.mjs +3 -3
  71. package/fesm2022/mintplayer-ng-bootstrap-input-group.mjs.map +1 -1
  72. package/fesm2022/mintplayer-ng-bootstrap-instance-of.mjs +16 -16
  73. package/fesm2022/mintplayer-ng-bootstrap-instance-of.mjs.map +1 -1
  74. package/fesm2022/mintplayer-ng-bootstrap-let.mjs +3 -3
  75. package/fesm2022/mintplayer-ng-bootstrap-let.mjs.map +1 -1
  76. package/fesm2022/mintplayer-ng-bootstrap-linify.mjs +3 -3
  77. package/fesm2022/mintplayer-ng-bootstrap-linify.mjs.map +1 -1
  78. package/fesm2022/mintplayer-ng-bootstrap-list-group.mjs +10 -10
  79. package/fesm2022/mintplayer-ng-bootstrap-list-group.mjs.map +1 -1
  80. package/fesm2022/mintplayer-ng-bootstrap-markdown.mjs +12 -12
  81. package/fesm2022/mintplayer-ng-bootstrap-markdown.mjs.map +1 -1
  82. package/fesm2022/mintplayer-ng-bootstrap-marquee.mjs +3 -3
  83. package/fesm2022/mintplayer-ng-bootstrap-marquee.mjs.map +1 -1
  84. package/fesm2022/mintplayer-ng-bootstrap-modal.mjs +25 -25
  85. package/fesm2022/mintplayer-ng-bootstrap-modal.mjs.map +1 -1
  86. package/fesm2022/mintplayer-ng-bootstrap-multiselect.mjs +16 -16
  87. package/fesm2022/mintplayer-ng-bootstrap-multiselect.mjs.map +1 -1
  88. package/fesm2022/mintplayer-ng-bootstrap-navbar-toggler.mjs +3 -3
  89. package/fesm2022/mintplayer-ng-bootstrap-navbar-toggler.mjs.map +1 -1
  90. package/fesm2022/mintplayer-ng-bootstrap-navbar.mjs +31 -31
  91. package/fesm2022/mintplayer-ng-bootstrap-navbar.mjs.map +1 -1
  92. package/fesm2022/mintplayer-ng-bootstrap-navigation-lock.mjs +10 -10
  93. package/fesm2022/mintplayer-ng-bootstrap-navigation-lock.mjs.map +1 -1
  94. package/fesm2022/mintplayer-ng-bootstrap-no-noscript.mjs +3 -3
  95. package/fesm2022/mintplayer-ng-bootstrap-no-noscript.mjs.map +1 -1
  96. package/fesm2022/mintplayer-ng-bootstrap-offcanvas.mjs +157 -203
  97. package/fesm2022/mintplayer-ng-bootstrap-offcanvas.mjs.map +1 -1
  98. package/fesm2022/mintplayer-ng-bootstrap-ordinal-number.mjs +3 -3
  99. package/fesm2022/mintplayer-ng-bootstrap-ordinal-number.mjs.map +1 -1
  100. package/fesm2022/mintplayer-ng-bootstrap-pagination.mjs +3 -3
  101. package/fesm2022/mintplayer-ng-bootstrap-pagination.mjs.map +1 -1
  102. package/fesm2022/mintplayer-ng-bootstrap-parallax.mjs +4 -4
  103. package/fesm2022/mintplayer-ng-bootstrap-parallax.mjs.map +1 -1
  104. package/fesm2022/mintplayer-ng-bootstrap-placeholder.mjs +10 -10
  105. package/fesm2022/mintplayer-ng-bootstrap-placeholder.mjs.map +1 -1
  106. package/fesm2022/mintplayer-ng-bootstrap-playlist-toggler.mjs +3 -3
  107. package/fesm2022/mintplayer-ng-bootstrap-playlist-toggler.mjs.map +1 -1
  108. package/fesm2022/mintplayer-ng-bootstrap-popover.mjs +16 -16
  109. package/fesm2022/mintplayer-ng-bootstrap-popover.mjs.map +1 -1
  110. package/fesm2022/mintplayer-ng-bootstrap-progress-bar.mjs +10 -10
  111. package/fesm2022/mintplayer-ng-bootstrap-progress-bar.mjs.map +1 -1
  112. package/fesm2022/mintplayer-ng-bootstrap-range.mjs +10 -10
  113. package/fesm2022/mintplayer-ng-bootstrap-range.mjs.map +1 -1
  114. package/fesm2022/mintplayer-ng-bootstrap-rating.mjs +3 -3
  115. package/fesm2022/mintplayer-ng-bootstrap-rating.mjs.map +1 -1
  116. package/fesm2022/mintplayer-ng-bootstrap-resizable.mjs +10 -10
  117. package/fesm2022/mintplayer-ng-bootstrap-resizable.mjs.map +1 -1
  118. package/fesm2022/mintplayer-ng-bootstrap-scheduler.mjs +25 -25
  119. package/fesm2022/mintplayer-ng-bootstrap-scheduler.mjs.map +1 -1
  120. package/fesm2022/mintplayer-ng-bootstrap-scrollspy.mjs +13 -13
  121. package/fesm2022/mintplayer-ng-bootstrap-scrollspy.mjs.map +1 -1
  122. package/fesm2022/mintplayer-ng-bootstrap-searchbox.mjs +16 -16
  123. package/fesm2022/mintplayer-ng-bootstrap-searchbox.mjs.map +1 -1
  124. package/fesm2022/mintplayer-ng-bootstrap-select.mjs +13 -13
  125. package/fesm2022/mintplayer-ng-bootstrap-select.mjs.map +1 -1
  126. package/fesm2022/mintplayer-ng-bootstrap-select2.mjs +13 -13
  127. package/fesm2022/mintplayer-ng-bootstrap-select2.mjs.map +1 -1
  128. package/fesm2022/mintplayer-ng-bootstrap-shell.mjs +10 -10
  129. package/fesm2022/mintplayer-ng-bootstrap-shell.mjs.map +1 -1
  130. package/fesm2022/mintplayer-ng-bootstrap-signature-pad.mjs +3 -3
  131. package/fesm2022/mintplayer-ng-bootstrap-signature-pad.mjs.map +1 -1
  132. package/fesm2022/mintplayer-ng-bootstrap-slugify.mjs +3 -3
  133. package/fesm2022/mintplayer-ng-bootstrap-slugify.mjs.map +1 -1
  134. package/fesm2022/mintplayer-ng-bootstrap-spinner.mjs +4 -4
  135. package/fesm2022/mintplayer-ng-bootstrap-spinner.mjs.map +1 -1
  136. package/fesm2022/mintplayer-ng-bootstrap-split-string.mjs +3 -3
  137. package/fesm2022/mintplayer-ng-bootstrap-split-string.mjs.map +1 -1
  138. package/fesm2022/mintplayer-ng-bootstrap-splitter.mjs +13 -13
  139. package/fesm2022/mintplayer-ng-bootstrap-splitter.mjs.map +1 -1
  140. package/fesm2022/mintplayer-ng-bootstrap-sticky-footer.mjs +10 -10
  141. package/fesm2022/mintplayer-ng-bootstrap-sticky-footer.mjs.map +1 -1
  142. package/fesm2022/mintplayer-ng-bootstrap-tab-control.mjs +13 -13
  143. package/fesm2022/mintplayer-ng-bootstrap-tab-control.mjs.map +1 -1
  144. package/fesm2022/mintplayer-ng-bootstrap-table.mjs +4 -4
  145. package/fesm2022/mintplayer-ng-bootstrap-table.mjs.map +1 -1
  146. package/fesm2022/mintplayer-ng-bootstrap-timepicker.mjs +3 -3
  147. package/fesm2022/mintplayer-ng-bootstrap-timepicker.mjs.map +1 -1
  148. package/fesm2022/mintplayer-ng-bootstrap-toast.mjs +25 -25
  149. package/fesm2022/mintplayer-ng-bootstrap-toast.mjs.map +1 -1
  150. package/fesm2022/mintplayer-ng-bootstrap-toggle-button.mjs +13 -13
  151. package/fesm2022/mintplayer-ng-bootstrap-toggle-button.mjs.map +1 -1
  152. package/fesm2022/mintplayer-ng-bootstrap-tooltip.mjs +10 -10
  153. package/fesm2022/mintplayer-ng-bootstrap-tooltip.mjs.map +1 -1
  154. package/fesm2022/mintplayer-ng-bootstrap-treeview.mjs +10 -10
  155. package/fesm2022/mintplayer-ng-bootstrap-treeview.mjs.map +1 -1
  156. package/fesm2022/mintplayer-ng-bootstrap-trust-html.mjs +3 -3
  157. package/fesm2022/mintplayer-ng-bootstrap-trust-html.mjs.map +1 -1
  158. package/fesm2022/mintplayer-ng-bootstrap-typeahead.mjs +3 -3
  159. package/fesm2022/mintplayer-ng-bootstrap-typeahead.mjs.map +1 -1
  160. package/fesm2022/mintplayer-ng-bootstrap-uc-first.mjs +3 -3
  161. package/fesm2022/mintplayer-ng-bootstrap-uc-first.mjs.map +1 -1
  162. package/fesm2022/mintplayer-ng-bootstrap-user-agent.mjs +3 -3
  163. package/fesm2022/mintplayer-ng-bootstrap-user-agent.mjs.map +1 -1
  164. package/fesm2022/mintplayer-ng-bootstrap-word-count.mjs +3 -3
  165. package/fesm2022/mintplayer-ng-bootstrap-word-count.mjs.map +1 -1
  166. package/fesm2022/mintplayer-ng-bootstrap.mjs.map +1 -1
  167. package/offcanvas/index.d.ts +41 -59
  168. package/package.json +12 -12
@@ -9,10 +9,10 @@ class BsDockPaneComponent {
9
9
  throw new Error('bs-dock-pane requires a unique "name" input.');
10
10
  }
11
11
  }
12
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockPaneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
13
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: BsDockPaneComponent, isStandalone: false, selector: "bs-dock-pane", inputs: { name: "name", title: "title" }, viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true, static: true }], ngImport: i0, template: `<ng-template><ng-content></ng-content></ng-template>`, isInline: true }); }
12
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockPaneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
13
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.13", type: BsDockPaneComponent, isStandalone: false, selector: "bs-dock-pane", inputs: { name: "name", title: "title" }, viewQueries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true, static: true }], ngImport: i0, template: `<ng-template><ng-content></ng-content></ng-template>`, isInline: true }); }
14
14
  }
15
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockPaneComponent, decorators: [{
15
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockPaneComponent, decorators: [{
16
16
  type: Component,
17
17
  args: [{
18
18
  selector: 'bs-dock-pane',
@@ -39,6 +39,7 @@ const templateHtml = `
39
39
  box-sizing: border-box;
40
40
  font-family: inherit;
41
41
  color: inherit;
42
+ --dock-split-gap: 0.25rem;
42
43
  }
43
44
 
44
45
  .dock-root,
@@ -73,6 +74,13 @@ const templateHtml = `
73
74
  z-index: 5;
74
75
  }
75
76
 
77
+ .dock-intersections-layer {
78
+ position: absolute;
79
+ inset: 0;
80
+ pointer-events: none;
81
+ z-index: 120;
82
+ }
83
+
76
84
  .dock-floating {
77
85
  position: absolute;
78
86
  display: flex;
@@ -127,7 +135,8 @@ const templateHtml = `
127
135
  transition: background 120ms ease;
128
136
  }
129
137
 
130
- .dock-floating__resizer:hover {
138
+ .dock-floating__resizer:hover,
139
+ .dock-floating__resizer[data-resizing='true'] {
131
140
  background: rgba(148, 163, 184, 0.4);
132
141
  }
133
142
 
@@ -200,7 +209,7 @@ const templateHtml = `
200
209
  .dock-split {
201
210
  display: flex;
202
211
  flex: 1 1 0;
203
- gap: 0.25rem;
212
+ gap: var(--dock-split-gap);
204
213
  position: relative;
205
214
  }
206
215
 
@@ -228,11 +237,17 @@ const templateHtml = `
228
237
  .dock-split[data-direction="horizontal"] > .dock-split__divider {
229
238
  width: 0.5rem;
230
239
  cursor: col-resize;
240
+ /* Extend through perpendicular gaps for visual continuity */
241
+ margin-top: calc(var(--dock-split-gap) * -1);
242
+ margin-bottom: calc(var(--dock-split-gap) * -1);
231
243
  }
232
244
 
233
245
  .dock-split[data-direction="vertical"] > .dock-split__divider {
234
246
  height: 0.5rem;
235
247
  cursor: row-resize;
248
+ /* Extend through perpendicular gaps for visual continuity */
249
+ margin-left: calc(var(--dock-split-gap) * -1);
250
+ margin-right: calc(var(--dock-split-gap) * -1);
236
251
  }
237
252
 
238
253
  .dock-split__divider::after {
@@ -261,6 +276,45 @@ const templateHtml = `
261
276
  background: rgba(59, 130, 246, 0.35);
262
277
  }
263
278
 
279
+ .dock-intersection-handle {
280
+ position: absolute;
281
+ width: 1rem;
282
+ height: 1rem;
283
+ margin-left: -0.5rem;
284
+ margin-top: -0.5rem;
285
+ border-radius: 0.375rem;
286
+ background: rgba(59, 130, 246, 0.2);
287
+ border: 1px solid rgba(59, 130, 246, 0.6);
288
+ box-shadow: 0 2px 6px rgba(15, 23, 42, 0.2);
289
+ cursor: all-scroll;
290
+ pointer-events: auto;
291
+ opacity: 0;
292
+ transition: background 120ms ease, border-color 120ms ease, opacity 120ms ease;
293
+ }
294
+
295
+ .dock-intersection-handle:hover,
296
+ .dock-intersection-handle:focus-visible,
297
+ .dock-intersection-handle[data-visible='true'],
298
+ .dock-intersection-handle[data-resizing='true'] {
299
+ background: rgba(59, 130, 246, 0.35);
300
+ border-color: rgba(59, 130, 246, 0.9);
301
+ opacity: 1;
302
+ outline: none;
303
+ }
304
+
305
+ .dock-snap-marker {
306
+ position: absolute;
307
+ width: 6px;
308
+ height: 6px;
309
+ margin-left: -3px;
310
+ margin-top: -3px;
311
+ border-radius: 50%;
312
+ background: rgba(59, 130, 246, 0.7);
313
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
314
+ pointer-events: none;
315
+ z-index: 130;
316
+ }
317
+
264
318
  .dock-stack {
265
319
  display: flex;
266
320
  flex-direction: column;
@@ -409,6 +463,7 @@ const templateHtml = `
409
463
  <div class="dock-root">
410
464
  <div class="dock-docked"></div>
411
465
  <div class="dock-floating-layer"></div>
466
+ <div class="dock-intersections-layer dock-intersection-layer"></div>
412
467
  </div>
413
468
  <div class="dock-drop-indicator"></div>
414
469
  <div class="dock-drop-joystick" data-visible="false">
@@ -477,8 +532,93 @@ class MintDockManagerElement extends HTMLElement {
477
532
  }
478
533
  static get observedAttributes() {
479
534
  return ['layout'];
535
+ // return ['layout', 'debug-snap-markers'];
480
536
  }
481
537
  static { this.instanceCounter = 0; }
538
+ renderSnapMarkersForDivider() {
539
+ if (!this.showSnapMarkers)
540
+ return;
541
+ const layer = this.shadowRoot?.querySelector('.dock-intersections-layer, .dock-intersection-layer');
542
+ if (!layer)
543
+ return;
544
+ // Clear previous
545
+ Array.from(layer.querySelectorAll('.dock-snap-marker')).forEach((el) => el.remove());
546
+ if (!this.resizeState || !this.activeSnapAxis || this.activeSnapTargets.length === 0)
547
+ return;
548
+ const rootRect = this.rootEl.getBoundingClientRect();
549
+ const dRect = this.resizeState.divider.getBoundingClientRect();
550
+ if (this.activeSnapAxis === 'x') {
551
+ const y = dRect.top + dRect.height / 2 - rootRect.top;
552
+ this.activeSnapTargets.forEach((sx) => {
553
+ const dot = this.documentRef.createElement('div');
554
+ dot.className = 'dock-snap-marker';
555
+ dot.style.left = `${rootRect.left + sx - rootRect.left}px`;
556
+ dot.style.top = `${y}px`;
557
+ layer.appendChild(dot);
558
+ });
559
+ }
560
+ else if (this.activeSnapAxis === 'y') {
561
+ const x = dRect.left + dRect.width / 2 - rootRect.left;
562
+ this.activeSnapTargets.forEach((sy) => {
563
+ const dot = this.documentRef.createElement('div');
564
+ dot.className = 'dock-snap-marker';
565
+ dot.style.left = `${x}px`;
566
+ dot.style.top = `${rootRect.top + sy - rootRect.top}px`;
567
+ layer.appendChild(dot);
568
+ });
569
+ }
570
+ }
571
+ renderSnapMarkersForCorner() {
572
+ if (!this.showSnapMarkers)
573
+ return;
574
+ const layer = this.shadowRoot?.querySelector('.dock-intersections-layer, .dock-intersection-layer');
575
+ if (!layer)
576
+ return;
577
+ Array.from(layer.querySelectorAll('.dock-snap-marker')).forEach((el) => el.remove());
578
+ if (!this.cornerResizeState)
579
+ return;
580
+ const rootRect = this.rootEl.getBoundingClientRect();
581
+ // Compute representative center lines from first entries
582
+ let centerX = null;
583
+ let centerY = null;
584
+ const st = this.cornerResizeState;
585
+ if (st.vs.length > 0) {
586
+ const vRect = st.vs[0].container.querySelector(':scope > .dock-split__divider')?.getBoundingClientRect();
587
+ if (vRect)
588
+ centerX = vRect.left + vRect.width / 2 - rootRect.left;
589
+ }
590
+ if (st.hs.length > 0) {
591
+ const hRect = st.hs[0].container.querySelector(':scope > .dock-split__divider')?.getBoundingClientRect();
592
+ if (hRect)
593
+ centerY = hRect.top + hRect.height / 2 - rootRect.top;
594
+ }
595
+ if (centerY != null) {
596
+ this.cornerSnapXTargets.forEach((sx) => {
597
+ const dot = this.documentRef.createElement('div');
598
+ dot.className = 'dock-snap-marker';
599
+ dot.style.left = `${sx}px`;
600
+ dot.style.top = `${centerY}px`;
601
+ layer.appendChild(dot);
602
+ });
603
+ }
604
+ if (centerX != null) {
605
+ this.cornerSnapYTargets.forEach((sy) => {
606
+ const dot = this.documentRef.createElement('div');
607
+ dot.className = 'dock-snap-marker';
608
+ dot.style.left = `${centerX}px`;
609
+ dot.style.top = `${sy}px`;
610
+ layer.appendChild(dot);
611
+ });
612
+ }
613
+ }
614
+ clearSnapMarkers() {
615
+ if (!this.showSnapMarkers)
616
+ return;
617
+ const layer = this.shadowRoot?.querySelector('.dock-intersections-layer, .dock-intersection-layer');
618
+ if (!layer)
619
+ return;
620
+ Array.from(layer.querySelectorAll('.dock-snap-marker')).forEach((el) => el.remove());
621
+ }
482
622
  constructor() {
483
623
  super();
484
624
  this.dropJoystickTarget = null;
@@ -490,10 +630,22 @@ class MintDockManagerElement extends HTMLElement {
490
630
  this.dragState = null;
491
631
  this.floatingDragState = null;
492
632
  this.floatingResizeState = null;
633
+ this.intersectionRaf = null;
634
+ this.intersectionHandles = new Map();
635
+ this.cornerResizeState = null;
493
636
  this.pointerTrackingActive = false;
494
637
  this.dragPointerTrackingActive = false;
495
638
  this.lastDragPointerPosition = null;
639
+ // Localized snapping while dragging a divider
640
+ this.activeSnapAxis = null;
641
+ this.activeSnapTargets = [];
642
+ // Localized snapping while dragging an intersection handle
643
+ this.cornerSnapXTargets = [];
644
+ this.cornerSnapYTargets = [];
645
+ // Debug: render snap markers while dragging
646
+ this.showSnapMarkers = false;
496
647
  this.pendingDragEndTimeout = null;
648
+ this.previousSplitSizes = new Map();
497
649
  const documentRef = this.resolveDocument();
498
650
  this.documentRef = documentRef;
499
651
  this.windowRef = this.resolveWindow(documentRef);
@@ -543,6 +695,7 @@ class MintDockManagerElement extends HTMLElement {
543
695
  this.onDragTouchMove = this.onDragTouchMove.bind(this);
544
696
  this.onDragMouseUp = this.onDragMouseUp.bind(this);
545
697
  this.onDragTouchEnd = this.onDragTouchEnd.bind(this);
698
+ this.onWindowResize = this.onWindowResize.bind(this);
546
699
  }
547
700
  connectedCallback() {
548
701
  if (!this.hasAttribute('role')) {
@@ -577,6 +730,7 @@ class MintDockManagerElement extends HTMLElement {
577
730
  win?.addEventListener('dragover', this.onGlobalDragOver);
578
731
  win?.addEventListener('drag', this.onDrag);
579
732
  win?.addEventListener('dragend', this.onGlobalDragEnd, true);
733
+ win?.addEventListener('resize', this.onWindowResize);
580
734
  }
581
735
  disconnectedCallback() {
582
736
  this.rootEl.removeEventListener('dragover', this.onDragOver);
@@ -593,11 +747,19 @@ class MintDockManagerElement extends HTMLElement {
593
747
  win?.removeEventListener('pointermove', this.onPointerMove);
594
748
  win?.removeEventListener('pointerup', this.onPointerUp);
595
749
  this.pointerTrackingActive = false;
750
+ const win2 = this.windowRef;
751
+ win2?.removeEventListener('resize', this.onWindowResize);
596
752
  }
597
753
  attributeChangedCallback(name, _oldValue, newValue) {
598
754
  if (name === 'layout') {
599
755
  this.layout = newValue ? this.parseLayout(newValue) : null;
600
756
  }
757
+ else if (name === 'debug-snap-markers') {
758
+ this.showSnapMarkers = !(newValue === null || newValue === 'false' || newValue === '0');
759
+ if (!this.showSnapMarkers) {
760
+ this.clearSnapMarkers();
761
+ }
762
+ }
601
763
  }
602
764
  get layout() {
603
765
  return {
@@ -673,6 +835,7 @@ class MintDockManagerElement extends HTMLElement {
673
835
  this.dockedEl.appendChild(fragment);
674
836
  }
675
837
  this.renderFloatingPanes();
838
+ this.scheduleRenderIntersectionHandles();
676
839
  }
677
840
  renderNode(node, path, floatingIndex) {
678
841
  if (node.kind === 'split') {
@@ -767,6 +930,479 @@ class MintDockManagerElement extends HTMLElement {
767
930
  this.floatingLayerEl.appendChild(wrapper);
768
931
  });
769
932
  }
933
+ onWindowResize() {
934
+ // Recompute intersection handles on window resize
935
+ this.scheduleRenderIntersectionHandles();
936
+ }
937
+ scheduleRenderIntersectionHandles() {
938
+ this.intersectionRaf = null;
939
+ this.renderIntersectionHandles();
940
+ }
941
+ renderIntersectionHandles() {
942
+ const layer = this.shadowRoot?.querySelector('.dock-intersections-layer, .dock-intersection-layer');
943
+ if (!layer)
944
+ return;
945
+ // Keep existing handles; we will diff and update positions
946
+ // 1) Clean up legacy handles (created before keying) that lack a data-key
947
+ Array.from(layer.querySelectorAll('.dock-intersection-handle'))
948
+ .filter((el) => !el.dataset['key'])
949
+ .forEach((el) => el.remove());
950
+ // 2) Rebuild the internal map from DOM to avoid drifting state and dedupe duplicates
951
+ const domByKey = new Map();
952
+ Array.from(layer.querySelectorAll('.dock-intersection-handle[data-key]')).forEach((el) => {
953
+ const key = el.dataset['key'] ?? '';
954
+ if (!key)
955
+ return;
956
+ if (domByKey.has(key)) {
957
+ // Remove duplicates with the same key, keep the first one
958
+ el.remove();
959
+ return;
960
+ }
961
+ domByKey.set(key, el);
962
+ // Ensure listener is attached only once
963
+ if (!el.dataset['listener']) {
964
+ el.dataset['listener'] = '1';
965
+ // Listener will be (re)assigned later when we know the current h/v pair
966
+ }
967
+ });
968
+ // Sync internal map with DOM
969
+ this.intersectionHandles = domByKey;
970
+ const rootRect = this.rootEl.getBoundingClientRect();
971
+ // If a corner resize is active, only update that handle's position and avoid creating new ones
972
+ if (this.cornerResizeState) {
973
+ const st = this.cornerResizeState;
974
+ const h0 = st.hs[0];
975
+ const v0 = st.vs[0];
976
+ const hPathStr = this.formatPath(h0.path);
977
+ const vPathStr = this.formatPath(v0.path);
978
+ const key = `${hPathStr}:${h0.index}|${vPathStr}:${v0.index}`;
979
+ // Find divider elements corresponding to active paths
980
+ const hDiv = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${hPathStr}"][data-index="${h0.index}"]`);
981
+ const vDiv = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${vPathStr}"][data-index="${v0.index}"]`);
982
+ if (hDiv && vDiv) {
983
+ const hr = hDiv.getBoundingClientRect();
984
+ const vr = vDiv.getBoundingClientRect();
985
+ const x = vr.left + vr.width / 2 - rootRect.left;
986
+ const y = hr.top + hr.height / 2 - rootRect.top;
987
+ const handle = st.handle;
988
+ if (!handle.dataset['key']) {
989
+ handle.dataset['key'] = key;
990
+ }
991
+ this.intersectionHandles.set(key, handle);
992
+ handle.style.left = `${x}px`;
993
+ handle.style.top = `${y}px`;
994
+ // Remove any other handles that don't match the active key
995
+ Array.from(layer.querySelectorAll('.dock-intersection-handle')).forEach((el) => {
996
+ if ((el.dataset['key'] ?? '') !== key) {
997
+ el.remove();
998
+ }
999
+ });
1000
+ // Normalize internal map as well
1001
+ this.intersectionHandles = new Map([[key, handle]]);
1002
+ }
1003
+ return;
1004
+ }
1005
+ const allDividers = Array.from(this.shadowRoot?.querySelectorAll('.dock-split__divider') ?? []);
1006
+ const hDividers = [];
1007
+ const vDividers = [];
1008
+ allDividers.forEach((el) => {
1009
+ const orientation = el.dataset['orientation'] ?? undefined;
1010
+ const rect = el.getBoundingClientRect();
1011
+ const container = el.closest('.dock-split');
1012
+ const path = this.parsePath(el.dataset['path']);
1013
+ const pathStr = el.dataset['path'] ?? '';
1014
+ const index = Number.parseInt(el.dataset['index'] ?? '', 10);
1015
+ if (!container || !Number.isFinite(index))
1016
+ return;
1017
+ const info = { el, rect, path, pathStr, index, container };
1018
+ // Note: node.direction === 'horizontal' means the split lays out children left-to-right,
1019
+ // which yields a VERTICAL divider bar. So mapping is inverted here.
1020
+ if (orientation === 'horizontal') {
1021
+ vDividers.push(info);
1022
+ }
1023
+ else if (orientation === 'vertical') {
1024
+ hDividers.push(info);
1025
+ }
1026
+ });
1027
+ const desiredKeys = new Set();
1028
+ const tol = 24; // px tolerance to account for gaps and subpixel layout
1029
+ const groupMap = new Map();
1030
+ const groupPairs = new Map();
1031
+ hDividers.forEach((h) => {
1032
+ const hCenterY = h.rect.top + h.rect.height / 2;
1033
+ vDividers.forEach((v) => {
1034
+ const vCenterX = v.rect.left + v.rect.width / 2;
1035
+ const dx = vCenterX < h.rect.left ? h.rect.left - vCenterX : vCenterX > h.rect.right ? vCenterX - h.rect.right : 0;
1036
+ const dy = hCenterY < v.rect.top ? v.rect.top - hCenterY : hCenterY > v.rect.bottom ? hCenterY - v.rect.bottom : 0;
1037
+ if (dx > tol || dy > tol)
1038
+ return;
1039
+ const x = vCenterX - rootRect.left;
1040
+ const y = hCenterY - rootRect.top;
1041
+ const key = `${h.pathStr}:${h.index}|${v.pathStr}:${v.index}`;
1042
+ const gk = `${Math.round(x)}:${Math.round(y)}`;
1043
+ let handle = groupMap.get(gk);
1044
+ if (!handle) {
1045
+ // Try reuse via existing pair mapping
1046
+ handle = this.intersectionHandles.get(key) ?? null;
1047
+ if (!handle) {
1048
+ handle = this.documentRef.createElement('div');
1049
+ handle.classList.add('dock-intersection-handle', 'glyph');
1050
+ handle.setAttribute('role', 'separator');
1051
+ handle.setAttribute('aria-label', 'Resize split intersection');
1052
+ handle.dataset['key'] = key;
1053
+ handle.dataset['listener'] = '1';
1054
+ handle.addEventListener('pointerdown', (ev) => this.beginCornerResize(ev, h, v, handle));
1055
+ handle.addEventListener('dblclick', (ev) => this.onIntersectionDoubleClick(ev, handle));
1056
+ layer.appendChild(handle);
1057
+ }
1058
+ groupMap.set(gk, handle);
1059
+ }
1060
+ // Track pairs for this group and map all pair keys to the same handle
1061
+ const arr = groupPairs.get(gk) ?? [];
1062
+ arr.push({ h: { pathStr: h.pathStr ?? '', index: h.index }, v: { pathStr: v.pathStr ?? '', index: v.index } });
1063
+ groupPairs.set(gk, arr);
1064
+ this.intersectionHandles.set(key, handle);
1065
+ // Update position for the grouped handle
1066
+ handle.style.left = `${x}px`;
1067
+ handle.style.top = `${y}px`;
1068
+ });
1069
+ });
1070
+ // Attach grouped pairs data to each handle and prune stale ones
1071
+ const keep = new Set(groupMap.values());
1072
+ groupMap.forEach((handle, gk) => {
1073
+ const pairs = groupPairs.get(gk) ?? [];
1074
+ handle.dataset['pairs'] = JSON.stringify(pairs);
1075
+ });
1076
+ Array.from(layer.querySelectorAll('.dock-intersection-handle')).forEach((el) => {
1077
+ if (!keep.has(el)) {
1078
+ el.remove();
1079
+ }
1080
+ });
1081
+ // Reset intersectionHandles to only currently mapped keys
1082
+ const newMap = new Map();
1083
+ groupPairs.forEach((pairs, gk) => {
1084
+ const handle = groupMap.get(gk);
1085
+ pairs.forEach((p) => newMap.set(`${p.h.pathStr}:${p.h.index}|${p.v.pathStr}:${p.v.index}`, handle));
1086
+ });
1087
+ this.intersectionHandles = newMap;
1088
+ }
1089
+ beginCornerResize(event, h, v, handle) {
1090
+ event.preventDefault();
1091
+ // Build pairs from dataset if available (grouped intersections), otherwise from the provided pair
1092
+ const pairsRaw = handle.dataset['pairs'];
1093
+ const parsed = pairsRaw ? JSON.parse(pairsRaw) : [];
1094
+ const hs = [];
1095
+ const vs = [];
1096
+ const ensureHV = (pathStr, index, axis) => {
1097
+ const path = this.parsePath(pathStr);
1098
+ if (!path)
1099
+ return;
1100
+ const div = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${pathStr}"][data-index="${index}"]`) ?? null;
1101
+ const container = div?.closest('.dock-split');
1102
+ if (!container)
1103
+ return;
1104
+ if (axis === 'h') {
1105
+ const children = Array.from(container.querySelectorAll(':scope > .dock-split__child'));
1106
+ const initial = children.map((c) => c.getBoundingClientRect().height);
1107
+ hs.push({ path, index, container, initialSizes: initial, before: initial[index], after: initial[index + 1] });
1108
+ }
1109
+ else {
1110
+ const children = Array.from(container.querySelectorAll(':scope > .dock-split__child'));
1111
+ const initial = children.map((c) => c.getBoundingClientRect().width);
1112
+ vs.push({ path, index, container, initialSizes: initial, before: initial[index], after: initial[index + 1] });
1113
+ }
1114
+ };
1115
+ if (parsed.length > 0) {
1116
+ parsed.forEach((p) => { ensureHV(p.h.pathStr, p.h.index, 'h'); ensureHV(p.v.pathStr, p.v.index, 'v'); });
1117
+ }
1118
+ else if (h.path && v.path) {
1119
+ ensureHV(this.formatPath(h.path), h.index, 'h');
1120
+ ensureHV(this.formatPath(v.path), v.index, 'v');
1121
+ }
1122
+ if (hs.length === 0 && vs.length === 0)
1123
+ return;
1124
+ try {
1125
+ handle.setPointerCapture(event.pointerId);
1126
+ handle.dataset['resizing'] = 'true';
1127
+ handle.classList.add('hovering');
1128
+ }
1129
+ catch { }
1130
+ this.cornerResizeState = {
1131
+ pointerId: event.pointerId,
1132
+ handle,
1133
+ hs: hs.map((e) => ({ path: this.clonePath(e.path), index: e.index, container: e.container, beforeSize: e.before, afterSize: e.after, initialSizes: e.initialSizes, startY: event.clientY })),
1134
+ vs: vs.map((e) => ({ path: this.clonePath(e.path), index: e.index, container: e.container, beforeSize: e.before, afterSize: e.after, initialSizes: e.initialSizes, startX: event.clientX })),
1135
+ };
1136
+ this.startPointerTracking();
1137
+ // Ensure handle has a stable key (use group if present)
1138
+ if (!handle.dataset['key']) {
1139
+ handle.dataset['key'] = handle.dataset['group'] ?? '';
1140
+ }
1141
+ this.renderSnapMarkersForCorner();
1142
+ // Compute localized snap targets for this intersection
1143
+ try {
1144
+ const rootRect = this.rootEl.getBoundingClientRect();
1145
+ // Use first pair to define the crossing lines
1146
+ let centerX = null;
1147
+ let centerY = null;
1148
+ // Resolve one vertical bar (from vs) and one horizontal bar (from hs)
1149
+ if (vs.length > 0) {
1150
+ const vPair = vs[0];
1151
+ const vPathStr = this.formatPath(vPair.path);
1152
+ const vDiv = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${vPathStr}"][data-index="${vPair.index}"]`) ?? null;
1153
+ const vr = vDiv?.getBoundingClientRect();
1154
+ if (vr)
1155
+ centerX = vr.left + vr.width / 2;
1156
+ }
1157
+ if (hs.length > 0) {
1158
+ const hPair = hs[0];
1159
+ const hPathStr = this.formatPath(hPair.path);
1160
+ const hDiv = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${hPathStr}"][data-index="${hPair.index}"]`) ?? null;
1161
+ const hr = hDiv?.getBoundingClientRect();
1162
+ if (hr)
1163
+ centerY = hr.top + hr.height / 2;
1164
+ }
1165
+ const xTargets = [];
1166
+ const yTargets = [];
1167
+ const allDividers = Array.from(this.shadowRoot?.querySelectorAll('.dock-split__divider') ?? []);
1168
+ allDividers.forEach((el) => {
1169
+ const o = el.dataset['orientation'] ?? undefined;
1170
+ const r = el.getBoundingClientRect();
1171
+ if (o === 'horizontal' && centerY != null) {
1172
+ // vertical bar → contributes X if it crosses centerY
1173
+ if (centerY >= r.top && centerY <= r.bottom) {
1174
+ xTargets.push(r.left + r.width / 2 - rootRect.left);
1175
+ }
1176
+ }
1177
+ else if (o === 'vertical' && centerX != null) {
1178
+ // horizontal bar → contributes Y if it crosses centerX
1179
+ if (centerX >= r.left && centerX <= r.right) {
1180
+ yTargets.push(r.top + r.height / 2 - rootRect.top);
1181
+ }
1182
+ }
1183
+ });
1184
+ this.cornerSnapXTargets = xTargets;
1185
+ this.cornerSnapYTargets = yTargets;
1186
+ }
1187
+ catch {
1188
+ this.cornerSnapXTargets = [];
1189
+ this.cornerSnapYTargets = [];
1190
+ }
1191
+ }
1192
+ handleCornerResizeMove(event) {
1193
+ const state = this.cornerResizeState;
1194
+ if (!state || state.pointerId !== event.pointerId)
1195
+ return;
1196
+ const snapValue = (val, total, active) => {
1197
+ if (!active || total <= 0)
1198
+ return val;
1199
+ const ratios = [1 / 3, 1 / 2, 2 / 3];
1200
+ const r = val / total;
1201
+ let best = ratios[0];
1202
+ let d = Math.abs(r - best);
1203
+ for (let i = 1; i < ratios.length; i++) {
1204
+ const dd = Math.abs(r - ratios[i]);
1205
+ if (dd < d) {
1206
+ d = dd;
1207
+ best = ratios[i];
1208
+ }
1209
+ }
1210
+ return best * total;
1211
+ };
1212
+ // Axis snapping to nearby intersections
1213
+ const tol = 10;
1214
+ const rootRect = this.rootEl.getBoundingClientRect();
1215
+ let clientX = event.clientX;
1216
+ let clientY = event.clientY;
1217
+ if (this.cornerSnapXTargets.length) {
1218
+ let best = clientX, bestDist = tol + 1;
1219
+ this.cornerSnapXTargets.forEach((sx) => {
1220
+ const px = rootRect.left + sx;
1221
+ const d = Math.abs(px - clientX);
1222
+ if (d < bestDist) {
1223
+ bestDist = d;
1224
+ best = px;
1225
+ }
1226
+ });
1227
+ if (bestDist <= tol)
1228
+ clientX = best;
1229
+ }
1230
+ if (this.cornerSnapYTargets.length) {
1231
+ let best = clientY, bestDist = tol + 1;
1232
+ this.cornerSnapYTargets.forEach((sy) => {
1233
+ const py = rootRect.top + sy;
1234
+ const d = Math.abs(py - clientY);
1235
+ if (d < bestDist) {
1236
+ bestDist = d;
1237
+ best = py;
1238
+ }
1239
+ });
1240
+ if (bestDist <= tol)
1241
+ clientY = best;
1242
+ }
1243
+ // Update all horizontal bars (vertical splits) with Y delta
1244
+ state.hs.forEach((h) => {
1245
+ const node = this.resolveSplitNode(h.path);
1246
+ if (!node)
1247
+ return;
1248
+ const deltaY = clientY - h.startY;
1249
+ const minSize = 48;
1250
+ const pairTotal = h.beforeSize + h.afterSize;
1251
+ let newBefore = Math.min(Math.max(h.beforeSize + deltaY, minSize), pairTotal - minSize);
1252
+ newBefore = snapValue(newBefore, pairTotal, event.shiftKey);
1253
+ const newAfter = pairTotal - newBefore;
1254
+ const sizesPx = [...h.initialSizes];
1255
+ sizesPx[h.index] = newBefore;
1256
+ sizesPx[h.index + 1] = newAfter;
1257
+ const total = sizesPx.reduce((a, s) => a + s, 0);
1258
+ const normalized = total > 0 ? sizesPx.map((s) => s / total) : [];
1259
+ node.sizes = normalized;
1260
+ const children = Array.from(h.container.querySelectorAll(':scope > .dock-split__child'));
1261
+ normalized.forEach((size, idx) => { if (children[idx])
1262
+ children[idx].style.flex = `${Math.max(size, 0)} 1 0`; });
1263
+ });
1264
+ // Update all vertical bars (horizontal splits) with X delta
1265
+ state.vs.forEach((v) => {
1266
+ const node = this.resolveSplitNode(v.path);
1267
+ if (!node)
1268
+ return;
1269
+ const deltaX = clientX - v.startX;
1270
+ const minSize = 48;
1271
+ const pairTotal = v.beforeSize + v.afterSize;
1272
+ let newBefore = Math.min(Math.max(v.beforeSize + deltaX, minSize), pairTotal - minSize);
1273
+ newBefore = snapValue(newBefore, pairTotal, event.shiftKey);
1274
+ const newAfter = pairTotal - newBefore;
1275
+ const sizesPx = [...v.initialSizes];
1276
+ sizesPx[v.index] = newBefore;
1277
+ sizesPx[v.index + 1] = newAfter;
1278
+ const total = sizesPx.reduce((a, s) => a + s, 0);
1279
+ const normalized = total > 0 ? sizesPx.map((s) => s / total) : [];
1280
+ node.sizes = normalized;
1281
+ const children = Array.from(v.container.querySelectorAll(':scope > .dock-split__child'));
1282
+ normalized.forEach((size, idx) => { if (children[idx])
1283
+ children[idx].style.flex = `${Math.max(size, 0)} 1 0`; });
1284
+ });
1285
+ this.dispatchLayoutChanged();
1286
+ }
1287
+ endCornerResize(pointerId) {
1288
+ const state = this.cornerResizeState;
1289
+ if (!state || pointerId !== state.pointerId)
1290
+ return;
1291
+ try {
1292
+ state.handle.releasePointerCapture(state.pointerId);
1293
+ }
1294
+ catch { }
1295
+ state.handle.dataset['resizing'] = 'false';
1296
+ this.cornerResizeState = null;
1297
+ // Re-render handles to account for new positions
1298
+ this.scheduleRenderIntersectionHandles();
1299
+ this.cornerSnapXTargets = [];
1300
+ this.cornerSnapYTargets = [];
1301
+ }
1302
+ onIntersectionDoubleClick(event, handle) {
1303
+ event.preventDefault();
1304
+ const pairsRaw = handle.dataset['pairs'];
1305
+ const parsed = pairsRaw ? JSON.parse(pairsRaw) : [];
1306
+ if (!Array.isArray(parsed) || parsed.length === 0) {
1307
+ const k = handle.dataset['key'] ?? '';
1308
+ const parts = k.split('|');
1309
+ if (parts.length === 2) {
1310
+ const [hPart, vPart] = parts;
1311
+ const hi = hPart.lastIndexOf(':');
1312
+ const vi = vPart.lastIndexOf(':');
1313
+ if (hi > 0 && vi > 0) {
1314
+ const hPathStr = hPart.slice(0, hi);
1315
+ const vPathStr = vPart.slice(0, vi);
1316
+ const hIdx = Number.parseInt(hPart.slice(hi + 1), 10);
1317
+ const vIdx = Number.parseInt(vPart.slice(vi + 1), 10);
1318
+ parsed.push({ h: { pathStr: hPathStr, index: hIdx }, v: { pathStr: vPathStr, index: vIdx } });
1319
+ }
1320
+ }
1321
+ }
1322
+ if (parsed.length === 0)
1323
+ return;
1324
+ const splitKeys = new Set();
1325
+ parsed.forEach((p) => { splitKeys.add(p.h.pathStr); splitKeys.add(p.v.pathStr); });
1326
+ let hasStored = false;
1327
+ splitKeys.forEach((k) => { if (this.previousSplitSizes.has(k))
1328
+ hasStored = true; });
1329
+ const applySizes = (pathStr, mutate) => {
1330
+ const path = this.parsePath(pathStr);
1331
+ if (!path)
1332
+ return;
1333
+ const node = this.resolveSplitNode(path);
1334
+ if (!node)
1335
+ return;
1336
+ const sizes = this.normalizeSizesArray(node.sizes ?? [], node.children.length);
1337
+ // Find divider index from any divider belonging to this path
1338
+ const divEl = this.shadowRoot?.querySelector(`.dock-split__divider[data-path="${pathStr}"]`);
1339
+ const index = divEl ? Number.parseInt(divEl.dataset['index'] ?? '0', 10) : 0;
1340
+ const newSizes = mutate([...sizes], index);
1341
+ node.sizes = newSizes;
1342
+ const segments = path.segments.join('/');
1343
+ const container = this.shadowRoot?.querySelector(`.dock-split[data-path="${segments}"]`);
1344
+ if (container) {
1345
+ const children = Array.from(container.querySelectorAll(':scope > .dock-split__child'));
1346
+ newSizes.forEach((s, i) => { if (children[i])
1347
+ children[i].style.flex = `${Math.max(s, 0)} 1 0`; });
1348
+ }
1349
+ };
1350
+ if (hasStored) {
1351
+ // Restore stored sizes
1352
+ this.previousSplitSizes.forEach((sizes, pathStr) => {
1353
+ const path = this.parsePath(pathStr);
1354
+ const node = path ? this.resolveSplitNode(path) : null;
1355
+ if (!node)
1356
+ return;
1357
+ const norm = this.normalizeSizesArray(sizes, node.children.length);
1358
+ node.sizes = norm;
1359
+ const segments = path.segments.join('/');
1360
+ const container = this.shadowRoot?.querySelector(`.dock-split[data-path="${segments}"]`);
1361
+ if (container) {
1362
+ const children = Array.from(container.querySelectorAll(':scope > .dock-split__child'));
1363
+ norm.forEach((s, i) => { if (children[i])
1364
+ children[i].style.flex = `${Math.max(s, 0)} 1 0`; });
1365
+ }
1366
+ });
1367
+ this.previousSplitSizes.clear();
1368
+ }
1369
+ else {
1370
+ // Equalize the two panes adjacent to each divider and store previous sizes
1371
+ const touched = new Set();
1372
+ parsed.forEach((p) => {
1373
+ [p.h.pathStr, p.v.pathStr].forEach((key) => {
1374
+ if (touched.has(key))
1375
+ return;
1376
+ const path = this.parsePath(key);
1377
+ const node = path ? this.resolveSplitNode(path) : null;
1378
+ if (node && Array.isArray(node.sizes)) {
1379
+ this.previousSplitSizes.set(key, [...node.sizes]);
1380
+ }
1381
+ touched.add(key);
1382
+ });
1383
+ applySizes(p.h.pathStr, (sizes, idx) => {
1384
+ const total = (sizes[idx] ?? 0) + (sizes[idx + 1] ?? 0);
1385
+ if (total <= 0)
1386
+ return sizes;
1387
+ sizes[idx] = total / 2;
1388
+ sizes[idx + 1] = total / 2;
1389
+ const sum = sizes.reduce((a, s) => a + s, 0);
1390
+ return sum > 0 ? sizes.map((s) => s / sum) : sizes;
1391
+ });
1392
+ applySizes(p.v.pathStr, (sizes, idx) => {
1393
+ const total = (sizes[idx] ?? 0) + (sizes[idx + 1] ?? 0);
1394
+ if (total <= 0)
1395
+ return sizes;
1396
+ sizes[idx] = total / 2;
1397
+ sizes[idx + 1] = total / 2;
1398
+ const sum = sizes.reduce((a, s) => a + s, 0);
1399
+ return sum > 0 ? sizes.map((s) => s / sum) : sizes;
1400
+ });
1401
+ });
1402
+ }
1403
+ this.dispatchLayoutChanged();
1404
+ this.scheduleRenderIntersectionHandles();
1405
+ }
770
1406
  beginFloatingDrag(event, index, wrapper, handle) {
771
1407
  const floating = this.floatingLayouts[index];
772
1408
  if (!floating) {
@@ -803,6 +1439,7 @@ class MintDockManagerElement extends HTMLElement {
803
1439
  event.stopPropagation();
804
1440
  try {
805
1441
  handle.setPointerCapture(event.pointerId);
1442
+ handle.dataset['resizing'] = 'true';
806
1443
  }
807
1444
  catch (err) {
808
1445
  /* pointer capture may not be supported */
@@ -848,6 +1485,7 @@ class MintDockManagerElement extends HTMLElement {
848
1485
  }
849
1486
  try {
850
1487
  state.handle.releasePointerCapture(state.pointerId);
1488
+ delete state.handle.dataset['resizing'];
851
1489
  }
852
1490
  catch (err) {
853
1491
  /* no-op */
@@ -997,7 +1635,8 @@ class MintDockManagerElement extends HTMLElement {
997
1635
  if (this.pointerTrackingActive &&
998
1636
  !this.resizeState &&
999
1637
  !this.floatingDragState &&
1000
- !this.floatingResizeState) {
1638
+ !this.floatingResizeState &&
1639
+ !this.cornerResizeState) {
1001
1640
  const win = this.windowRef;
1002
1641
  win?.removeEventListener('pointermove', this.onPointerMove);
1003
1642
  win?.removeEventListener('pointerup', this.onPointerUp);
@@ -1059,6 +1698,13 @@ class MintDockManagerElement extends HTMLElement {
1059
1698
  divider.classList.add('dock-split__divider');
1060
1699
  divider.setAttribute('role', 'separator');
1061
1700
  divider.tabIndex = 0;
1701
+ // Tag divider with metadata for intersection detection
1702
+ const dividerPath = typeof floatingIndex === 'number'
1703
+ ? { type: 'floating', index: floatingIndex, segments: [...path] }
1704
+ : { type: 'docked', segments: [...path] };
1705
+ divider.dataset['path'] = this.formatPath(dividerPath);
1706
+ divider.dataset['index'] = String(index);
1707
+ divider.dataset['orientation'] = node.direction;
1062
1708
  divider.addEventListener('pointerdown', (event) => this.beginResize(event, container, floatingIndex !== undefined
1063
1709
  ? { type: 'floating', index: floatingIndex, segments: [...path] }
1064
1710
  : { type: 'docked', segments: [...path] }, index));
@@ -1183,19 +1829,121 @@ class MintDockManagerElement extends HTMLElement {
1183
1829
  afterSize,
1184
1830
  };
1185
1831
  this.startPointerTracking();
1832
+ // Compute localized snap targets: intersections with perpendicular dividers near this divider
1833
+ try {
1834
+ const rootRect = this.rootEl.getBoundingClientRect();
1835
+ const dividerRect = divider.getBoundingClientRect();
1836
+ const allDividers = Array.from(this.shadowRoot?.querySelectorAll('.dock-split__divider') ?? []);
1837
+ const targets = [];
1838
+ if (orientation === 'horizontal') {
1839
+ // Current bar is vertical → snap X to centers of other vertical bars (no crossing check needed)
1840
+ allDividers.forEach((el) => {
1841
+ if (el === divider)
1842
+ return;
1843
+ const o = el.dataset['orientation'] ?? undefined;
1844
+ if (o !== 'horizontal')
1845
+ return; // vertical divider bars (split direction horizontal)
1846
+ const r = el.getBoundingClientRect();
1847
+ const xCenter = r.left + r.width / 2 - rootRect.left;
1848
+ targets.push(xCenter);
1849
+ });
1850
+ this.activeSnapAxis = 'x';
1851
+ this.activeSnapTargets = targets;
1852
+ this.renderSnapMarkersForDivider();
1853
+ }
1854
+ else {
1855
+ // Current bar is horizontal → snap Y to centers of other horizontal bars (no crossing check needed)
1856
+ allDividers.forEach((el) => {
1857
+ if (el === divider)
1858
+ return;
1859
+ const o = el.dataset['orientation'] ?? undefined;
1860
+ if (o !== 'vertical')
1861
+ return; // horizontal divider bars (split direction vertical)
1862
+ const r = el.getBoundingClientRect();
1863
+ const yCenter = r.top + r.height / 2 - rootRect.top;
1864
+ targets.push(yCenter);
1865
+ });
1866
+ this.activeSnapAxis = 'y';
1867
+ this.activeSnapTargets = targets;
1868
+ this.renderSnapMarkersForDivider();
1869
+ }
1870
+ }
1871
+ catch {
1872
+ this.activeSnapAxis = null;
1873
+ this.activeSnapTargets = [];
1874
+ this.clearSnapMarkers();
1875
+ }
1186
1876
  }
1187
1877
  onPointerMove(event) {
1878
+ if (this.cornerResizeState && event.pointerId === this.cornerResizeState.pointerId) {
1879
+ this.handleCornerResizeMove(event);
1880
+ }
1188
1881
  if (this.resizeState && event.pointerId === this.resizeState.pointerId) {
1189
1882
  const state = this.resizeState;
1190
1883
  const splitNode = this.resolveSplitNode(state.path);
1191
1884
  if (!splitNode) {
1192
1885
  return;
1193
1886
  }
1194
- const currentPos = state.orientation === 'horizontal' ? event.clientX : event.clientY;
1887
+ let currentPos = state.orientation === 'horizontal' ? event.clientX : event.clientY;
1888
+ // Localized axis snap near neighboring intersections
1889
+ const tol = 10;
1890
+ const rootRect = this.rootEl.getBoundingClientRect();
1891
+ if (this.activeSnapTargets.length) {
1892
+ if (state.orientation === 'horizontal' && this.activeSnapAxis === 'x') {
1893
+ // Vertical divider snapping along X
1894
+ let closest = Number.POSITIVE_INFINITY;
1895
+ let best = currentPos;
1896
+ const pointerX = event.clientX;
1897
+ this.activeSnapTargets.forEach((sx) => {
1898
+ const px = rootRect.left + sx;
1899
+ const d = Math.abs(pointerX - px);
1900
+ if (d < closest) {
1901
+ closest = d;
1902
+ best = px;
1903
+ }
1904
+ });
1905
+ if (closest <= tol)
1906
+ currentPos = best;
1907
+ this.renderSnapMarkersForDivider();
1908
+ }
1909
+ else if (state.orientation === 'vertical' && this.activeSnapAxis === 'y') {
1910
+ // Horizontal divider snapping along Y
1911
+ let closest = Number.POSITIVE_INFINITY;
1912
+ let best = currentPos;
1913
+ const pointerY = event.clientY;
1914
+ this.activeSnapTargets.forEach((sy) => {
1915
+ const py = rootRect.top + sy;
1916
+ const d = Math.abs(pointerY - py);
1917
+ if (d < closest) {
1918
+ closest = d;
1919
+ best = py;
1920
+ }
1921
+ });
1922
+ if (closest <= tol)
1923
+ currentPos = best;
1924
+ this.renderSnapMarkersForDivider();
1925
+ }
1926
+ }
1195
1927
  const delta = currentPos - state.startPos;
1196
1928
  const minSize = 48;
1197
1929
  const pairTotal = state.beforeSize + state.afterSize;
1198
- let newBefore = Math.min(Math.max(state.beforeSize + delta, minSize), pairTotal - minSize);
1930
+ let newBefore = state.beforeSize + delta;
1931
+ // Optional snap with Shift
1932
+ if (event.shiftKey && pairTotal > 0) {
1933
+ const ratios = [1 / 3, 1 / 2, 2 / 3];
1934
+ const target = newBefore / pairTotal;
1935
+ let best = ratios[0];
1936
+ let bestDist = Math.abs(target - best);
1937
+ for (let i = 1; i < ratios.length; i++) {
1938
+ const d = Math.abs(target - ratios[i]);
1939
+ if (d < bestDist) {
1940
+ best = ratios[i];
1941
+ bestDist = d;
1942
+ }
1943
+ }
1944
+ newBefore = best * pairTotal;
1945
+ }
1946
+ newBefore = Math.min(Math.max(newBefore, minSize), pairTotal - minSize);
1199
1947
  let newAfter = pairTotal - newBefore;
1200
1948
  if (!Number.isFinite(newBefore) || !Number.isFinite(newAfter)) {
1201
1949
  return;
@@ -1226,11 +1974,17 @@ class MintDockManagerElement extends HTMLElement {
1226
1974
  }
1227
1975
  }
1228
1976
  onPointerUp(event) {
1977
+ if (this.cornerResizeState && event.pointerId === this.cornerResizeState.pointerId) {
1978
+ this.endCornerResize(event.pointerId);
1979
+ }
1229
1980
  if (this.resizeState && event.pointerId === this.resizeState.pointerId) {
1230
1981
  const divider = this.resizeState.divider;
1231
1982
  divider.dataset['resizing'] = 'false';
1232
1983
  divider.releasePointerCapture(this.resizeState.pointerId);
1233
1984
  this.resizeState = null;
1985
+ this.scheduleRenderIntersectionHandles();
1986
+ this.activeSnapAxis = null;
1987
+ this.activeSnapTargets = [];
1234
1988
  }
1235
1989
  if (this.floatingDragState && event.pointerId === this.floatingDragState.pointerId) {
1236
1990
  this.endFloatingDrag(event.pointerId);
@@ -3159,10 +3913,10 @@ class BsDockManagerComponent {
3159
3913
  cloneLayout(layout) {
3160
3914
  return JSON.parse(JSON.stringify(layout));
3161
3915
  }
3162
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockManagerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
3163
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.7", type: BsDockManagerComponent, isStandalone: false, selector: "bs-dock-manager", inputs: { layout: "layout" }, outputs: { layoutChange: "layoutChange", layoutSnapshotChange: "layoutSnapshotChange" }, queries: [{ propertyName: "panes", predicate: BsDockPaneComponent }], viewQueries: [{ propertyName: "managerRef", first: true, predicate: ["manager"], descendants: true, static: true }], ngImport: i0, template: "<mint-dock-manager\n #manager\n class=\"bs-dock-manager\"\n [attr.layout]=\"layoutString\"\n (dock-layout-changed)=\"onLayoutChanged($event)\"\n>\n <ng-container *ngFor=\"let pane of panes; trackBy: trackByPane\">\n <div class=\"bs-dock-pane\" [attr.slot]=\"pane.name\">\n <ng-container *ngTemplateOutlet=\"pane.template\"></ng-container>\n </div>\n </ng-container>\n</mint-dock-manager>\n", styles: [":host{display:block;width:100%;height:100%}.bs-dock-manager{display:block;width:100%;height:100%}.bs-dock-pane{display:contents}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3916
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockManagerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
3917
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.13", type: BsDockManagerComponent, isStandalone: false, selector: "bs-dock-manager", inputs: { layout: "layout" }, outputs: { layoutChange: "layoutChange", layoutSnapshotChange: "layoutSnapshotChange" }, queries: [{ propertyName: "panes", predicate: BsDockPaneComponent }], viewQueries: [{ propertyName: "managerRef", first: true, predicate: ["manager"], descendants: true, static: true }], ngImport: i0, template: "<mint-dock-manager\n #manager\n class=\"bs-dock-manager\"\n [attr.layout]=\"layoutString\"\n (dock-layout-changed)=\"onLayoutChanged($event)\"\n>\n <ng-container *ngFor=\"let pane of panes; trackBy: trackByPane\">\n <div class=\"bs-dock-pane\" [attr.slot]=\"pane.name\">\n <ng-container *ngTemplateOutlet=\"pane.template\"></ng-container>\n </div>\n </ng-container>\n</mint-dock-manager>\n", styles: [":host{display:block;width:100%;height:100%}.bs-dock-manager{display:block;width:100%;height:100%}.bs-dock-pane{display:contents}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3164
3918
  }
3165
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockManagerComponent, decorators: [{
3919
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockManagerComponent, decorators: [{
3166
3920
  type: Component,
3167
3921
  args: [{ selector: 'bs-dock-manager', standalone: false, changeDetection: ChangeDetectionStrategy.OnPush, template: "<mint-dock-manager\n #manager\n class=\"bs-dock-manager\"\n [attr.layout]=\"layoutString\"\n (dock-layout-changed)=\"onLayoutChanged($event)\"\n>\n <ng-container *ngFor=\"let pane of panes; trackBy: trackByPane\">\n <div class=\"bs-dock-pane\" [attr.slot]=\"pane.name\">\n <ng-container *ngTemplateOutlet=\"pane.template\"></ng-container>\n </div>\n </ng-container>\n</mint-dock-manager>\n", styles: [":host{display:block;width:100%;height:100%}.bs-dock-manager{display:block;width:100%;height:100%}.bs-dock-pane{display:contents}\n"] }]
3168
3922
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: Document, decorators: [{
@@ -3183,11 +3937,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
3183
3937
  }] } });
3184
3938
 
3185
3939
  class BsDockModule {
3186
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3187
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.7", ngImport: i0, type: BsDockModule, declarations: [BsDockManagerComponent, BsDockPaneComponent], imports: [CommonModule], exports: [BsDockManagerComponent, BsDockPaneComponent] }); }
3188
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockModule, imports: [CommonModule] }); }
3940
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3941
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.13", ngImport: i0, type: BsDockModule, declarations: [BsDockManagerComponent, BsDockPaneComponent], imports: [CommonModule], exports: [BsDockManagerComponent, BsDockPaneComponent] }); }
3942
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockModule, imports: [CommonModule] }); }
3189
3943
  }
3190
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: BsDockModule, decorators: [{
3944
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: BsDockModule, decorators: [{
3191
3945
  type: NgModule,
3192
3946
  args: [{
3193
3947
  declarations: [BsDockManagerComponent, BsDockPaneComponent],