@witchcraft/layout 0.4.3 → 0.4.4

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 (50) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/LayoutWindow.d.vue.ts +10 -1
  3. package/dist/runtime/components/LayoutWindow.vue +4 -4
  4. package/dist/runtime/components/LayoutWindow.vue.d.ts +10 -1
  5. package/dist/runtime/composables/useFrames.d.ts +37 -22
  6. package/dist/runtime/composables/useFrames.js +32 -8
  7. package/dist/runtime/demo/tailwind.css +1 -1
  8. package/dist/runtime/drag/DragActionHandler.d.ts +4 -1
  9. package/dist/runtime/drag/DragActionHandler.js +2 -2
  10. package/dist/runtime/helpers/findEdgesTouchingWindow.d.ts +7 -0
  11. package/dist/runtime/helpers/findEdgesTouchingWindow.js +20 -0
  12. package/dist/runtime/helpers/index.d.ts +8 -1
  13. package/dist/runtime/helpers/index.js +8 -1
  14. package/dist/runtime/layout/debugFrame.js +5 -1
  15. package/dist/runtime/layout/getFrameCollapseInfo.d.ts +1 -1
  16. package/dist/runtime/layout/getFrameCollapseInfo.js +9 -29
  17. package/dist/runtime/layout/getFrameDockInfo.d.ts +1 -1
  18. package/dist/runtime/layout/getFrameDockInfo.js +61 -44
  19. package/dist/runtime/layout/getFrameExpandInfo.d.ts +11 -0
  20. package/dist/runtime/layout/getFrameExpandInfo.js +49 -0
  21. package/dist/runtime/layout/getFrameShrinkInfo.d.ts +14 -0
  22. package/dist/runtime/layout/getFrameShrinkInfo.js +48 -0
  23. package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +1 -1
  24. package/dist/runtime/layout/getFrameUncollapseInfo.js +6 -20
  25. package/dist/runtime/layout/getUpdateWindowSizeInfo.js +10 -12
  26. package/dist/runtime/layout/index.d.ts +3 -0
  27. package/dist/runtime/layout/index.js +3 -0
  28. package/dist/runtime/types/index.d.ts +70 -0
  29. package/dist/runtime/types/index.js +2 -1
  30. package/dist/runtime/types/vue.d.ts +12 -3
  31. package/package.json +3 -2
  32. package/src/runtime/components/LayoutWindow.vue +5 -5
  33. package/src/runtime/composables/useFrames.ts +46 -32
  34. package/src/runtime/demo/tailwind.css +4 -0
  35. package/src/runtime/drag/DragActionHandler.ts +3 -3
  36. package/src/runtime/helpers/findEdgesTouchingWindow.ts +30 -0
  37. package/src/runtime/helpers/index.ts +8 -1
  38. package/src/runtime/layout/debugFrame.ts +6 -1
  39. package/src/runtime/layout/getFrameCollapseInfo.ts +12 -41
  40. package/src/runtime/layout/getFrameDockInfo.ts +77 -57
  41. package/src/runtime/layout/getFrameExpandInfo.ts +84 -0
  42. package/src/runtime/layout/getFrameShrinkInfo.ts +87 -0
  43. package/src/runtime/layout/getFrameUncollapseInfo.ts +8 -30
  44. package/src/runtime/layout/getUpdateWindowSizeInfo.ts +16 -22
  45. package/src/runtime/layout/index.ts +3 -0
  46. package/src/runtime/types/index.ts +39 -1
  47. package/src/runtime/types/vue.ts +5 -3
  48. package/dist/runtime/drag/defaultDragActions.d.ts +0 -9
  49. package/dist/runtime/drag/defaultDragActions.js +0 -10
  50. package/src/runtime/drag/defaultDragActions.ts +0 -20
@@ -6,4 +6,4 @@ import { KnownError } from "../utils/KnownError.js";
6
6
  *
7
7
  * Changes can be applied to a window with {@link applyFrameChanges}.
8
8
  */
