Sheet Modal

Sheet Modal is a special overlay type which is similar to Picker/Calendar's overlay. Such modal allows to create custom overlays with custom content

Sheet Layout

Sheet layout is pretty simple:

<body>
  ...
  <!-- Sheet Modal Container -->
  <div class="sheet-modal">
    <!-- Sheet Modal Toolbar, optional -->
    <div class="toolbar">
      <div class="toolbar-inner">
        <div class="left"></div>
        <div class="right">
          <a href="#" class="link sheet-close">Done</a>
        </div>
      </div>
    </div>
    <!-- Sheet Modal Inner -->
    <div class="sheet-modal-inner">
      <!-- Sheet Modal content -->
      <div class="block">
        <p>Integer mollis nulla id nibh elementum finibus...</p>
      </div>
    </div>
  </div>

</body>

Sheet Top Layout

By default sheet modal opens from the bottom of the screen. It is also possible to open it from the top of the screen. In this case we need to add sheet-modal-top class to sheet element. It is also recommended to use bottom toolbar in this case:

<!-- Additional "sheet-modal-top" class to open it from top -->
<div class="sheet-modal sheet-modal-top">
  <!-- Bottom toolbar for top sheet -->
  <div class="toolbar toolbar-bottom">
    <div class="toolbar-inner">
      <div class="left"></div>
      <div class="right">
        <a href="#" class="link sheet-close">Done</a>
      </div>
    </div>
  </div>
  <!-- Sheet Modal Inner -->
  <div class="sheet-modal-inner">
    <!-- Sheet Modal content -->
    <div class="block">
      <p>Integer mollis nulla id nibh elementum finibus...</p>
    </div>
  </div>
</div>

Sheet App Methods

Let's look at related App methods to work with Sheet:

app.sheet.create(parameters)- create Sheet instance

  • parameters - object. Object with sheet parameters

Method returns created Sheet's instance

app.sheet.destroy(el)- destroy Sheet instance

  • el - HTMLElement or string (with CSS Selector) or object. Sheet element or Sheet instance to destroy.

app.sheet.get(el)- get Sheet instance by HTML element

  • el - HTMLElement or string (with CSS Selector). Sheet element.

Method returns Sheet's instance

app.sheet.open(el, animate)- opens Sheet

  • el - HTMLElement or string (with CSS Selector). Sheet element to open.
  • animate - boolean. Open Sheet with animation.

Method returns Sheet's instance

app.sheet.close(el, animate)- closes Sheet

  • el - HTMLElement or string (with CSS Selector). Sheet element to close.
  • animate - boolean. Close Sheet with animation.

Method returns Sheet's instance

app.sheet.stepOpen(el)- open/expand Sheet swipe step

  • el - HTMLElement or string (with CSS Selector). Sheet element to open swipe step.

Method returns Sheet's instance

app.sheet.stepClose(el)- close/collapse Sheet swipe step

  • el - HTMLElement or string (with CSS Selector). Sheet element to close swipe step.

Method returns Sheet's instance

app.sheet.stepToggle(el)- toggle (open or close) Sheet swipe step

  • el - HTMLElement or string (with CSS Selector). Sheet element to toggle swipe step.

Method returns Sheet's instance

Sheet Parameters

Now let's look at list of available parameters we need to create Sheet:

