npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nrk/core-dialog

v4.0.4

Published

> `@nrk/core-dialog` is an elevated element with which the user interacts with to perform some task or decision. > It supports nestability, keyboard navigation containment and restoring focus when dialog is closed.

Downloads

3,429

Maintainers

hammeralfhammeralfjanerikbrjanerikbrthormodbthormodbsiiverssiiverstorsrextorsrexharaldskharaldskeskilgheskilghragnaroh-nrkragnaroh-nrkdaardaldaardalarevjensenarevjensenjulusianjulusianmadsernmadsernandrefauandrefaujfjeldskaarjfjeldskaarmuddahmuddahjensragejensrageoysteinkoppangoysteinkoppangphajsiphajsijorn_georgjorn_georgbjornhelsbjornhelshalvorhhalvorhmorten-nrkmorten-nrknicklassvendsrudnicklassvendsrudkjellvnnrkkjellvnnrksanderknrksanderknrknikolaianikolaiaeirikjstnrkeirikjstnrkcarinafraningcarinafraninghelenperhelenperstefanogdennrkstefanogdennrkjimmeloysundjimmeloysundtobiasrptobiasrpmartioskmartioskjimalexbergerjimalexbergergunderwondergunderwonderhamnishamnisluminrkluminrksupermeisensupermeisenvagifabilovvagifabilovclaudio-nrkclaudio-nrkhaakemonhaakemonzenangstzenangstrannveigncrannveignceschoieneschoienbaltebaltetoshbtoshbemte123emte123opetopetklizterkliztermikkelnygardmikkelnygardfeiringfeiringdervodevdervodevgrimburgrimburgardkroyergardkroyerkariaankariaanedplayzedplayzelias-chairielias-chairimiatollaksvikmiatollaksvikytterboytterbomachineboycommachineboycomtrulsltrulslmslhmmslhmcbjerkancbjerkanhermangudesenhermangudesenandreeldareideandreeldareidehenningkollerhenningkollerespenhalstensenespenhalstensendanjohnrkdanjohnrkolapeterolapeterteodor-elstadteodor-elstadlorecasterlorecasternrk-ps-teamcitynrk-ps-teamcityswlaswlanrk-midas-jenkinsnrk-midas-jenkinsandorpandorandorpandornrkrichardnrkrichardgesigesigundelsby-nrkgundelsby-nrkjonstalecarlsenjonstalecarlsennrk-sofie-cinrk-sofie-cinytaminnytaminjesperstarkarjesperstarkarskjalgepalgskjalgepalgeirikhalvardeirikhalvardastokkeastokken640071n640071n07073n07073henrik-mattssonhenrik-mattssonhaavardmhaavardmyryrnrk-kurator-jenkinsnrk-kurator-jenkinstorgeilotorgeilonrk-user-syncnrk-user-syncdhdeploydhdeployespenwaespenwaovstetunovstetunstianljstianljharaldkjharaldkjmariusumariusucristobalcristobalknuthaugknuthaugthohalvthohalvjohnarnejohnarneeshaswinieshaswinimorrowmorrowoyvindehoyvindehlaatlaattoggutoggunrk-jenkinsnrk-jenkinsplommaplommaevjandevjandmoltubakkmoltubakkingridgureningridgurenlu-luxlu-luxanderslianderslisiljesiljestiandgstiandgsjurlursjurlurandipodnrkandipodnrkpkejpkejyosrimtiyosrimtimorten.nyhaugmorten.nyhaugingvildcathingvildcatherlend.joneserlend.jonesbrneirikbrneirikmollersemollersetbnrktbnrknordankenordankesimonmitternachtsimonmitternachtmartintorgersenmartintorgersenrebchrrebchrsteipalsteipaldiscobusdiscobusmartingundersenmartingundersentinkajtstinkajtshallvardlidhallvardlidtomivartomivarajacoajacotobinustobinusmortenokmortenoknrk-ark-deploynrk-ark-deployjeangilbertlouisjeangilbertlouisheidimorkheidimorkingriddraageningriddraagenfridajalborgfridajalborgbruusibruusirosvollrosvollchristianeidechristianeideenordbyenordbyglen_imrieglen_imriemia.aasbakkenmia.aasbakkenelathamnaelathamnaevjjan17evjjan17olatoftolatoftkongsrudkongsrudchrpeterchrpeteringvildforsethingvildforsethharaldk76haraldk76stigokstigokjohannesodlandjohannesodlandanders993anders993vildefjvildefjvildepkvildepkrolerbolerrolerbolermeloyguttmeloyguttanders.baggethunanders.baggethun

