Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 30e946a

Browse files
author
Nick Heinbaugh
committed
apply patch from gridstack#3076
1 parent 6339147 commit 30e946a

File tree

7 files changed

+207
-28
lines changed

7 files changed

+207
-28
lines changed

‎demo/index.html‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ <h1>Demos</h1>
2828
<li><a href="responsive_break.html">Responsive: using breakpoints</a></li>
2929
<li><a href="responsive_none.html">Responsive: using layout:'none'</a></li>
3030
<li><a href="right-to-left(rtl).html">Right-To-Left (RTL)</a></li>
31+
<li><a href="scrollSpeed.html">Scroll Speed</a></li>
3132
<li><a href="serialization.html">Serialization</a></li>
3233
<li><a href="sizeToContent.html">Size To Content</a></li>
3334
<li><a href="static.html">Static</a></li>

‎demo/scrollSpeed.html‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>Scroll Speed Demo</title>
8+
9+
<link rel="stylesheet" href="demo.css"/>
10+
<script src="../dist/gridstack-all.js"></script>
11+
12+
</head>
13+
<body>
14+
<div class="container-fluid">
15+
<h1>Scroll Speed Demo</h1>
16+
<p>This demo shows 20 widgets to test scroll speed behavior when dragging items. Try dragging widgets near the top or bottom of the viewport to see the scrolling effect.</p>
17+
<div>
18+
<label for="maxScrollSpeed">Max Scroll Speed:</label>
19+
<input type="number" id="maxScrollSpeed" value="0" min="0" step="1" onchange="updateMaxScrollSpeed()" style="margin-left: 5px; width: 80px;">
20+
<small style="margin-left: 10px;">0 = no limit, higher values = slower scrolling</small>
21+
</div>
22+
<br><br>
23+
<div class="grid-stack"></div>
24+
</div>
25+
<script src="events.js"></script>
26+
<script type="text/javascript">
27+
let grid = GridStack.init({
28+
float: true,
29+
maxScrollSpeed: 0, // default to 0 (no limit)
30+
resizable: { handles: 'all'}
31+
});
32+
addEvents(grid);
33+
34+
// Create 20 widgets with varied sizes and positions (all sizes between 2-5, no overlaps)
35+
let items = [
36+
// Row 1
37+
{x: 0, y: 0, w: 3, h: 2, content: "Widget 1"},
38+
{x: 3, y: 0, w: 4, h: 3, content: "Widget 2"},
39+
{x: 7, y: 0, w: 2, h: 2, content: "Widget 3"},
40+
{x: 9, y: 0, w: 3, h: 2, content: "Widget 4"},
41+
42+
// Row 2
43+
{x: 0, y: 2, w: 2, h: 3, content: "Widget 5"},
44+
{x: 2, y: 2, w: 5, h: 2, content: "Widget 6"},
45+
{x: 9, y: 2, w: 3, h: 3, content: "Widget 7"},
46+
47+
// Row 3
48+
{x: 0, y: 5, w: 4, h: 2, content: "Widget 8"},
49+
{x: 4, y: 5, w: 2, h: 3, content: "Widget 9"},
50+
{x: 6, y: 5, w: 3, h: 2, content: "Widget 10"},
51+
52+
// Row 4
53+
{x: 0, y: 7, w: 3, h: 3, content: "Widget 11"},
54+
{x: 3, y: 7, w: 2, h: 2, content: "Widget 12"},
55+
{x: 5, y: 7, w: 4, h: 3, content: "Widget 13"},
56+
{x: 9, y: 7, w: 3, h: 2, content: "Widget 14"},
57+
58+
// Row 5
59+
{x: 0, y: 10, w: 5, h: 2, content: "Widget 15"},
60+
{x: 5, y: 10, w: 2, h: 3, content: "Widget 16"},
61+
{x: 7, y: 10, w: 3, h: 2, content: "Widget 17"},
62+
63+
// Row 6
64+
{x: 0, y: 12, w: 2, h: 2, content: "Widget 18"},
65+
{x: 2, y: 12, w: 4, h: 3, content: "Widget 19"},
66+
{x: 6, y: 12, w: 3, h: 2, content: "Widget 20"}
67+
];
68+
69+
grid.load(items);
70+
71+
updateMaxScrollSpeed = function() {
72+
const value = parseInt(document.getElementById('maxScrollSpeed').value) || 0;
73+
grid.opts.maxScrollSpeed = value;
74+
console.log('Max scroll speed updated to:', value);
75+
};
76+
</script>
77+
</body>
78+
</html>