ParameterTypeDefaultDescription
elHTMLElementSheet element. Can be useful if you already have Sheet element in your HTML and want to create new instance using this element
contentstringFull Sheet HTML layout string. Can be useful if you want to create Sheet element dynamically
backdropbooleanEnables Sheet backdrop (dark semi transparent layer behind). By default it is true for MD and Aurora themes and false for iOS theme
backdropElHTMLElement
string
HTML element or string CSS selector of custom backdrop element
scrollToElHTMLElement
string
HTML element or string (with CSS selector) of element. If specified, then sheet will try to scroll page content to this element on open
closeByBackdropClickbooleantrueWhen enabled will be closed on backdrop click
closeByOutsideClickbooleanfalseWhen enabled will be closed on when click outside of it
closeOnEscapebooleanfalseWhen enabled will be closed on ESC keyboard key press
animatebooleantrueWhether the Sheet should be opened/closed with animation or not. Can be overwritten in .open() and .close() methods
swipeToClosebooleanfalseWhether the Sheet can be closed with swipe gesture
swipeToStepbooleanfalseWhen enabled it will be possible to split opened sheet into two states: partially opened and fully opened that can be controlled with swipe
swipeHandlerHTMLElement
string
If not passed, then whole Sheet can be swiped to close. You can pass here HTML element or string CSS selector of custom element that will be used as a swipe target. (swipeToClose or swipeToStep must be also enabled)
pushbooleanfalseWhen enabled it will push view behind on open. Works only when top safe area is in place. It can also be enabled by adding sheet-modal-push class to Sheet element.
containerElHTMLElement
string
Element to mount modal to (default to app root element)
onobject

Object with events handlers. For example:

var sheet = app.sheet.create({
  content: '<div class="sheet-modal">...</div>',
  on: {
    opened: function () {
      console.log('Sheet opened')
    }
  }
})

Note that all following parameters can be used in global app parameters under sheet property to set defaults for all sheets. For example:

var app = new Framework7({
  sheet: {
    closeByBackdropClick: false,
  }
});

If you use auto-initialized sheet modals (e.g. you don't create them via app.sheet.create), it is possible to pass all available sheet parameters via data- attributes. For example:

<!-- Pass parameters as kebab-case data attributes -->
<div class="sheet-modal" data-close-on-escape="true" data-swipe-to-close="true">
  ...
</div>

Sheet Methods & Properties

So to create Sheet we have to call:

var sheet = app.sheet.create({ /* parameters */ })

After that we have its initialized instance (like sheet variable in example above) with useful methods and properties:

Properties
sheet.appLink to global app instance
sheet.elSheet HTML element
sheet.$elDom7 instance with sheet HTML element
sheet.backdropElBackdrop HTML element
sheet.$backdropElDom7 instance with backdrop HTML element
sheet.paramsSheet parameters
sheet.openedBoolean prop indicating whether sheet is opened or not
Methods
sheet.open(animate)Open sheet. Where
  • animate - boolean (by default true) - defines whether it should be opened with animation
sheet.close(animate)Close sheet. Where
  • animate - boolean (by default true) - defines whether it should be closed with animation
sheet.stepOpen()Open/expand sheet swipe step
sheet.stepClose()Close/collapse sheet swipe step
sheet.stepToggle()Toggle (open or close) sheet swipe step
sheet.setSwipeStep()Update step position. Required to call after content of sheet modal has been modified manually when it is opened
sheet.destroy()Destroy sheet
sheet.on(event, handler)Add event handler
sheet.once(event, handler)Add event handler that will be removed after it was fired
sheet.off(event, handler)Remove event handler
sheet.off(event)Remove all handlers for specified event
sheet.emit(event, ...args)Fire event on instance

It is possible to open and close required sheet (if you have them in DOM) using special classes and data attributes on links:

  • To open sheet we need to add "sheet-open" class to any HTML element (prefered to link)

  • To close sheet we need to add "sheet-close" class to any HTML element (prefered to link)

  • If you have more than one sheet in DOM, you need to specify appropriate sheet via additional data-sheet=".my-sheet" attribute on this HTML element

According to above note:

<!-- In data-sheet attribute we specify CSS selector of sheet we need to open -->
<p><a href="#" data-sheet=".my-sheet" class="sheet-open">Open Sheet</a></p>

<!-- And somewhere in DOM -->
<div class="sheet-modal my-sheet">
  <div class="sheet-modal-inner">
    <!-- Link to close sheet -->
    <a class="link sheet-close">Close</a>
  </div>
</div>

Swipe Step

If you pass swipeToStep parameter, then sheet will be opened partially, and with swipe it can be further expanded. To make it work, we also need to define that first/initial step in sheet HTML, so Framework7 can know on how much Sheet should be opened.

To make it work, we need to wrap initial Sheet content with <div class="sheet-modal-swipe-step"> element, and set height:auto on that Sheet modal:

<div class="sheet-modal" style="height: auto">
  <div class="sheet-modal-inner">
    <!-- initial sheet modal content -->
    <div class="sheet-modal-swipe-step">
      ...
    </div>
    <!-- rest of the content that will be opened with extra swipe -->
    ...
  </div>
</div>

For top-positioned Sheet modal, this swipe step should be at the bottom:

<div class="sheet-modal sheet-modal-top" style="height: auto">
  <div class="sheet-modal-inner">
    <!-- rest of the content that will be opened with extra swipe -->
    ...
    <!-- initial sheet modal content -->
    <div class="sheet-modal-swipe-step">
      ...
    </div>
  </div>
</div>

Sheet Events

Sheet will fire the following DOM events on sheet element and events on app and sheet instance:

DOM Events

EventTargetDescription
sheet:openSheet Element<div class="sheet">Event will be triggered when Sheet starts its opening animation
sheet:openedSheet Element<div class="sheet">Event will be triggered after Sheet completes its opening animation
sheet:closeSheet Element<div class="sheet">Event will be triggered when Sheet starts its closing animation
sheet:closedSheet Element<div class="sheet">Event will be triggered after Sheet completes its closing animation
sheet:stepopenSheet Element<div class="sheet">Event will be triggered on Sheet swipe step open/expand
sheet:stepcloseSheet Element<div class="sheet">Event will be triggered on Sheet swipe step close/collapse
sheet:stepprogressSheet Element<div class="sheet">Event will be triggered on Sheet swipe step between step opened and closed state. As event.detail it receives step open progress number (from 0 to 1)
sheet:beforedestroySheet Element<div class="sheet">Event will be triggered right before Sheet instance will be destroyed

App and Sheet Instance Events

Sheet instance emits events on both self instance and app instance. App instance events has same names prefixed with popup.

EventArgumentsTargetDescription
opensheetsheetEvent will be triggered when Sheet starts its opening animation. As an argument event handler receives sheet instance
sheetOpensheetapp
openedsheetsheetEvent will be triggered after Sheet completes its opening animation. As an argument event handler receives sheet instance
sheetOpenedsheetapp
closesheetsheetEvent will be triggered when Sheet starts its closing animation. As an argument event handler receives sheet instance
sheetClosesheetapp
closedsheetsheetEvent will be triggered after Sheet completes its closing animation. As an argument event handler receives sheet instance
sheetClosedsheetapp
beforeDestroysheetsheetEvent will be triggered right before Sheet instance will be destroyed. As an argument event handler receives sheet instance
sheetBeforeDestroysheetapp
stepOpensheetsheetEvent will be triggered on Sheet swipe step open/expand
sheetStepOpensheetapp
stepClosesheetsheetEvent will be triggered on Sheet swipe step close/collapse
sheetStepClosesheetapp
stepProgresssheet,progresssheetEvent will be triggered on Sheet swipe step between step opened and closed state. As progress it receives step open progress number (from 0 to 1)
sheetStepProgresssheet,progressapp

CSS Variables

Below is the list of related CSS variables (CSS custom properties).

:root {
  --f7-sheet-height: 260px;
  --f7-sheet-border-color: transparent;
  --f7-sheet-transition-duration: 300ms;
  --f7-sheet-push-border-radius: 10px;
  --f7-sheet-push-offset: var(--f7-safe-area-top);
  --f7-sheet-bg-color: #fff;
}
:root .theme-dark,
:root.theme-dark {
  --f7-sheet-bg-color: #202020;
}
.ios {
  --f7-sheet-border-color: var(--f7-bars-border-color);
}

Examples

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <!-- In data-sheet attribute we specify CSS selector of sheet we need to open-->
        <div class="title">Sheet Modal</div>
        <div class="right"><a class="link sheet-open" href="#" data-sheet=".my-sheet">Open Sheet</a></div>
      </div>
    </div>
    <div class="page-content">
      <div class="block block-strong">
        <!-- In data-sheet attribute we specify CSS selector of sheet we need to open-->
        <p><a class="button button-fill sheet-open" href="#" data-sheet=".my-sheet">Open Sheet</a></p>
        <!-- Link to close sheet-->
        <p><a class="button button-fill sheet-close" href="#" data-sheet=".my-sheet">Close Sheet</a></p>
        <p><a class="button button-fill dynamic-sheet" href="#">Create Dynamic Sheet</a></p>
        <p><a class="button button-fill sheet-open" href="#" data-sheet=".my-sheet-top">Open Top Sheet</a></p>
        <p><a class="button button-fill sheet-open" href="#" data-sheet=".my-sheet-swipe-to-close">Swipe To
            Close</a></p>
        <p><a class="button button-fill sheet-open" href="#" data-sheet=".my-sheet-swipe-to-step">Swipe To Step</a>
        </p>
        <p><a class="button button-fill sheet-open" href="#" data-sheet=".my-sheet-push">Sheet Push</a></p>
      </div>
    </div>
  </div>
  <div class="sheet-modal my-sheet">
    <div class="toolbar">
      <div class="toolbar-inner">
        <div class="left"></div>
        <div class="right"><a class="link sheet-close" href="#">Done</a></div>
      </div>
    </div>
    <div class="sheet-modal-inner">
      <div class="block">
        <h4>Info</h4>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ac diam ac quam euismod porta vel a
          nunc. Quisque sodales scelerisque est, at porta justo cursus ac.</p>
      </div>
    </div>
  </div>
  <div class="sheet-modal sheet-modal-top my-sheet-top">
    <div class="toolbar toolbar-bottom">
      <div class="toolbar-inner">
        <div class="left"></div>
        <div class="right"><a class="link sheet-close" href="#">Done</a></div>
      </div>
    </div>
    <div class="sheet-modal-inner">
      <div class="block">
        <h4>Info</h4>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ac diam ac quam euismod porta vel a
          nunc. Quisque sodales scelerisque est, at porta justo cursus ac.</p>
      </div>
    </div>
  </div>
  <div class="sheet-modal my-sheet-swipe-to-close" style="height:auto; --f7-sheet-bg-color: #fff;">
    <div class="sheet-modal-inner">
      <div class="page-content">
        <div class="block-title block-title-large">Hello!</div>
        <div class="block">
          <p><b>Swipe me down to close</b></p>
          <p>Eaque maiores ducimus, impedit unde culpa qui, explicabo accusamus, non vero corporis voluptatibus
            similique odit ab. Quaerat quasi consectetur quidem libero? Repudiandae adipisci vel voluptatum, autem
            libero minus dignissimos repellat.</p>
        </div>
      </div>
    </div>
  </div>
  <div class="sheet-modal my-sheet-swipe-to-step" style="height:auto; --f7-sheet-bg-color: #fff;">
    <div class="sheet-modal-inner">
      <div class="sheet-modal-swipe-step">
        <div class="display-flex padding justify-content-space-between align-items-center">
          <div style="font-size: 18px"><b>Total:</b></div>
          <div style="font-size: 22px"><b>$500</b></div>
        </div>
        <div class="padding-horizontal padding-bottom">
          <a class="button button-large button-fill">Make Payment</a>
          <div class="margin-top text-align-center">Swipe up for more details</div>
        </div>
      </div>
      <div class="block-title block-title-medium margin-top">Your order:</div>
      <div class="list no-hairlines">
        <ul>
          <li class="item-content">
            <div class="item-inner">
              <div class="item-title">Item 1</div>
              <div class="item-after text-color-black"><b>$200</b></div>
            </div>
          </li>
          <li class="item-content">
            <div class="item-inner">
              <div class="item-title">Item 2</div>
              <div class="item-after text-color-black"><b>$180</b></div>
            </div>
          </li>
          <li class="item-content">
            <div class="item-inner">
              <div class="item-title">Delivery</div>
              <div class="item-after text-color-black"><b>$120</b></div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <div class="sheet-modal sheet-modal-push my-sheet-push">
    <div class="toolbar">
      <div class="toolbar-inner">
        <div class="left"></div>
        <div class="right">
          <a class="link sheet-close">Close</a>
        </div>
      </div>
    </div>
    <div class="sheet-modal-inner">
      <div class="page-content">
        <div class="block">
          <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quae ducimus dolorum ipsa aliquid accusamus
            perferendis laboriosam delectus numquam minima animi, libero illo in tempora harum sequi corporis alias
            ex adipisci.</p>
          <p>Sunt magni enim saepe quasi aspernatur delectus consectetur fugiat necessitatibus qui sed, similique
            quis facere tempora, laudantium quae expedita ea, aperiam dolores. Aut deserunt soluta alias magnam.
            Consequatur, nisi, enim.</p>
          <p>Eaque maiores ducimus, impedit unde culpa qui, explicabo accusamus, non vero corporis voluptatibus
            similique odit ab. Quaerat quasi consectetur quidem libero? Repudiandae adipisci vel voluptatum, autem
            libero minus dignissimos repellat.</p>
          <p>Iusto, est corrupti! Totam minus voluptas natus esse possimus nobis, delectus veniam expedita sapiente
            ut cum reprehenderit aliquid odio amet praesentium vero temporibus obcaecati beatae aspernatur incidunt,
            perferendis voluptates doloribus?</p>
          <p>Illum id laborum tempore, doloribus culpa labore ex iusto odit. Quibusdam consequuntur totam nam
            obcaecati, enim cumque nobis, accusamus, quos voluptates, voluptatibus sapiente repellendus nesciunt
            praesentium velit ipsa illo iusto.</p>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $on, $, $f7 }) => {
    $on('pageInit', () => {
      // DOM events for my-sheet sheet
      $('.my-sheet').on('sheet:open', function (e) {
        console.log('my-sheet open');
      });
      $('.my-sheet').on('sheet:opened', function (e) {
        console.log('my-sheet opened');
      });

      // Create dynamic Sheet
      var dynamicSheet = app.sheet.create({
        content: `
          <div class="sheet-modal">
            <div class="toolbar">
              <div class="toolbar-inner">
                <div class="left"></div>
                <div class="right">
                  <a class="link sheet-close">Done</a>
                </div>
              </div>
            </div>
            <div class="sheet-modal-inner">
              <div class="block">
                <p>Sheet created dynamically.</p>
                <p><a href="#" class="link sheet-close">Close me</a></p>
              </div>
            </div>
          </div>
        `,
        // Events
        on: {
          open: function (sheet) {
            console.log('Sheet open');
          },
          opened: function (sheet) {
            console.log('Sheet opened');
          },
        }
      });
      // Events also can be assigned on instance later
      dynamicSheet.on('close', function (sheet) {
        console.log('Sheet close');
      });
      dynamicSheet.on('closed', function (sheet) {
        console.log('Sheet closed');
      });

      // Open dynamic sheet
      $('.dynamic-sheet').on('click', function () {
        // Close inline sheet before
        app.sheet.close('.my-sheet');

        // Open dynamic sheet
        dynamicSheet.open();
      });

      // Create swipe-to-close Sheet
      app.sheet.create({
        el: '.my-sheet-swipe-to-close',
        swipeToClose: true,
        backdrop: true,
      });
      // Create swipe-to-step Sheet
      app.sheet.create({
        el: '.my-sheet-swipe-to-step',
        swipeToClose: true,
        swipeToStep: true,
        backdrop: true,
      });
    })

    return $render;
  }
</script>