@peter.prib/renderengine 0.2.2 → 0.2.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.
@@ -7041,6 +7041,21 @@
7041
7041
  };
7042
7042
  }
7043
7043
 
7044
+ /**
7045
+ * Where a submenu opens beside its item (the item's box `{left, right, top}`),
7046
+ * to be all inside the window: to the right, or to the left when the right
7047
+ * would pass the edge (and there is more room on the left); level with the
7048
+ * item, or lifted by as much as it would pass the bottom. `top` is relative
7049
+ * to the item, as the submenu is positioned.
7050
+ */
7051
+ static submenuPlacement(item, width, height, windowWidth, windowHeight, margin = 4) {
7052
+ const fitsRight = item.right + width + margin <= windowWidth;
7053
+ const side = fitsRight || item.left < windowWidth - item.right ? 'right' : 'left';
7054
+ const top = -4;
7055
+ const overflow = item.top + top + height + margin - windowHeight;
7056
+ return { side, top: overflow > 0 ? Math.max(top - overflow, margin - item.top) : top };
7057
+ }
7058
+
7044
7059
  static close() {
7045
7060
  const open = PopupMenu._open;
7046
7061
  if (!open) return;
@@ -7078,7 +7093,16 @@
7078
7093
  ...base,
7079
7094
  attrs: { ...base.attrs, 'aria-haspopup': 'menu' },
7080
7095
  on: {
7081
- mouseenter: (e) => { e.currentTarget.style.background = hover; sub.style.display = 'block'; },
7096
+ mouseenter: (e) => {
7097
+ e.currentTarget.style.background = hover;
7098
+ sub.style.display = 'block';
7099
+ // Opened near the window's right or bottom edge, it opens left, or lifts.
7100
+ const win = e.currentTarget.ownerDocument.defaultView;
7101
+ const box = sub.getBoundingClientRect?.();
7102
+ if (!win || !box?.width) return;
7103
+ const at = PopupMenu.submenuPlacement(e.currentTarget.getBoundingClientRect(), box.width, box.height, win.innerWidth, win.innerHeight);
7104
+ Object.assign(sub.style, at.side === 'left' ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' }, { top: `${at.top}px` });
7105
+ },
7082
7106
  mouseleave: (e) => { e.currentTarget.style.background = ''; sub.style.display = 'none'; },
7083
7107
  click: (e) => e.stopPropagation(),
7084
7108
  },
@@ -4277,6 +4277,21 @@
4277
4277
  };
4278
4278
  }
4279
4279
 
4280
+ /**
4281
+ * Where a submenu opens beside its item (the item's box `{left, right, top}`),
4282
+ * to be all inside the window: to the right, or to the left when the right
4283
+ * would pass the edge (and there is more room on the left); level with the
4284
+ * item, or lifted by as much as it would pass the bottom. `top` is relative
4285
+ * to the item, as the submenu is positioned.
4286
+ */
4287
+ static submenuPlacement(item, width, height, windowWidth, windowHeight, margin = 4) {
4288
+ const fitsRight = item.right + width + margin <= windowWidth;
4289
+ const side = fitsRight || item.left < windowWidth - item.right ? 'right' : 'left';
4290
+ const top = -4;
4291
+ const overflow = item.top + top + height + margin - windowHeight;
4292
+ return { side, top: overflow > 0 ? Math.max(top - overflow, margin - item.top) : top };
4293
+ }
4294
+
4280
4295
  static close() {
4281
4296
  const open = PopupMenu._open;
4282
4297
  if (!open) return;
@@ -4314,7 +4329,16 @@
4314
4329
  ...base,
4315
4330
  attrs: { ...base.attrs, 'aria-haspopup': 'menu' },
4316
4331
  on: {
4317
- mouseenter: (e) => { e.currentTarget.style.background = hover; sub.style.display = 'block'; },
4332
+ mouseenter: (e) => {
4333
+ e.currentTarget.style.background = hover;
4334
+ sub.style.display = 'block';
4335
+ // Opened near the window's right or bottom edge, it opens left, or lifts.
4336
+ const win = e.currentTarget.ownerDocument.defaultView;
4337
+ const box = sub.getBoundingClientRect?.();
4338
+ if (!win || !box?.width) return;
4339
+ const at = PopupMenu.submenuPlacement(e.currentTarget.getBoundingClientRect(), box.width, box.height, win.innerWidth, win.innerHeight);
4340
+ Object.assign(sub.style, at.side === 'left' ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' }, { top: `${at.top}px` });
4341
+ },
4318
4342
  mouseleave: (e) => { e.currentTarget.style.background = ''; sub.style.display = 'none'; },
4319
4343
  click: (e) => e.stopPropagation(),
4320
4344
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peter.prib/renderengine",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "ArchiMate modeller, diagram editor and chart library for the browser, with no runtime dependencies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -48,6 +48,21 @@ export class PopupMenu {
48
48
  };
49
49
  }
50
50
 
51
+ /**
52
+ * Where a submenu opens beside its item (the item's box `{left, right, top}`),
53
+ * to be all inside the window: to the right, or to the left when the right
54
+ * would pass the edge (and there is more room on the left); level with the
55
+ * item, or lifted by as much as it would pass the bottom. `top` is relative
56
+ * to the item, as the submenu is positioned.
57
+ */
58
+ static submenuPlacement(item, width, height, windowWidth, windowHeight, margin = 4) {
59
+ const fitsRight = item.right + width + margin <= windowWidth;
60
+ const side = fitsRight || item.left < windowWidth - item.right ? 'right' : 'left';
61
+ const top = -4;
62
+ const overflow = item.top + top + height + margin - windowHeight;
63
+ return { side, top: overflow > 0 ? Math.max(top - overflow, margin - item.top) : top };
64
+ }
65
+
51
66
  static close() {
52
67
  const open = PopupMenu._open;
53
68
  if (!open) return;
@@ -85,7 +100,16 @@ export class PopupMenu {
85
100
  ...base,
86
101
  attrs: { ...base.attrs, 'aria-haspopup': 'menu' },
87
102
  on: {
88
- mouseenter: (e) => { e.currentTarget.style.background = hover; sub.style.display = 'block'; },
103
+ mouseenter: (e) => {
104
+ e.currentTarget.style.background = hover;
105
+ sub.style.display = 'block';
106
+ // Opened near the window's right or bottom edge, it opens left, or lifts.
107
+ const win = e.currentTarget.ownerDocument.defaultView;
108
+ const box = sub.getBoundingClientRect?.();
109
+ if (!win || !box?.width) return;
110
+ const at = PopupMenu.submenuPlacement(e.currentTarget.getBoundingClientRect(), box.width, box.height, win.innerWidth, win.innerHeight);
111
+ Object.assign(sub.style, at.side === 'left' ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' }, { top: `${at.top}px` });
112
+ },
89
113
  mouseleave: (e) => { e.currentTarget.style.background = ''; sub.style.display = 'none'; },
90
114
  click: (e) => e.stopPropagation(),
91
115
  },