@surdeddd/wmkit 0.7.2 → 0.9.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 +60 -3
- package/README.ru.md +60 -3
- package/dist/angular.cjs +4 -3
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.d.cts +2 -1
- package/dist/angular.d.ts +2 -1
- package/dist/angular.js +2 -1
- package/dist/angular.js.map +1 -1
- package/dist/binder-DGoj0xG1.d.cts +13 -0
- package/dist/binder-Lo13iTVB.d.ts +13 -0
- package/dist/chrome.cjs +122 -0
- package/dist/chrome.cjs.map +1 -0
- package/dist/chrome.d.cts +25 -0
- package/dist/chrome.d.ts +25 -0
- package/dist/chrome.js +114 -0
- package/dist/chrome.js.map +1 -0
- package/dist/chunk-5XVIC5LH.js +140 -0
- package/dist/chunk-5XVIC5LH.js.map +1 -0
- package/dist/chunk-BDHFXEJN.cjs +149 -0
- package/dist/chunk-BDHFXEJN.cjs.map +1 -0
- package/dist/{chunk-NJAI5R4J.cjs → chunk-TUGQX3CX.js} +205 -175
- package/dist/chunk-TUGQX3CX.js.map +1 -0
- package/dist/{chunk-FOWW4LXS.js → chunk-ZWUVWRUS.cjs} +236 -178
- package/dist/chunk-ZWUVWRUS.cjs.map +1 -0
- package/dist/devtools.cjs +330 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +43 -0
- package/dist/devtools.d.ts +43 -0
- package/dist/devtools.js +326 -0
- package/dist/devtools.js.map +1 -0
- package/dist/gestures.cjs +219 -0
- package/dist/gestures.cjs.map +1 -0
- package/dist/gestures.d.cts +19 -0
- package/dist/gestures.d.ts +19 -0
- package/dist/gestures.js +215 -0
- package/dist/gestures.js.map +1 -0
- package/dist/index.cjs +35 -34
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/react.cjs +4 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -1
- package/dist/react.d.ts +2 -1
- package/dist/react.js +2 -1
- package/dist/react.js.map +1 -1
- package/dist/{binder-DvQFmO3K.d.ts → shared-BoHXlPCu.d.ts} +55 -8
- package/dist/{binder-_L3Iww0u.d.cts → shared-DdGFz3oy.d.cts} +55 -8
- package/dist/solid.cjs +4 -3
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.cts +2 -1
- package/dist/solid.d.ts +2 -1
- package/dist/solid.js +2 -1
- package/dist/solid.js.map +1 -1
- package/dist/svelte.cjs +4 -3
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.cts +2 -1
- package/dist/svelte.d.ts +2 -1
- package/dist/svelte.js +2 -1
- package/dist/svelte.js.map +1 -1
- package/dist/themes/amber.css +316 -0
- package/dist/themes/aqua.css +285 -0
- package/dist/themes/blueprint.css +303 -0
- package/dist/themes/brutalist.css +307 -0
- package/dist/themes/candy.css +277 -0
- package/dist/themes/carbon.css +297 -0
- package/dist/themes/forest.css +278 -0
- package/dist/themes/frost.css +279 -0
- package/dist/themes/glass.css +2 -1
- package/dist/themes/light.css +2 -1
- package/dist/themes/neon.css +289 -0
- package/dist/themes/noir.css +282 -0
- package/dist/themes/paper.css +258 -0
- package/dist/themes/retro.css +8 -0
- package/dist/themes/synth.css +283 -0
- package/dist/themes/terminal.css +273 -0
- package/dist/themes.cjs +8747 -0
- package/dist/themes.cjs.map +1 -0
- package/dist/themes.d.cts +12 -0
- package/dist/themes.d.ts +12 -0
- package/dist/themes.js +8742 -0
- package/dist/themes.js.map +1 -0
- package/dist/vue.cjs +4 -3
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +2 -1
- package/dist/vue.d.ts +2 -1
- package/dist/vue.js +2 -1
- package/dist/vue.js.map +1 -1
- package/package.json +80 -7
- package/dist/chunk-FOWW4LXS.js.map +0 -1
- package/dist/chunk-NJAI5R4J.cjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/chrome/messages.ts","../src/plugins/chrome/template.ts","../src/plugins/chrome/index.ts"],"names":[],"mappings":"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","file":"chrome.js","sourcesContent":["export interface ChromeMessages {\n minimize: string\n maximize: string\n close: string\n}\n\nexport const chromeMessages: ChromeMessages = {\n minimize: 'Minimize',\n maximize: 'Maximize',\n close: 'Close',\n}\n\nexport const chromeMessagesRu: ChromeMessages = {\n minimize: 'Свернуть',\n maximize: 'Развернуть',\n close: 'Закрыть',\n}\n","const ESCAPES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => ESCAPES[char] as string)\n}\n\nexport function compileTemplate(template: string): (values: Record<string, string>) => string {\n const parts = template.split(/\\{\\{\\s*([a-z]+)\\s*\\}\\}/i)\n return (values) =>\n parts.map((part, index) => (index % 2 === 0 ? part : escapeHtml(values[part] ?? ''))).join('')\n}\n","import type { WindowState } from '../../core/types'\nimport type { SkinMount, WindowSkin } from '../../dom/shared'\nimport { type ChromeMessages, chromeMessages } from './messages'\nimport { compileTemplate } from './template'\n\nexport type { ChromeMessages } from './messages'\nexport { chromeMessages, chromeMessagesRu } from './messages'\nexport { compileTemplate } from './template'\n\nexport interface SkinSpec {\n template: string\n styles?: string\n shadow?: boolean\n name?: string\n}\n\nconst SLOT = '[data-wm-content]'\nconst SLOT_PATTERN = /data-wm-content\\b/g\n\nconst BAD_SLOT_COUNT = 'wmkit: a skin template needs exactly one [data-wm-content]'\nconst BAD_STRUCTURE =\n 'wmkit: a skin template needs a single root element that holds its [data-wm-content]'\nconst UNSCOPED_STYLES = 'wmkit: a skin with styles needs a name to scope them to'\n\nfunction valuesOf(win: WindowState): Record<string, string> {\n const variant = win.meta.variant\n return {\n id: win.id,\n title: win.title,\n stage: win.stage,\n layer: win.layer,\n workspace: String(win.workspace),\n variant: typeof variant === 'string' ? variant : '',\n }\n}\n\nfunction injectStyles(doc: Document, name: string, styles: string): void {\n if (doc.head.querySelector(`style[data-wm-skin-styles=\"${name}\"]`)) return\n const tag = doc.createElement('style')\n tag.dataset.wmSkinStyles = name\n tag.textContent = styles\n doc.head.append(tag)\n}\n\nexport function skin(spec: SkinSpec): WindowSkin {\n if ((spec.template.match(SLOT_PATTERN) ?? []).length !== 1) throw new Error(BAD_SLOT_COUNT)\n if (spec.styles !== undefined && spec.name === undefined) throw new Error(UNSCOPED_STYLES)\n const render = compileTemplate(spec.template)\n const sheets = new WeakMap<Document, CSSStyleSheet>()\n\n function adopt(root: ShadowRoot, doc: Document, css: string): void {\n if (typeof CSSStyleSheet.prototype.replaceSync !== 'function') {\n const tag = doc.createElement('style')\n tag.textContent = css\n root.prepend(tag)\n return\n }\n let sheet = sheets.get(doc)\n if (!sheet) {\n sheet = new CSSStyleSheet()\n sheet.replaceSync(css)\n sheets.set(doc, sheet)\n }\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet]\n }\n\n return ({ doc, window: win }): SkinMount => {\n const holder = doc.createElement('template')\n holder.innerHTML = render(valuesOf(win))\n const built = holder.content.firstElementChild as HTMLElement | null\n const slot =\n built === null ? null : built.matches(SLOT) ? built : built.querySelector<HTMLElement>(SLOT)\n if (built === null || slot === null) throw new Error(BAD_STRUCTURE)\n\n if (spec.shadow !== true) {\n if (spec.name !== undefined) built.dataset.wmSkin = spec.name\n if (spec.styles !== undefined) injectStyles(doc, spec.name as string, spec.styles)\n return { element: built, content: slot }\n }\n\n const element = doc.createElement('div')\n if (spec.name !== undefined) element.dataset.wmSkin = spec.name\n const root = element.attachShadow({ mode: 'open' })\n const projection = doc.createElement('slot')\n if (slot === built) root.append(projection)\n else {\n slot.replaceWith(projection)\n root.append(...built.childNodes)\n }\n if (spec.styles !== undefined) adopt(root, doc, spec.styles)\n const content = doc.createElement('div')\n content.dataset.wmContent = ''\n element.append(content)\n return { element, content }\n }\n}\n\nexport function windowChrome(messages: ChromeMessages = chromeMessages): WindowSkin {\n return skin({\n name: 'wmkit',\n template:\n '<section>' +\n '<header data-wm-drag>' +\n '<span data-wm-title>{{title}}</span>' +\n '<span data-wm-controls>' +\n `<button type=\"button\" data-wm-minimize aria-label=\"${messages.minimize}\"></button>` +\n `<button type=\"button\" data-wm-maximize aria-label=\"${messages.maximize}\"></button>` +\n `<button type=\"button\" data-wm-close aria-label=\"${messages.close}\"></button>` +\n '</span>' +\n '</header>' +\n '<div data-wm-content></div>' +\n '</section>',\n })\n}\n\nexport const defaultSkin: WindowSkin = windowChrome()\n\nexport const barebones: WindowSkin = skin({\n name: 'barebones',\n template:\n '<section>' +\n '<header data-wm-drag><span data-wm-title>{{title}}</span></header>' +\n '<div data-wm-content></div>' +\n '</section>',\n})\n"]}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/core/geometry.ts
|
|
2
|
+
function clamp(value, min, max) {
|
|
3
|
+
return Math.min(Math.max(value, min), max);
|
|
4
|
+
}
|
|
5
|
+
function clampSize(size, min, max) {
|
|
6
|
+
return {
|
|
7
|
+
width: clamp(size.width, min.width, max ? max.width : Number.POSITIVE_INFINITY),
|
|
8
|
+
height: clamp(size.height, min.height, max ? max.height : Number.POSITIVE_INFINITY)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function clampToViewport(bounds, viewport, minVisible) {
|
|
12
|
+
if (viewport.width <= 0 || viewport.height <= 0) return bounds;
|
|
13
|
+
const visibleX = Math.min(minVisible, bounds.width);
|
|
14
|
+
const visibleY = Math.min(minVisible, bounds.height);
|
|
15
|
+
const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX));
|
|
16
|
+
const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY));
|
|
17
|
+
return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y };
|
|
18
|
+
}
|
|
19
|
+
function boundsEqual(a, b) {
|
|
20
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
21
|
+
}
|
|
22
|
+
function applyAspect(size, ratio, min, max, drive) {
|
|
23
|
+
if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max);
|
|
24
|
+
const maxWidth = max ? max.width : Number.POSITIVE_INFINITY;
|
|
25
|
+
const maxHeight = max ? max.height : Number.POSITIVE_INFINITY;
|
|
26
|
+
if (drive === "height") {
|
|
27
|
+
const height = clamp(
|
|
28
|
+
size.height,
|
|
29
|
+
Math.max(min.height, min.width / ratio),
|
|
30
|
+
Math.min(maxHeight, maxWidth / ratio)
|
|
31
|
+
);
|
|
32
|
+
return { width: height * ratio, height };
|
|
33
|
+
}
|
|
34
|
+
const width = clamp(
|
|
35
|
+
size.width,
|
|
36
|
+
Math.max(min.width, min.height * ratio),
|
|
37
|
+
Math.min(maxWidth, maxHeight * ratio)
|
|
38
|
+
);
|
|
39
|
+
return { width, height: width / ratio };
|
|
40
|
+
}
|
|
41
|
+
function zoneBounds(zone, viewport) {
|
|
42
|
+
const w = viewport.width;
|
|
43
|
+
const h = viewport.height;
|
|
44
|
+
const halfW = Math.round(w / 2);
|
|
45
|
+
const halfH = Math.round(h / 2);
|
|
46
|
+
const oneThird = Math.round(w / 3);
|
|
47
|
+
const twoThirds = Math.round(w * 2 / 3);
|
|
48
|
+
switch (zone) {
|
|
49
|
+
case "left-third":
|
|
50
|
+
return { x: 0, y: 0, width: oneThird, height: h };
|
|
51
|
+
case "center-third":
|
|
52
|
+
return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h };
|
|
53
|
+
case "right-third":
|
|
54
|
+
return { x: twoThirds, y: 0, width: w - twoThirds, height: h };
|
|
55
|
+
case "left":
|
|
56
|
+
return { x: 0, y: 0, width: halfW, height: h };
|
|
57
|
+
case "right":
|
|
58
|
+
return { x: halfW, y: 0, width: w - halfW, height: h };
|
|
59
|
+
case "top":
|
|
60
|
+
return { x: 0, y: 0, width: w, height: halfH };
|
|
61
|
+
case "bottom":
|
|
62
|
+
return { x: 0, y: halfH, width: w, height: h - halfH };
|
|
63
|
+
case "top-left":
|
|
64
|
+
return { x: 0, y: 0, width: halfW, height: halfH };
|
|
65
|
+
case "top-right":
|
|
66
|
+
return { x: halfW, y: 0, width: w - halfW, height: halfH };
|
|
67
|
+
case "bottom-left":
|
|
68
|
+
return { x: 0, y: halfH, width: halfW, height: h - halfH };
|
|
69
|
+
case "bottom-right":
|
|
70
|
+
return { x: halfW, y: halfH, width: w - halfW, height: h - halfH };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function detectSnapZone(x, y, viewport, options = {}) {
|
|
74
|
+
const threshold = options.threshold ?? 12;
|
|
75
|
+
const cornerSize = options.cornerSize ?? 64;
|
|
76
|
+
const w = viewport.width;
|
|
77
|
+
const h = viewport.height;
|
|
78
|
+
if (w <= 0 || h <= 0) return null;
|
|
79
|
+
const nearLeft = x <= threshold;
|
|
80
|
+
const nearRight = x >= w - threshold;
|
|
81
|
+
const nearTop = y <= threshold;
|
|
82
|
+
const nearBottom = y >= h - threshold;
|
|
83
|
+
if (nearLeft || nearRight) {
|
|
84
|
+
const side = nearLeft ? "left" : "right";
|
|
85
|
+
if (y <= cornerSize) return `top-${side}`;
|
|
86
|
+
if (y >= h - cornerSize) return `bottom-${side}`;
|
|
87
|
+
return side;
|
|
88
|
+
}
|
|
89
|
+
if (nearTop) {
|
|
90
|
+
if (x <= cornerSize) return "top-left";
|
|
91
|
+
if (x >= w - cornerSize) return "top-right";
|
|
92
|
+
return "top";
|
|
93
|
+
}
|
|
94
|
+
if (nearBottom) {
|
|
95
|
+
if (x <= cornerSize) return "bottom-left";
|
|
96
|
+
if (x >= w - cornerSize) return "bottom-right";
|
|
97
|
+
return "bottom";
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function nearestEdge(low, high, targetLow, targetHigh) {
|
|
102
|
+
return [targetLow - low, targetHigh - low, targetLow - high, targetHigh - high];
|
|
103
|
+
}
|
|
104
|
+
function magnetize(bounds, targets, threshold) {
|
|
105
|
+
const result = { x: bounds.x, y: bounds.y, snappedX: false, snappedY: false };
|
|
106
|
+
if (threshold <= 0 || targets.length === 0) return result;
|
|
107
|
+
let bestX = threshold + 1;
|
|
108
|
+
let bestY = threshold + 1;
|
|
109
|
+
for (const target of targets) {
|
|
110
|
+
for (const delta of nearestEdge(
|
|
111
|
+
bounds.x,
|
|
112
|
+
bounds.x + bounds.width,
|
|
113
|
+
target.x,
|
|
114
|
+
target.x + target.width
|
|
115
|
+
)) {
|
|
116
|
+
if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestX)) bestX = delta;
|
|
117
|
+
}
|
|
118
|
+
for (const delta of nearestEdge(
|
|
119
|
+
bounds.y,
|
|
120
|
+
bounds.y + bounds.height,
|
|
121
|
+
target.y,
|
|
122
|
+
target.y + target.height
|
|
123
|
+
)) {
|
|
124
|
+
if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestY)) bestY = delta;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (Math.abs(bestX) <= threshold) {
|
|
128
|
+
result.x = bounds.x + bestX;
|
|
129
|
+
result.snappedX = true;
|
|
130
|
+
}
|
|
131
|
+
if (Math.abs(bestY) <= threshold) {
|
|
132
|
+
result.y = bounds.y + bestY;
|
|
133
|
+
result.snappedY = true;
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { applyAspect, boundsEqual, clamp, clampSize, clampToViewport, detectSnapZone, magnetize, zoneBounds };
|
|
139
|
+
//# sourceMappingURL=chunk-5XVIC5LH.js.map
|
|
140
|
+
//# sourceMappingURL=chunk-5XVIC5LH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/geometry.ts"],"names":[],"mappings":"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","file":"chunk-5XVIC5LH.js","sourcesContent":["import type { Bounds, Size, SnapZone } from './types'\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\nexport function clampSize(size: Size, min: Size, max: Size | null): Size {\n return {\n width: clamp(size.width, min.width, max ? max.width : Number.POSITIVE_INFINITY),\n height: clamp(size.height, min.height, max ? max.height : Number.POSITIVE_INFINITY),\n }\n}\n\nexport function clampToViewport(bounds: Bounds, viewport: Size, minVisible: number): Bounds {\n if (viewport.width <= 0 || viewport.height <= 0) return bounds\n const visibleX = Math.min(minVisible, bounds.width)\n const visibleY = Math.min(minVisible, bounds.height)\n const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX))\n const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY))\n return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y }\n}\n\nexport function boundsEqual(a: Bounds, b: Bounds): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height\n}\n\nexport function applyAspect(\n size: Size,\n ratio: number,\n min: Size,\n max: Size | null,\n drive: 'width' | 'height',\n): Size {\n if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max)\n const maxWidth = max ? max.width : Number.POSITIVE_INFINITY\n const maxHeight = max ? max.height : Number.POSITIVE_INFINITY\n if (drive === 'height') {\n const height = clamp(\n size.height,\n Math.max(min.height, min.width / ratio),\n Math.min(maxHeight, maxWidth / ratio),\n )\n return { width: height * ratio, height }\n }\n const width = clamp(\n size.width,\n Math.max(min.width, min.height * ratio),\n Math.min(maxWidth, maxHeight * ratio),\n )\n return { width, height: width / ratio }\n}\n\nexport function zoneBounds(zone: SnapZone, viewport: Size): Bounds {\n const w = viewport.width\n const h = viewport.height\n const halfW = Math.round(w / 2)\n const halfH = Math.round(h / 2)\n const oneThird = Math.round(w / 3)\n const twoThirds = Math.round((w * 2) / 3)\n switch (zone) {\n case 'left-third':\n return { x: 0, y: 0, width: oneThird, height: h }\n case 'center-third':\n return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h }\n case 'right-third':\n return { x: twoThirds, y: 0, width: w - twoThirds, height: h }\n case 'left':\n return { x: 0, y: 0, width: halfW, height: h }\n case 'right':\n return { x: halfW, y: 0, width: w - halfW, height: h }\n case 'top':\n return { x: 0, y: 0, width: w, height: halfH }\n case 'bottom':\n return { x: 0, y: halfH, width: w, height: h - halfH }\n case 'top-left':\n return { x: 0, y: 0, width: halfW, height: halfH }\n case 'top-right':\n return { x: halfW, y: 0, width: w - halfW, height: halfH }\n case 'bottom-left':\n return { x: 0, y: halfH, width: halfW, height: h - halfH }\n case 'bottom-right':\n return { x: halfW, y: halfH, width: w - halfW, height: h - halfH }\n }\n}\n\nexport interface SnapDetectOptions {\n threshold?: number\n cornerSize?: number\n}\n\nexport function detectSnapZone(\n x: number,\n y: number,\n viewport: Size,\n options: SnapDetectOptions = {},\n): SnapZone | null {\n const threshold = options.threshold ?? 12\n const cornerSize = options.cornerSize ?? 64\n const w = viewport.width\n const h = viewport.height\n if (w <= 0 || h <= 0) return null\n\n const nearLeft = x <= threshold\n const nearRight = x >= w - threshold\n const nearTop = y <= threshold\n const nearBottom = y >= h - threshold\n\n if (nearLeft || nearRight) {\n const side = nearLeft ? 'left' : 'right'\n if (y <= cornerSize) return `top-${side}` as SnapZone\n if (y >= h - cornerSize) return `bottom-${side}` as SnapZone\n return side\n }\n if (nearTop) {\n if (x <= cornerSize) return 'top-left'\n if (x >= w - cornerSize) return 'top-right'\n return 'top'\n }\n if (nearBottom) {\n if (x <= cornerSize) return 'bottom-left'\n if (x >= w - cornerSize) return 'bottom-right'\n return 'bottom'\n }\n return null\n}\n\nexport interface MagnetResult {\n x: number\n y: number\n snappedX: boolean\n snappedY: boolean\n}\n\nfunction nearestEdge(low: number, high: number, targetLow: number, targetHigh: number): number[] {\n return [targetLow - low, targetHigh - low, targetLow - high, targetHigh - high]\n}\n\nexport function magnetize(\n bounds: Bounds,\n targets: readonly Bounds[],\n threshold: number,\n): MagnetResult {\n const result: MagnetResult = { x: bounds.x, y: bounds.y, snappedX: false, snappedY: false }\n if (threshold <= 0 || targets.length === 0) return result\n let bestX = threshold + 1\n let bestY = threshold + 1\n for (const target of targets) {\n for (const delta of nearestEdge(\n bounds.x,\n bounds.x + bounds.width,\n target.x,\n target.x + target.width,\n )) {\n if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestX)) bestX = delta\n }\n for (const delta of nearestEdge(\n bounds.y,\n bounds.y + bounds.height,\n target.y,\n target.y + target.height,\n )) {\n if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestY)) bestY = delta\n }\n }\n if (Math.abs(bestX) <= threshold) {\n result.x = bounds.x + bestX\n result.snappedX = true\n }\n if (Math.abs(bestY) <= threshold) {\n result.y = bounds.y + bestY\n result.snappedY = true\n }\n return result\n}\n"]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/geometry.ts
|
|
4
|
+
function clamp(value, min, max) {
|
|
5
|
+
return Math.min(Math.max(value, min), max);
|
|
6
|
+
}
|
|
7
|
+
function clampSize(size, min, max) {
|
|
8
|
+
return {
|
|
9
|
+
width: clamp(size.width, min.width, max ? max.width : Number.POSITIVE_INFINITY),
|
|
10
|
+
height: clamp(size.height, min.height, max ? max.height : Number.POSITIVE_INFINITY)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function clampToViewport(bounds, viewport, minVisible) {
|
|
14
|
+
if (viewport.width <= 0 || viewport.height <= 0) return bounds;
|
|
15
|
+
const visibleX = Math.min(minVisible, bounds.width);
|
|
16
|
+
const visibleY = Math.min(minVisible, bounds.height);
|
|
17
|
+
const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX));
|
|
18
|
+
const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY));
|
|
19
|
+
return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y };
|
|
20
|
+
}
|
|
21
|
+
function boundsEqual(a, b) {
|
|
22
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
23
|
+
}
|
|
24
|
+
function applyAspect(size, ratio, min, max, drive) {
|
|
25
|
+
if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max);
|
|
26
|
+
const maxWidth = max ? max.width : Number.POSITIVE_INFINITY;
|
|
27
|
+
const maxHeight = max ? max.height : Number.POSITIVE_INFINITY;
|
|
28
|
+
if (drive === "height") {
|
|
29
|
+
const height = clamp(
|
|
30
|
+
size.height,
|
|
31
|
+
Math.max(min.height, min.width / ratio),
|
|
32
|
+
Math.min(maxHeight, maxWidth / ratio)
|
|
33
|
+
);
|
|
34
|
+
return { width: height * ratio, height };
|
|
35
|
+
}
|
|
36
|
+
const width = clamp(
|
|
37
|
+
size.width,
|
|
38
|
+
Math.max(min.width, min.height * ratio),
|
|
39
|
+
Math.min(maxWidth, maxHeight * ratio)
|
|
40
|
+
);
|
|
41
|
+
return { width, height: width / ratio };
|
|
42
|
+
}
|
|
43
|
+
function zoneBounds(zone, viewport) {
|
|
44
|
+
const w = viewport.width;
|
|
45
|
+
const h = viewport.height;
|
|
46
|
+
const halfW = Math.round(w / 2);
|
|
47
|
+
const halfH = Math.round(h / 2);
|
|
48
|
+
const oneThird = Math.round(w / 3);
|
|
49
|
+
const twoThirds = Math.round(w * 2 / 3);
|
|
50
|
+
switch (zone) {
|
|
51
|
+
case "left-third":
|
|
52
|
+
return { x: 0, y: 0, width: oneThird, height: h };
|
|
53
|
+
case "center-third":
|
|
54
|
+
return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h };
|
|
55
|
+
case "right-third":
|
|
56
|
+
return { x: twoThirds, y: 0, width: w - twoThirds, height: h };
|
|
57
|
+
case "left":
|
|
58
|
+
return { x: 0, y: 0, width: halfW, height: h };
|
|
59
|
+
case "right":
|
|
60
|
+
return { x: halfW, y: 0, width: w - halfW, height: h };
|
|
61
|
+
case "top":
|
|
62
|
+
return { x: 0, y: 0, width: w, height: halfH };
|
|
63
|
+
case "bottom":
|
|
64
|
+
return { x: 0, y: halfH, width: w, height: h - halfH };
|
|
65
|
+
case "top-left":
|
|
66
|
+
return { x: 0, y: 0, width: halfW, height: halfH };
|
|
67
|
+
case "top-right":
|
|
68
|
+
return { x: halfW, y: 0, width: w - halfW, height: halfH };
|
|
69
|
+
case "bottom-left":
|
|
70
|
+
return { x: 0, y: halfH, width: halfW, height: h - halfH };
|
|
71
|
+
case "bottom-right":
|
|
72
|
+
return { x: halfW, y: halfH, width: w - halfW, height: h - halfH };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function detectSnapZone(x, y, viewport, options = {}) {
|
|
76
|
+
const threshold = options.threshold ?? 12;
|
|
77
|
+
const cornerSize = options.cornerSize ?? 64;
|
|
78
|
+
const w = viewport.width;
|
|
79
|
+
const h = viewport.height;
|
|
80
|
+
if (w <= 0 || h <= 0) return null;
|
|
81
|
+
const nearLeft = x <= threshold;
|
|
82
|
+
const nearRight = x >= w - threshold;
|
|
83
|
+
const nearTop = y <= threshold;
|
|
84
|
+
const nearBottom = y >= h - threshold;
|
|
85
|
+
if (nearLeft || nearRight) {
|
|
86
|
+
const side = nearLeft ? "left" : "right";
|
|
87
|
+
if (y <= cornerSize) return `top-${side}`;
|
|
88
|
+
if (y >= h - cornerSize) return `bottom-${side}`;
|
|
89
|
+
return side;
|
|
90
|
+
}
|
|
91
|
+
if (nearTop) {
|
|
92
|
+
if (x <= cornerSize) return "top-left";
|
|
93
|
+
if (x >= w - cornerSize) return "top-right";
|
|
94
|
+
return "top";
|
|
95
|
+
}
|
|
96
|
+
if (nearBottom) {
|
|
97
|
+
if (x <= cornerSize) return "bottom-left";
|
|
98
|
+
if (x >= w - cornerSize) return "bottom-right";
|
|
99
|
+
return "bottom";
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
function nearestEdge(low, high, targetLow, targetHigh) {
|
|
104
|
+
return [targetLow - low, targetHigh - low, targetLow - high, targetHigh - high];
|
|
105
|
+
}
|
|
106
|
+
function magnetize(bounds, targets, threshold) {
|
|
107
|
+
const result = { x: bounds.x, y: bounds.y, snappedX: false, snappedY: false };
|
|
108
|
+
if (threshold <= 0 || targets.length === 0) return result;
|
|
109
|
+
let bestX = threshold + 1;
|
|
110
|
+
let bestY = threshold + 1;
|
|
111
|
+
for (const target of targets) {
|
|
112
|
+
for (const delta of nearestEdge(
|
|
113
|
+
bounds.x,
|
|
114
|
+
bounds.x + bounds.width,
|
|
115
|
+
target.x,
|
|
116
|
+
target.x + target.width
|
|
117
|
+
)) {
|
|
118
|
+
if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestX)) bestX = delta;
|
|
119
|
+
}
|
|
120
|
+
for (const delta of nearestEdge(
|
|
121
|
+
bounds.y,
|
|
122
|
+
bounds.y + bounds.height,
|
|
123
|
+
target.y,
|
|
124
|
+
target.y + target.height
|
|
125
|
+
)) {
|
|
126
|
+
if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestY)) bestY = delta;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (Math.abs(bestX) <= threshold) {
|
|
130
|
+
result.x = bounds.x + bestX;
|
|
131
|
+
result.snappedX = true;
|
|
132
|
+
}
|
|
133
|
+
if (Math.abs(bestY) <= threshold) {
|
|
134
|
+
result.y = bounds.y + bestY;
|
|
135
|
+
result.snappedY = true;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
exports.applyAspect = applyAspect;
|
|
141
|
+
exports.boundsEqual = boundsEqual;
|
|
142
|
+
exports.clamp = clamp;
|
|
143
|
+
exports.clampSize = clampSize;
|
|
144
|
+
exports.clampToViewport = clampToViewport;
|
|
145
|
+
exports.detectSnapZone = detectSnapZone;
|
|
146
|
+
exports.magnetize = magnetize;
|
|
147
|
+
exports.zoneBounds = zoneBounds;
|
|
148
|
+
//# sourceMappingURL=chunk-BDHFXEJN.cjs.map
|
|
149
|
+
//# sourceMappingURL=chunk-BDHFXEJN.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/geometry.ts"],"names":[],"mappings":"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","file":"chunk-BDHFXEJN.cjs","sourcesContent":["import type { Bounds, Size, SnapZone } from './types'\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max)\n}\n\nexport function clampSize(size: Size, min: Size, max: Size | null): Size {\n return {\n width: clamp(size.width, min.width, max ? max.width : Number.POSITIVE_INFINITY),\n height: clamp(size.height, min.height, max ? max.height : Number.POSITIVE_INFINITY),\n }\n}\n\nexport function clampToViewport(bounds: Bounds, viewport: Size, minVisible: number): Bounds {\n if (viewport.width <= 0 || viewport.height <= 0) return bounds\n const visibleX = Math.min(minVisible, bounds.width)\n const visibleY = Math.min(minVisible, bounds.height)\n const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX))\n const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY))\n return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y }\n}\n\nexport function boundsEqual(a: Bounds, b: Bounds): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height\n}\n\nexport function applyAspect(\n size: Size,\n ratio: number,\n min: Size,\n max: Size | null,\n drive: 'width' | 'height',\n): Size {\n if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max)\n const maxWidth = max ? max.width : Number.POSITIVE_INFINITY\n const maxHeight = max ? max.height : Number.POSITIVE_INFINITY\n if (drive === 'height') {\n const height = clamp(\n size.height,\n Math.max(min.height, min.width / ratio),\n Math.min(maxHeight, maxWidth / ratio),\n )\n return { width: height * ratio, height }\n }\n const width = clamp(\n size.width,\n Math.max(min.width, min.height * ratio),\n Math.min(maxWidth, maxHeight * ratio),\n )\n return { width, height: width / ratio }\n}\n\nexport function zoneBounds(zone: SnapZone, viewport: Size): Bounds {\n const w = viewport.width\n const h = viewport.height\n const halfW = Math.round(w / 2)\n const halfH = Math.round(h / 2)\n const oneThird = Math.round(w / 3)\n const twoThirds = Math.round((w * 2) / 3)\n switch (zone) {\n case 'left-third':\n return { x: 0, y: 0, width: oneThird, height: h }\n case 'center-third':\n return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h }\n case 'right-third':\n return { x: twoThirds, y: 0, width: w - twoThirds, height: h }\n case 'left':\n return { x: 0, y: 0, width: halfW, height: h }\n case 'right':\n return { x: halfW, y: 0, width: w - halfW, height: h }\n case 'top':\n return { x: 0, y: 0, width: w, height: halfH }\n case 'bottom':\n return { x: 0, y: halfH, width: w, height: h - halfH }\n case 'top-left':\n return { x: 0, y: 0, width: halfW, height: halfH }\n case 'top-right':\n return { x: halfW, y: 0, width: w - halfW, height: halfH }\n case 'bottom-left':\n return { x: 0, y: halfH, width: halfW, height: h - halfH }\n case 'bottom-right':\n return { x: halfW, y: halfH, width: w - halfW, height: h - halfH }\n }\n}\n\nexport interface SnapDetectOptions {\n threshold?: number\n cornerSize?: number\n}\n\nexport function detectSnapZone(\n x: number,\n y: number,\n viewport: Size,\n options: SnapDetectOptions = {},\n): SnapZone | null {\n const threshold = options.threshold ?? 12\n const cornerSize = options.cornerSize ?? 64\n const w = viewport.width\n const h = viewport.height\n if (w <= 0 || h <= 0) return null\n\n const nearLeft = x <= threshold\n const nearRight = x >= w - threshold\n const nearTop = y <= threshold\n const nearBottom = y >= h - threshold\n\n if (nearLeft || nearRight) {\n const side = nearLeft ? 'left' : 'right'\n if (y <= cornerSize) return `top-${side}` as SnapZone\n if (y >= h - cornerSize) return `bottom-${side}` as SnapZone\n return side\n }\n if (nearTop) {\n if (x <= cornerSize) return 'top-left'\n if (x >= w - cornerSize) return 'top-right'\n return 'top'\n }\n if (nearBottom) {\n if (x <= cornerSize) return 'bottom-left'\n if (x >= w - cornerSize) return 'bottom-right'\n return 'bottom'\n }\n return null\n}\n\nexport interface MagnetResult {\n x: number\n y: number\n snappedX: boolean\n snappedY: boolean\n}\n\nfunction nearestEdge(low: number, high: number, targetLow: number, targetHigh: number): number[] {\n return [targetLow - low, targetHigh - low, targetLow - high, targetHigh - high]\n}\n\nexport function magnetize(\n bounds: Bounds,\n targets: readonly Bounds[],\n threshold: number,\n): MagnetResult {\n const result: MagnetResult = { x: bounds.x, y: bounds.y, snappedX: false, snappedY: false }\n if (threshold <= 0 || targets.length === 0) return result\n let bestX = threshold + 1\n let bestY = threshold + 1\n for (const target of targets) {\n for (const delta of nearestEdge(\n bounds.x,\n bounds.x + bounds.width,\n target.x,\n target.x + target.width,\n )) {\n if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestX)) bestX = delta\n }\n for (const delta of nearestEdge(\n bounds.y,\n bounds.y + bounds.height,\n target.y,\n target.y + target.height,\n )) {\n if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestY)) bestY = delta\n }\n }\n if (Math.abs(bestX) <= threshold) {\n result.x = bounds.x + bestX\n result.snappedX = true\n }\n if (Math.abs(bestY) <= threshold) {\n result.y = bounds.y + bestY\n result.snappedY = true\n }\n return result\n}\n"]}
|