9
- export declare function getFrameDockInfo(win: LayoutWindow, frameId: string, side: EdgeSide, maxPerpendicularLength?: number): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES> | KnownError<typeof LAYOUT_ERROR.FRAME_ALREADY_DOCKED_ON_SIDE> | KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME> | KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES>;
9
+ export declare function getFrameDockInfo(win: LayoutWindow, frameId: string, side: EdgeSide, maxPerpendicularLength?: number): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES> | KnownError<typeof LAYOUT_ERROR.FRAME_ALREADY_DOCKED_ON_SIDE> | KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME> | KnownError<typeof LAYOUT_ERROR.NO_FILL_CANDIDATES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -2,9 +2,11 @@ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "./applyFrameChanges.js";
4
4
  import { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
5
+ import { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
5
6
  import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
6
7
  import { getFrameUndockInfo } from "./getFrameUndockInfo.js";
7
8
  import { cloneFrame } from "../helpers/cloneFrame.js";
9
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
8
10
  import { getDockBoundaries } from "../helpers/getDockBoundaries.js";
9
11
  import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
10
12
  import { oppositeSide } from "../helpers/oppositeSide.js";
@@ -29,58 +31,73 @@ export function getFrameDockInfo(win, frameId, side, maxPerpendicularLength) {
29
31
  }
30
32
  const isHorizontal = side === "left" || side === "right";
31
33
  const perpendicular = isHorizontal ? "width" : "height";
32
- const oldFrame = cloneFrame(frame);
34
+ const posKey = isHorizontal ? "x" : "y";
35
+ const sizeKey = isHorizontal ? "width" : "height";
33
36
  const otherFrameIds = Object.keys(win.frames).filter((_) => _ !== frameId);
34
37
  const nonDockedFrameIds = otherFrameIds.filter((id) => !win.frames[id].docked);
35
38
  if (nonDockedFrameIds.length === 0) {
36
39
  return new KnownError(LAYOUT_ERROR.CANT_LEAVE_NO_UNDOCKED_FRAMES, `Can't dock frame ${frameId} because there are no other undocked frames.`, { frameId });
37
40
  }
38
- const effectiveMaxPerpendicular = maxPerpendicularLength ?? settings.maxPerpendicularLengthScaled.width;
39
- const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular], effectiveMaxPerpendicular) : frame[perpendicular];
41
+ const maxPerpendicular = maxPerpendicularLength ?? settings.maxPerpendicularLengthScaled.width;
42
+ const perpendicularLength = otherFrameIds.length > 0 ? Math.min(frame[perpendicular], maxPerpendicular) : frame[perpendicular];
40
43
  frame.docked = side;
41
44
  frame.collapsed = void 0;
42
45
  const toExtract = [frame.id];
43
- const changes = getFillEmptySpaceInfo(win, oldFrame, [], [frameId]);
44
- if (changes instanceof Error) return changes;
45
- applyFrameChanges(win, changes);
46
- pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
47
- const sideToPushTowards = oppositeSide(side);
48
- const posKey = isHorizontal ? "x" : "y";
49
- const sizeKey = isHorizontal ? "width" : "height";
50
- const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey);
51
- const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates });
52
- if (redistributeChanges instanceof KnownError) {
53
- return redistributeChanges;
46
+ if (frame[perpendicular] === perpendicularLength) {
47
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
54
48
  }
55
- applyFrameChanges(win, redistributeChanges);
56
- pushIfNotIn(toExtract, redistributeChanges.modified.map((_) => _.id));
57
- const { minX, maxX, minY, maxY } = getDockBoundaries(win);
58
- switch (side) {
59
- case "left":
60
- frame.x = 0;
61
- frame.y = minY;
62
- frame.width = perpendicularLength;
63
- frame.height = maxY - minY;
64
- break;
65
- case "right":
66
- frame.x = settings.maxInt - perpendicularLength;
67
- frame.y = minY;
68
- frame.width = perpendicularLength;
69
- frame.height = maxY - minY;
70
- break;
71
- case "top":
72
- frame.x = minX;
73
- frame.y = 0;
74
- frame.width = maxX - minX;
75
- frame.height = perpendicularLength;
76
- break;
77
- case "bottom":
78
- frame.x = minX;
79
- frame.y = settings.maxInt - perpendicularLength;
80
- frame.width = maxX - minX;
81
- frame.height = perpendicularLength;
82
- break;
49
+ const touchesEntireEdge = findEdgesTouchingWindow(frame)[side] === "full";
50
+ if (touchesEntireEdge) {
51
+ const shrinkAmount = frame[sizeKey] - perpendicularLength;
52
+ if (shrinkAmount > 0) {
53
+ const shrinkResult = getFrameShrinkInfo(win, frameId, shrinkAmount, side);
54
+ if (shrinkResult instanceof KnownError) {
55
+ return shrinkResult;
56
+ }
57
+ applyFrameChanges(win, shrinkResult);
58
+ pushIfNotIn(toExtract, shrinkResult.modified.map((_) => _.id));
59
+ }
60
+ } else {
61
+ const oldFrame = cloneFrame(frame);
62
+ const changes = getFillEmptySpaceInfo(win, oldFrame, [], [frameId]);
63
+ if (changes instanceof Error) return changes;
64
+ applyFrameChanges(win, changes);
65
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
66
+ const sideToPushTowards = oppositeSide(side);
67
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, side, posKey, sizeKey);
68
+ const redistributeChanges = getFramesRedistributeInfo(win, sideToPushTowards, nonDockedFrameIds, perpendicularLength, { pinnedEdgeCoordinates });
69
+ if (redistributeChanges instanceof KnownError) {
70
+ return redistributeChanges;
71
+ }
72
+ applyFrameChanges(win, redistributeChanges);
73
+ pushIfNotIn(toExtract, redistributeChanges.modified.map((_) => _.id));
74
+ const { minX, maxX, minY, maxY } = getDockBoundaries(win);
75
+ switch (side) {
76
+ case "left":
77
+ frame.x = 0;
78
+ frame.y = minY;
79
+ frame.width = perpendicularLength;
80
+ frame.height = maxY - minY;
81
+ break;
82
+ case "right":
83
+ frame.x = settings.maxInt - perpendicularLength;
84
+ frame.y = minY;
85
+ frame.width = perpendicularLength;
86
+ frame.height = maxY - minY;
87
+ break;
88
+ case "top":
89
+ frame.x = minX;
90
+ frame.y = 0;
91
+ frame.width = maxX - minX;
92
+ frame.height = perpendicularLength;
93
+ break;
94
+ case "bottom":
95
+ frame.x = minX;
96
+ frame.y = settings.maxInt - perpendicularLength;
97
+ frame.width = maxX - minX;
98
+ frame.height = perpendicularLength;
99
+ break;
100
+ }
83
101
  }
84
- const res = toExtract.map((_) => win.frames[_]);
85
- return { modified: res, created: [], deleted: [] };
102
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
86
103
  }