Keywords

Readme

Core Dialog

@nrk/core-dialog is an elevated element with which the user interacts with to perform some task or decision. It supports nestability, keyboard navigation containment and restoring focus when dialog is closed.

Examples (plain JS)

Nested dialog

<!--demo-->
<button data-for="my-dialog" type="button">Open dialog</button>
<core-dialog id="my-dialog" class="my-dialog" aria-label="first dialog title" hidden>
  <h1>Dialog title</h1>
  <p>Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.</p>
  <label>
    <small>Label for autofocused input</small>
    <input type="text" autofocus placeholder="Input with autofocus">
  </label>
  <br>
  <br>
  <button data-for="close" type="button">Close</button>
  <button data-for="my-dialog-nested" type="button">Open an additional dialog</button>
  <core-dialog id="my-dialog-nested" class="my-dialog" aria-label="other dialog title" hidden>
    <h1>Another dialog, triggered inside the first dialog</h1>
    <p>Nunc mi felis, condimentum quis hendrerit sed, porta eget libero.</p>
    <button data-for="close" type="button">Close</button>
  </core-dialog>
</core-dialog>

Strict dialog

<!--demo-->
<button data-for="strict-dialog">Open strict dialog</button>
<core-dialog id="strict-dialog" class="my-dialog" aria-label="strict dialog title" hidden strict>
  <h1>Strict dialog title</h1>
  <p>Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.</p>
  <button type="button">This button does nothing</button>
  <button data-for="close" type="button">Close</button>
</core-dialog>

Modal dialog

<!--demo-->
<button data-for="modal-dialog">Open dialog without backdrop</button>
<core-dialog id="modal-dialog" class="my-dialog" aria-label="modal dialog title" hidden backdrop="off">
  <h1>Dialog without backdrop</h1>
  <p>Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.</p>
  <button data-for="close">Close</button>
</core-dialog>

Custom backdrop

<!--demo-->
<button data-for="modal-custom">Open dialog with custom backdrop</button>
<core-dialog id="modal-custom" class="my-dialog" aria-label="modal dialog title" hidden backdrop="back-custom">
  <h1>Dialog title</h1>
  <p>Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.</p>
  <button data-for="close">Close</button>
</core-dialog>
<div id="back-custom" class="my-backdrop" style="background:rgba(0,0,50,.8)" hidden></div>

Preventing overscroll

Prevent scrolling the background when scrolling inside a scrollable element inside the dialog by setting overscroll-behavior: contain on the dialog-element.

Note that overscroll-behavior is only supported through enabling experimental features in Safari and Safari on iOS (caniuse).

Read this if you want a primer on how overscroll-behavior works.

<!--demo-->
<button data-for="overscroll-dialog">Open scrollable dialog</button>
<core-dialog id="overscroll-dialog" class="my-dialog" aria-label="Scrollable dialog title" hidden style="overscroll-behavior: contain">
  <h1>Scrollable dialog title</h1>
  <p style="max-height: 100px;overflow: scroll;border: 1px solid gray;">
    Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
  </p>
  <p style="max-height: 100px;overflow: scroll;border: 1px solid gray;">
    Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
  </p>
  <button data-for="close" type="button">Close</button>
</core-dialog>

Examples (React)

Class component

<!--demo-->
<div id="jsx-dialog"></div>
<script type="text/javascript">
  class DialogContainerDemo extends React.Component {
    constructor (props) {
      super(props)
      this.state = { hidden: true }
      this.toggleDialog = this.toggleDialog.bind(this)
      this.handleToggle = this.handleToggle.bind(this)
    }

    toggleDialog () {
      this.setState({ hidden: !this.state.hidden })
    }

    handleToggle (event) {
      this.setState({ hidden: event.target.hidden })
    }

    render () {
      return (
        <>
          <button onClick={this.toggleDialog} type="button">Open React dialog</button>
          <CoreDialog
            className="my-dialog"
            hidden={this.state.hidden}
            onDialogToggle={this.handleToggle}
            aria-label="React dialog"
          >
            <h1>Dialog for JSX</h1>
            <p>
              Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
            </p>
            <button onClick={this.toggleDialog} type="button">Lukk</button>
          </CoreDialog>
        </>
      )
    }
  }

  ReactDOM.render(<DialogContainerDemo />, document.getElementById('jsx-dialog'))
</script>

Strict dialog

<!--demo-->
<div id="jsx-dialog-strict"></div>
<script type="text/javascript">
  ReactDOM.render(
    <>
      <button data-for="dialog-strict-jsx" type="button">Open strict React dialog</button>
      <CoreDialog
        id="dialog-strict-jsx"
        className="my-dialog"
        aria-label="Strict React dialog"
        hidden
        strict
      >
        <h1>Strict dialog for JSX</h1>
        <p>
          Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
        </p>
        <button data-for="close" type="button">Lukk</button>
      </CoreDialog>
    </>,
    document.getElementById('jsx-dialog-strict')
  )
</script>

No backdrop

<!--demo-->
<div id="jsx-dialog-no-backdrop"></div>
<script type="text/javascript">
  ReactDOM.render(
    <>
      <button data-for="dialog-no-back-jsx" type="button">Open React dialog without backdrop</button>
      <CoreDialog
        id="dialog-no-back-jsx"
        className="my-dialog"
        aria-label="React dialog without backdrop"
        backdrop="off"
        hidden
      >
        <h1>React dialog without backdrop</h1>
        <p>
          Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
        </p>
        <button data-for="close" type="button">Lukk</button>
      </CoreDialog>
    </>,
    document.getElementById('jsx-dialog-no-backdrop')
  )
</script>

Custom backdrop

NB! Do not wrap CoreDialog with custom backdrop as direct children of React.Fragments (instead, wrap in a block element like <div>), to ensure access to the backdrop element on mount.

<!--demo-->
<div id="jsx-dialog-custom"></div>
<script type="text/javascript">
  ReactDOM.render(
    <div>
      <button data-for="dialog-cust-jsx" type="button">Open React dialog with custom backdrop</button>
      <CoreDialog
        id="dialog-cust-jsx"
        className="my-dialog"
        aria-label="React dialog with custom backdrop"
        backdrop="custom-backdrop-jsx"
        hidden
      >
        <h1>React dialog with custom backdrop</h1>
        <p>
          Nunc mi felis, condimentum quis hendrerit sed, porta eget libero. Aenean scelerisque ex eu nisi varius hendrerit. Suspendisse elementum quis massa at vehicula. Nulla lacinia mi pulvinar, venenatis nisi ut, commodo quam. Praesent egestas mi sit amet quam porttitor, mollis mattis mi rhoncus.
        </p>
        <button data-for="close" type="button">Lukk</button>
      </CoreDialog>
      <div
        id="custom-backdrop-jsx"
        className="my-backdrop"
        style={{background:'rgba(0,0,50,.8)'}}
        hidden
      ></div>
    </div>,
    document.getElementById('jsx-dialog-custom')
  )
</script>

Installation

Using NPM provides own element namespace and extensibility. Recommended:

npm install @nrk/core-dialog  # Using NPM

Using static registers the custom element with default name automatically:

<script src="https://static.nrk.no/core-components/major/10/core-dialog/core-dialog.min.js"></script>  <!-- Using static -->

Remember to polyfill custom elements if needed.

Usage

HTML / JavaScript

<button data-for="my-dialog">Open</button>    <!-- Opens dialog with id="my-dialog" -->
<core-dialog id="my-dialog"
             hidden                      <!-- Hide dialog by default -->
             strict                      <!-- Optional. If set, prevents the dialog from closing on ESC-key and on backdrop click -->
             backdrop="{on|off|String}"  <!-- Optional. Default is "on" and will use standard backdrop. "off" disables backdrop and string ID points to custom backdrop element -->
             aria-label="{String}">      <!-- Optional. Is read by screen readers -->
  <h1>Title of dialog</h1>
  <p>Some content</p>
  <button data-for="close">Close dialog</button>   <!-- Closes dialog when data-for="close" -->
</core-dialog>
import CoreDialog from '@nrk/core-dialog'                 // Using NPM
window.customElements.define('core-dialog', CoreDialog)   // Using NPM. Replace 'core-dialog' with 'my-dialog' to namespace

const myDialog = document.querySelector('core-dialog')

// Getters
myDialog.hidden         // True if hidden
myDialog.strict         // True if strict
myDialog.backdrop       // Get backdrop element (if enabled) (see "Markup" for more info)

// Setters
myDialog.hidden = false // Open dialog
myDialog.strict = false // Set strict mode. If set, prevents the dialog from closing on ESC-key and on backdrop click
myDialog.backdrop = 'on' | 'off' | 'my-drop' // Set "on"/"off" to enable/disable standard backdrop or string ID to point to custom backdrop element
myDialog.style.zIndex = '9' // Set z-index manually. If unset, z-index is automatically set for both dialog and backdrop element. Default unset.

// Methods
myDialog.close()        // Close dialog
myDialog.show()         // Open dialog

React / Preact

import CoreDialog from '@nrk/core-dialog/jsx'

<button data-for="my-dialog">Open</button>    // Opens dialog with id="my-dialog"
<CoreDialog id="my-dialog"
            hidden                            // Hide dialog by default
            strict                            // Optional. If set, prevents the dialog from closing on ESC-key and on backdrop click
            backdrop={Boolean|String}         // Optional. If false, disables backdrop, string ID points to custom backdrop element
            aria-label={String}               // Optional. Is read by screen readers
            ref={(comp) => {}}                // Optional. Get reference to React component
            forwardRef={(el) => {}}           // Optional. Get reference to underlying DOM custom element
            onDialogToggle={Function}>        // Optional. Toggle event handler. See event 'dialog.toggle'
  <h1>My React/Preact dialog</h1>
  <p>Some content</p>
  <button data-for="close"></button>          // Closes dialog when data-for="close"
</CoreDialog>

Markup

Required focusable element

Your dialog must contain a tabbable element (e.g. visible <input>, <button>, <select>, <textarea>, <a>, <summary>, <audio>, <video>, <iframe>, <area> or with the contenteditable or draggable attributes) or a focusable element (with tabindex="-1") to ensure the users focus is navigated into the <core-dialog>. As a best practice; if your dialog contains a form element, use autofocus. If you dialog is without form elements, start your dialog content with <h1 tabindex="-1">Dialog title</h1>.

Elements order

Though not strictly required, the <button> opening a dialog should be placed directly before the <core-dialog> itself. This eases the mental model for screen reader users. Othewise, use <button data-for="my-dialog-id"></button>.

Backdrop

core-dialog automatically creates a <backdrop> element as next adjacent sibling if needed. If the backdrop attribute is set to an id (something else than true|false), the element with the corresponding ID will be used as backdrop. Note that a backdrop is needed to enable click-outside-to-close. Custom backdrop example:

<core-dialog backdrop="my-backdrop"></core-dialog>
<div id="my-backdrop"></div>

Stacking

To manually control z-index of dialogs (and their corresponding backdrop element, set z-index from either HTML, CSS or JS. When set, the dialog will not try to place itself automatically over the topmost dialog and you are responsible for stacking order.

For example:

<style>
  .my-dialog { z-index: 100 }
  .my-backdrop { z-index: 90 }
</style>

<script>
  myDialog.style.zIndex = 100
  myBackdrop.style.zIndex = 90
</script>

<core-dialog style="z-index: 100" hidden>...</core-dialog>
<backdrop style="z-index: 90" hidden>...</backdrop>

Events

dialog.toggle

Fired when a dialog is toggled:

document.addEventListener('dialog.toggle', (event) => {
  event.target    // The dialog element
})

Styling

.my-dialog {}                         /* Target dialog in any state */
.my-dialog[hidden] {}                 /* Target dialog in closed state */
.my-dialog:not([hidden]) {}           /* Target dialog in open state */
.my-dialog + backdrop {}              /* Target backdrop in open state */
.my-dialog + backdrop[hidden] {}      /* Target backdrop in closed state */

Note: There is a z-index limit for the backdrop at 2000000000. Do not use higher z-index values in your site in order for core-dialog to work properly. The limit exists because some browser extensions, like ghostery have absurdly high z-indexes. The issue is further explained here.