@tmagic/stage 1.8.0-beta.13 → 1.8.0-beta.15

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.
@@ -1,5 +1,5 @@
1
1
  import { Mode, ZIndex } from "./const.js";
2
- import { getScrollParent, isFixedParent } from "./util.js";
2
+ import { getScrollParent, isFixedParent, scrollElementIntoView } from "./util.js";
3
3
  import Rule from "./Rule.js";
4
4
  import { createDiv, getDocument, injectStyle } from "@tmagic/core";
5
5
  //#region packages/stage/src/StageMask.ts
@@ -118,8 +118,8 @@ var StageMask = class extends Rule {
118
118
  this.scrollIntoView(scrollParent);
119
119
  return;
120
120
  }
121
- el.scrollIntoView();
122
121
  if (!this.pageScrollParent) return;
122
+ scrollElementIntoView(el, this.pageScrollParent);
123
123
  this.scrollLeft = this.pageScrollParent.scrollLeft;
124
124
  this.scrollTop = this.pageScrollParent.scrollTop;
125
125
  this.scroll();
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AbleActionEventType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, PAGE_CLASS, RenderType, SELECTED_CLASS, SelectStatus, StageDragStatus, ZIndex } from "./const.js";
2
- import { addSelectedClassName, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up } from "./util.js";
2
+ import { addSelectedClassName, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, scrollElementIntoView, up } from "./util.js";
3
3
  import MoveableActionsAble_default from "./MoveableActionsAble.js";
4
4
  import StageDragResize from "./StageDragResize.js";
5
5
  import StageMask from "./StageMask.js";
@@ -9,4 +9,4 @@ export * from "moveable";
9
9
  //#region packages/stage/src/index.ts
10
10
  var src_default = StageCore;
11
11
  //#endregion
12
- export { AbleActionEventType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, MoveableActionsAble_default as MoveableActionsAble, PAGE_CLASS, RenderType, SELECTED_CLASS, SelectStatus, StageDragResize, StageDragStatus, StageMask, StageRender, ZIndex, addSelectedClassName, src_default as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };
12
+ export { AbleActionEventType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, MoveableActionsAble_default as MoveableActionsAble, PAGE_CLASS, RenderType, SELECTED_CLASS, SelectStatus, StageDragResize, StageDragStatus, StageMask, StageRender, ZIndex, addSelectedClassName, src_default as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, scrollElementIntoView, up };
@@ -1,4 +1,4 @@
1
1
  //#region packages/stage/src/moveable-able.css?raw
