@witchcraft/layout 0.1.3 → 0.2.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/README.md +27 -24
- package/dist/module.json +1 -1
- package/dist/runtime/components/FrameDragHandle.d.vue.ts +15 -0
- package/dist/runtime/components/FrameDragHandle.vue +28 -0
- package/dist/runtime/components/FrameDragHandle.vue.d.ts +15 -0
- package/dist/runtime/components/LayoutDecos.d.vue.ts +2 -4
- package/dist/runtime/components/LayoutDecos.vue +10 -29
- package/dist/runtime/components/LayoutDecos.vue.d.ts +2 -4
- package/dist/runtime/components/LayoutEdges.d.vue.ts +3 -3
- package/dist/runtime/components/LayoutEdges.vue +8 -8
- package/dist/runtime/components/LayoutEdges.vue.d.ts +3 -3
- package/dist/runtime/components/LayoutFrame.d.vue.ts +1 -1
- package/dist/runtime/components/LayoutFrame.vue +0 -1
- package/dist/runtime/components/LayoutFrame.vue.d.ts +1 -1
- package/dist/runtime/components/LayoutShapeSquare.d.vue.ts +3 -1
- package/dist/runtime/components/LayoutShapeSquare.vue.d.ts +3 -1
- package/dist/runtime/components/LayoutWindow.d.vue.ts +26 -12
- package/dist/runtime/components/LayoutWindow.vue +95 -84
- package/dist/runtime/components/LayoutWindow.vue.d.ts +26 -12
- package/dist/runtime/composables/useFrames.d.ts +15 -13
- package/dist/runtime/composables/useFrames.js +59 -39
- package/dist/runtime/demo/App.vue +116 -30
- package/dist/runtime/demo/DemoControls.d.vue.ts +4 -1
- package/dist/runtime/demo/DemoControls.vue +98 -4
- package/dist/runtime/demo/DemoControls.vue.d.ts +4 -1
- package/dist/runtime/drag/CloseAction.d.ts +26 -5
- package/dist/runtime/drag/CloseAction.js +87 -40
- package/dist/runtime/drag/DragActionHandler.d.ts +20 -8
- package/dist/runtime/drag/DragActionHandler.js +47 -12
- package/dist/runtime/drag/FrameDragAction.d.ts +45 -0
- package/dist/runtime/drag/FrameDragAction.js +143 -0
- package/dist/runtime/drag/SplitAction.d.ts +32 -11
- package/dist/runtime/drag/SplitAction.js +82 -24
- package/dist/runtime/drag/createDefaultHandlers.d.ts +9 -0
- package/dist/runtime/drag/createDefaultHandlers.js +10 -0
- package/dist/runtime/drag/defaultDragActions.d.ts +9 -0
- package/dist/runtime/drag/defaultDragActions.js +10 -0
- package/dist/runtime/drag/types.d.ts +82 -13
- package/dist/runtime/drag/types.js +1 -0
- package/dist/runtime/helpers/createZoneSideClipPath.d.ts +12 -0
- package/dist/runtime/helpers/createZoneSideClipPath.js +17 -0
- package/dist/runtime/helpers/doEdgesOverlap.d.ts +3 -1
- package/dist/runtime/helpers/doEdgesOverlap.js +5 -5
- package/dist/runtime/helpers/getDockBoundaries.d.ts +19 -0
- package/dist/runtime/helpers/getDockBoundaries.js +14 -0
- package/dist/runtime/helpers/getEdgeLength.d.ts +2 -0
- package/dist/runtime/helpers/getEdgeLength.js +5 -0
- package/dist/runtime/helpers/getIntersections.js +2 -2
- package/dist/runtime/helpers/getIntersectionsCss.js +2 -2
- package/dist/runtime/helpers/getMoveEdgeInfo.js +2 -2
- package/dist/runtime/helpers/getResizeLimit.js +2 -2
- package/dist/runtime/helpers/getShapeSquareCss.js +2 -2
- package/dist/runtime/helpers/getVisualEdgeCss.js +2 -2
- package/dist/runtime/helpers/getVisualEdges.d.ts +1 -1
- package/dist/runtime/helpers/getVisualEdges.js +4 -3
- package/dist/runtime/helpers/index.d.ts +4 -0
- package/dist/runtime/helpers/index.js +4 -0
- package/dist/runtime/helpers/isEdgeEqual.js +2 -4
- package/dist/runtime/helpers/isWindowEdge.js +2 -2
- package/dist/runtime/helpers/isWindowEdgePoint.js +2 -2
- package/dist/runtime/helpers/moveEdge.js +2 -2
- package/dist/runtime/helpers/numberToScaledPercent.d.ts +1 -1
- package/dist/runtime/helpers/numberToScaledPercent.js +2 -2
- package/dist/runtime/helpers/numberToScaledSize.js +2 -2
- package/dist/runtime/helpers/rotateFrames.d.ts +7 -0
- package/dist/runtime/helpers/rotateFrames.js +36 -0
- package/dist/runtime/helpers/scaledPointToPx.d.ts +13 -0
- package/dist/runtime/helpers/scaledPointToPx.js +7 -0
- package/dist/runtime/helpers/toWindowCoord.js +2 -2
- package/dist/runtime/layout/applyFrameChanges.d.ts +10 -0
- package/dist/runtime/layout/applyFrameChanges.js +29 -0
- package/dist/runtime/layout/createSplitDecoFromDrag.d.ts +6 -1
- package/dist/runtime/layout/createSplitDecoFromDrag.js +4 -4
- package/dist/runtime/layout/createSplitDecoShapes.d.ts +7 -0
- package/dist/runtime/layout/{createSplitDecoEdge.js → createSplitDecoShapes.js} +6 -3
- package/dist/runtime/layout/debugFrame.js +2 -1
- package/dist/runtime/layout/findSafeSplitEdge.js +2 -2
- package/dist/runtime/layout/frameCreate.js +2 -2
- package/dist/runtime/layout/getCloseFrameInfo.d.ts +7 -6
- package/dist/runtime/layout/getCloseFrameInfo.js +10 -3
- package/dist/runtime/layout/getDragZones.d.ts +8 -0
- package/dist/runtime/layout/getDragZones.js +32 -0
- package/dist/runtime/layout/getFillEmptySpaceInfo.d.ts +65 -0
- package/dist/runtime/layout/getFillEmptySpaceInfo.js +69 -0
- package/dist/runtime/layout/getFrameCollapseInfo.d.ts +13 -0
- package/dist/runtime/layout/getFrameCollapseInfo.js +93 -0
- package/dist/runtime/layout/getFrameDockInfo.d.ts +9 -0
- package/dist/runtime/layout/getFrameDockInfo.js +82 -0
- package/dist/runtime/layout/getFrameDragZones.d.ts +16 -0
- package/dist/runtime/layout/getFrameDragZones.js +74 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.d.ts +139 -0
- package/dist/runtime/layout/getFrameRearrangeInfo.js +87 -0
- package/dist/runtime/layout/getFrameSplitInfo.d.ts +7 -5
- package/dist/runtime/layout/getFrameSplitInfo.js +10 -3
- package/dist/runtime/layout/getFrameSwapInfo.d.ts +9 -0
- package/dist/runtime/layout/getFrameSwapInfo.js +27 -0
- package/dist/runtime/layout/getFrameTo.js +2 -2
- package/dist/runtime/layout/getFrameUncollapseInfo.d.ts +12 -0
- package/dist/runtime/layout/getFrameUncollapseInfo.js +88 -0
- package/dist/runtime/layout/getFrameUndockInfo.d.ts +13 -0
- package/dist/runtime/layout/getFrameUndockInfo.js +51 -0
- package/dist/runtime/layout/getFramesRedistributeInfo.d.ts +29 -0
- package/dist/runtime/layout/getFramesRedistributeInfo.js +53 -0
- package/dist/runtime/layout/getWindowDragZones.d.ts +6 -0
- package/dist/runtime/layout/getWindowDragZones.js +49 -0
- package/dist/runtime/layout/index.d.ts +14 -5
- package/dist/runtime/layout/index.js +14 -5
- package/dist/runtime/layout/isPointInRect.d.ts +7 -0
- package/dist/runtime/layout/{isPointInFrame.js → isPointInRect.js} +1 -1
- package/dist/runtime/layout/resizeFrame.js +2 -2
- package/dist/runtime/settings.d.ts +41 -16
- package/dist/runtime/settings.js +95 -53
- package/dist/runtime/types/index.d.ts +324 -54
- package/dist/runtime/types/index.js +54 -20
- package/package.json +28 -29
- package/src/runtime/components/FrameDragHandle.vue +30 -0
- package/src/runtime/components/LayoutDecos.vue +12 -36
- package/src/runtime/components/LayoutEdges.vue +27 -23
- package/src/runtime/components/LayoutFrame.vue +6 -5
- package/src/runtime/components/LayoutShapeSquare.vue +9 -3
- package/src/runtime/components/LayoutWindow.vue +110 -101
- package/src/runtime/composables/useFrames.ts +80 -50
- package/src/runtime/demo/App.vue +126 -36
- package/src/runtime/demo/DemoControls.vue +115 -6
- package/src/runtime/drag/CloseAction.ts +106 -44
- package/src/runtime/drag/DragActionHandler.ts +71 -20
- package/src/runtime/drag/FrameDragAction.ts +202 -0
- package/src/runtime/drag/SplitAction.ts +106 -34
- package/src/runtime/drag/createDefaultHandlers.ts +19 -0
- package/src/runtime/drag/defaultDragActions.ts +19 -0
- package/src/runtime/drag/types.ts +90 -20
- package/src/runtime/helpers/createZoneSideClipPath.ts +41 -0
- package/src/runtime/helpers/doEdgesOverlap.ts +11 -5
- package/src/runtime/helpers/getDockBoundaries.ts +36 -0
- package/src/runtime/helpers/getEdgeLength.ts +10 -0
- package/src/runtime/helpers/getIntersections.ts +2 -2
- package/src/runtime/helpers/getIntersectionsCss.ts +2 -2
- package/src/runtime/helpers/getMoveEdgeInfo.ts +2 -2
- package/src/runtime/helpers/getResizeLimit.ts +2 -2
- package/src/runtime/helpers/getShapeSquareCss.ts +2 -2
- package/src/runtime/helpers/getVisualEdgeCss.ts +2 -2
- package/src/runtime/helpers/getVisualEdges.ts +5 -4
- package/src/runtime/helpers/index.ts +4 -0
- package/src/runtime/helpers/isEdgeEqual.ts +2 -4
- package/src/runtime/helpers/isWindowEdge.ts +2 -2
- package/src/runtime/helpers/isWindowEdgePoint.ts +2 -2
- package/src/runtime/helpers/moveEdge.ts +2 -2
- package/src/runtime/helpers/numberToScaledPercent.ts +3 -3
- package/src/runtime/helpers/numberToScaledSize.ts +2 -2
- package/src/runtime/helpers/rotateFrames.ts +45 -0
- package/src/runtime/helpers/scaledPointToPx.ts +13 -0
- package/src/runtime/helpers/toWindowCoord.ts +2 -2
- package/src/runtime/layout/applyFrameChanges.ts +39 -0
- package/src/runtime/layout/createSplitDecoFromDrag.ts +12 -6
- package/src/runtime/layout/{createSplitDecoEdge.ts → createSplitDecoShapes.ts} +17 -7
- package/src/runtime/layout/debugFrame.ts +1 -1
- package/src/runtime/layout/findSafeSplitEdge.ts +3 -3
- package/src/runtime/layout/frameCreate.ts +2 -2
- package/src/runtime/layout/getCloseFrameInfo.ts +21 -8
- package/src/runtime/layout/getDragZones.ts +48 -0
- package/src/runtime/layout/getFillEmptySpaceInfo.ts +177 -0
- package/src/runtime/layout/getFrameCollapseInfo.ts +164 -0
- package/src/runtime/layout/getFrameDockInfo.ts +126 -0
- package/src/runtime/layout/getFrameDragZones.ts +100 -0
- package/src/runtime/layout/getFrameRearrangeInfo.ts +261 -0
- package/src/runtime/layout/getFrameSplitInfo.ts +21 -8
- package/src/runtime/layout/getFrameSwapInfo.ts +45 -0
- package/src/runtime/layout/getFrameTo.ts +2 -2
- package/src/runtime/layout/getFrameUncollapseInfo.ts +160 -0
- package/src/runtime/layout/getFrameUndockInfo.ts +97 -0
- package/src/runtime/layout/getFramesRedistributeInfo.ts +98 -0
- package/src/runtime/layout/getWindowDragZones.ts +59 -0
- package/src/runtime/layout/index.ts +14 -5
- package/src/runtime/layout/isPointInRect.ts +7 -0
- package/src/runtime/layout/resizeFrame.ts +2 -2
- package/src/runtime/settings.ts +69 -49
- package/src/runtime/types/index.ts +143 -28
- package/dist/runtime/layout/closeFrame.d.ts +0 -5
- package/dist/runtime/layout/closeFrame.js +0 -13
- package/dist/runtime/layout/closeFrames.d.ts +0 -2
- package/dist/runtime/layout/closeFrames.js +0 -8
- package/dist/runtime/layout/createSplitDecoEdge.d.ts +0 -2
- package/dist/runtime/layout/frameSplit.d.ts +0 -16
- package/dist/runtime/layout/frameSplit.js +0 -9
- package/dist/runtime/layout/isPointInFrame.d.ts +0 -2
- package/src/runtime/layout/closeFrame.ts +0 -33
- package/src/runtime/layout/closeFrames.ts +0 -14
- package/src/runtime/layout/frameSplit.ts +0 -31
- package/src/runtime/layout/isPointInFrame.ts +0 -7
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { pushIfNotIn } from "@alanscodelog/utils/pushIfNotIn"
|
|
2
|
+
import { walk } from "@alanscodelog/utils/walk"
|
|
3
|
+
|
|
4
|
+
import { applyFrameChanges } from "./applyFrameChanges.js"
|
|
5
|
+
import { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js"
|
|
6
|
+
|
|
7
|
+
import { oppositeSide } from "../helpers/oppositeSide.js"
|
|
8
|
+
import { settings } from "../settings.js"
|
|
9
|
+
import type { EdgeSide, LayoutChange, LayoutWindow } from "../types/index.js"
|
|
10
|
+
import { LAYOUT_ERROR } from "../types/index.js"
|
|
11
|
+
import { KnownError } from "../utils/KnownError.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns a {@link LayoutChange} with the information necessary to uncollapse a docked frame.
|
|
15
|
+
*
|
|
16
|
+
* Changes can be applied to a window with {@link applyFrameChanges}.
|
|
17
|
+
*
|
|
18
|
+
* Uncollapsing restores the frame to its pre-collapse size, shrinking neighboring
|
|
19
|
+
* frames to make room.
|
|
20
|
+
*/
|
|
21
|
+
export function getFrameUncollapseInfo(
|
|
22
|
+
win: LayoutWindow,
|
|
23
|
+
frameId: string
|
|
24
|
+
): LayoutChange
|
|
25
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED>
|
|
26
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS>
|
|
27
|
+
| KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> {
|
|
28
|
+
win = walk(win, undefined, { save: true })
|
|
29
|
+
const frame = win.frames[frameId]
|
|
30
|
+
if (!frame) { throw new Error(`Unknown frame ${frameId}`) }
|
|
31
|
+
if (!frame.docked) { throw new Error(`Frame ${frameId} is not docked.`) }
|
|
32
|
+
const toExtract = [frame.id]
|
|
33
|
+
|
|
34
|
+
const storedSize = frame.collapsed
|
|
35
|
+
if (storedSize === false || storedSize === undefined) {
|
|
36
|
+
return new KnownError(
|
|
37
|
+
LAYOUT_ERROR.CANT_UNCOLLAPSE_NOT_COLLAPSED,
|
|
38
|
+
`Frame ${frameId} is not collapsed.`,
|
|
39
|
+
{ frame }
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isVertical = frame.docked === "left" || frame.docked === "right"
|
|
44
|
+
const sizeKey = isVertical ? "width" : "height" as const
|
|
45
|
+
const posKey = isVertical ? "x" : "y"
|
|
46
|
+
const currentSize = frame[sizeKey]
|
|
47
|
+
const expandAmount = storedSize - currentSize
|
|
48
|
+
|
|
49
|
+
const dockedSide = frame.docked as EdgeSide
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Mirrors the framesToFix logic in getFrameCollapseInfo:
|
|
53
|
+
*
|
|
54
|
+
* The only difference being we can skip the fixes if the frame was fully collapsed to 0.
|
|
55
|
+
*
|
|
56
|
+
* Why is this? Well take this example where A is docked and collapsed and B is docked, in the following two cases we would have no problems expanding as B ends where A begins, but if B shares the edge and A is not fully collapsed it is no longer aligned with the "end" of A.
|
|
57
|
+
*
|
|
58
|
+
* Ok:
|
|
59
|
+
* A - fully collapsed to 0
|
|
60
|
+
* ~ ┌──────────┐
|
|
61
|
+
* │ │B* │
|
|
62
|
+
* │ ├──────────┤
|
|
63
|
+
* │ │ │
|
|
64
|
+
* │ │ │
|
|
65
|
+
* │ └──────────┘
|
|
66
|
+
*
|
|
67
|
+
* Ok:
|
|
68
|
+
* A collapsed to non-zero size
|
|
69
|
+
* Note B still shares A's right edge
|
|
70
|
+
* ┌──┬─────────┐
|
|
71
|
+
* │A~│B* │
|
|
72
|
+
* │ ├─────────┤
|
|
73
|
+
* │ │ │
|
|
74
|
+
* │ │ │
|
|
75
|
+
* └──┴─────────┘
|
|
76
|
+
*
|
|
77
|
+
* Causes issues:
|
|
78
|
+
* A collapsed to non-zero size with B docked first
|
|
79
|
+
* B looses alignment with A causing issues when redistributing
|
|
80
|
+
* ┌────────────┐
|
|
81
|
+
* │B* │
|
|
82
|
+
* ├──┬─────────┤
|
|
83
|
+
* │A~│ │
|
|
84
|
+
* │ │ │
|
|
85
|
+
* └──┴─────────┘
|
|
86
|
+
*/
|
|
87
|
+
const framesToFix: ({
|
|
88
|
+
id: string
|
|
89
|
+
posKey: "x" | "y"
|
|
90
|
+
sizeKey: "width" | "height"
|
|
91
|
+
type: "start" | "end"
|
|
92
|
+
})[] = []
|
|
93
|
+
|
|
94
|
+
const opposite = oppositeSide(dockedSide)
|
|
95
|
+
if (frame[sizeKey] !== 0) {
|
|
96
|
+
for (const other of Object.values(win.frames)) {
|
|
97
|
+
if (frame.id === other.id || !other.docked) continue
|
|
98
|
+
if (other.width === 0 || other.height === 0) continue
|
|
99
|
+
|
|
100
|
+
if (other.docked === opposite) continue
|
|
101
|
+
if (dockedSide === "left" || dockedSide === "top") {
|
|
102
|
+
if (other[posKey] !== 0) continue
|
|
103
|
+
other[posKey] = frame[posKey] + frame[sizeKey]
|
|
104
|
+
other[sizeKey] -= frame[sizeKey] // this is collapsed size
|
|
105
|
+
framesToFix.push({
|
|
106
|
+
id: other.id,
|
|
107
|
+
posKey: posKey,
|
|
108
|
+
sizeKey: sizeKey,
|
|
109
|
+
type: "start"
|
|
110
|
+
})
|
|
111
|
+
} else {
|
|
112
|
+
if (other[posKey] + other[sizeKey] !== settings.maxInt) continue
|
|
113
|
+
other[sizeKey] -= frame[sizeKey]
|
|
114
|
+
framesToFix.push({
|
|
115
|
+
id: other.id,
|
|
116
|
+
posKey: posKey,
|
|
117
|
+
sizeKey: sizeKey,
|
|
118
|
+
type: "end"
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// note fully collapsed frames without an area are already excluded by getFramesRedistributeInfo
|
|
125
|
+
const otherFrameIds = Object.keys(win.frames).filter(id => id !== frameId)
|
|
126
|
+
|
|
127
|
+
const redistributeSide = oppositeSide(frame.docked)
|
|
128
|
+
|
|
129
|
+
const changes = getFramesRedistributeInfo(win, redistributeSide, otherFrameIds, expandAmount)
|
|
130
|
+
|
|
131
|
+
if (changes instanceof KnownError) {
|
|
132
|
+
return changes
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
applyFrameChanges(win, changes)
|
|
136
|
+
pushIfNotIn(toExtract, changes.modified.map(_ => _.id))
|
|
137
|
+
|
|
138
|
+
for (const fix of framesToFix) {
|
|
139
|
+
const other = win.frames[fix.id]
|
|
140
|
+
if (fix.type === "start") {
|
|
141
|
+
const sizeDiff = other[fix.posKey]
|
|
142
|
+
other[fix.posKey] = 0
|
|
143
|
+
other[fix.sizeKey] += sizeDiff
|
|
144
|
+
} else {
|
|
145
|
+
const sizeDiff = settings.maxInt - (other[fix.posKey] + other[fix.sizeKey])
|
|
146
|
+
other[fix.sizeKey] += sizeDiff
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pushIfNotIn(toExtract, framesToFix.map(_ => _.id))
|
|
151
|
+
|
|
152
|
+
frame[sizeKey] = storedSize
|
|
153
|
+
frame.collapsed = false
|
|
154
|
+
|
|
155
|
+
if (frame.docked === "right" || frame.docked === "bottom") {
|
|
156
|
+
frame[posKey] -= expandAmount
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { modified: toExtract.map(_ => win.frames[_]), created: [], deleted: [] }
|
|
160
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { settings } from "../settings.js"
|
|
2
|
+
import type { LayoutChange, LayoutFrame, LayoutWindow } from "../types/index.js"
|
|
3
|
+
import { LAYOUT_ERROR } from "../types/index.js"
|
|
4
|
+
import { KnownError } from "../utils/KnownError.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a {@link LayoutChange} with the information necessary to undock a frame.
|
|
8
|
+
*
|
|
9
|
+
* Changes can be applied to a window with {@link applyFrameChanges}.
|
|
10
|
+
*
|
|
11
|
+
* If a frame is not docked it will throw.
|
|
12
|
+
*
|
|
13
|
+
* If the frame was collapsed it will be uncollapsed.
|
|
14
|
+
*/
|
|
15
|
+
export function getFrameUndockInfo(
|
|
16
|
+
win: LayoutWindow,
|
|
17
|
+
frameId: string
|
|
18
|
+
):
|
|
19
|
+
| LayoutChange
|
|
20
|
+
| KnownError<typeof LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME> {
|
|
21
|
+
const frame = win.frames[frameId]
|
|
22
|
+
if (!frame) { throw new Error(`Unknown frame ${frameId}`) }
|
|
23
|
+
if (!frame.docked) throw new Error("Can't undock frame that is not docked.")
|
|
24
|
+
|
|
25
|
+
const frameSide = frame.docked
|
|
26
|
+
const isVertical = frameSide === "left" || frameSide === "right"
|
|
27
|
+
const sizeKey = isVertical ? "height" : "width" as const
|
|
28
|
+
const posKey = isVertical ? "y" : "x" as const
|
|
29
|
+
const maxInt = settings.maxInt
|
|
30
|
+
const shrinkStartKey = isVertical ? "top" : "left" as const
|
|
31
|
+
const shrinkEndKey = isVertical ? "bottom" : "right" as const
|
|
32
|
+
|
|
33
|
+
if (frame.collapsed) {
|
|
34
|
+
return new KnownError(LAYOUT_ERROR.CANT_UNDOCK_COLLAPSED_FRAME, `Can't undock collapsed frame ${frame.id}.`, { frame: frame.id })
|
|
35
|
+
}
|
|
36
|
+
const frameClone: LayoutFrame = { ...frame, docked: false, collapsed: false }
|
|
37
|
+
const otherDockedFramesToResize = []
|
|
38
|
+
// if the frame is touching either corner, there could be other docked frames that will need to get expanded like here, * means docked, and B and D would need expanding if we undock A.
|
|
39
|
+
/**
|
|
40
|
+
* ┌─────┬──────┐
|
|
41
|
+
* │A* │B* │
|
|
42
|
+
* │ ├──────┤
|
|
43
|
+
* │ │C │
|
|
44
|
+
* │ ├──────┤
|
|
45
|
+
* │ │D* │
|
|
46
|
+
* └─────┴──────┘
|
|
47
|
+
*/
|
|
48
|
+
const dockedSidesToSearch = [
|
|
49
|
+
...frame[posKey] === 0 ? [shrinkStartKey] : [],
|
|
50
|
+
...(frame[posKey] + frame[sizeKey]) === maxInt ? [shrinkEndKey] : []
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
for (const other of Object.values(win.frames)) {
|
|
54
|
+
if (other.id !== frame.id && other.docked && dockedSidesToSearch.includes(other.docked)) {
|
|
55
|
+
otherDockedFramesToResize.push(other)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// if there aren't others we can undock
|
|
60
|
+
if (otherDockedFramesToResize.length === 0) {
|
|
61
|
+
return { modified: [frameClone], created: [], deleted: [] }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const adjustmentKey = frameSide === "left"
|
|
65
|
+
? "x"
|
|
66
|
+
: frameSide === "top"
|
|
67
|
+
? "y"
|
|
68
|
+
: frameSide === "right"
|
|
69
|
+
? "width"
|
|
70
|
+
: "height"
|
|
71
|
+
|
|
72
|
+
const secondaryAdjustmentKey = frameSide === "left"
|
|
73
|
+
? "width"
|
|
74
|
+
: frameSide === "top"
|
|
75
|
+
? "height"
|
|
76
|
+
: frameSide === "right"
|
|
77
|
+
? "x"
|
|
78
|
+
: "y"
|
|
79
|
+
|
|
80
|
+
const adjustmentValue = frameSide === "left" || frameSide === "top" ? 0 : maxInt
|
|
81
|
+
const otherChanges = otherDockedFramesToResize.map(other => {
|
|
82
|
+
// shrink the ends of the undocking frame
|
|
83
|
+
if (other.docked === shrinkStartKey) {
|
|
84
|
+
frameClone[posKey] += other[sizeKey]
|
|
85
|
+
frameClone[sizeKey] -= other[sizeKey]
|
|
86
|
+
} else if (other.docked === shrinkEndKey) {
|
|
87
|
+
frameClone[sizeKey] -= other[sizeKey]
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
...other,
|
|
91
|
+
[adjustmentKey]: adjustmentValue,
|
|
92
|
+
[secondaryAdjustmentKey]: other[secondaryAdjustmentKey] + other[adjustmentKey]
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return { modified: [frameClone, ...otherChanges], created: [], deleted: [] }
|
|
97
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { settings } from "../settings.js"
|
|
2
|
+
import type { EdgeSide, LayoutChange, LayoutFrame, LayoutWindow } from "../types/index.js"
|
|
3
|
+
import { LAYOUT_ERROR } from "../types/index.js"
|
|
4
|
+
import { KnownError } from "../utils/KnownError.js"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns a {@link LayoutChange} with the information necessary to redistribute frames to expand/shrink a certain amount towards the given side.
|
|
9
|
+
*
|
|
10
|
+
* Changes can be applied to a window with {@link applyFrameChanges}.
|
|
11
|
+
*
|
|
12
|
+
* While it checks for bounds/space issues (unless allowOutOfBounds is true), it does not check that all the frames correctly share/fill the start/end edges.
|
|
13
|
+
*
|
|
14
|
+
* If you try to resize a layout like this to the right, you will get issues with frame edges not lining up as frame B would be resized less than A. If you had a frame at the empty space that you excluded, it's right edge would no longer align with B's.
|
|
15
|
+
*
|
|
16
|
+
* See {@link getFrameCollapseInfo} and {@link getFrameUncollapseInfo} for how to work around this (you must move frames like A to share the edge then move them back). Excluding them from the calcuation usually leads to subtler errors. Such as if there are frames after A and B to the right, excluding A would mean A's right edge would stop aligning with those other frames.
|
|
17
|
+
*
|
|
18
|
+
* ┌────────────┐
|
|
19
|
+
* │A │
|
|
20
|
+
* └──┬─────────┤
|
|
21
|
+
* │B │
|
|
22
|
+
* └─────────┘
|
|
23
|
+
*
|
|
24
|
+
* Also note that it automatically excludes all collapsed frames without an area (height or width === 0).
|
|
25
|
+
*/
|
|
26
|
+
export function getFramesRedistributeInfo(
|
|
27
|
+
win: LayoutWindow,
|
|
28
|
+
/** Side to expand/shrink to. */
|
|
29
|
+
side: EdgeSide,
|
|
30
|
+
frameIds: string[],
|
|
31
|
+
/** This can be negative to expand the frames instead (e.g. when collapsing a docked frame). */
|
|
32
|
+
amountScaled: number,
|
|
33
|
+
/** Allow the resize span to exceed window bounds. */
|
|
34
|
+
allowOutOfBounds = false
|
|
35
|
+
): LayoutChange
|
|
36
|
+
| KnownError<typeof LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS>
|
|
37
|
+
| KnownError<typeof LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE> {
|
|
38
|
+
const isLeftOrTop = side === "left" || side === "top"
|
|
39
|
+
const isHorizontalSide = side === "left" || side === "right"
|
|
40
|
+
const posKey = isHorizontalSide ? "x" : "y" as const
|
|
41
|
+
const sizeKey = isHorizontalSide ? "width" : "height" as const
|
|
42
|
+
|
|
43
|
+
const frames = frameIds.map(id => win.frames[id])
|
|
44
|
+
|
|
45
|
+
const edgeSet = new Set<number>()
|
|
46
|
+
for (const frame of frames) {
|
|
47
|
+
edgeSet.add(frame[posKey])
|
|
48
|
+
edgeSet.add(frame[posKey] + frame[sizeKey])
|
|
49
|
+
}
|
|
50
|
+
const uniqueEdges = [...edgeSet].sort((a, b) => a - b)
|
|
51
|
+
|
|
52
|
+
const maxInt = settings.maxInt
|
|
53
|
+
const resizeSpan = uniqueEdges[uniqueEdges.length - 1] - uniqueEdges[0]
|
|
54
|
+
const newSpan = resizeSpan - amountScaled
|
|
55
|
+
if (!allowOutOfBounds && (newSpan > maxInt || newSpan < 0)) {
|
|
56
|
+
return new KnownError(LAYOUT_ERROR.REDISTRIBUTE_OUT_OF_BOUNDS, `Not enough space to resize frames, needed ${newSpan} but min/max as 0/${maxInt}.`, { max: maxInt, min: 0, wanted: newSpan })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const anchorEdge = isLeftOrTop ? uniqueEdges[0] : uniqueEdges[uniqueEdges.length - 1]
|
|
60
|
+
const dirMultiplier = isLeftOrTop ? -1 : 1
|
|
61
|
+
|
|
62
|
+
const newEdges: number[] = []
|
|
63
|
+
for (let i = 0; i < uniqueEdges.length; i++) {
|
|
64
|
+
const edge = uniqueEdges[i]
|
|
65
|
+
const distFromAnchor = anchorEdge === uniqueEdges[0] ? edge - anchorEdge : anchorEdge - edge
|
|
66
|
+
const rawMovement = dirMultiplier * distFromAnchor / resizeSpan * amountScaled
|
|
67
|
+
const newEdge = edge + Math.trunc(rawMovement)
|
|
68
|
+
newEdges.push(newEdge)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ensure the edge opposite the anchor lands exactly on the correct position
|
|
72
|
+
const oppositeEdgeIndex = isLeftOrTop ? uniqueEdges.length - 1 : 0
|
|
73
|
+
newEdges[oppositeEdgeIndex] = isLeftOrTop ? anchorEdge + newSpan : anchorEdge - newSpan
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const minSize = settings.minSizeScaled[sizeKey]
|
|
77
|
+
const result: LayoutFrame[] = []
|
|
78
|
+
for (const frame of frames) {
|
|
79
|
+
if (frame.collapsed && (frame.width === 0 || frame.height === 0)) continue
|
|
80
|
+
const startEdge = frame[posKey]
|
|
81
|
+
const endEdge = frame[posKey] + frame[sizeKey]
|
|
82
|
+
const newStart = newEdges[uniqueEdges.indexOf(startEdge)]
|
|
83
|
+
const newEnd = newEdges[uniqueEdges.indexOf(endEdge)]
|
|
84
|
+
const newSize = newEnd - newStart
|
|
85
|
+
|
|
86
|
+
if (newSize < minSize) {
|
|
87
|
+
return new KnownError(LAYOUT_ERROR.NO_SPACE_TO_REDISTRIBUTE, `Redistribute would cause frame ${frame.id} to shrink to ${newSize} which is below minimum ${minSize}.`, { frameSizeNeeded: newSize, minFrameSize: minSize })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
result.push({
|
|
91
|
+
...frame,
|
|
92
|
+
[posKey]: newStart,
|
|
93
|
+
[sizeKey]: newSize
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { modified: result, created: [], deleted: [] }
|
|
98
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { numberToScaledPercent } from "../helpers/numberToScaledPercent.js"
|
|
2
|
+
import { settings } from "../settings.js"
|
|
3
|
+
import type { LayoutWindow, WindowEdgeZone } from "../types/index.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns window edge drag zones (in scaled coordinates)
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export function getWindowDragZones(
|
|
10
|
+
win: LayoutWindow,
|
|
11
|
+
thresholdPx: number
|
|
12
|
+
): WindowEdgeZone[] {
|
|
13
|
+
const thX = numberToScaledPercent (thresholdPx, win.pxWidth)
|
|
14
|
+
const thY = numberToScaledPercent (thresholdPx, win.pxHeight)
|
|
15
|
+
const maxInt = settings.maxInt
|
|
16
|
+
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
type: "window",
|
|
20
|
+
side: "top",
|
|
21
|
+
x: 0,
|
|
22
|
+
y: 0,
|
|
23
|
+
width: maxInt,
|
|
24
|
+
height: thY,
|
|
25
|
+
pxWidth: win.pxWidth,
|
|
26
|
+
pxHeight: thresholdPx
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: "window",
|
|
30
|
+
side: "bottom",
|
|
31
|
+
x: 0,
|
|
32
|
+
y: maxInt - thY,
|
|
33
|
+
width: maxInt,
|
|
34
|
+
height: thY,
|
|
35
|
+
pxWidth: win.pxWidth,
|
|
36
|
+
pxHeight: thresholdPx
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "window",
|
|
40
|
+
side: "left",
|
|
41
|
+
x: 0,
|
|
42
|
+
y: 0,
|
|
43
|
+
width: thX,
|
|
44
|
+
height: maxInt,
|
|
45
|
+
pxWidth: thresholdPx,
|
|
46
|
+
pxHeight: win.pxHeight
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: "window",
|
|
50
|
+
side: "right",
|
|
51
|
+
x: maxInt - thX,
|
|
52
|
+
y: 0,
|
|
53
|
+
width: thX,
|
|
54
|
+
height: maxInt,
|
|
55
|
+
pxWidth: thresholdPx,
|
|
56
|
+
pxHeight: win.pxHeight
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
/* Autogenerated Index */
|
|
2
2
|
|
|
3
|
-
export {
|
|
4
|
-
export { closeFrames } from "./closeFrames.js"
|
|
5
|
-
export { createSplitDecoEdge } from "./createSplitDecoEdge.js"
|
|
3
|
+
export { applyFrameChanges } from "./applyFrameChanges.js"
|
|
6
4
|
export { createSplitDecoFromDrag } from "./createSplitDecoFromDrag.js"
|
|
5
|
+
export { createSplitDecoShapes } from "./createSplitDecoShapes.js"
|
|
7
6
|
export { debugFrame } from "./debugFrame.js"
|
|
8
7
|
export { findFramesTouchingEdge } from "./findFramesTouchingEdge.js"
|
|
9
8
|
export { findSafeSplitEdgeAndPosition } from "./findSafeSplitEdge.js"
|
|
10
9
|
export { findVisualEdge } from "./findVisualEdge.js"
|
|
11
10
|
export { frameCreate } from "./frameCreate.js"
|
|
12
|
-
export { frameSplit } from "./frameSplit.js"
|
|
13
11
|
export { getCloseFrameInfo } from "./getCloseFrameInfo.js"
|
|
12
|
+
export { getDragZones } from "./getDragZones.js"
|
|
13
|
+
export { getFillEmptySpaceInfo } from "./getFillEmptySpaceInfo.js"
|
|
14
|
+
export { getFrameCollapseInfo } from "./getFrameCollapseInfo.js"
|
|
15
|
+
export { getFrameDockInfo } from "./getFrameDockInfo.js"
|
|
16
|
+
export { getFrameDragZones } from "./getFrameDragZones.js"
|
|
17
|
+
export { getFrameRearrangeInfo } from "./getFrameRearrangeInfo.js"
|
|
14
18
|
export { getFrameSplitInfo } from "./getFrameSplitInfo.js"
|
|
19
|
+
export { getFramesRedistributeInfo } from "./getFramesRedistributeInfo.js"
|
|
20
|
+
export { getFrameSwapInfo } from "./getFrameSwapInfo.js"
|
|
15
21
|
export { getFrameTo } from "./getFrameTo.js"
|
|
16
|
-
export {
|
|
22
|
+
export { getFrameUncollapseInfo } from "./getFrameUncollapseInfo.js"
|
|
23
|
+
export { getFrameUndockInfo } from "./getFrameUndockInfo.js"
|
|
24
|
+
export { getWindowDragZones } from "./getWindowDragZones.js"
|
|
25
|
+
export { isPointInRect } from "./isPointInRect.js"
|
|
17
26
|
export { layoutAddWindow } from "./layoutAddWindow.js"
|
|
18
27
|
export { layoutCreate } from "./layoutCreate.js"
|
|
19
28
|
export { layoutRemoveWindow } from "./layoutRemoveWindow.js"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { inRange } from "../helpers/inRange.js"
|
|
2
|
+
import type { Point } from "../types/index.js"
|
|
3
|
+
|
|
4
|
+
export function isPointInRect(frame: { x: number, y: number, width: number, height: number }, point: Point): boolean {
|
|
5
|
+
return inRange(point.x, frame.x, frame.x + frame.width)
|
|
6
|
+
&& inRange(point.y, frame.y, frame.y + frame.height)
|
|
7
|
+
}
|
|
@@ -7,7 +7,7 @@ import { getResizeLimit } from "../helpers/getResizeLimit.js"
|
|
|
7
7
|
import { getVisualEdges } from "../helpers/getVisualEdges.js"
|
|
8
8
|
import { isWindowEdge } from "../helpers/isWindowEdge.js"
|
|
9
9
|
import { resizeByEdge } from "../helpers/resizeByEdge.js"
|
|
10
|
-
import {
|
|
10
|
+
import { settings } from "../settings.js"
|
|
11
11
|
import type { Edge, ExtendedDirection, LayoutFrame, LayoutWindow, Size } from "../types/index.js"
|
|
12
12
|
|
|
13
13
|
export function resizeFrame(
|
|
@@ -17,7 +17,7 @@ export function resizeFrame(
|
|
|
17
17
|
/** Scaled */
|
|
18
18
|
distance: Size,
|
|
19
19
|
/** Scaled */
|
|
20
|
-
minSize: Size =
|
|
20
|
+
minSize: Size = settings.minSizeScaled
|
|
21
21
|
): boolean {
|
|
22
22
|
const originalDistance = distance
|
|
23
23
|
const frameEdges = frameToEdges(frame)
|
package/src/runtime/settings.ts
CHANGED
|
@@ -1,63 +1,83 @@
|
|
|
1
1
|
import type { Point, Size } from "./types/index.js"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// todo think of better way :/
|
|
3
|
+
export class Settings {
|
|
4
|
+
private _scale = 3
|
|
5
|
+
private _maxInt = 100 * (10 ** 3)
|
|
6
|
+
get scale() { return this._scale }
|
|
7
|
+
set scale(v: number) {
|
|
8
|
+
this._scale = v
|
|
9
|
+
this._maxInt = 100 * (10 ** v)
|
|
10
|
+
this._recalcAll()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get maxInt() { return this._maxInt }
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
|
|
16
|
+
private _snapPoint = { x: 0.5, y: 0.5 }
|
|
17
|
+
private _snapPointScaled = { x: Math.round(0.5 * (10 ** 3)), y: Math.round(0.5 * (10 ** 3)) }
|
|
18
|
+
|
|
19
|
+
get snapPoint() { return this._snapPoint }
|
|
20
|
+
set snapPoint(v: number | Point) {
|
|
21
|
+
if (typeof v === "number") { this._snapPoint = { x: v, y: v } } else { this._snapPoint = { x: v.x, y: v.y } }
|
|
22
|
+
this._snapPointScaled = this._scalePoint(this._snapPoint)
|
|
20
23
|
}
|
|
21
|
-
g.SCALE = scale
|
|
22
|
-
g.maxInt = max
|
|
23
|
-
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
get snapPointScaled() { return this._snapPointScaled }
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
private _minSize = { width: 10 ** 3, height: 10 ** 3 }
|
|
29
|
+
private _minSizeScaled = { width: 10 ** 3, height: 10 ** 3 }
|
|
30
|
+
|
|
31
|
+
get minSize() { return this._minSize }
|
|
32
|
+
set minSize(v: number | Size) {
|
|
33
|
+
if (typeof v === "number") { this._minSize = { width: v, height: v } } else { this._minSize = { width: v.width, height: v.height } }
|
|
34
|
+
this._minSizeScaled = this._scaleSize(this._minSize)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get minSizeScaled() { return this._minSizeScaled }
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
private _collapseSize = { width: 0, height: 0 }
|
|
40
|
+
private _collapseSizeScaled = { width: 0, height: 0 }
|
|
41
|
+
|
|
42
|
+
get collapseSize() { return this._collapseSize }
|
|
43
|
+
set collapseSize(v: number | Size) {
|
|
44
|
+
if (typeof v === "number") { this._collapseSize = { width: v, height: v } } else { this._collapseSize = { width: v.width, height: v.height } }
|
|
45
|
+
this._collapseSizeScaled = this._scaleSize(this._collapseSize)
|
|
36
46
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
|
|
48
|
+
get collapseSizeScaled() { return this._collapseSizeScaled }
|
|
49
|
+
|
|
50
|
+
private _maxPerpendicularLength = { width: 20, height: 20 }
|
|
51
|
+
private _maxPerpendicularLengthScaled = { width: Math.round(20 * (10 ** 3)), height: Math.round(20 * (10 ** 3)) }
|
|
52
|
+
|
|
53
|
+
get maxPerpendicularLength() { return this._maxPerpendicularLength }
|
|
54
|
+
set maxPerpendicularLength(v: number | Size) {
|
|
55
|
+
if (typeof v === "number") { this._maxPerpendicularLength = { width: v, height: v } } else { this._maxPerpendicularLength = { ...v } }
|
|
56
|
+
this._maxPerpendicularLengthScaled = this._scaleSize(this._maxPerpendicularLength)
|
|
40
57
|
}
|
|
41
|
-
}
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
return g.snapPoint
|
|
45
|
-
}
|
|
59
|
+
get maxPerpendicularLengthScaled() { return this._maxPerpendicularLengthScaled }
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} else {
|
|
52
|
-
g.MARGIN_PERCENTAGE_WIDTH = margin.width
|
|
53
|
-
g.MARGIN_PERCENTAGE_HEIGHT = margin.height
|
|
61
|
+
|
|
62
|
+
private _scalePoint(p: Point): Point {
|
|
63
|
+
const m = 10 ** this._scale
|
|
64
|
+
return { x: Math.round(p.x * m), y: Math.round(p.y * m) }
|
|
54
65
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
|
|
67
|
+
private _scaleSize(s: Size): Size {
|
|
68
|
+
const m = 10 ** this._scale
|
|
69
|
+
return { width: Math.round(s.width * m), height: Math.round(s.height * m) }
|
|
58
70
|
}
|
|
59
|
-
}
|
|
60
71
|
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
private _recalcAll() {
|
|
73
|
+
this.snapPoint = this._snapPoint
|
|
74
|
+
this.minSize = this._minSize
|
|
75
|
+
this.collapseSize = this._collapseSize
|
|
76
|
+
this.maxPerpendicularLength = this._maxPerpendicularLength
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ==== px sized, don't require recalc
|
|
80
|
+
zoneSizes = { frameEdgePx: 40, windowEdgePx: 20 }
|
|
63
81
|
}
|
|
82
|
+
|
|
83
|
+
export const settings = new Settings()
|