Skip to content

Commit 3ec8cc1

Browse files
committed
feat(sheet): new feature to configure opened breakpoints
1 parent 31b94b0 commit 3ec8cc1

8 files changed

Lines changed: 312 additions & 45 deletions

File tree

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const rules = {
2+
'no-lonely-if': 'off',
23
'no-bitwise': 'off',
34
'import/no-extraneous-dependencies': 'off',
45
'no-nested-ternary': 'off',

kitchen-sink/core/pages/sheet-modal.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@
256256
sheetSwipeToStep = $f7.sheet.create({
257257
el: '.demo-sheet-swipe-to-step',
258258
swipeToClose: true,
259-
// swipeToStep: true,
260-
breakpoints: [0.25, 0.5, 0.75],
259+
swipeToStep: true,
261260
push: true,
262261
backdrop: true,
263262
});

src/core/components/sheet/sheet-class.js

Lines changed: 241 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ class Sheet extends Modal {
158158
let isMoved = false;
159159
let isTopSheetModal;
160160
let swipeStepTranslate;
161-
let breakpointsTranslate = [];
162161
let startTranslate;
163162
let currentTranslate;
164163
let sheetElOffsetHeight;
@@ -170,6 +169,9 @@ class Sheet extends Modal {
170169
let sheetPageContentScrollTop;
171170
let sheetPageContentScrollHeight;
172171
let sheetPageContentOffsetHeight;
172+
let breakpointsTranslate = [];
173+
let currentBreakpointIndex;
174+
let backdropBreakpointSet = true;
173175

174176
function handleTouchStart(e) {
175177
if (isTouched || !(sheet.params.swipeToClose || sheet.params.swipeToStep) || !e.isTrusted)
@@ -259,7 +261,18 @@ class Sheet extends Modal {
259261
currentTranslate = startTranslate - touchesDiff;
260262
currentTranslate = Math.min(Math.max(currentTranslate, minTranslate), maxTranslate);
261263
e.preventDefault();
262-
if (sheet.push && pushOffset) {
264+
if (useBreakpoints) {
265+
let progress = isTopSheetModal
266+
? 1 + currentTranslate / sheetElOffsetHeight
267+
: 1 - currentTranslate / sheetElOffsetHeight;
268+
progress = Math.abs(progress);
269+
progress = Math.min(Math.max(progress, 0), 1);
270+
// eslint-disable-next-line
271+
setBackdropBreakpoint(progress);
272+
// eslint-disable-next-line
273+
setPushBreakpoint(progress);
274+
}
275+
if (sheet.push && pushOffset && !useBreakpoints) {
263276
let progress = (currentTranslate - startTranslate) / sheetElOffsetHeight;
264277
if (sheet.params.swipeToStep) {
265278
if (isTopSheetModal) {
@@ -300,8 +313,11 @@ class Sheet extends Modal {
300313
isMoved = false;
301314
$el.transform('').transition('');
302315
if (sheet.push && pushOffset) {
303-
$pushViewEl.transition('').transform('');
304-
$pushViewEl.css('border-radius', '');
316+
$pushViewEl.transition('');
317+
if (!useBreakpoints) {
318+
$pushViewEl.transform('');
319+
$pushViewEl.css('border-radius', '');
320+
}
305321
}
306322

307323
const direction = touchesDiff < 0 ? 'to-bottom' : 'to-top';
@@ -311,7 +327,7 @@ class Sheet extends Modal {
311327

312328
const timeDiff = new Date().getTime() - touchStartTime;
313329

314-
if (!sheet.params.swipeToStep) {
330+
if (!sheet.params.swipeToStep && !useBreakpoints) {
315331
if (direction !== (isTopSheetModal ? 'to-top' : 'to-bottom')) {
316332
return;
317333
}
@@ -325,8 +341,37 @@ class Sheet extends Modal {
325341
const closeDirection = isTopSheetModal ? 'to-top' : 'to-bottom';
326342
const absCurrentTranslate = Math.abs(currentTranslate);
327343
const absSwipeStepTranslate = Math.abs(swipeStepTranslate);
328-
329-
if (timeDiff < 300 && diff > 10) {
344+
if (timeDiff < 300 && diff > 10 && useBreakpoints) {
345+
// SHORT SWIPES BREAKPOINTS
346+
if (direction === openDirection && typeof currentBreakpointIndex !== 'undefined') {
347+
if (currentBreakpointIndex === params.breakpoints.length - 1) {
348+
// open
349+
sheet.setBreakpoint(1);
350+
} else {
351+
// move to next breakpoint
352+
currentBreakpointIndex = Math.min(
353+
breakpointsTranslate.length - 1,
354+
currentBreakpointIndex + 1,
355+
);
356+
sheet.setBreakpoint(params.breakpoints[currentBreakpointIndex]);
357+
}
358+
}
359+
if (direction === closeDirection) {
360+
if (currentBreakpointIndex === 0) {
361+
// close
362+
sheet.close();
363+
} else {
364+
// move to prev breakpoint
365+
if (typeof currentBreakpointIndex === 'undefined') {
366+
currentBreakpointIndex = params.breakpoints.length - 1;
367+
} else {
368+
currentBreakpointIndex = Math.max(0, currentBreakpointIndex - 1);
369+
}
370+
sheet.setBreakpoint(params.breakpoints[currentBreakpointIndex]);
371+
}
372+
}
373+
} else if (timeDiff < 300 && diff > 10) {
374+
// SHORT SWIPES SWIPE STEP
330375
if (direction === openDirection && absCurrentTranslate < absSwipeStepTranslate) {
331376
// open step
332377
$el.removeClass('modal-in-swipe-step');
@@ -373,7 +418,28 @@ class Sheet extends Modal {
373418
}
374419
return;
375420
}
376-
if (timeDiff >= 300) {
421+
if (timeDiff >= 300 && useBreakpoints) {
422+
// LONG SWIPES BREAKPOINTS
423+
const allBreakpoints = [sheetElOffsetHeight, ...breakpointsTranslate, 0];
424+
const closestTranslate = allBreakpoints.reduce((prev, curr) => {
425+
return Math.abs(curr - currentTranslate) < Math.abs(prev - currentTranslate)
426+
? curr
427+
: prev;
428+
});
429+
const closestIndex = allBreakpoints.indexOf(closestTranslate);
430+
if (closestTranslate === 0) {
431+
// open
432+
sheet.setBreakpoint(1);
433+
} else if (closestIndex === 0) {
434+
// close
435+
sheet.close();
436+
} else {
437+
// set bp
438+
currentBreakpointIndex = closestIndex - 1;
439+
sheet.setBreakpoint(params.breakpoints[currentBreakpointIndex]);
440+
}
441+
} else if (timeDiff >= 300) {
442+
// LONG SWIPES SWIPE STEP
377443
const stepOpened = !$el.hasClass('modal-in-swipe-step');
378444
if (!stepOpened) {
379445
if (absCurrentTranslate < absSwipeStepTranslate / 2) {
@@ -422,44 +488,168 @@ class Sheet extends Modal {
422488
}
423489
}
424490

425-
sheet.setSwipeStep = function setSwipeStep(byResize = true) {
426-
const $swipeStepEl = $el.find('.sheet-modal-swipe-step').eq(0);
427-
if (!useBreakpoints && !$swipeStepEl.length) return;
428-
if (useBreakpoints) {
429-
const fullSize = $el[0].offsetHeight;
430-
breakpointsTranslate = [];
431-
sheet.params.breakpoints.forEach((ratio) => {
432-
breakpointsTranslate.push(fullSize - fullSize * ratio);
491+
const setPushBreakpoint = (breakpoint) => {
492+
const { pushBreakpoint } = params;
493+
if (
494+
pushBreakpoint === null ||
495+
typeof pushBreakpoint === 'undefined' ||
496+
!sheet.push ||
497+
!pushOffset
498+
)
499+
return;
500+
if (breakpoint >= pushBreakpoint) {
501+
sheet.$htmlEl
502+
.addClass('with-modal-sheet-push')
503+
.removeClass('with-modal-sheet-push-closing');
504+
$pushViewEl.transition('').forEach((el) => {
505+
el.style.setProperty(
506+
'transform',
507+
`translate3d(0,0,0) scale(${pushViewScale(pushOffset)})`,
508+
'important',
509+
);
433510
});
434-
console.log(breakpointsTranslate);
435-
$el[0].style.setProperty('--f7-sheet-breakpoint', `${breakpointsTranslate[0]}px`);
436-
if (!byResize) {
437-
$el.addClass('modal-in-breakpoint');
438-
sheet.emit('local::_swipeStep', true);
511+
$pushViewEl.css('border-radius', `${pushBorderRadius * 1}px`);
512+
} else {
513+
const pushBreakpoints = [0, ...params.breakpoints, 1];
514+
const pushTransparentBreakpoint =
515+
pushBreakpoints[pushBreakpoints.indexOf(pushBreakpoint) - 1];
516+
if (breakpoint <= pushTransparentBreakpoint) {
517+
$pushViewEl.transition('').css('transform', '');
518+
$pushViewEl.css('border-radius', '');
519+
sheet.$htmlEl.removeClass('with-modal-sheet-push');
520+
if (breakpoint === pushTransparentBreakpoint) {
521+
sheet.$htmlEl.addClass('with-modal-sheet-push-closing');
522+
}
523+
} else {
524+
const progress =
525+
(breakpoint - pushTransparentBreakpoint) / (pushBreakpoint - pushTransparentBreakpoint);
526+
sheet.$htmlEl
527+
.addClass('with-modal-sheet-push')
528+
.removeClass('with-modal-sheet-push-closing');
529+
$pushViewEl.transition(0).forEach((el) => {
530+
el.style.setProperty(
531+
'transform',
532+
`translate3d(0,0,0) scale(${1 - (1 - pushViewScale(pushOffset)) * progress})`,
533+
'important',
534+
);
535+
});
536+
$pushViewEl.css('border-radius', `${pushBorderRadius * progress}px`);
439537
}
538+
}
539+
};
540+
const setBackdropBreakpoint = (breakpoint) => {
541+
const { backdrop, backdropBreakpoint } = params;
542+
if (!backdropBreakpoint || !backdrop || !$backdropEl.length) return;
543+
544+
if (breakpoint >= backdropBreakpoint) {
545+
if (!backdropBreakpointSet) {
546+
$backdropEl.transition('').css({ opacity: '', pointerEvents: '' });
547+
}
548+
backdropBreakpointSet = true;
440549
} else {
441-
// eslint-disable-next-line
442-
if ($el.hasClass('sheet-modal-top')) {
443-
swipeStepTranslate = -(
444-
$swipeStepEl.offset().top -
445-
$el.offset().top +
446-
$swipeStepEl[0].offsetHeight
447-
);
550+
const backdropBreakpoints = [0, ...params.breakpoints, 1];
551+
const backdropTransparentBreakpoint =
552+
backdropBreakpoints[backdropBreakpoints.indexOf(backdropBreakpoint) - 1];
553+
if (breakpoint <= backdropTransparentBreakpoint) {
554+
if (backdropBreakpointSet) {
555+
$backdropEl.transition('').css({ opacity: 0, pointerEvents: 'none' });
556+
}
557+
backdropBreakpointSet = false;
448558
} else {
449-
swipeStepTranslate =
450-
$el[0].offsetHeight -
451-
($swipeStepEl.offset().top - $el.offset().top + $swipeStepEl[0].offsetHeight);
559+
const progress =
560+
(breakpoint - backdropTransparentBreakpoint) /
561+
(backdropBreakpoint - backdropTransparentBreakpoint);
562+
$backdropEl.transition(0).css({ opacity: progress, pointerEvents: 'auto' });
452563
}
453-
$el[0].style.setProperty('--f7-sheet-swipe-step', `${swipeStepTranslate}px`);
454-
if (!byResize) {
455-
$el.addClass('modal-in-swipe-step');
456-
sheet.emit('local::_swipeStep', true);
564+
}
565+
};
566+
567+
sheet.calcBreakpoints = () => {
568+
if (!useBreakpoints) {
569+
return;
570+
}
571+
const fullSize = $el[0].offsetHeight;
572+
// eslint-disable-next-line
573+
const isTopSheetModal = $el.hasClass('sheet-modal-top');
574+
breakpointsTranslate = [];
575+
sheet.params.breakpoints.forEach((ratio) => {
576+
breakpointsTranslate.push((fullSize - fullSize * ratio) * (isTopSheetModal ? -1 : 1));
577+
});
578+
};
579+
580+
sheet.setBreakpoint = (value) => {
581+
if (!useBreakpoints) {
582+
return sheet;
583+
}
584+
if (value === 1) {
585+
// open
586+
if (!sheet.opened) {
587+
sheet.open();
588+
}
589+
$el.removeClass('modal-in-breakpoint');
590+
currentBreakpointIndex = undefined;
591+
setBackdropBreakpoint(value);
592+
setPushBreakpoint(value);
593+
$el.trigger('sheet:breakpoint', value);
594+
sheet.emit('local::breakpoint sheetBreakpoint', sheet, value);
595+
} else if (value === 0) {
596+
// close
597+
$el.trigger('sheet:breakpoint', value);
598+
sheet.emit('local::breakpoint sheetBreakpoint', sheet, value);
599+
sheet.close();
600+
} else {
601+
const index = params.breakpoints.indexOf(value);
602+
if (index < 0) return sheet;
603+
if (!sheet.opened) {
604+
sheet.open();
457605
}
606+
setBackdropBreakpoint(value);
607+
setPushBreakpoint(value);
608+
$el.trigger('sheet:breakpoint', index);
609+
sheet.emit('local::breakpoint sheetBreakpoint', sheet, index);
610+
currentBreakpointIndex = index;
611+
$el[0].style.setProperty('--f7-sheet-breakpoint', `${breakpointsTranslate[index]}px`);
612+
$el.addClass('modal-in-breakpoint');
613+
}
614+
return sheet;
615+
};
616+
617+
const setBreakpointsOnResize = () => {
618+
sheet.calcBreakpoints();
619+
if (typeof currentBreakpointIndex !== 'undefined') {
620+
sheet.setBreakpoint(params.breakpoints[currentBreakpointIndex]);
621+
}
622+
};
623+
624+
sheet.setSwipeStep = function setSwipeStep(byResize = true) {
625+
const $swipeStepEl = $el.find('.sheet-modal-swipe-step').eq(0);
626+
if (!$swipeStepEl.length) return;
627+
628+
// eslint-disable-next-line
629+
if ($el.hasClass('sheet-modal-top')) {
630+
swipeStepTranslate = -(
631+
$swipeStepEl.offset().top -
632+
$el.offset().top +
633+
$swipeStepEl[0].offsetHeight
634+
);
635+
} else {
636+
swipeStepTranslate =
637+
$el[0].offsetHeight -
638+
($swipeStepEl.offset().top - $el.offset().top + $swipeStepEl[0].offsetHeight);
639+
}
640+
$el[0].style.setProperty('--f7-sheet-swipe-step', `${swipeStepTranslate}px`);
641+
if (!byResize) {
642+
$el.addClass('modal-in-swipe-step');
643+
sheet.emit('local::_swipeStep', true);
458644
}
459645
};
460646

461647
function onResize() {
462-
sheet.setSwipeStep(true);
648+
if (useBreakpoints) {
649+
setBreakpointsOnResize();
650+
} else {
651+
sheet.setSwipeStep(true);
652+
}
463653
}
464654

465655
const passive = support.passiveListener ? { passive: true } : false;
@@ -479,10 +669,8 @@ class Sheet extends Modal {
479669
$(document).on('keydown', onKeyDown);
480670
}
481671
$el.prevAll('.popup.modal-in').addClass('popup-behind');
482-
if (sheet.params.swipeToStep || useBreakpoints) {
483-
sheet.setSwipeStep(false);
484-
app.on('resize', onResize);
485-
}
672+
673+
app.on('resize', onResize);
486674
if (sheet.params.scrollToEl) {
487675
scrollToElementOnOpen();
488676
}
@@ -493,7 +681,9 @@ class Sheet extends Modal {
493681
if (!pushOffset) pushOffset = app.theme === 'ios' ? 44 : 48;
494682
sheet.$htmlEl[0].style.setProperty('--f7-sheet-push-offset', `${pushOffset}px`);
495683
$el.addClass('sheet-modal-push');
496-
sheet.$htmlEl.addClass('with-modal-sheet-push');
684+
if (!useBreakpoints) {
685+
sheet.$htmlEl.addClass('with-modal-sheet-push');
686+
}
497687
if (!sheet.params.swipeToStep && !useBreakpoints) {
498688
sheet.$htmlEl[0].style.setProperty('--f7-sheet-push-scale', pushViewScale(pushOffset));
499689
} else {
@@ -504,13 +694,21 @@ class Sheet extends Modal {
504694
$pushViewEl.css('border-radius', '0px');
505695
}
506696
}
697+
698+
if (useBreakpoints) {
699+
sheet.calcBreakpoints();
700+
sheet.setBreakpoint(params.breakpoints[0]);
701+
} else if (sheet.params.swipeToStep) {
702+
sheet.setSwipeStep(false);
703+
}
507704
});
508705
sheet.on('opened', () => {
509706
if (sheet.params.closeByOutsideClick || sheet.params.closeByBackdropClick) {
510707
app.on('click', handleClick);
511708
}
512709
});
513710
sheet.on('close', () => {
711+
currentBreakpointIndex = undefined;
514712
if (sheet.params.swipeToStep || useBreakpoints) {
515713
$el.removeClass('modal-in-swipe-step modal-in-breakpoint');
516714
sheet.emit('local::_swipeStep', false);
@@ -529,6 +727,8 @@ class Sheet extends Modal {
529727
if (sheet.push && pushOffset) {
530728
sheet.$htmlEl.removeClass('with-modal-sheet-push');
531729
sheet.$htmlEl.addClass('with-modal-sheet-push-closing');
730+
$pushViewEl.transform('');
731+
$pushViewEl.css('border-radius', '');
532732
}
533733
});
534734
sheet.on('closed', () => {

0 commit comments

Comments
 (0)