2
- var moveable_able_default = ".moveable-button {\n width: 20px;\n height: 20px;\n background: #4af;\n border-radius: 4px;\n appearance: none;\n border: 0;\n color: white;\n font-size: 12px;\n font-weight: bold;\n margin-left: 2px;\n position: relative;\n cursor: pointer;\n}\n.moveable-remove-button:before, .moveable-remove-button:after {\n content: \"\";\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%) rotate(45deg);\n width: 14px;\n height: 2px;\n background: #fff;\n border-radius: 1px;\n cursor: pointer;\n}\n.moveable-remove-button:after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n\n.moveable-select-parent-arrow-top-icon {\n transform: rotateZ(-45deg);\n width: 4px;\n height: 4px;\n border-color: #fff;\n border-width: 2px 2px 0 0;\n border-style: solid;\n position: absolute;\n left: 4px;\n top: 4px;\n}\n\n.moveable-select-parent-arrow-body-icon {\n width: 7px;\n height: 11px;\n border-color: #fff;\n border-width: 0 0 2px 2px;\n border-style: solid;\n}\n\n.moveable-drag-area-button {\n cursor: move;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon {\n width: 2px;\n height: 2px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {\n transform: rotateZ(-45deg) translateX(-50%);\n left: 50%;\n top: 3px;\n transform-origin: left;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {\n transform: rotateZ(135deg) translateX(-50%);\n transform-origin: left;\n left: 50%;\n top: auto;\n bottom: 3px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {\n transform: rotateZ(45deg) translateY(-50%);\n transform-origin: top;\n right: 3px;\n left: auto;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {\n transform: rotateZ(235deg) translateY(-50%);\n transform-origin: top;\n left: 3px;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {\n width: 2px;\n height: 11px;\n background-color: #fff;\n position: absolute;\n transform: translateX(-50%);\n left: 50%;\n top: 4px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {\n width: 11px;\n height: 2px;\n background-color: #fff;\n position: absolute;\n transform: translateY(-50%);\n left: 4px;\n top: 50%;;\n}\n\n.moveable-rerender-button img {\n position: absolute;\n left: 2px;\n top: 2px;\n}\n";
2
+ var moveable_able_default = ".moveable-button {\n width: 20px;\n height: 20px;\n background: var(--el-color-primary,#4af);\n border-radius: 4px;\n appearance: none;\n border: 0;\n color: white;\n font-size: 12px;\n font-weight: bold;\n margin-left: 2px;\n position: relative;\n cursor: pointer;\n}\n.moveable-remove-button:before, .moveable-remove-button:after {\n content: \"\";\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%) rotate(45deg);\n width: 14px;\n height: 2px;\n background: #fff;\n border-radius: 1px;\n cursor: pointer;\n}\n.moveable-remove-button:after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n\n.moveable-select-parent-arrow-top-icon {\n transform: rotateZ(-45deg);\n width: 4px;\n height: 4px;\n border-color: #fff;\n border-width: 2px 2px 0 0;\n border-style: solid;\n position: absolute;\n left: 4px;\n top: 4px;\n}\n\n.moveable-select-parent-arrow-body-icon {\n width: 7px;\n height: 11px;\n border-color: #fff;\n border-width: 0 0 2px 2px;\n border-style: solid;\n}\n\n.moveable-drag-area-button {\n cursor: move;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon {\n width: 2px;\n height: 2px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {\n transform: rotateZ(-45deg) translateX(-50%);\n left: 50%;\n top: 3px;\n transform-origin: left;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {\n transform: rotateZ(135deg) translateX(-50%);\n transform-origin: left;\n left: 50%;\n top: auto;\n bottom: 3px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {\n transform: rotateZ(45deg) translateY(-50%);\n transform-origin: top;\n right: 3px;\n left: auto;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {\n transform: rotateZ(235deg) translateY(-50%);\n transform-origin: top;\n left: 3px;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {\n width: 2px;\n height: 11px;\n background-color: #fff;\n position: absolute;\n transform: translateX(-50%);\n left: 50%;\n top: 4px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {\n width: 11px;\n height: 2px;\n background-color: #fff;\n position: absolute;\n transform: translateY(-50%);\n left: 4px;\n top: 50%;;\n}\n\n.moveable-rerender-button img {\n position: absolute;\n left: 2px;\n top: 2px;\n}\n";
3
3
  //#endregion
4
4
  export { moveable_able_default as default };
package/dist/es/util.js CHANGED
@@ -91,6 +91,33 @@ var getScrollParent = (element, includeHidden = false) => {
91
91
  }
92
92
  return null;
93
93
  };
94
+ /**
95
+ * 将元素滚动到指定滚动容器的可视区域内(仅垂直方向,滚动最小距离)
96
+ * @description 与原生 scrollIntoView 不同,只滚动指定的容器自身,不会连带滚动外层祖先滚动容器,
97
+ * 避免编辑器画布外层容器(如 stage 外层使用 transform 模拟滚动的容器)被浏览器自动滚动导致整个画布位移
98
+ * @param el 目标元素
99
+ * @param container 滚动容器
100
+ */
101
+ var scrollElementIntoView = (el, container) => {
102
+ const { ownerDocument } = container;
103
+ const win = ownerDocument?.defaultView;
104
+ if (!ownerDocument || !win) return;
105
+ const elRect = el.getBoundingClientRect();
106
+ let viewTop = 0;
107
+ let viewHeight = 0;
108
+ if (container === ownerDocument.documentElement || container === ownerDocument.body) {
109
+ viewTop = 0;
110
+ viewHeight = win.innerHeight;
111
+ } else {
112
+ viewTop = container.getBoundingClientRect().top + container.clientTop;
113
+ viewHeight = container.clientHeight;
114
+ }
115
+ const viewBottom = viewTop + viewHeight;
116
+ let delta = 0;
117
+ if (elRect.top < viewTop) delta = elRect.top - viewTop;
118
+ else if (elRect.bottom > viewBottom) delta = Math.min(elRect.bottom - viewBottom, elRect.top - viewTop);
119
+ if (delta !== 0) container.scrollTop += delta;
120
+ };
94
121
  var removeSelectedClassName = (doc) => {
95
122
  const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
96
123
  if (oldEl) {
@@ -189,4 +216,4 @@ var getBorderWidth = (el) => {
189
216
  };
190
217
  };
191
218
  //#endregion
192
- export { addSelectedClassName, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };
219
+ export { addSelectedClassName, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, scrollElementIntoView, up };
@@ -2382,6 +2382,33 @@
2382
2382
  }
2383
2383
  return null;
2384
2384
  };
2385
+ /**
2386
+ * 将元素滚动到指定滚动容器的可视区域内(仅垂直方向,滚动最小距离)
2387
+ * @description 与原生 scrollIntoView 不同,只滚动指定的容器自身,不会连带滚动外层祖先滚动容器,
2388
+ * 避免编辑器画布外层容器(如 stage 外层使用 transform 模拟滚动的容器)被浏览器自动滚动导致整个画布位移
2389
+ * @param el 目标元素
2390
+ * @param container 滚动容器
2391
+ */
2392
+ var scrollElementIntoView = (el, container) => {
2393
+ const { ownerDocument } = container;
2394
+ const win = ownerDocument?.defaultView;
2395
+ if (!ownerDocument || !win) return;
2396
+ const elRect = el.getBoundingClientRect();
2397
+ let viewTop = 0;
2398
+ let viewHeight = 0;
2399
+ if (container === ownerDocument.documentElement || container === ownerDocument.body) {
2400
+ viewTop = 0;
2401
+ viewHeight = win.innerHeight;
2402
+ } else {
2403
+ viewTop = container.getBoundingClientRect().top + container.clientTop;
2404
+ viewHeight = container.clientHeight;
2405
+ }
2406
+ const viewBottom = viewTop + viewHeight;
2407
+ let delta = 0;
2408
+ if (elRect.top < viewTop) delta = elRect.top - viewTop;
2409
+ else if (elRect.bottom > viewBottom) delta = Math.min(elRect.bottom - viewBottom, elRect.top - viewTop);
2410
+ if (delta !== 0) container.scrollTop += delta;
2411
+ };
2385
2412
  var removeSelectedClassName = (doc) => {
2386
2413
  const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
2387
2414
  if (oldEl) {
@@ -2819,7 +2846,7 @@
2819
2846
  };
2820
2847
  //#endregion
2821
2848
  //#region packages/stage/src/moveable-able.css?raw
2822
- var moveable_able_default = ".moveable-button {\n width: 20px;\n height: 20px;\n background: #4af;\n border-radius: 4px;\n appearance: none;\n border: 0;\n color: white;\n font-size: 12px;\n font-weight: bold;\n margin-left: 2px;\n position: relative;\n cursor: pointer;\n}\n.moveable-remove-button:before, .moveable-remove-button:after {\n content: \"\";\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%) rotate(45deg);\n width: 14px;\n height: 2px;\n background: #fff;\n border-radius: 1px;\n cursor: pointer;\n}\n.moveable-remove-button:after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n\n.moveable-select-parent-arrow-top-icon {\n transform: rotateZ(-45deg);\n width: 4px;\n height: 4px;\n border-color: #fff;\n border-width: 2px 2px 0 0;\n border-style: solid;\n position: absolute;\n left: 4px;\n top: 4px;\n}\n\n.moveable-select-parent-arrow-body-icon {\n width: 7px;\n height: 11px;\n border-color: #fff;\n border-width: 0 0 2px 2px;\n border-style: solid;\n}\n\n.moveable-drag-area-button {\n cursor: move;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon {\n width: 2px;\n height: 2px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {\n transform: rotateZ(-45deg) translateX(-50%);\n left: 50%;\n top: 3px;\n transform-origin: left;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {\n transform: rotateZ(135deg) translateX(-50%);\n transform-origin: left;\n left: 50%;\n top: auto;\n bottom: 3px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {\n transform: rotateZ(45deg) translateY(-50%);\n transform-origin: top;\n right: 3px;\n left: auto;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {\n transform: rotateZ(235deg) translateY(-50%);\n transform-origin: top;\n left: 3px;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {\n width: 2px;\n height: 11px;\n background-color: #fff;\n position: absolute;\n transform: translateX(-50%);\n left: 50%;\n top: 4px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {\n width: 11px;\n height: 2px;\n background-color: #fff;\n position: absolute;\n transform: translateY(-50%);\n left: 4px;\n top: 50%;;\n}\n\n.moveable-rerender-button img {\n position: absolute;\n left: 2px;\n top: 2px;\n}\n";
2849
+ var moveable_able_default = ".moveable-button {\n width: 20px;\n height: 20px;\n background: var(--el-color-primary,#4af);\n border-radius: 4px;\n appearance: none;\n border: 0;\n color: white;\n font-size: 12px;\n font-weight: bold;\n margin-left: 2px;\n position: relative;\n cursor: pointer;\n}\n.moveable-remove-button:before, .moveable-remove-button:after {\n content: \"\";\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%) rotate(45deg);\n width: 14px;\n height: 2px;\n background: #fff;\n border-radius: 1px;\n cursor: pointer;\n}\n.moveable-remove-button:after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n\n.moveable-select-parent-arrow-top-icon {\n transform: rotateZ(-45deg);\n width: 4px;\n height: 4px;\n border-color: #fff;\n border-width: 2px 2px 0 0;\n border-style: solid;\n position: absolute;\n left: 4px;\n top: 4px;\n}\n\n.moveable-select-parent-arrow-body-icon {\n width: 7px;\n height: 11px;\n border-color: #fff;\n border-width: 0 0 2px 2px;\n border-style: solid;\n}\n\n.moveable-drag-area-button {\n cursor: move;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon {\n width: 2px;\n height: 2px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {\n transform: rotateZ(-45deg) translateX(-50%);\n left: 50%;\n top: 3px;\n transform-origin: left;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {\n transform: rotateZ(135deg) translateX(-50%);\n transform-origin: left;\n left: 50%;\n top: auto;\n bottom: 3px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {\n transform: rotateZ(45deg) translateY(-50%);\n transform-origin: top;\n right: 3px;\n left: auto;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {\n transform: rotateZ(235deg) translateY(-50%);\n transform-origin: top;\n left: 3px;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {\n width: 2px;\n height: 11px;\n background-color: #fff;\n position: absolute;\n transform: translateX(-50%);\n left: 50%;\n top: 4px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {\n width: 11px;\n height: 2px;\n background-color: #fff;\n position: absolute;\n transform: translateY(-50%);\n left: 4px;\n top: 50%;;\n}\n\n.moveable-rerender-button img {\n position: absolute;\n left: 2px;\n top: 2px;\n}\n";
2823
2850
  //#endregion
2824
2851
  //#region packages/stage/src/MoveableActionsAble.ts
2825
2852
  var MoveableActionsAble_default = (handler, customizedButton = []) => ({
@@ -4375,8 +4402,8 @@
4375
4402
  this.scrollIntoView(scrollParent);
4376
4403
  return;
4377
4404
  }
4378
- el.scrollIntoView();
4379
4405
  if (!this.pageScrollParent) return;
4406
+ scrollElementIntoView(el, this.pageScrollParent);
4380
4407
  this.scrollLeft = this.pageScrollParent.scrollLeft;
4381
4408
  this.scrollTop = this.pageScrollParent.scrollTop;
4382
4409
  this.scroll();
@@ -5101,6 +5128,7 @@
5101
5128
  exports.isRelative = isRelative;
5102
5129
  exports.isStatic = isStatic;
5103
5130
  exports.removeSelectedClassName = removeSelectedClassName;
5131
+ exports.scrollElementIntoView = scrollElementIntoView;
5104
5132
  exports.up = up;
5105
5133
  Object.keys(moveable).forEach(function(k) {
5106
5134
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.13",
2
+ "version": "1.8.0-beta.15",
3
3
  "name": "@tmagic/stage",
4
4
  "type": "module",
5
5
  "sideEffects": false,
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "typescript": "^6.0.3",
47
- "@tmagic/core": "1.8.0-beta.13"
47
+ "@tmagic/core": "1.8.0-beta.15"
48
48
  },
49
49
  "peerDependenciesMeta": {
50
50
  "typescript": {
package/src/StageMask.ts CHANGED
@@ -21,7 +21,7 @@ import { createDiv, getDocument, injectStyle } from '@tmagic/core';
21
21
  import { Mode, ZIndex } from './const';
22
22
  import Rule from './Rule';
23
23
  import type { MaskEvents, RuleOptions } from './types';
24
- import { getScrollParent, isFixedParent } from './util';
24
+ import { getScrollParent, isFixedParent, scrollElementIntoView } from './util';
25
25
 
26
26
  const wrapperClassName = 'editor-mask-wrapper';
27
27
 
@@ -165,10 +165,12 @@ export default class StageMask extends Rule {
165
165
  return;
166
166
  }
167
167
 
168
- el.scrollIntoView();
169
-
170
168
  if (!this.pageScrollParent) return;
171
169
 
170
+ // 不使用原生 scrollIntoView,避免编辑器画布外层滚动容器被浏览器连带滚动导致整个 stage 位移,
171
+ // 只滚动页面所在的滚动容器
172
+ scrollElementIntoView(el, this.pageScrollParent);
173
+
172
174
  this.scrollLeft = this.pageScrollParent.scrollLeft;
173
175
  this.scrollTop = this.pageScrollParent.scrollTop;
174
176
 
@@ -1,7 +1,7 @@
1
1
  .moveable-button {
2
2
  width: 20px;
3
3
  height: 20px;
4
- background: #4af;
4
+ background: var(--el-color-primary,#4af);
5
5
  border-radius: 4px;
6
6
  appearance: none;
7
7
  border: 0;
package/src/util.ts CHANGED
@@ -141,6 +141,48 @@ export const getScrollParent = (element: HTMLElement, includeHidden = false): HT
141
141
  return null;
142
142
  };
143
143
 
144
+ /**
145
+ * 将元素滚动到指定滚动容器的可视区域内(仅垂直方向,滚动最小距离)
146
+ * @description 与原生 scrollIntoView 不同,只滚动指定的容器自身,不会连带滚动外层祖先滚动容器,
147
+ * 避免编辑器画布外层容器(如 stage 外层使用 transform 模拟滚动的容器)被浏览器自动滚动导致整个画布位移
148
+ * @param el 目标元素
149
+ * @param container 滚动容器
150
+ */
151
+ export const scrollElementIntoView = (el: Element, container: HTMLElement): void => {
152
+ const { ownerDocument } = container;
153
+ const win = ownerDocument?.defaultView;
154
+ if (!ownerDocument || !win) return;
155
+
156
+ const elRect = el.getBoundingClientRect();
157
+
158
+ let viewTop = 0;
159
+ let viewHeight = 0;
160
+
161
+ if (container === ownerDocument.documentElement || container === ownerDocument.body) {
162
+ // 文档滚动元素的可视区域为窗口视口
163
+ viewTop = 0;
164
+ viewHeight = win.innerHeight;
165
+ } else {
166
+ const containerRect = container.getBoundingClientRect();
167
+ viewTop = containerRect.top + container.clientTop;
168
+ viewHeight = container.clientHeight;
169
+ }
170
+
171
+ const viewBottom = viewTop + viewHeight;
172
+
173
+ let delta = 0;
174
+ if (elRect.top < viewTop) {
175
+ delta = elRect.top - viewTop;
176
+ } else if (elRect.bottom > viewBottom) {
177
+ // 元素高度超过可视区域高度时,优先保证元素顶部可见
178
+ delta = Math.min(elRect.bottom - viewBottom, elRect.top - viewTop);
179
+ }
180
+
181
+ if (delta !== 0) {
182
+ container.scrollTop += delta;
183
+ }
184
+ };
185
+
144
186
  export const removeSelectedClassName = (doc: Document) => {
145
187
  const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
146
188
 
package/types/index.d.ts CHANGED
@@ -1104,6 +1104,14 @@ declare const isFixed: (style: {
1104
1104
  declare const isFixedParent: (el: Element) => boolean;
1105
1105
  declare const getMode: (el: Element) => Mode;
1106
1106
  declare const getScrollParent: (element: HTMLElement, includeHidden?: boolean) => HTMLElement | null;
1107
+ /**
1108
+ * 将元素滚动到指定滚动容器的可视区域内(仅垂直方向,滚动最小距离)
1109
+ * @description 与原生 scrollIntoView 不同,只滚动指定的容器自身,不会连带滚动外层祖先滚动容器,
1110
+ * 避免编辑器画布外层容器(如 stage 外层使用 transform 模拟滚动的容器)被浏览器自动滚动导致整个画布位移
1111
+ * @param el 目标元素
1112
+ * @param container 滚动容器
1113
+ */
1114
+ declare const scrollElementIntoView: (el: Element, container: HTMLElement) => void;
1107
1115
  declare const removeSelectedClassName: (doc: Document) => void;
1108
1116
  declare const addSelectedClassName: (el: Element, doc: Document) => void;
1109
1117
  /**
@@ -1149,4 +1157,4 @@ declare const _default: (handler: (type: AbleActionEventType) => void, customize
1149
1157
  render(moveable: MoveableManagerInterface<any, any>, React: Renderer): any;
1150
1158
  };
1151
1159
  //#endregion
1152
- export { AbleActionEventType, AbleCustomizedButton, ActionManagerConfig, ActionManagerEvents, CONTAINER_HIGHLIGHT_CLASS_NAME, CanDropIn, CanSelect, ContainerHighlightType, CoreEvents, CustomizeMoveableOptions, CustomizeMoveableOptionsCallbackConfig, CustomizeMoveableOptionsFunction, CustomizeRender, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, DelayedMarkContainer, DrEvents, DragResizeHelperConfig, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GetElementsFromPoint, GetRenderDocument, GetRootContainer, GetTargetElement, GuidesEventData, type GuidesOptions, GuidesType, HIGHLIGHT_EL_ID_PREFIX, IsContainer, Magic, MarkContainerEnd, MaskEvents, Mode, MouseButton, _default as MoveableActionsAble, MoveableOptionsManagerConfig, MultiDrEvents, Offset, PAGE_CLASS, Point, Rect, RemoveData, RemoveEventData, RenderEvents, RenderType, RuleOptions, Runtime, RuntimeWindow, SELECTED_CLASS, SelectStatus, SortEventData, StageCoreConfig, StageDragResize, StageDragResizeConfig, StageDragStatus, StageFlashHighlightConfig, StageHighlightConfig, StageMask, StageMaskConfig, StageMultiDragResizeConfig, StageRender, StageRenderConfig, TargetElement, TargetShadowConfig, UpdateData, UpdateDragEl, UpdateEventData, ZIndex, addSelectedClassName, StageCore as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };
1160
+ export { AbleActionEventType, AbleCustomizedButton, ActionManagerConfig, ActionManagerEvents, CONTAINER_HIGHLIGHT_CLASS_NAME, CanDropIn, CanSelect, ContainerHighlightType, CoreEvents, CustomizeMoveableOptions, CustomizeMoveableOptionsCallbackConfig, CustomizeMoveableOptionsFunction, CustomizeRender, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, DelayedMarkContainer, DrEvents, DragResizeHelperConfig, FLASH_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GetElementsFromPoint, GetRenderDocument, GetRootContainer, GetTargetElement, GuidesEventData, type GuidesOptions, GuidesType, HIGHLIGHT_EL_ID_PREFIX, IsContainer, Magic, MarkContainerEnd, MaskEvents, Mode, MouseButton, _default as MoveableActionsAble, MoveableOptionsManagerConfig, MultiDrEvents, Offset, PAGE_CLASS, Point, Rect, RemoveData, RemoveEventData, RenderEvents, RenderType, RuleOptions, Runtime, RuntimeWindow, SELECTED_CLASS, SelectStatus, SortEventData, StageCoreConfig, StageDragResize, StageDragResizeConfig, StageDragStatus, StageFlashHighlightConfig, StageHighlightConfig, StageMask, StageMaskConfig, StageMultiDragResizeConfig, StageRender, StageRenderConfig, TargetElement, TargetShadowConfig, UpdateData, UpdateDragEl, UpdateEventData, ZIndex, addSelectedClassName, StageCore as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, scrollElementIntoView, up };