@@ -0,0 +1,11 @@
1
+ import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js";
2
+ import { LAYOUT_ERROR } from "../types/index.js";
3
+ import { KnownError } from "../utils/KnownError.js";
4
+ /**
5
+ * Returns a {@link LayoutChange} with the information necessary to expand a frame touching a window edge by a given amount.
6
+ *
7
+ * Changes can be applied to a window with {@link applyFrameChanges}.
8
+ *
9
+ * It expands the frame by redistributing space from neighboring frames.
10
+ */
11
+ export declare function getFrameExpandInfo(win: LayoutWindow, frameId: string, amount: number, expandSide: EdgeSide): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -0,0 +1,49 @@
1
+ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
+ import { walk } from "@alanscodelog/utils/walk";
3
+ import { applyFrameChanges } from "./applyFrameChanges.js";
4
+ import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
6
+ import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
7
+ import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
8
+ import { oppositeSide } from "../helpers/oppositeSide.js";
9
+ import { LAYOUT_ERROR } from "../types/index.js";
10
+ import { KnownError } from "../utils/KnownError.js";
11
+ export function getFrameExpandInfo(win, frameId, amount, expandSide) {
12
+ win = walk(win, void 0, { save: true });
13
+ const frame = win.frames[frameId];
14
+ if (!frame) {
15
+ throw new Error(`Unknown frame ${frameId}`);
16
+ }
17
+ if (!frame.docked) {
18
+ throw new Error(`Frame ${frameId} is not docked.`);
19
+ }
20
+ const toExtract = [frame.id];
21
+ if (Object.keys(win.frames).length === 1) {
22
+ return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot expand.`, { frame });
23
+ }
24
+ if (!(expandSide in findEdgesTouchingWindow(frame))) {
25
+ throw new Error(`Frame ${frameId} does not touch the ${expandSide} edge.`);
26
+ }
27
+ const isVertical = frame.docked === "left" || frame.docked === "right";
28
+ const sizeKey = isVertical ? "width" : "height";
29
+ const posKey = isVertical ? "x" : "y";
30
+ const currentSize = frame[sizeKey];
31
+ const dockedSide = frame.docked;
32
+ const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "expand");
33
+ const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
34
+ const redistributeSide = oppositeSide(frame.docked);
35
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey);
36
+ const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, amount, { pinnedEdgeCoordinates });
37
+ if (changes instanceof KnownError) {
38
+ return changes;
39
+ }
40
+ applyFrameChanges(win, changes);
41
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
42
+ applyFixes();
43
+ pushIfNotIn(toExtract, toFix);
44
+ frame[sizeKey] = currentSize + amount;
45
+ if (frame.docked === "right" || frame.docked === "bottom") {
46
+ frame[posKey] -= amount;
47
+ }
48
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
49
+ }
@@ -0,0 +1,14 @@
1
+ import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js";
2
+ import { LAYOUT_ERROR } from "../types/index.js";
3
+ import { KnownError } from "../utils/KnownError.js";
4
+ /**
5
+ * Returns a {@link LayoutChange} with the information necessary to shrink a frame touching a window edge by a given amount.
6
+ *
7
+ * Changes can be applied to a window with {@link applyFrameChanges}.
8
+ *
9
+ * It shrinks the frame by redistributing the freed space to neighboring frames.
10
+ */
11
+ export declare function getFrameShrinkInfo(win: LayoutWindow, frameId: string, amount: number, shrinkSide: EdgeSide, { allowOutOfBounds }?: {
12
+ /** Allow the resize span to exceed window bounds. */
13
+ allowOutOfBounds?: boolean;
14
+ }): LayoutChange | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -0,0 +1,48 @@
1
+ import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
+ import { walk } from "@alanscodelog/utils/walk";
3
+ import { applyFrameChanges } from "./applyFrameChanges.js";
4
+ import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
+ import { findEdgesTouchingWindow } from "../helpers/findEdgesTouchingWindow.js";
6
+ import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
7
+ import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
8
+ import { oppositeSide } from "../helpers/oppositeSide.js";
9
+ import { LAYOUT_ERROR } from "../types/index.js";
10
+ import { KnownError } from "../utils/KnownError.js";
11
+ export function getFrameShrinkInfo(win, frameId, amount, shrinkSide, {
12
+ allowOutOfBounds = false
13
+ } = {}) {
14
+ win = walk(win, void 0, { save: true });
15
+ const frame = win.frames[frameId];
16
+ if (!frame) throw new Error(`Unknown frame ${frameId}`);
17
+ if (Object.keys(win.frames).length === 1) {
18
+ return new KnownError(LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME, `Frame ${frameId} is the only frame, cannot shrink.`, { frame });
19
+ }
20
+ if (!(shrinkSide in findEdgesTouchingWindow(frame))) {
21
+ throw new Error(`Frame ${frameId} does not touch the ${shrinkSide} edge.`);
22
+ }
23
+ const isVertical = shrinkSide === "left" || shrinkSide === "right";
24
+ const sizeKey = isVertical ? "width" : "height";
25
+ const posKey = isVertical ? "x" : "y";
26
+ const toExtract = [frame.id];
27
+ const { applyFixes, toFix } = framesRedistributeFix(win, frame, shrinkSide, posKey, sizeKey, "shrink");
28
+ const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
29
+ const redistributeSide = oppositeSide(shrinkSide);
30
+ const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, shrinkSide, posKey, sizeKey);
31
+ const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, -amount, { allowOutOfBounds, pinnedEdgeCoordinates });
32
+ if (changes instanceof KnownError) {
33
+ if (changes.code === LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS || changes.code === LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE) {
34
+ changes.message = `This error should never happen, please file a bug report: ${changes.message}`;
35
+ throw changes;
36
+ }
37
+ return changes;
38
+ }
39
+ applyFrameChanges(win, changes);
40
+ pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
41
+ applyFixes();
42
+ pushIfNotIn(toExtract, toFix);
43
+ if (shrinkSide === "right" || shrinkSide === "bottom") {
44
+ frame[posKey] = frame[posKey] + amount;
45
+ }
46
+ frame[sizeKey] = frame[sizeKey] - amount;
47
+ return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
48
+ }
@@ -12,4 +12,4 @@ import { KnownError } from "../utils/KnownError.js";
12
12
  export declare function getFrameUncollapseInfo(win: LayoutWindow, frameId: string, { restoreSize }?: {
13
13
  /** Override the size to restore the frame to. */
14
14
  restoreSize?: Size;
15
- }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES>;
15
+ }): LayoutChange | KnownError<typeof LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS> | KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> | KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES> | KnownError<typeof LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME>;
@@ -1,10 +1,7 @@
1
1
  import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "./applyFrameChanges.js";
4
- import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
5
- import { framesRedistributeFix } from "../helpers/framesRedistributeFix.js";
6
- import { getPinnedEdgesForCollapsedFrames } from "../helpers/getPinnedEdgesForCollapsedFrames.js";
7
- import { oppositeSide } from "../helpers/oppositeSide.js";
4
+ import { getFrameExpandInfo } from "./getFrameExpandInfo.js";
8
5
  import { LAYOUT_ERROR } from "../types/index.js";
9
6
  import { KnownError } from "../utils/KnownError.js";
10
7
  export function getFrameUncollapseInfo(win, frameId, {
@@ -21,7 +18,6 @@ export function getFrameUncollapseInfo(win, frameId, {
21
18
  const toExtract = [frame.id];
22
19
  const isVertical = frame.docked === "left" || frame.docked === "right";
23
20
  const sizeKey = isVertical ? "width" : "height";
24
- const posKey = isVertical ? "x" : "y";
25
21
  const currentSize = frame[sizeKey];
26
22
  const storedSize = restoreSize !== void 0 ? restoreSize[sizeKey] : frame.collapsed;
27
23
  if (frame.collapsed === void 0) {
@@ -33,22 +29,12 @@ export function getFrameUncollapseInfo(win, frameId, {
33
29
  }
34
30
  const expandAmount = storedSize - currentSize;
35
31
  const dockedSide = frame.docked;
36
- const { applyFixes, toFix } = framesRedistributeFix(win, frame, dockedSide, posKey, sizeKey, "expand");
37
- const otherFrameIds = Object.keys(win.frames).filter((id) => id !== frameId);
38
- const redistributeSide = oppositeSide(frame.docked);
39
- const pinnedEdgeCoordinates = getPinnedEdgesForCollapsedFrames(win, frame, dockedSide, posKey, sizeKey);
40
- const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, expandAmount, { pinnedEdgeCoordinates });
41
- if (changes instanceof KnownError) {
42
- return changes;
32
+ const expandResult = getFrameExpandInfo(win, frameId, expandAmount, dockedSide);
33
+ if (expandResult instanceof KnownError) {
34
+ return expandResult;
43
35
  }
44
- applyFrameChanges(win, changes);
45
- pushIfNotIn(toExtract, changes.modified.map((_) => _.id));
46
- applyFixes();
47
- pushIfNotIn(toExtract, toFix);
48
- frame[sizeKey] = storedSize;
36
+ applyFrameChanges(win, expandResult);
37
+ pushIfNotIn(toExtract, expandResult.modified.map((_) => _.id));
49
38
  frame.collapsed = void 0;
50
- if (frame.docked === "right" || frame.docked === "bottom") {
51
- frame[posKey] -= expandAmount;
52
- }
53
39
  return { modified: toExtract.map((_) => win.frames[_]), created: [], deleted: [] };
54
40
  }
@@ -1,8 +1,8 @@
1
1
  import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn";
2
2
  import { walk } from "@alanscodelog/utils/walk";
3
3
  import { applyFrameChanges } from "../layout/applyFrameChanges.js";
4
- import { getFrameCollapseInfo } from "../layout/getFrameCollapseInfo.js";
5
- import { getFrameUncollapseInfo } from "../layout/getFrameUncollapseInfo.js";
4
+ import { getFrameExpandInfo } from "../layout/getFrameExpandInfo.js";
5
+ import { getFrameShrinkInfo } from "../layout/getFrameShrinkInfo.js";
6
6
  import { getRelaxFramesInfo } from "../layout/getRelaxFramesInfo.js";
7
7
  import { settings } from "../settings.js";
8
8
  import { KnownError } from "../utils/KnownError.js";
@@ -30,23 +30,21 @@ export function getUpdateWindowSizeInfo(win, event, { relaxPasses = 2 } = {}) {
30
30
  const targetScaled = Math.round(targetPxSize / (orientation === "horizontal" ? win.pxWidth : win.pxHeight) * maxInt);
31
31
  if (frame[sizeKey] === targetScaled) continue;
32
32
  if (frame[sizeKey] === 0) continue;
33
+ const side = frame.docked;
34
+ const diff = targetScaled - frame[sizeKey];
33
35
  let result;
34
- const wasCollapsed = frame.collapsed;
35
- const wasMinSize = { ...settings.minSize };
36
- settings.minSize = 1;
37
- if (targetScaled < frame[sizeKey]) {
38
- frame.collapsed = void 0;
39
- result = getFrameCollapseInfo(win, frame.id, { collapseSizeScaled: { width: targetScaled, height: targetScaled } });
36
+ if (diff < 0) {
37
+ result = getFrameShrinkInfo(win, frame.id, -diff, side, { allowOutOfBounds: true });
40
38
  } else {
41
- frame.collapsed = Infinity;
42
- result = getFrameUncollapseInfo(win, frame.id, { restoreSize: { width: targetScaled, height: targetScaled } });
39
+ const wasMinSize = { ...settings.minSize };
40
+ settings.minSize = 1;
41
+ result = getFrameExpandInfo(win, frame.id, diff, side);
42
+ settings.minSize = wasMinSize;
43
43
  }
44
44
  if (!(result instanceof KnownError)) {
45
45
  applyFrameChanges(win, result);
46
46
  pushIfNotIn(toExtract, result.modified.map((f) => f.id));
47
47
  }
48
- frame.collapsed = wasCollapsed;
49
- settings.minSize = wasMinSize;
50
48
  const result2 = getRelaxFramesInfo(win, relaxPasses);
51
49
  if (!(result2 instanceof KnownError)) {
52
50
  applyFrameChanges(win, result2);
@@ -12,13 +12,16 @@ export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
12
12
  export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js";
13
13
  export { getFrameDockInfo } from "./getFrameDockInfo.js";
14
14
  export { getFrameDragZones } from "./getFrameDragZones.js";
15
+ export { getFrameExpandInfo } from "./getFrameExpandInfo.js";
15
16
  export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js";
17
+ export { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
16
18
  export { getFrameSplitInfo } from "./getFrameSplitInfo.js";
17
19
  export { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
18
20
  export { getFrameSwapInfo } from "./getFrameSwapInfo.js";
19
21
  export { getFrameTo } from "./getFrameTo.js";
20
22
  export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js";
21
23
  export { getFrameUndockInfo } from "./getFrameUndockInfo.js";
24
+ export { getRelaxFramesInfo } from "./getRelaxFramesInfo.js";
22
25
  export { getUpdateWindowSizeInfo } from "./getUpdateWindowSizeInfo.js";
23
26
  export { getWindowDragZones } from "./getWindowDragZones.js";
24
27
  export { isPointInRect } from "./isPointInRect.js";
@@ -12,13 +12,16 @@ export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js";
12
12
  export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js";
13
13
  export { getFrameDockInfo } from "./getFrameDockInfo.js";
14
14
  export { getFrameDragZones } from "./getFrameDragZones.js";
15
+ export { getFrameExpandInfo } from "./getFrameExpandInfo.js";
15
16
  export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js";
17
+ export { getFrameShrinkInfo } from "./getFrameShrinkInfo.js";
16
18
  export { getFrameSplitInfo } from "./getFrameSplitInfo.js";
17
19
  export { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js";
18
20
  export { getFrameSwapInfo } from "./getFrameSwapInfo.js";
19
21
  export { getFrameTo } from "./getFrameTo.js";
20
22
  export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js";
21
23
  export { getFrameUndockInfo } from "./getFrameUndockInfo.js";
24
+ export { getRelaxFramesInfo } from "./getRelaxFramesInfo.js";
22
25
  export { getUpdateWindowSizeInfo } from "./getUpdateWindowSizeInfo.js";
23
26
  export { getWindowDragZones } from "./getWindowDragZones.js";
24
27
  export { isPointInRect } from "./isPointInRect.js";
@@ -105,6 +105,8 @@ export declare const zEdge: z.ZodObject<{
105
105
  frame: LayoutFrame;
106
106
  } | {
107
107
  frame: LayoutFrame;
108
+ } | {
109
+ frame: LayoutFrame;
108
110
  }>, KnownError<LayoutError, {} | {
109
111
  id: string | undefined;
110
112
  } | {
@@ -162,6 +164,8 @@ export declare const zEdge: z.ZodObject<{
162
164
  frame: LayoutFrame;
163
165
  } | {
164
166
  frame: LayoutFrame;
167
+ } | {
168
+ frame: LayoutFrame;
165
169
  }>>, z.ZodObject<{
166
170
  code: z.ZodString;
167
171
  message: z.ZodString;
@@ -504,6 +508,8 @@ declare const zLayoutShape: z.ZodDiscriminatedUnion<[z.ZodObject<{
504
508
  frame: LayoutFrame;
505
509
  } | {
506
510
  frame: LayoutFrame;
511
+ } | {
512
+ frame: LayoutFrame;
507
513
  }>, KnownError<LayoutError, {} | {
508
514
  id: string | undefined;
509
515
  } | {
@@ -561,6 +567,8 @@ declare const zLayoutShape: z.ZodDiscriminatedUnion<[z.ZodObject<{
561
567
  frame: LayoutFrame;
562
568
  } | {
563
569
  frame: LayoutFrame;
570
+ } | {
571
+ frame: LayoutFrame;
564
572
  }>>, z.ZodObject<{
565
573
  code: z.ZodString;
566
574
  message: z.ZodString;
@@ -643,6 +651,8 @@ declare const zBaseDeco: z.ZodObject<{
643
651
  frame: LayoutFrame;
644
652
  } | {
645
653
  frame: LayoutFrame;
654
+ } | {
655
+ frame: LayoutFrame;
646
656
  }>, KnownError<LayoutError, {} | {
647
657
  id: string | undefined;
648
658
  } | {
@@ -700,6 +710,8 @@ declare const zBaseDeco: z.ZodObject<{
700
710
  frame: LayoutFrame;
701
711
  } | {
702
712
  frame: LayoutFrame;
713
+ } | {
714
+ frame: LayoutFrame;
703
715
  }>>, z.ZodObject<{
704
716
  code: z.ZodString;
705
717
  message: z.ZodString;
@@ -783,6 +795,8 @@ export declare const zSplitDeco: z.ZodObject<{
783
795
  frame: LayoutFrame;
784
796
  } | {
785
797
  frame: LayoutFrame;
798
+ } | {
799
+ frame: LayoutFrame;
786
800
  }>, KnownError<LayoutError, {} | {
787
801
  id: string | undefined;
788
802
  } | {
@@ -840,6 +854,8 @@ export declare const zSplitDeco: z.ZodObject<{
840
854
  frame: LayoutFrame;
841
855
  } | {
842
856
  frame: LayoutFrame;
857
+ } | {
858
+ frame: LayoutFrame;
843
859
  }>>, z.ZodObject<{
844
860
  code: z.ZodString;
845
861
  message: z.ZodString;
@@ -944,6 +960,8 @@ declare const zCloseDeco: z.ZodObject<{
944
960
  frame: LayoutFrame;
945
961
  } | {
946
962
  frame: LayoutFrame;
963
+ } | {
964
+ frame: LayoutFrame;
947
965
  }>, KnownError<LayoutError, {} | {
948
966
  id: string | undefined;
949
967
  } | {
@@ -1001,6 +1019,8 @@ declare const zCloseDeco: z.ZodObject<{
1001
1019
  frame: LayoutFrame;
1002
1020
  } | {
1003
1021
  frame: LayoutFrame;
1022
+ } | {
1023
+ frame: LayoutFrame;
1004
1024
  }>>, z.ZodObject<{
1005
1025
  code: z.ZodString;
1006
1026
  message: z.ZodString;
@@ -1087,6 +1107,8 @@ declare const zFrameDragDeco: z.ZodObject<{
1087
1107
  frame: LayoutFrame;
1088
1108
  } | {
1089
1109
  frame: LayoutFrame;
1110
+ } | {
1111
+ frame: LayoutFrame;
1090
1112
  }>, KnownError<LayoutError, {} | {
1091
1113
  id: string | undefined;
1092
1114
  } | {
@@ -1144,6 +1166,8 @@ declare const zFrameDragDeco: z.ZodObject<{
1144
1166
  frame: LayoutFrame;
1145
1167
  } | {
1146
1168
  frame: LayoutFrame;
1169
+ } | {
1170
+ frame: LayoutFrame;
1147
1171
  }>>, z.ZodObject<{
1148
1172
  code: z.ZodString;
1149
1173
  message: z.ZodString;
@@ -1237,6 +1261,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1237
1261
  frame: LayoutFrame;
1238
1262
  } | {
1239
1263
  frame: LayoutFrame;
1264
+ } | {
1265
+ frame: LayoutFrame;
1240
1266
  }>, KnownError<LayoutError, {} | {
1241
1267
  id: string | undefined;
1242
1268
  } | {
@@ -1294,6 +1320,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1294
1320
  frame: LayoutFrame;
1295
1321
  } | {
1296
1322
  frame: LayoutFrame;
1323
+ } | {
1324
+ frame: LayoutFrame;
1297
1325
  }>>, z.ZodObject<{
1298
1326
  code: z.ZodString;
1299
1327
  message: z.ZodString;
@@ -1384,6 +1412,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1384
1412
  frame: LayoutFrame;
1385
1413
  } | {
1386
1414
  frame: LayoutFrame;
1415
+ } | {
1416
+ frame: LayoutFrame;
1387
1417
  }>, KnownError<LayoutError, {} | {
1388
1418
  id: string | undefined;
1389
1419
  } | {
@@ -1441,6 +1471,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1441
1471
  frame: LayoutFrame;
1442
1472
  } | {
1443
1473
  frame: LayoutFrame;
1474
+ } | {
1475
+ frame: LayoutFrame;
1444
1476
  }>>, z.ZodObject<{
1445
1477
  code: z.ZodString;
1446
1478
  message: z.ZodString;
@@ -1525,6 +1557,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1525
1557
  frame: LayoutFrame;
1526
1558
  } | {
1527
1559
  frame: LayoutFrame;
1560
+ } | {
1561
+ frame: LayoutFrame;
1528
1562
  }>, KnownError<LayoutError, {} | {
1529
1563
  id: string | undefined;
1530
1564
  } | {
@@ -1582,6 +1616,8 @@ export declare const zDeco: z.ZodUnion<readonly [z.ZodObject<{
1582
1616
  frame: LayoutFrame;
1583
1617
  } | {
1584
1618
  frame: LayoutFrame;
1619
+ } | {
1620
+ frame: LayoutFrame;
1585
1621
  }>>, z.ZodObject<{
1586
1622
  code: z.ZodString;
1587
1623
  message: z.ZodString;
@@ -1627,6 +1663,7 @@ export declare const LAYOUT_ERROR: {
1627
1663
  CANT_UNCOLLAPSE_NOT_COLLAPSED: "CANT_UNCOLLAPSE_NOT_COLLAPSED";
1628
1664
  NO_FILL_CANDIDATES: "NO_FILL_CANDIDATES";
1629
1665
  CANT_RESIZE_COLLAPSED_FRAME: "CANT_RESIZE_COLLAPSED_FRAME";
1666
+ CANT_RESIZE_SINGLE_FRAME: "CANT_RESIZE_SINGLE_FRAME";
1630
1667
  };
1631
1668
  export type LayoutError = EnumLike<typeof LAYOUT_ERROR>;
1632
1669
  export type LayoutErrorInfo<T extends LayoutError> = LayoutErrorsInfo[T] extends undefined ? never : LayoutErrorsInfo[T];
@@ -1710,6 +1747,9 @@ export type LayoutErrorsInfo = {
1710
1747
  [LAYOUT_ERROR.CANT_RESIZE_COLLAPSED_FRAME]: {
1711
1748
  frame: LayoutFrame;
1712
1749
  };
1750
+ [LAYOUT_ERROR.CANT_RESIZE_SINGLE_FRAME]: {
1751
+ frame: LayoutFrame;
1752
+ };
1713
1753
  [LAYOUT_ERROR.REDISTRIBUTE_WOULD_RESULT_IN_INVALID_FRAMES]: {
1714
1754
  problemEdgeCoordinates: number[];
1715
1755
  };
@@ -1852,6 +1892,8 @@ export type DragState = {
1852
1892
  * Whether the drag was initiated from a point along the window edge.
1853
1893
  */
1854
1894
  isDraggingFromWindowEdge: boolean;
1895
+ /** Custom context passed to dragStart, available to action handlers via state.eventContext. */
1896
+ eventContext?: Record<string, unknown>;
1855
1897
  win: LayoutWindow;
1856
1898
  };
1857
1899
  export interface ActionDragChangeResult {
@@ -1956,6 +1998,34 @@ export type EdgeDragStartData = {
1956
1998
  export type FrameDragStartData = {
1957
1999
  frameId: FrameId;
1958
2000
  };
2001
+ /**
2002
+ * Handler interface for drag actions.
2003
+ */
2004
+ export interface ActionHandler {
2005
+ eventHandler: (e: KeyboardEvent, state: DragState, forceRecalculateEdges: () => void) => void;
2006
+ /**
2007
+ * Called when the drag coordinates change (during any event). Should return true to allow the edges to be updated/moved, or false to prevent it.
2008
+ *
2009
+ * Can return anything for the end event as it's ignored.
2010
+ *
2011
+ * Can be used to save some context/info to later apply safely during onDragApply.
2012
+ */
2013
+ onDragChange: (...args: Parameters<DragChangeHandler>) => DragChangeResult;
2014
+ /**
2015
+ * Called when drag will be applied. If dragEnd was called with apply false, it will not be called.
2016
+ * Return false to not apply the regular drag end changes (i.e. return false to reset to the position before dragging).
2017
+ */
2018
+ onDragApply: (state: DragState, forceRecalculateEdges: () => void) => {
2019
+ /** Whether to apply the regular drag end changes. Return false to reset to the position before dragging. */
2020
+ apply: boolean;
2021
+ /** Value to resolve the drag promise with. Ignored if `apply` is false. */
2022
+ result: any;
2023
+ };
2024
+ /**
2025
+ * Called after visual edges are recalculated. Action handlers can annotate edges with error info.
2026
+ */
2027
+ annotateEdges?: (edges: Edge[], frames: LayoutFrame[]) => void;
2028
+ }
1959
2029
  export type DragStartFn = {
1960
2030
  (e: PointerEvent, type: "edge", data: EdgeDragStartData): void;
1961
2031
  (e: PointerEvent, type: "frame", data: FrameDragStartData): void;
@@ -153,7 +153,8 @@ export const LAYOUT_ERROR = enumFromArray([
153
153
  "CANT_COLLAPSE_NOT_DOCKED",
154
154
  "CANT_UNCOLLAPSE_NOT_COLLAPSED",
155
155
  "NO_FILL_CANDIDATES",
156
- "CANT_RESIZE_COLLAPSED_FRAME"
156
+ "CANT_RESIZE_COLLAPSED_FRAME",
157
+ "CANT_RESIZE_SINGLE_FRAME"
157
158
  ]);
158
159
  export const zWindowCreate = zLayoutWindowLoose.partial({ id: true, pxWidth: true, pxHeight: true, pxX: true, pxY: true }).extend({ frames: zLayoutWindow.shape.frames.optional() });
159
160
  export const zLayoutCreate = baseLayout.loose().extend({