‎doc/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ gridstack.js API
116116
- `marginBottom`: numberOrString
117117
- `marginLeft`: numberOrString
118118
- `maxRow` - maximum rows amount. Default is `0` which means no max.
119+
- `maxScrollSpeed` - (number) limits the speed that the user wll scroll up and down in the grid. This is most noticable in large grids. Default: `0` which indicates no limit on the scroll speed. Any value provided here should be positive.
119120
- `minRow` - minimum rows amount which is handy to prevent grid from collapsing when empty. Default is `0`. You can also do this with `min-height` CSS attribute on the grid div in pixels, which will round to the closest row.
120121
- `nonce` - If you are using a nonce-based Content Security Policy, pass your nonce here and
121122
GridStack will add it to the `<style>` elements it creates.

‎spec/utils-spec.ts‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,88 @@ describe('gridstack utils', function() {
116116
});
117117
});
118118

119+
describe('_getScrollAmount', () => {
120+
const innerHeight = 800;
121+
const elHeight = 600;
122+
123+
it('should not scroll if element is inside viewport', () => {
124+
const rect = { top: 100, bottom: 700, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
125+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, -10);
126+
expect(scrollAmount).toBe(0);
127+
});
128+
129+
it('should not limit the scroll speed if the user has set maxScrollSpeed to 0', () => {
130+
const rect = { top: 220, bottom: 850, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
131+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 50);
132+
expect(scrollAmount).toBe(50);
133+
});
134+
135+
it('should treat a negative maxScrollSpeed as positive', () => {
136+
const rect = { top: 220, bottom: 850, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
137+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 50, -4 );
138+
expect(scrollAmount).toBe(4);
139+
});
140+
141+
describe('scrolling up', () => {
142+
it('should scroll up', () => {
143+
const rect = { top: -20, bottom: 580, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
144+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, -30);
145+
expect(scrollAmount).toBe(-20);
146+
});
147+
it('should scroll up to bring dragged element into view', () => {
148+
const rect = { top: -20, bottom: 580, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
149+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, -10);
150+
expect(scrollAmount).toBe(-10);
151+
});
152+
it('should scroll up when dragged element is larger than viewport', () => {
153+
const rect = { top: -20, bottom: 880, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
154+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, 900, -30);
155+
expect(scrollAmount).toBe(-30);
156+
});
157+
158+
it('should limit the scroll speed when the expected scroll speed is greater than the maxScrollSpeed', () => {
159+
const rect = { top: -30, bottom: 880, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
160+
const scrollAmountWithoutLimit = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, -30);
161+
expect(scrollAmountWithoutLimit).toBe(-30); // be completely sure that the scroll amount should be limited
162+
163+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, -30, 10);
164+
expect(scrollAmount).toBe(-10);
165+
});
166+
});
167+
168+
describe('scrolling down', () => {
169+
it('should not scroll down if element is inside viewport', () => {
170+
const rect = { top: 100, bottom: 700, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
171+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 10);
172+
expect(scrollAmount).toBe(0);
173+
});
174+
it('should scroll down', () => {
175+
const rect = { top: 220, bottom: 820, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
176+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 10);
177+
expect(scrollAmount).toBe(10);
178+
});
179+
it('should scroll down to bring dragged element into view', () => {
180+
const rect = { top: 220, bottom: 820, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
181+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 30);
182+
expect(scrollAmount).toBe(20);
183+
});
184+
it('should scroll down when dragged element is larger than viewport', () => {
185+
const rect = { top: -100, bottom: 820, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
186+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, 920, 10);
187+
expect(scrollAmount).toBe(10);
188+
});
189+
190+
it('should limit the scroll speed when the expected scroll speed is greater than the maxScrollSpeed', () => {
191+
const rect = { top: 220, bottom: 850, left: 0, right: 0, width: 50, height: 50, toJSON: () => '' };
192+
const scrollAmountWithoutLimit = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 50);
193+
expect(scrollAmountWithoutLimit).toBe(50); // be completely sure that the scroll amount should be limited
194+
195+
const scrollAmount = Utils._getScrollAmount(rect as DOMRect, innerHeight, elHeight, 10, 10);
196+
expect(scrollAmount).toBe(10);
197+
});
198+
});
199+
});
200+
119201
describe('clone', () => {
120202
const a: any = {first: 1, second: 'text'};
121203
const b: any = {first: 1, second: {third: 3}};

‎src/gridstack.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2541,7 +2541,7 @@ export class GridStack {
25412541
const distance = ui.position.top - node._prevYPix;
25422542
node._prevYPix = ui.position.top;
25432543
if (this.opts.draggable.scroll !== false) {
2544-
Utils.updateScrollPosition(el, ui.position, distance);
2544+
Utils.updateScrollPosition(el, ui.position, distance,this.opts.maxScrollSpeed);
25452545
}
25462546

25472547
// get new position taking into account the margin in the direction we are moving! (need to pass mid point by margin)

‎src/types.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ export interface GridStackOptions {
217217
/** maximum rows amount. Default? is 0 which means no maximum rows */
218218
maxRow?: number;
219219

220+
/** maximum scroll speed when dragging items. Any negative value will be converted to the positive value. (default?: 0 = no limit) */
221+
maxScrollSpeed?: number;
222+
220223
/** minimum rows amount. Default is `0`. You can also do this with `min-height` CSS attribute
221224
* on the grid div in pixels, which will round to the closest row.
222225
*/

‎src/utils.ts‎

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -351,37 +351,51 @@ export class Utils {
351351
}
352352

353353
/** @internal */
354-
static updateScrollPosition(el: HTMLElement, position: {top: number}, distance: number): void {
354+
static _getScrollAmount(rect: DOMRect, viewportHeight: number, elHeight: number, distance: number, maxScrollSpeed?: number): number {
355+
const offsetDiffDown = rect.bottom - viewportHeight;
356+
const offsetDiffUp = rect.top;
357+
const elementIsLargerThanViewport = elHeight > viewportHeight;
358+
let scrollAmount = 0;
359+
360+
if (rect.top < 0 && distance < 0) {
361+
// moving up
362+
if (elementIsLargerThanViewport) {
363+
scrollAmount = distance;
364+
} else {
365+
scrollAmount = Math.abs(offsetDiffUp) > Math.abs(distance) ? distance : offsetDiffUp;
366+
}
367+
} else if (rect.bottom > viewportHeight && distance > 0) {
368+
// moving down
369+
if (elementIsLargerThanViewport) {
370+
scrollAmount = distance;
371+
} else {
372+
scrollAmount = offsetDiffDown > distance ? distance : offsetDiffDown;
373+
}
374+
}
375+
376+
if (maxScrollSpeed) {
377+
maxScrollSpeed = Math.abs(maxScrollSpeed);
378+
if (scrollAmount > maxScrollSpeed) {
379+
scrollAmount = maxScrollSpeed;
380+
} else if (scrollAmount < -maxScrollSpeed) {
381+
scrollAmount = -maxScrollSpeed;
382+
}
383+
}
384+
return scrollAmount;
385+
}
386+
387+
/** @internal */
388+
static updateScrollPosition(el: HTMLElement, position: {top: number}, distance: number, maxScrollSpeed?: number): void {
355389
// is widget in view?
356390
const rect = el.getBoundingClientRect();
357-
const innerHeightOrClientHeight = (window.innerHeight || document.documentElement.clientHeight);
358-
if (rect.top < 0 ||
359-
rect.bottom > innerHeightOrClientHeight
360-
) {
361-
// set scrollTop of first parent that scrolls
362-
// if parent is larger than el, set as low as possible
363-
// to get entire widget on screen
364-
const offsetDiffDown = rect.bottom - innerHeightOrClientHeight;
365-
const offsetDiffUp = rect.top;
391+
const viewportHeight = (window.innerHeight || document.documentElement.clientHeight);
392+
const scrollAmount = this._getScrollAmount(rect, viewportHeight, el.offsetHeight, distance, maxScrollSpeed);
393+
394+
if (scrollAmount) {
366395
const scrollEl = this.getScrollElement(el);
367-
if (scrollEl!==null) {
396+
if (scrollEl) {
368397
const prevScroll = scrollEl.scrollTop;
369-
if (rect.top < 0 && distance < 0) {
370-
// moving up
371-
if (el.offsetHeight > innerHeightOrClientHeight) {
372-
scrollEl.scrollTop += distance;
373-
} else {
374-
scrollEl.scrollTop += Math.abs(offsetDiffUp) > Math.abs(distance) ? distance : offsetDiffUp;
375-
}
376-
} else if (distance > 0) {
377-
// moving down
378-
if (el.offsetHeight > innerHeightOrClientHeight) {
379-
scrollEl.scrollTop += distance;
380-
} else {
381-
scrollEl.scrollTop += offsetDiffDown > distance ? distance : offsetDiffDown;
382-
}
383-
}
384-
// move widget y by amount scrolled
398+
scrollEl.scrollTop += scrollAmount;
385399
position.top += scrollEl.scrollTop - prevScroll;
386400
}
387401
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /