@sepveneto/free-dom 0.0.7 → 0.3.0

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.
package/dist/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* ../../../../../../tmp/tmp-23081-wnXhBVUmkYXa/core/src/style/index.css */
1
+ /* ../../../../../../../tmp/tmp-1812-xR8NNKBE7ahD/core/src/style/index.css */
2
2
  :root {
3
3
  --free-dom-theme: #4089ef;
4
4
  --free-dom-line: var(--free-dom-theme);
@@ -23,12 +23,14 @@
23
23
  }
24
24
  .free-dom__widget-wrapper {
25
25
  display: inline-block;
26
- position: absolute;
27
26
  border: 1px dashed transparent;
28
27
  transition: border-color 0.3s;
29
28
  box-sizing: content-box;
30
29
  user-select: none;
31
30
  }
31
+ .free-dom__widget-wrapper.is-absolute {
32
+ position: absolute;
33
+ }
32
34
  .free-dom__widget-wrapper.is-active,
33
35
  .free-dom__widget-wrapper.can-move:hover {
34
36
  border: 1px dashed var(--free-dom-dot-border);
@@ -38,6 +40,13 @@
38
40
  .free-dom__widget-wrapper:hover .free-dom__widget-dot {
39
41
  opacity: 1;
40
42
  }
43
+ .free-dom__widget-wrapper.is-active .free-dom__resizable-handler,
44
+ .free-dom__widget-wrapper:hover .free-dom__resizable-handler {
45
+ opacity: 1;
46
+ }
47
+ .free-dom__widget-wrapper.is-scale {
48
+ transition: none !important;
49
+ }
41
50
  .free-dom__widget-dot {
42
51
  opacity: 0;
43
52
  position: absolute;
@@ -49,3 +58,19 @@
49
58
  border: 1px solid var(--free-dom-dot-border);
50
59
  background-color: var(--free-dom-dot-border);
51
60
  }
61
+ .free-dom__resizable-handler {
62
+ opacity: 0;
63
+ position: absolute;
64
+ width: 6px;
65
+ height: 6px;
66
+ }
67
+ .free-dom__resizable-handler::after {
68
+ content: "";
69
+ position: absolute;
70
+ right: 3px;
71
+ bottom: 3px;
72
+ width: 5px;
73
+ height: 5px;
74
+ border-right: 2px solid rgba(0, 0, 0, 0.4);
75
+ border-bottom: 2px solid rgba(0, 0, 0, 0.4);
76
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,25 @@
1
1
  import * as vue_demi from 'vue-demi';
2
2
 
3
3
  declare const freeDom: vue_demi.DefineComponent<{
4
- customStyle: {
5
- type: vue_demi.PropType<Partial<vue_demi.CSSProperties>>;
6
- required: true;
4
+ x: {
5
+ type: NumberConstructor;
6
+ default: number;
7
+ };
8
+ y: {
9
+ type: NumberConstructor;
10
+ default: number;
11
+ };
12
+ width: {
13
+ type: NumberConstructor;
14
+ default: number;
15
+ };
16
+ height: {
17
+ type: NumberConstructor;
18
+ default: number;
19
+ };
20
+ absolute: {
21
+ type: BooleanConstructor;
22
+ default: undefined;
7
23
  };
8
24
  scale: {
9
25
  type: vue_demi.PropType<boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[]>;
@@ -19,6 +35,18 @@ declare const freeDom: vue_demi.DefineComponent<{
19
35
  type: NumberConstructor;
20
36
  default: undefined;
21
37
  };
38
+ handler: {
39
+ type: vue_demi.PropType<"dot" | "mark">;
40
+ default: undefined;
41
+ };
42
+ diagonal: {
43
+ type: BooleanConstructor;
44
+ default: undefined;
45
+ };
46
+ grid: {
47
+ type: vue_demi.PropType<[number, number]>;
48
+ default: undefined;
49
+ };
22
50
  }, {
23
51
  widgetRef: vue_demi.ShallowRef<any>;
24
52
  canMove: vue_demi.ComputedRef<boolean>;
@@ -28,13 +56,32 @@ declare const freeDom: vue_demi.DefineComponent<{
28
56
  canScale: vue_demi.ComputedRef<boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[] | undefined>;
29
57
  dots: vue_demi.ComputedRef<readonly ["t", "r", "l", "b", "lt", "lb", "rt", "rb"] | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[]>;
30
58
  active: vue_demi.Ref<boolean>;
59
+ isAbsolute: vue_demi.ComputedRef<boolean>;
60
+ isScale: vue_demi.Ref<boolean>;
61
+ handlerType: vue_demi.ComputedRef<"dot" | "mark">;
31
62
  getDotPos: (dot: string) => vue_demi.CSSProperties;
32
63
  onMousedown: (evt: MouseEvent) => void;
33
64
  onMousedownDot: (evt: MouseEvent, dot: string) => void;
34
- }, unknown, {}, {}, vue_demi.ComponentOptionsMixin, vue_demi.ComponentOptionsMixin, ("update:customStyle" | "select")[], "update:customStyle" | "select", vue_demi.VNodeProps & vue_demi.AllowedComponentProps & vue_demi.ComponentCustomProps, Readonly<vue_demi.ExtractPropTypes<{
35
- customStyle: {
36
- type: vue_demi.PropType<Partial<vue_demi.CSSProperties>>;
37
- required: true;
65
+ }, unknown, {}, {}, vue_demi.ComponentOptionsMixin, vue_demi.ComponentOptionsMixin, ("update:x" | "update:y" | "update:width" | "update:height" | "select")[], "update:x" | "update:y" | "update:width" | "update:height" | "select", vue_demi.VNodeProps & vue_demi.AllowedComponentProps & vue_demi.ComponentCustomProps, Readonly<vue_demi.ExtractPropTypes<{
66
+ x: {
67
+ type: NumberConstructor;
68
+ default: number;
69
+ };
70
+ y: {
71
+ type: NumberConstructor;
72
+ default: number;
73
+ };
74
+ width: {
75
+ type: NumberConstructor;
76
+ default: number;
77
+ };
78
+ height: {
79
+ type: NumberConstructor;
80
+ default: number;
81
+ };
82
+ absolute: {
83
+ type: BooleanConstructor;
84
+ default: undefined;
38
85
  };
39
86
  scale: {
40
87
  type: vue_demi.PropType<boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[]>;
@@ -50,17 +97,44 @@ declare const freeDom: vue_demi.DefineComponent<{
50
97
  type: NumberConstructor;
51
98
  default: undefined;
52
99
  };
100
+ handler: {
101
+ type: vue_demi.PropType<"dot" | "mark">;
102
+ default: undefined;
103
+ };
104
+ diagonal: {
105
+ type: BooleanConstructor;
106
+ default: undefined;
107
+ };
108
+ grid: {
109
+ type: vue_demi.PropType<[number, number]>;
110
+ default: undefined;
111
+ };
53
112
  }>> & {
54
- "onUpdate:customStyle"?: ((...args: any[]) => any) | undefined;
113
+ "onUpdate:x"?: ((...args: any[]) => any) | undefined;
114
+ "onUpdate:y"?: ((...args: any[]) => any) | undefined;
115
+ "onUpdate:width"?: ((...args: any[]) => any) | undefined;
116
+ "onUpdate:height"?: ((...args: any[]) => any) | undefined;
55
117
  onSelect?: ((...args: any[]) => any) | undefined;
56
118
  }, {
119
+ x: number;
120
+ y: number;
121
+ width: number;
122
+ height: number;
123
+ absolute: boolean;
57
124
  scale: boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[];
58
125
  move: boolean;
59
126
  preview: boolean;
60
127
  limitWidth: number;
61
128
  limitHeight: number;
129
+ handler: "dot" | "mark";
130
+ diagonal: boolean;
131
+ grid: [number, number];
62
132
  }>;
63
133
  declare const freeScene: vue_demi.DefineComponent<{
134
+ absolute: {
135
+ type: BooleanConstructor;
136
+ default: undefined;
137
+ };
64
138
  preview: BooleanConstructor;
65
139
  move: BooleanConstructor;
66
140
  scale: vue_demi.PropType<boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[]>;
@@ -68,9 +142,25 @@ declare const freeScene: vue_demi.DefineComponent<{
68
142
  type: NumberConstructor;
69
143
  default: number;
70
144
  };
145
+ handler: {
146
+ type: vue_demi.PropType<"dot" | "mark">;
147
+ default: undefined;
148
+ };
149
+ diagonal: {
150
+ type: BooleanConstructor;
151
+ default: undefined;
152
+ };
153
+ grid: {
154
+ type: vue_demi.PropType<[number, number]>;
155
+ default: undefined;
156
+ };
71
157
  }, {
72
158
  rectRef: vue_demi.ShallowRef<null>;
73
159
  }, unknown, {}, {}, vue_demi.ComponentOptionsMixin, vue_demi.ComponentOptionsMixin, {}, string, vue_demi.VNodeProps & vue_demi.AllowedComponentProps & vue_demi.ComponentCustomProps, Readonly<vue_demi.ExtractPropTypes<{
160
+ absolute: {
161
+ type: BooleanConstructor;
162
+ default: undefined;
163
+ };
74
164
  preview: BooleanConstructor;
75
165
  move: BooleanConstructor;
76
166
  scale: vue_demi.PropType<boolean | ("t" | "r" | "l" | "b" | "lt" | "lb" | "rt" | "rb")[]>;
@@ -78,9 +168,25 @@ declare const freeScene: vue_demi.DefineComponent<{
78
168
  type: NumberConstructor;
79
169
  default: number;
80
170
  };
171
+ handler: {
172
+ type: vue_demi.PropType<"dot" | "mark">;
173
+ default: undefined;
174
+ };
175
+ diagonal: {
176
+ type: BooleanConstructor;
177
+ default: undefined;
178
+ };
179
+ grid: {
180
+ type: vue_demi.PropType<[number, number]>;
181
+ default: undefined;
182
+ };
81
183
  }>>, {
184
+ absolute: boolean;
82
185
  move: boolean;
83
186
  preview: boolean;
187
+ handler: "dot" | "mark";
188
+ diagonal: boolean;
189
+ grid: [number, number];
84
190
  diff: number;
85
191
  }>;
86
192
 
package/dist/index.js CHANGED
@@ -60,9 +60,6 @@ function useNormalizeStyle(style) {
60
60
  return _style;
61
61
  }
62
62
 
63
- // src/components/freeDom.ts
64
- var import_core = require("@vueuse/core");
65
-
66
63
  // src/util/EventBus.ts
67
64
  var _EventBus = class {
68
65
  static on(name, callback) {
@@ -84,15 +81,88 @@ __publicField(EventBus, "_callbacks", {});
84
81
  // src/util/tokens.ts
85
82
  var SceneToken = Symbol("Scene");
86
83
 
84
+ // src/util/index.ts
85
+ function clamp(value, min, max = Infinity) {
86
+ return Math.max(Math.min(value, max), min);
87
+ }
88
+ function snapToGrid(grid, pendingX, pendingY) {
89
+ const x = Math.round(pendingX / grid[0]) * grid[0];
90
+ const y = Math.round(pendingY / grid[1]) * grid[1];
91
+ return [x, y];
92
+ }
93
+
94
+ // src/hooks/use-resize.ts
95
+ var MIN_SIZE = 20;
96
+ function useResize(startX, startY, rect, dot, diagonal, snapGrid, callbacks) {
97
+ const isT = dot ? /t/.test(dot) : false;
98
+ const isL = dot ? /l/.test(dot) : false;
99
+ const isB = dot ? /b/.test(dot) : false;
100
+ const isR = dot ? /r/.test(dot) : false;
101
+ const isDiagonal = dot ? dot.length === 2 : false;
102
+ const { width: cWidth, height: cHeight, x, y } = rect;
103
+ const move = (mouseEvt) => {
104
+ const { clientX, clientY } = mouseEvt;
105
+ let deltaX = clientX - startX;
106
+ let deltaY = clientY - startY;
107
+ if (Array.isArray(snapGrid)) {
108
+ [deltaX, deltaY] = snapToGrid(snapGrid, deltaX, deltaY);
109
+ }
110
+ const rate = cWidth / cHeight;
111
+ const newWidth = cWidth + (isL ? -deltaX : isR ? deltaX : 0);
112
+ const newHeight = cHeight + (isT ? -deltaY : isB ? deltaY : 0);
113
+ if (isDiagonal && diagonal) {
114
+ if (Math.abs(deltaX) >= Math.abs(deltaY)) {
115
+ rect.x = x + (isL ? deltaX : 0);
116
+ rect.width = clamp(newWidth, MIN_SIZE);
117
+ rect.height = clamp(newWidth / rate, MIN_SIZE);
118
+ } else {
119
+ rect.y = y + (isT ? deltaY : 0);
120
+ rect.height = clamp(newHeight, MIN_SIZE);
121
+ rect.width = clamp(newHeight * rate, MIN_SIZE);
122
+ }
123
+ } else {
124
+ rect.x = x + (isL ? deltaX : 0);
125
+ rect.y = y + (isT ? deltaY : 0);
126
+ rect.width = clamp(newWidth, MIN_SIZE);
127
+ rect.height = clamp(newHeight, MIN_SIZE);
128
+ }
129
+ callbacks && callbacks.onMove && callbacks.onMove();
130
+ };
131
+ const up = () => {
132
+ document.removeEventListener("mousemove", move);
133
+ document.removeEventListener("mouseup", up);
134
+ callbacks && callbacks.onMove && callbacks.onUp();
135
+ };
136
+ document.addEventListener("mousemove", move);
137
+ document.addEventListener("mouseup", up);
138
+ }
139
+
87
140
  // src/components/freeDom.ts
141
+ var import_core = require("@vueuse/core");
88
142
  var import_uuid = require("uuid");
89
143
  var Dots = ["t", "r", "l", "b", "lt", "lb", "rt", "rb"];
90
144
  var FreeDom = (0, import_vue_demi2.defineComponent)({
91
145
  name: "FreeDom",
92
146
  props: {
93
- customStyle: {
94
- type: Object,
95
- required: true
147
+ x: {
148
+ type: Number,
149
+ default: 0
150
+ },
151
+ y: {
152
+ type: Number,
153
+ default: 0
154
+ },
155
+ width: {
156
+ type: Number,
157
+ default: 0
158
+ },
159
+ height: {
160
+ type: Number,
161
+ default: 0
162
+ },
163
+ absolute: {
164
+ type: Boolean,
165
+ default: void 0
96
166
  },
97
167
  scale: {
98
168
  type: [Boolean, Array],
@@ -107,15 +177,31 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
107
177
  limitHeight: {
108
178
  type: Number,
109
179
  default: void 0
180
+ },
181
+ handler: {
182
+ type: String,
183
+ default: void 0
184
+ },
185
+ diagonal: {
186
+ type: Boolean,
187
+ default: void 0
188
+ },
189
+ grid: {
190
+ type: Object,
191
+ default: void 0
110
192
  }
111
193
  },
112
- emits: ["update:customStyle", "select"],
194
+ emits: ["update:x", "update:y", "update:width", "update:height", "select"],
113
195
  setup(props, { emit }) {
114
196
  const active = (0, import_vue_demi2.ref)(false);
115
197
  const SceneContext = (0, import_vue_demi2.inject)(SceneToken);
116
198
  const _preview = (0, import_vue_demi2.computed)(() => SceneContext?.preview || props.preview);
117
199
  const canScale = (0, import_vue_demi2.computed)(() => !_preview.value && (SceneContext?.scale || props.scale));
118
200
  const canMove = (0, import_vue_demi2.computed)(() => !_preview.value && (SceneContext?.move || props.move));
201
+ const isAbsolute = (0, import_vue_demi2.computed)(() => props.absolute ?? SceneContext?.absolute ?? true);
202
+ const handlerType = (0, import_vue_demi2.computed)(() => props.handler ?? SceneContext?.handler ?? "dot");
203
+ const snapGrid = (0, import_vue_demi2.computed)(() => props.grid ?? SceneContext?.grid);
204
+ const diagonal = (0, import_vue_demi2.computed)(() => props.diagonal ?? SceneContext?.diagonal ?? true);
119
205
  const widgetRef = (0, import_vue_demi2.shallowRef)();
120
206
  const _style = (0, import_vue_demi2.ref)({});
121
207
  const wrapStyle = useNormalizeStyle(_style);
@@ -128,44 +214,40 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
128
214
  width: 0,
129
215
  height: 0
130
216
  });
217
+ const triggerThrottle = (0, import_core.useThrottleFn)(trigger);
131
218
  const context = {
132
219
  _rect,
133
220
  trigger
134
221
  };
135
- (0, import_vue_demi2.onMounted)(() => {
136
- SceneContext?.register(uuid, context);
137
- });
138
222
  (0, import_core.onClickOutside)(widgetRef, () => {
139
223
  active.value = false;
140
224
  });
141
- function normalize(style) {
142
- const { transform, width, height } = style;
143
- const { x, y } = getPos(transform);
144
- _rect.width = parseNum(width ?? 0);
145
- _rect.height = parseNum(height ?? 0);
146
- _rect.x = x;
147
- _rect.y = y;
148
- }
149
225
  function parseNum(val) {
150
226
  return typeof val === "number" ? val : parseFloat(val);
151
227
  }
152
- (0, import_vue_demi2.watch)(() => props.customStyle, (_style2) => {
153
- normalize(_style2);
154
- trigger();
228
+ let init = false;
229
+ (0, import_vue_demi2.watchEffect)(() => {
230
+ _rect.width = props.width;
231
+ _rect.height = props.height;
232
+ _rect.x = props.x;
233
+ _rect.y = props.y;
234
+ init && triggerThrottle();
235
+ init = true;
155
236
  });
156
237
  (0, import_vue_demi2.onMounted)(async () => {
157
- _style.value = props.customStyle;
238
+ SceneContext?.register(uuid, context);
158
239
  await (0, import_vue_demi2.nextTick)();
159
240
  const rect = widgetRef.value.getBoundingClientRect();
160
- normalize(props.customStyle);
161
- _rect.width = rect.width;
162
- _rect.height = rect.height;
241
+ _rect.width = _rect.width || rect.width;
242
+ _rect.height = _rect.height || rect.height;
243
+ _rect.x = _rect.x || 0;
244
+ _rect.y = _rect.y || 0;
163
245
  trigger();
246
+ emitPos();
164
247
  });
165
248
  function trigger() {
166
249
  const { x, y, width, height } = _rect;
167
250
  _style.value = {
168
- ...props.customStyle,
169
251
  transform: `translate(${x}px, ${y}px)`,
170
252
  width,
171
253
  height
@@ -192,54 +274,27 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
192
274
  if (isMove.value)
193
275
  return;
194
276
  isScale.value = true;
195
- const { x, y, width, height } = getStyle(_style.value);
196
- const cWidth = width;
197
- const cHeight = height;
198
- const startX = evt.clientX;
199
- const startY = evt.clientY;
200
- const isT = /t/.test(dot);
201
- const isL = /l/.test(dot);
202
- const isB = /b/.test(dot);
203
- const isR = /r/.test(dot);
204
- const isDiagonal = dot.length === 2;
205
- const move = (mouseEvt) => {
206
- const currX = mouseEvt.clientX;
207
- const currY = mouseEvt.clientY;
208
- const deltaX = currX - startX;
209
- const deltaY = currY - startY;
210
- const rate = cWidth / cHeight;
211
- const newWidth = cWidth + (isL ? -deltaX : isR ? deltaX : 0);
212
- const newHeight = cHeight + (isT ? -deltaY : isB ? deltaY : 0);
213
- if (isDiagonal) {
214
- if (Math.abs(deltaX) >= Math.abs(deltaY)) {
215
- _rect.x = x + (isL ? deltaX : 0);
216
- _rect.width = newWidth < 0 ? 0 : newWidth;
217
- _rect.height = newWidth / rate;
218
- } else {
219
- _rect.y = y + (isT ? deltaY : 0);
220
- _rect.height = newHeight < 0 ? 0 : newHeight;
221
- _rect.width = newHeight * rate;
222
- }
223
- } else {
224
- _rect.x = x + (isL ? deltaX : 0);
225
- _rect.y = y + (isT ? deltaY : 0);
226
- _rect.width = newWidth < 0 ? 0 : newWidth;
227
- _rect.height = newHeight < 0 ? 0 : newHeight;
277
+ active.value = true;
278
+ const { clientX, clientY } = evt;
279
+ useResize(clientX, clientY, _rect, dot, diagonal.value, snapGrid.value, {
280
+ onMove() {
281
+ if (!checkValid(_rect))
282
+ return;
283
+ EventBus.emit("move", uuid);
284
+ trigger();
285
+ },
286
+ onUp() {
287
+ isScale.value = false;
288
+ EventBus.emit("moveup", uuid);
289
+ emitPos();
228
290
  }
229
- if (!checkValid(_rect))
230
- return;
231
- EventBus.emit("move", uuid);
232
- trigger();
233
- };
234
- const up = () => {
235
- isScale.value = false;
236
- EventBus.emit("moveup", uuid);
237
- document.removeEventListener("mousemove", move);
238
- document.removeEventListener("mouseup", up);
239
- emit("update:customStyle", _style.value);
240
- };
241
- document.addEventListener("mousemove", move);
242
- document.addEventListener("mouseup", up);
291
+ });
292
+ }
293
+ function emitPos() {
294
+ emit("update:x", _rect.x);
295
+ emit("update:y", _rect.y);
296
+ emit("update:width", _rect.width);
297
+ emit("update:height", _rect.height);
243
298
  }
244
299
  function getDotPos(dot) {
245
300
  if (!_style.value)
@@ -262,8 +317,8 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
262
317
  }
263
318
  }
264
319
  return {
265
- top: top + "px",
266
- left: left + "px",
320
+ top: handlerType.value === "dot" ? top : top - 3 + "px",
321
+ left: handlerType.value === "dot" ? left : left - 3 + "px",
267
322
  cursor: dot.split("").reverse().map((item) => direct[item]).join("") + "-resize"
268
323
  };
269
324
  }
@@ -292,7 +347,7 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
292
347
  EventBus.emit("moveup", uuid);
293
348
  document.removeEventListener("mousemove", move);
294
349
  document.removeEventListener("mouseup", up);
295
- emit("update:customStyle", _style.value);
350
+ emitPos();
296
351
  emit("select", _rect);
297
352
  };
298
353
  document.addEventListener("mousemove", move);
@@ -336,6 +391,9 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
336
391
  canScale,
337
392
  dots,
338
393
  active,
394
+ isAbsolute,
395
+ isScale,
396
+ handlerType,
339
397
  getDotPos,
340
398
  onMousedown,
341
399
  onMousedownDot
@@ -353,7 +411,7 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
353
411
  });
354
412
  }
355
413
  return (0, import_vue_demi2.h)("div", {
356
- class: "free-dom__widget-dot",
414
+ class: this.handlerType === "dot" ? "free-dom__widget-dot" : "free-dom__resizable-handler",
357
415
  style: this.getDotPos(dot),
358
416
  onMousedown: (evt) => this.onMousedownDot(evt, dot)
359
417
  });
@@ -365,6 +423,8 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
365
423
  {
366
424
  class: [
367
425
  "free-dom__widget-wrapper",
426
+ { "is-scale": this.isScale },
427
+ { "is-absolute": this.isAbsolute },
368
428
  { "can-move": this.canMove },
369
429
  { "is-active": this.active }
370
430
  ],
@@ -383,13 +443,15 @@ var FreeDom = (0, import_vue_demi2.defineComponent)({
383
443
  ref: "widgetRef",
384
444
  class: [
385
445
  "free-dom__widget-wrapper",
446
+ { "is-scale": this.isScale },
447
+ { "is-absolute": this.isAbsolute },
386
448
  { "can-move": this.canMove },
387
449
  { "is-active": this.active }
388
450
  ],
389
451
  style: this.wrapStyle,
390
452
  onMousedown: this.onMousedown
391
453
  },
392
- [dots, defaultSlot]
454
+ [defaultSlot, dots]
393
455
  );
394
456
  }
395
457
  });
@@ -561,12 +623,28 @@ var markLine_default = (0, import_vue_demi3.defineComponent)({
561
623
 
562
624
  // src/components/freeDomWrap.ts
563
625
  var freeDomWrapProps = {
626
+ absolute: {
627
+ type: Boolean,
628
+ default: void 0
629
+ },
564
630
  preview: Boolean,
565
631
  move: Boolean,
566
632
  scale: [Boolean, Array],
567
633
  diff: {
568
634
  type: Number,
569
635
  default: 3
636
+ },
637
+ handler: {
638
+ type: String,
639
+ default: void 0
640
+ },
641
+ diagonal: {
642
+ type: Boolean,
643
+ default: void 0
644
+ },
645
+ grid: {
646
+ type: Object,
647
+ default: void 0
570
648
  }
571
649
  };
572
650
  var FreeDomWrap = (0, import_vue_demi4.defineComponent)({
package/dist/index.mjs CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  reactive,
18
18
  isVue2,
19
19
  shallowRef,
20
- watch as watch2
20
+ watchEffect
21
21
  } from "vue-demi";
22
22
 
23
23
  // src/hooks/use-normalize-style.ts
@@ -47,9 +47,6 @@ function useNormalizeStyle(style) {
47
47
  return _style;
48
48
  }
49
49
 
50
- // src/components/freeDom.ts
51
- import { onClickOutside } from "@vueuse/core";
52
-
53
50
  // src/util/EventBus.ts
54
51
  var _EventBus = class {
55
52
  static on(name, callback) {
@@ -71,15 +68,88 @@ __publicField(EventBus, "_callbacks", {});
71
68
  // src/util/tokens.ts
72
69
  var SceneToken = Symbol("Scene");
73
70
 
71
+ // src/util/index.ts
72
+ function clamp(value, min, max = Infinity) {
73
+ return Math.max(Math.min(value, max), min);
74
+ }
75
+ function snapToGrid(grid, pendingX, pendingY) {
76
+ const x = Math.round(pendingX / grid[0]) * grid[0];
77
+ const y = Math.round(pendingY / grid[1]) * grid[1];
78
+ return [x, y];
79
+ }
80
+
81
+ // src/hooks/use-resize.ts
82
+ var MIN_SIZE = 20;
83
+ function useResize(startX, startY, rect, dot, diagonal, snapGrid, callbacks) {
84
+ const isT = dot ? /t/.test(dot) : false;
85
+ const isL = dot ? /l/.test(dot) : false;
86
+ const isB = dot ? /b/.test(dot) : false;
87
+ const isR = dot ? /r/.test(dot) : false;
88
+ const isDiagonal = dot ? dot.length === 2 : false;
89
+ const { width: cWidth, height: cHeight, x, y } = rect;
90
+ const move = (mouseEvt) => {
91
+ const { clientX, clientY } = mouseEvt;
92
+ let deltaX = clientX - startX;
93
+ let deltaY = clientY - startY;
94
+ if (Array.isArray(snapGrid)) {
95
+ [deltaX, deltaY] = snapToGrid(snapGrid, deltaX, deltaY);
96
+ }
97
+ const rate = cWidth / cHeight;
98
+ const newWidth = cWidth + (isL ? -deltaX : isR ? deltaX : 0);
99
+ const newHeight = cHeight + (isT ? -deltaY : isB ? deltaY : 0);
100
+ if (isDiagonal && diagonal) {
101
+ if (Math.abs(deltaX) >= Math.abs(deltaY)) {
102
+ rect.x = x + (isL ? deltaX : 0);
103
+ rect.width = clamp(newWidth, MIN_SIZE);
104
+ rect.height = clamp(newWidth / rate, MIN_SIZE);
105
+ } else {
106
+ rect.y = y + (isT ? deltaY : 0);
107
+ rect.height = clamp(newHeight, MIN_SIZE);
108
+ rect.width = clamp(newHeight * rate, MIN_SIZE);
109
+ }
110
+ } else {
111
+ rect.x = x + (isL ? deltaX : 0);
112
+ rect.y = y + (isT ? deltaY : 0);
113
+ rect.width = clamp(newWidth, MIN_SIZE);
114
+ rect.height = clamp(newHeight, MIN_SIZE);
115
+ }
116
+ callbacks && callbacks.onMove && callbacks.onMove();
117
+ };
118
+ const up = () => {
119
+ document.removeEventListener("mousemove", move);
120
+ document.removeEventListener("mouseup", up);
121
+ callbacks && callbacks.onMove && callbacks.onUp();
122
+ };
123
+ document.addEventListener("mousemove", move);
124
+ document.addEventListener("mouseup", up);
125
+ }
126
+
74
127
  // src/components/freeDom.ts
128
+ import { onClickOutside, useThrottleFn } from "@vueuse/core";
75
129
  import { v4 as uuidv4 } from "uuid";
76
130
  var Dots = ["t", "r", "l", "b", "lt", "lb", "rt", "rb"];
77
131
  var FreeDom = defineComponent({
78
132
  name: "FreeDom",
79
133
  props: {
80
- customStyle: {
81
- type: Object,
82
- required: true
134
+ x: {
135
+ type: Number,
136
+ default: 0
137
+ },
138
+ y: {
139
+ type: Number,
140
+ default: 0
141
+ },
142
+ width: {
143
+ type: Number,
144
+ default: 0
145
+ },
146
+ height: {
147
+ type: Number,
148
+ default: 0
149
+ },
150
+ absolute: {
151
+ type: Boolean,
152
+ default: void 0
83
153
  },
84
154
  scale: {
85
155
  type: [Boolean, Array],
@@ -94,15 +164,31 @@ var FreeDom = defineComponent({
94
164
  limitHeight: {
95
165
  type: Number,
96
166
  default: void 0
167
+ },
168
+ handler: {
169
+ type: String,
170
+ default: void 0
171
+ },
172
+ diagonal: {
173
+ type: Boolean,
174
+ default: void 0
175
+ },
176
+ grid: {
177
+ type: Object,
178
+ default: void 0
97
179
  }
98
180
  },
99
- emits: ["update:customStyle", "select"],
181
+ emits: ["update:x", "update:y", "update:width", "update:height", "select"],
100
182
  setup(props, { emit }) {
101
183
  const active = ref2(false);
102
184
  const SceneContext = inject(SceneToken);
103
185
  const _preview = computed(() => SceneContext?.preview || props.preview);
104
186
  const canScale = computed(() => !_preview.value && (SceneContext?.scale || props.scale));
105
187
  const canMove = computed(() => !_preview.value && (SceneContext?.move || props.move));
188
+ const isAbsolute = computed(() => props.absolute ?? SceneContext?.absolute ?? true);
189
+ const handlerType = computed(() => props.handler ?? SceneContext?.handler ?? "dot");
190
+ const snapGrid = computed(() => props.grid ?? SceneContext?.grid);
191
+ const diagonal = computed(() => props.diagonal ?? SceneContext?.diagonal ?? true);
106
192
  const widgetRef = shallowRef();
107
193
  const _style = ref2({});
108
194
  const wrapStyle = useNormalizeStyle(_style);
@@ -115,44 +201,40 @@ var FreeDom = defineComponent({
115
201
  width: 0,
116
202
  height: 0
117
203
  });
204
+ const triggerThrottle = useThrottleFn(trigger);
118
205
  const context = {
119
206
  _rect,
120
207
  trigger
121
208
  };
122
- onMounted(() => {
123
- SceneContext?.register(uuid, context);
124
- });
125
209
  onClickOutside(widgetRef, () => {
126
210
  active.value = false;
127
211
  });
128
- function normalize(style) {
129
- const { transform, width, height } = style;
130
- const { x, y } = getPos(transform);
131
- _rect.width = parseNum(width ?? 0);
132
- _rect.height = parseNum(height ?? 0);
133
- _rect.x = x;
134
- _rect.y = y;
135
- }
136
212
  function parseNum(val) {
137
213
  return typeof val === "number" ? val : parseFloat(val);
138
214
  }
139
- watch2(() => props.customStyle, (_style2) => {
140
- normalize(_style2);
141
- trigger();
215
+ let init = false;
216
+ watchEffect(() => {
217
+ _rect.width = props.width;
218
+ _rect.height = props.height;
219
+ _rect.x = props.x;
220
+ _rect.y = props.y;
221
+ init && triggerThrottle();
222
+ init = true;
142
223
  });
143
224
  onMounted(async () => {
144
- _style.value = props.customStyle;
225
+ SceneContext?.register(uuid, context);
145
226
  await nextTick();
146
227
  const rect = widgetRef.value.getBoundingClientRect();
147
- normalize(props.customStyle);
148
- _rect.width = rect.width;
149
- _rect.height = rect.height;
228
+ _rect.width = _rect.width || rect.width;
229
+ _rect.height = _rect.height || rect.height;
230
+ _rect.x = _rect.x || 0;
231
+ _rect.y = _rect.y || 0;
150
232
  trigger();
233
+ emitPos();
151
234
  });
152
235
  function trigger() {
153
236
  const { x, y, width, height } = _rect;
154
237
  _style.value = {
155
- ...props.customStyle,
156
238
  transform: `translate(${x}px, ${y}px)`,
157
239
  width,
158
240
  height
@@ -179,54 +261,27 @@ var FreeDom = defineComponent({
179
261
  if (isMove.value)
180
262
  return;
181
263
  isScale.value = true;
182
- const { x, y, width, height } = getStyle(_style.value);
183
- const cWidth = width;
184
- const cHeight = height;
185
- const startX = evt.clientX;
186
- const startY = evt.clientY;
187
- const isT = /t/.test(dot);
188
- const isL = /l/.test(dot);
189
- const isB = /b/.test(dot);
190
- const isR = /r/.test(dot);
191
- const isDiagonal = dot.length === 2;
192
- const move = (mouseEvt) => {
193
- const currX = mouseEvt.clientX;
194
- const currY = mouseEvt.clientY;
195
- const deltaX = currX - startX;
196
- const deltaY = currY - startY;
197
- const rate = cWidth / cHeight;
198
- const newWidth = cWidth + (isL ? -deltaX : isR ? deltaX : 0);
199
- const newHeight = cHeight + (isT ? -deltaY : isB ? deltaY : 0);
200
- if (isDiagonal) {
201
- if (Math.abs(deltaX) >= Math.abs(deltaY)) {
202
- _rect.x = x + (isL ? deltaX : 0);
203
- _rect.width = newWidth < 0 ? 0 : newWidth;
204
- _rect.height = newWidth / rate;
205
- } else {
206
- _rect.y = y + (isT ? deltaY : 0);
207
- _rect.height = newHeight < 0 ? 0 : newHeight;
208
- _rect.width = newHeight * rate;
209
- }
210
- } else {
211
- _rect.x = x + (isL ? deltaX : 0);
212
- _rect.y = y + (isT ? deltaY : 0);
213
- _rect.width = newWidth < 0 ? 0 : newWidth;
214
- _rect.height = newHeight < 0 ? 0 : newHeight;
264
+ active.value = true;
265
+ const { clientX, clientY } = evt;
266
+ useResize(clientX, clientY, _rect, dot, diagonal.value, snapGrid.value, {
267
+ onMove() {
268
+ if (!checkValid(_rect))
269
+ return;
270
+ EventBus.emit("move", uuid);
271
+ trigger();
272
+ },
273
+ onUp() {
274
+ isScale.value = false;
275
+ EventBus.emit("moveup", uuid);
276
+ emitPos();
215
277
  }
216
- if (!checkValid(_rect))
217
- return;
218
- EventBus.emit("move", uuid);
219
- trigger();
220
- };
221
- const up = () => {
222
- isScale.value = false;
223
- EventBus.emit("moveup", uuid);
224
- document.removeEventListener("mousemove", move);
225
- document.removeEventListener("mouseup", up);
226
- emit("update:customStyle", _style.value);
227
- };
228
- document.addEventListener("mousemove", move);
229
- document.addEventListener("mouseup", up);
278
+ });
279
+ }
280
+ function emitPos() {
281
+ emit("update:x", _rect.x);
282
+ emit("update:y", _rect.y);
283
+ emit("update:width", _rect.width);
284
+ emit("update:height", _rect.height);
230
285
  }
231
286
  function getDotPos(dot) {
232
287
  if (!_style.value)
@@ -249,8 +304,8 @@ var FreeDom = defineComponent({
249
304
  }
250
305
  }
251
306
  return {
252
- top: top + "px",
253
- left: left + "px",
307
+ top: handlerType.value === "dot" ? top : top - 3 + "px",
308
+ left: handlerType.value === "dot" ? left : left - 3 + "px",
254
309
  cursor: dot.split("").reverse().map((item) => direct[item]).join("") + "-resize"
255
310
  };
256
311
  }
@@ -279,7 +334,7 @@ var FreeDom = defineComponent({
279
334
  EventBus.emit("moveup", uuid);
280
335
  document.removeEventListener("mousemove", move);
281
336
  document.removeEventListener("mouseup", up);
282
- emit("update:customStyle", _style.value);
337
+ emitPos();
283
338
  emit("select", _rect);
284
339
  };
285
340
  document.addEventListener("mousemove", move);
@@ -323,6 +378,9 @@ var FreeDom = defineComponent({
323
378
  canScale,
324
379
  dots,
325
380
  active,
381
+ isAbsolute,
382
+ isScale,
383
+ handlerType,
326
384
  getDotPos,
327
385
  onMousedown,
328
386
  onMousedownDot
@@ -340,7 +398,7 @@ var FreeDom = defineComponent({
340
398
  });
341
399
  }
342
400
  return h("div", {
343
- class: "free-dom__widget-dot",
401
+ class: this.handlerType === "dot" ? "free-dom__widget-dot" : "free-dom__resizable-handler",
344
402
  style: this.getDotPos(dot),
345
403
  onMousedown: (evt) => this.onMousedownDot(evt, dot)
346
404
  });
@@ -352,6 +410,8 @@ var FreeDom = defineComponent({
352
410
  {
353
411
  class: [
354
412
  "free-dom__widget-wrapper",
413
+ { "is-scale": this.isScale },
414
+ { "is-absolute": this.isAbsolute },
355
415
  { "can-move": this.canMove },
356
416
  { "is-active": this.active }
357
417
  ],
@@ -370,13 +430,15 @@ var FreeDom = defineComponent({
370
430
  ref: "widgetRef",
371
431
  class: [
372
432
  "free-dom__widget-wrapper",
433
+ { "is-scale": this.isScale },
434
+ { "is-absolute": this.isAbsolute },
373
435
  { "can-move": this.canMove },
374
436
  { "is-active": this.active }
375
437
  ],
376
438
  style: this.wrapStyle,
377
439
  onMousedown: this.onMousedown
378
440
  },
379
- [dots, defaultSlot]
441
+ [defaultSlot, dots]
380
442
  );
381
443
  }
382
444
  });
@@ -548,12 +610,28 @@ var markLine_default = defineComponent2({
548
610
 
549
611
  // src/components/freeDomWrap.ts
550
612
  var freeDomWrapProps = {
613
+ absolute: {
614
+ type: Boolean,
615
+ default: void 0
616
+ },
551
617
  preview: Boolean,
552
618
  move: Boolean,
553
619
  scale: [Boolean, Array],
554
620
  diff: {
555
621
  type: Number,
556
622
  default: 3
623
+ },
624
+ handler: {
625
+ type: String,
626
+ default: void 0
627
+ },
628
+ diagonal: {
629
+ type: Boolean,
630
+ default: void 0
631
+ },
632
+ grid: {
633
+ type: Object,
634
+ default: void 0
557
635
  }
558
636
  };
559
637
  var FreeDomWrap = defineComponent3({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sepveneto/free-dom",
3
- "version": "0.0.7",
3
+ "version": "0.3.0",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -15,6 +15,12 @@
15
15
  },
16
16
  "./css": "./dist/index.css"
17
17
  },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "vue-demi-switch 3 && vitepress dev docs",
21
+ "release": "bumpp package.json --commit --push --tag",
22
+ "test": "vitest"
23
+ },
18
24
  "keywords": [],
19
25
  "author": "",
20
26
  "license": "ISC",
@@ -23,15 +29,6 @@
23
29
  "uuid": "^9.0.0",
24
30
  "vue-demi": "latest"
25
31
  },
26
- "peerDependencies": {
27
- "@vue/composition-api": "^1.0.0-rc.1",
28
- "vue": "^2.0.0 || >=3.0.0"
29
- },
30
- "peerDependenciesMeta": {
31
- "@vue/composition-api": {
32
- "optional": true
33
- }
34
- },
35
32
  "devDependencies": {
36
33
  "@types/uuid": "^8.3.4",
37
34
  "bumpp": "^8.2.1",
@@ -44,11 +41,5 @@
44
41
  "repository": {
45
42
  "type": "git",
46
43
  "url": "https://github.com/SepVeneto/free-dom"
47
- },
48
- "scripts": {
49
- "build": "tsup",
50
- "dev": "vue-demi-switch 3 && vitepress dev docs",
51
- "release": "pnpm build && bumpp package.json --commit --push --tag && pnpm -r publish --access public",
52
- "test": "vitest"
53
44
  }
54
- }
45
+ }