@griddle/vue 0.1.7 → 0.1.9
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 +1 -1
- package/dist/GriddleGrid.vue.d.ts.map +1 -1
- package/dist/LoopGrid.vue.d.ts.map +1 -1
- package/dist/index.js +447 -406
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +30 -0
- package/dist/interaction.d.ts.map +1 -1
- package/dist/interaction.js +69 -13
- package/dist/interaction.js.map +1 -1
- package/package.json +3 -3
- package/src/GriddleGrid.vue +48 -21
- package/src/LoopGrid.vue +22 -13
- package/src/interaction.ts +100 -0
package/dist/interaction.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interaction.js","sources":["../src/interaction.ts"],"sourcesContent":["export interface InteractionScale {\n x: number;\n y: number;\n}\n\ntype MeasurableElement = Pick<\n HTMLElement,\n 'getBoundingClientRect' | 'offsetWidth' | 'offsetHeight'\n>;\n\nfunction validScale(value: number): number | null {\n return Number.isFinite(value) && value > 0 ? value : null;\n}\n\n/**\n * Measure how CSS transforms map an element's local coordinate space into\n * viewport pixels. PointerEvent.clientX/clientY are viewport coordinates,\n * while Griddle's cells and inline transforms use the element's local CSS\n * pixels, so drag math must cross this boundary explicitly.\n */\nexport function measureInteractionScale(\n element: MeasurableElement | null,\n): InteractionScale {\n if (!element) return { x: 1, y: 1 };\n\n const rect = element.getBoundingClientRect();\n const measuredX = validScale(\n element.offsetWidth > 0 ? rect.width / element.offsetWidth : NaN,\n );\n const measuredY = validScale(\n element.offsetHeight > 0 ? rect.height / element.offsetHeight : NaN,\n );\n\n // A content-sized grid can temporarily report a zero height while mounting.\n // Prefer the other measured axis before falling back to an unscaled value;\n // this also handles the common uniform transform: scale(...) host.\n return {\n x: measuredX ?? measuredY ?? 1,\n y: measuredY ?? measuredX ?? 1,\n };\n}\n\n/** Convert a viewport-space pointer delta into the grid's local CSS pixels. */\nexport function toLocalInteractionDelta(\n dx: number,\n dy: number,\n scale: InteractionScale,\n): { dx: number; dy: number } {\n return {\n dx: dx / scale.x,\n dy: dy / scale.y,\n };\n}\n"],"names":["validScale","value","measureInteractionScale","element","rect","measuredX","measuredY","toLocalInteractionDelta","dx","dy","scale"],"mappings":"
|
|
1
|
+
{"version":3,"file":"interaction.js","sources":["../src/interaction.ts"],"sourcesContent":["export interface InteractionScale {\n x: number;\n y: number;\n}\n\nexport interface ResizePreviewInput {\n corner: 'nw' | 'ne' | 'sw' | 'se';\n startCol: number;\n startRow: number;\n startW: number;\n startH: number;\n stepsX: number;\n stepsY: number;\n minW: number;\n minH: number;\n maxW: number;\n maxH: number;\n cols: number;\n rows: number;\n infiniteX: boolean;\n infiniteY: boolean;\n}\n\nexport interface ResizePreview {\n col: number;\n row: number;\n w: number;\n h: number;\n}\n\ntype MeasurableElement = Pick<\n HTMLElement,\n 'getBoundingClientRect' | 'offsetWidth' | 'offsetHeight'\n>;\n\nfunction validScale(value: number): number | null {\n return Number.isFinite(value) && value > 0 ? value : null;\n}\n\n/**\n * Measure how CSS transforms map an element's local coordinate space into\n * viewport pixels. PointerEvent.clientX/clientY are viewport coordinates,\n * while Griddle's cells and inline transforms use the element's local CSS\n * pixels, so drag math must cross this boundary explicitly.\n */\nexport function measureInteractionScale(\n element: MeasurableElement | null,\n): InteractionScale {\n if (!element) return { x: 1, y: 1 };\n\n const rect = element.getBoundingClientRect();\n const measuredX = validScale(\n element.offsetWidth > 0 ? rect.width / element.offsetWidth : NaN,\n );\n const measuredY = validScale(\n element.offsetHeight > 0 ? rect.height / element.offsetHeight : NaN,\n );\n\n // A content-sized grid can temporarily report a zero height while mounting.\n // Prefer the other measured axis before falling back to an unscaled value;\n // this also handles the common uniform transform: scale(...) host.\n return {\n x: measuredX ?? measuredY ?? 1,\n y: measuredY ?? measuredX ?? 1,\n };\n}\n\n/** Convert a viewport-space pointer delta into the grid's local CSS pixels. */\nexport function toLocalInteractionDelta(\n dx: number,\n dy: number,\n scale: InteractionScale,\n): { dx: number; dy: number } {\n return {\n dx: dx / scale.x,\n dy: dy / scale.y,\n };\n}\n\nfunction clamp(value: number, lower: number, upper: number): number {\n if (upper < lower) return upper;\n return Math.min(upper, Math.max(lower, value));\n}\n\n/**\n * Resolve a corner resize while preserving the opposite edges and trimming\n * the dragged edges at finite grid bounds.\n */\nexport function resolveResizePreview({\n corner,\n startCol,\n startRow,\n startW,\n startH,\n stepsX,\n stepsY,\n minW,\n minH,\n maxW,\n maxH,\n cols,\n rows,\n infiniteX,\n infiniteY,\n}: ResizePreviewInput): ResizePreview {\n const startRight = startCol + startW;\n const startBottom = startRow + startH;\n const east = corner === 'ne' || corner === 'se';\n const south = corner === 'se' || corner === 'sw';\n\n let col = startCol;\n let right = startRight;\n if (east) {\n const maxRight = Math.min(\n startCol + maxW,\n infiniteX ? Infinity : cols,\n );\n right = clamp(startRight + stepsX, startCol + minW, maxRight);\n } else {\n const minLeft = Math.max(startRight - maxW, 0);\n col = clamp(startCol + stepsX, minLeft, startRight - minW);\n }\n\n let row = startRow;\n let bottom = startBottom;\n if (south) {\n const maxBottom = Math.min(\n startRow + maxH,\n infiniteY ? Infinity : rows,\n );\n bottom = clamp(startBottom + stepsY, startRow + minH, maxBottom);\n } else {\n const minTop = Math.max(startBottom - maxH, 0);\n row = clamp(startRow + stepsY, minTop, startBottom - minH);\n }\n\n return {\n col,\n row,\n w: right - col,\n h: bottom - row,\n };\n}\n\n/** Clamp a pointer-derived cell index to the cells addressable by the grid. */\nexport function clampInteractionCell(\n cell: number,\n extent: number,\n infinite: boolean,\n): number {\n const nonNegative = Math.max(0, cell);\n return infinite ? nonNegative : Math.min(extent - 1, nonNegative);\n}\n"],"names":["validScale","value","measureInteractionScale","element","rect","measuredX","measuredY","toLocalInteractionDelta","dx","dy","scale","clamp","lower","upper","resolveResizePreview","corner","startCol","startRow","startW","startH","stepsX","stepsY","minW","minH","maxW","maxH","cols","rows","infiniteX","infiniteY","startRight","startBottom","east","south","col","right","maxRight","minLeft","row","bottom","maxBottom","minTop","clampInteractionCell","cell","extent","infinite","nonNegative"],"mappings":"AAmCA,SAASA,EAAWC,GAA8B;AAChD,SAAO,OAAO,SAASA,CAAK,KAAKA,IAAQ,IAAIA,IAAQ;AACvD;AAQO,SAASC,EACdC,GACkB;AAClB,MAAI,CAACA,EAAS,QAAO,EAAE,GAAG,GAAG,GAAG,EAAA;AAEhC,QAAMC,IAAOD,EAAQ,sBAAA,GACfE,IAAYL;AAAA,IAChBG,EAAQ,cAAc,IAAIC,EAAK,QAAQD,EAAQ,cAAc;AAAA,EAAA,GAEzDG,IAAYN;AAAA,IAChBG,EAAQ,eAAe,IAAIC,EAAK,SAASD,EAAQ,eAAe;AAAA,EAAA;AAMlE,SAAO;AAAA,IACL,GAAGE,KAAaC,KAAa;AAAA,IAC7B,GAAGA,KAAaD,KAAa;AAAA,EAAA;AAEjC;AAGO,SAASE,EACdC,GACAC,GACAC,GAC4B;AAC5B,SAAO;AAAA,IACL,IAAIF,IAAKE,EAAM;AAAA,IACf,IAAID,IAAKC,EAAM;AAAA,EAAA;AAEnB;AAEA,SAASC,EAAMV,GAAeW,GAAeC,GAAuB;AAClE,SAAIA,IAAQD,IAAcC,IACnB,KAAK,IAAIA,GAAO,KAAK,IAAID,GAAOX,CAAK,CAAC;AAC/C;AAMO,SAASa,EAAqB;AAAA,EACnC,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAC;AAAA,EACA,WAAAC;AACF,GAAsC;AACpC,QAAMC,IAAad,IAAWE,GACxBa,IAAcd,IAAWE,GACzBa,IAAOjB,MAAW,QAAQA,MAAW,MACrCkB,IAAQlB,MAAW,QAAQA,MAAW;AAE5C,MAAImB,IAAMlB,GACNmB,IAAQL;AACZ,MAAIE,GAAM;AACR,UAAMI,IAAW,KAAK;AAAA,MACpBpB,IAAWQ;AAAA,MACXI,IAAY,QAAWF;AAAA,IAAA;AAEzB,IAAAS,IAAQxB,EAAMmB,IAAaV,GAAQJ,IAAWM,GAAMc,CAAQ;AAAA,EAC9D,OAAO;AACL,UAAMC,IAAU,KAAK,IAAIP,IAAaN,GAAM,CAAC;AAC7C,IAAAU,IAAMvB,EAAMK,IAAWI,GAAQiB,GAASP,IAAaR,CAAI;AAAA,EAC3D;AAEA,MAAIgB,IAAMrB,GACNsB,IAASR;AACb,MAAIE,GAAO;AACT,UAAMO,IAAY,KAAK;AAAA,MACrBvB,IAAWQ;AAAA,MACXI,IAAY,QAAWF;AAAA,IAAA;AAEzB,IAAAY,IAAS5B,EAAMoB,IAAcV,GAAQJ,IAAWM,GAAMiB,CAAS;AAAA,EACjE,OAAO;AACL,UAAMC,IAAS,KAAK,IAAIV,IAAcN,GAAM,CAAC;AAC7C,IAAAa,IAAM3B,EAAMM,IAAWI,GAAQoB,GAAQV,IAAcR,CAAI;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,KAAAW;AAAA,IACA,KAAAI;AAAA,IACA,GAAGH,IAAQD;AAAA,IACX,GAAGK,IAASD;AAAA,EAAA;AAEhB;AAGO,SAASI,EACdC,GACAC,GACAC,GACQ;AACR,QAAMC,IAAc,KAAK,IAAI,GAAGH,CAAI;AACpC,SAAOE,IAAWC,IAAc,KAAK,IAAIF,IAAS,GAAGE,CAAW;AAClE;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddle/vue",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Vue 3 bindings for Griddle — a headless, zero-dependency grid/canvas engine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"grid",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"vue": "^3.3.0",
|
|
56
|
-
"@griddle/core": "^0.1.
|
|
56
|
+
"@griddle/core": "^0.1.9"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"vue": "^3.4.0",
|
|
60
|
-
"@griddle/core": "^0.1.
|
|
60
|
+
"@griddle/core": "^0.1.9",
|
|
61
61
|
"vite": "^5.4.0",
|
|
62
62
|
"@vitejs/plugin-vue": "^5.2.0",
|
|
63
63
|
"vue-tsc": "^2.2.0"
|
package/src/GriddleGrid.vue
CHANGED
|
@@ -114,6 +114,8 @@ import type { GriddleApi } from './useGriddle.js';
|
|
|
114
114
|
import { animateReposition, liftTransition } from './animation.js';
|
|
115
115
|
import {
|
|
116
116
|
measureInteractionScale,
|
|
117
|
+
clampInteractionCell,
|
|
118
|
+
resolveResizePreview,
|
|
117
119
|
toLocalInteractionDelta,
|
|
118
120
|
type InteractionScale,
|
|
119
121
|
} from './interaction.js';
|
|
@@ -406,11 +408,20 @@ function onBackgroundPointerDown(e: PointerEvent) {
|
|
|
406
408
|
|
|
407
409
|
const el = scrollEl.value;
|
|
408
410
|
if (!el) return;
|
|
411
|
+
interactionScale = measureInteractionScale(el);
|
|
409
412
|
const rect = el.getBoundingClientRect();
|
|
410
|
-
const x = e.clientX - rect.left + el.scrollLeft;
|
|
411
|
-
const y = e.clientY - rect.top + el.scrollTop;
|
|
412
|
-
const col =
|
|
413
|
-
|
|
413
|
+
const x = (e.clientX - rect.left) / interactionScale.x + el.scrollLeft;
|
|
414
|
+
const y = (e.clientY - rect.top) / interactionScale.y + el.scrollTop;
|
|
415
|
+
const col = clampInteractionCell(
|
|
416
|
+
Math.floor(x / colSize.value),
|
|
417
|
+
config.value.cols,
|
|
418
|
+
config.value.infiniteX ?? config.value.cols === Infinity,
|
|
419
|
+
);
|
|
420
|
+
const row = clampInteractionCell(
|
|
421
|
+
Math.floor(y / rowSize.value),
|
|
422
|
+
config.value.rows,
|
|
423
|
+
config.value.infiniteY ?? config.value.rows === Infinity,
|
|
424
|
+
);
|
|
414
425
|
drawState.value = { anchorCol: col, anchorRow: row, currentCol: col, currentRow: row };
|
|
415
426
|
(el as HTMLDivElement).setPointerCapture(e.pointerId);
|
|
416
427
|
}
|
|
@@ -442,10 +453,18 @@ function onPointerMove(e: PointerEvent) {
|
|
|
442
453
|
const el = scrollEl.value;
|
|
443
454
|
if (el) {
|
|
444
455
|
const rect = el.getBoundingClientRect();
|
|
445
|
-
const x = e.clientX - rect.left + el.scrollLeft;
|
|
446
|
-
const y = e.clientY - rect.top + el.scrollTop;
|
|
447
|
-
const col =
|
|
448
|
-
|
|
456
|
+
const x = (e.clientX - rect.left) / interactionScale.x + el.scrollLeft;
|
|
457
|
+
const y = (e.clientY - rect.top) / interactionScale.y + el.scrollTop;
|
|
458
|
+
const col = clampInteractionCell(
|
|
459
|
+
Math.floor(x / colSize.value),
|
|
460
|
+
config.value.cols,
|
|
461
|
+
config.value.infiniteX ?? config.value.cols === Infinity,
|
|
462
|
+
);
|
|
463
|
+
const row = clampInteractionCell(
|
|
464
|
+
Math.floor(y / rowSize.value),
|
|
465
|
+
config.value.rows,
|
|
466
|
+
config.value.infiniteY ?? config.value.rows === Infinity,
|
|
467
|
+
);
|
|
449
468
|
drawState.value = { ...drawState.value, currentCol: col, currentRow: row };
|
|
450
469
|
}
|
|
451
470
|
return;
|
|
@@ -523,22 +542,30 @@ function onPointerMove(e: PointerEvent) {
|
|
|
523
542
|
e.clientY - r.startPointerY,
|
|
524
543
|
interactionScale,
|
|
525
544
|
);
|
|
526
|
-
let dw = 0, dh = 0, dcol = 0, drow = 0;
|
|
527
545
|
const stepsX = Math.round(dx / colSize.value);
|
|
528
546
|
const stepsY = Math.round(dy / rowSize.value);
|
|
529
|
-
if (r.corner === 'se' || r.corner === 'ne') dw = stepsX;
|
|
530
|
-
if (r.corner === 'se' || r.corner === 'sw') dh = stepsY;
|
|
531
|
-
if (r.corner === 'sw' || r.corner === 'nw') { dw = -stepsX; dcol = stepsX; }
|
|
532
|
-
if (r.corner === 'ne' || r.corner === 'nw') { dh = -stepsY; drow = stepsY; }
|
|
533
547
|
const tile = props.api.grid.getTile(r.tileId);
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
548
|
+
const preview = resolveResizePreview({
|
|
549
|
+
corner: r.corner,
|
|
550
|
+
startCol: r.startCol,
|
|
551
|
+
startRow: r.startRow,
|
|
552
|
+
startW: r.startW,
|
|
553
|
+
startH: r.startH,
|
|
554
|
+
stepsX,
|
|
555
|
+
stepsY,
|
|
556
|
+
minW: tile?.minW ?? 1,
|
|
557
|
+
minH: tile?.minH ?? 1,
|
|
558
|
+
maxW: tile?.maxW ?? Infinity,
|
|
559
|
+
maxH: tile?.maxH ?? Infinity,
|
|
560
|
+
cols: config.value.cols,
|
|
561
|
+
rows: config.value.rows,
|
|
562
|
+
infiniteX: config.value.infiniteX ?? config.value.cols === Infinity,
|
|
563
|
+
infiniteY: config.value.infiniteY ?? config.value.rows === Infinity,
|
|
564
|
+
});
|
|
565
|
+
const nW = preview.w;
|
|
566
|
+
const nH = preview.h;
|
|
567
|
+
const nC = preview.col;
|
|
568
|
+
const nR = preview.row;
|
|
542
569
|
if (nW !== r.previewW || nH !== r.previewH || nC !== r.previewCol || nR !== r.previewRow) {
|
|
543
570
|
resize.value = { ...r, previewW: nW, previewH: nH, previewCol: nC, previewRow: nR };
|
|
544
571
|
}
|
package/src/LoopGrid.vue
CHANGED
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
import type { LoopTileInstance } from '@griddle/core';
|
|
81
81
|
import type { GriddleApi } from './useGriddle.js';
|
|
82
82
|
import { animateReposition } from './animation.js';
|
|
83
|
+
import { resolveResizePreview } from './interaction.js';
|
|
83
84
|
|
|
84
85
|
const props = defineProps<{
|
|
85
86
|
api: GriddleApi;
|
|
@@ -359,22 +360,30 @@ function onPointerMove(e: PointerEvent) {
|
|
|
359
360
|
if (r) {
|
|
360
361
|
const dx = e.clientX - r.startPointerX;
|
|
361
362
|
const dy = e.clientY - r.startPointerY;
|
|
362
|
-
let dw = 0, dh = 0, dcol = 0, drow = 0;
|
|
363
363
|
const stepsX = Math.round(dx / colSize.value);
|
|
364
364
|
const stepsY = Math.round(dy / rowSize.value);
|
|
365
|
-
if (r.corner === 'se' || r.corner === 'ne') dw = stepsX;
|
|
366
|
-
if (r.corner === 'se' || r.corner === 'sw') dh = stepsY;
|
|
367
|
-
if (r.corner === 'sw' || r.corner === 'nw') { dw = -stepsX; dcol = stepsX; }
|
|
368
|
-
if (r.corner === 'ne' || r.corner === 'nw') { dh = -stepsY; drow = stepsY; }
|
|
369
365
|
const tile = props.api.grid.getTile(r.tileId);
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
366
|
+
const preview = resolveResizePreview({
|
|
367
|
+
corner: r.corner,
|
|
368
|
+
startCol: r.startCol,
|
|
369
|
+
startRow: r.startRow,
|
|
370
|
+
startW: r.startW,
|
|
371
|
+
startH: r.startH,
|
|
372
|
+
stepsX,
|
|
373
|
+
stepsY,
|
|
374
|
+
minW: tile?.minW ?? 1,
|
|
375
|
+
minH: tile?.minH ?? 1,
|
|
376
|
+
maxW: tile?.maxW ?? Infinity,
|
|
377
|
+
maxH: tile?.maxH ?? Infinity,
|
|
378
|
+
cols: config.value.cols,
|
|
379
|
+
rows: config.value.rows,
|
|
380
|
+
infiniteX: config.value.infiniteX ?? config.value.cols === Infinity,
|
|
381
|
+
infiniteY: config.value.infiniteY ?? config.value.rows === Infinity,
|
|
382
|
+
});
|
|
383
|
+
const nW = preview.w;
|
|
384
|
+
const nH = preview.h;
|
|
385
|
+
const nC = preview.col;
|
|
386
|
+
const nR = preview.row;
|
|
378
387
|
if (nW !== r.previewW || nH !== r.previewH || nC !== r.previewCol || nR !== r.previewRow) {
|
|
379
388
|
resize.value = { ...r, previewW: nW, previewH: nH, previewCol: nC, previewRow: nR };
|
|
380
389
|
}
|
package/src/interaction.ts
CHANGED
|
@@ -3,6 +3,31 @@ export interface InteractionScale {
|
|
|
3
3
|
y: number;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
export interface ResizePreviewInput {
|
|
7
|
+
corner: 'nw' | 'ne' | 'sw' | 'se';
|
|
8
|
+
startCol: number;
|
|
9
|
+
startRow: number;
|
|
10
|
+
startW: number;
|
|
11
|
+
startH: number;
|
|
12
|
+
stepsX: number;
|
|
13
|
+
stepsY: number;
|
|
14
|
+
minW: number;
|
|
15
|
+
minH: number;
|
|
16
|
+
maxW: number;
|
|
17
|
+
maxH: number;
|
|
18
|
+
cols: number;
|
|
19
|
+
rows: number;
|
|
20
|
+
infiniteX: boolean;
|
|
21
|
+
infiniteY: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ResizePreview {
|
|
25
|
+
col: number;
|
|
26
|
+
row: number;
|
|
27
|
+
w: number;
|
|
28
|
+
h: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
type MeasurableElement = Pick<
|
|
7
32
|
HTMLElement,
|
|
8
33
|
'getBoundingClientRect' | 'offsetWidth' | 'offsetHeight'
|
|
@@ -51,3 +76,78 @@ export function toLocalInteractionDelta(
|
|
|
51
76
|
dy: dy / scale.y,
|
|
52
77
|
};
|
|
53
78
|
}
|
|
79
|
+
|
|
80
|
+
function clamp(value: number, lower: number, upper: number): number {
|
|
81
|
+
if (upper < lower) return upper;
|
|
82
|
+
return Math.min(upper, Math.max(lower, value));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Resolve a corner resize while preserving the opposite edges and trimming
|
|
87
|
+
* the dragged edges at finite grid bounds.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveResizePreview({
|
|
90
|
+
corner,
|
|
91
|
+
startCol,
|
|
92
|
+
startRow,
|
|
93
|
+
startW,
|
|
94
|
+
startH,
|
|
95
|
+
stepsX,
|
|
96
|
+
stepsY,
|
|
97
|
+
minW,
|
|
98
|
+
minH,
|
|
99
|
+
maxW,
|
|
100
|
+
maxH,
|
|
101
|
+
cols,
|
|
102
|
+
rows,
|
|
103
|
+
infiniteX,
|
|
104
|
+
infiniteY,
|
|
105
|
+
}: ResizePreviewInput): ResizePreview {
|
|
106
|
+
const startRight = startCol + startW;
|
|
107
|
+
const startBottom = startRow + startH;
|
|
108
|
+
const east = corner === 'ne' || corner === 'se';
|
|
109
|
+
const south = corner === 'se' || corner === 'sw';
|
|
110
|
+
|
|
111
|
+
let col = startCol;
|
|
112
|
+
let right = startRight;
|
|
113
|
+
if (east) {
|
|
114
|
+
const maxRight = Math.min(
|
|
115
|
+
startCol + maxW,
|
|
116
|
+
infiniteX ? Infinity : cols,
|
|
117
|
+
);
|
|
118
|
+
right = clamp(startRight + stepsX, startCol + minW, maxRight);
|
|
119
|
+
} else {
|
|
120
|
+
const minLeft = Math.max(startRight - maxW, 0);
|
|
121
|
+
col = clamp(startCol + stepsX, minLeft, startRight - minW);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let row = startRow;
|
|
125
|
+
let bottom = startBottom;
|
|
126
|
+
if (south) {
|
|
127
|
+
const maxBottom = Math.min(
|
|
128
|
+
startRow + maxH,
|
|
129
|
+
infiniteY ? Infinity : rows,
|
|
130
|
+
);
|
|
131
|
+
bottom = clamp(startBottom + stepsY, startRow + minH, maxBottom);
|
|
132
|
+
} else {
|
|
133
|
+
const minTop = Math.max(startBottom - maxH, 0);
|
|
134
|
+
row = clamp(startRow + stepsY, minTop, startBottom - minH);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
col,
|
|
139
|
+
row,
|
|
140
|
+
w: right - col,
|
|
141
|
+
h: bottom - row,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Clamp a pointer-derived cell index to the cells addressable by the grid. */
|
|
146
|
+
export function clampInteractionCell(
|
|
147
|
+
cell: number,
|
|
148
|
+
extent: number,
|
|
149
|
+
infinite: boolean,
|
|
150
|
+
): number {
|
|
151
|
+
const nonNegative = Math.max(0, cell);
|
|
152
|
+
return infinite ? nonNegative : Math.min(extent - 1, nonNegative);
|
|
153
|
+
}
|