github.com/derat/nup@v0.0.0-20230418113745-15592ba7c620/web/dialog.ts (about)

     1  // Copyright 2022 Daniel Erat.
     2  // All rights reserved.
     3  
     4  import {
     5    $,
     6    commonStyles,
     7    createElement,
     8    createShadow,
     9    createTemplate,
    10  } from './common.js';
    11  
    12  const dialogStyle = createTemplate(`
    13  <style>
    14    :host {
    15      display: inline-block; /* let width be set */
    16      text-align: left;
    17    }
    18    div.title {
    19      color: var(--dialog-title-color);
    20      font-size: 18px;
    21      font-weight: bold;
    22      overflow: hidden;
    23      text-overflow: ellipsis;
    24      user-select: none;
    25      white-space: nowrap;
    26    }
    27    hr.title {
    28      background-color: var(--border-color);
    29      border: 0;
    30      height: 1px;
    31      margin: 4px 0;
    32    }
    33    div.button-container {
    34      margin-top: 4px;
    35      text-align: right;
    36    }
    37  </style>
    38  `);
    39  
    40  const msgTemplate = createTemplate(`
    41  <style>
    42    :host {
    43      width: 400px;
    44    }
    45    #message {
    46      line-height: 18px;
    47      margin-top: 10px;
    48    }
    49  </style>
    50  <div id="title" class="title"></div>
    51  <hr class="title" />
    52  <div id="message"></div>
    53  <form method="dialog">
    54    <div class="button-container">
    55      <button id="ok-button" autofocus>OK</button>
    56    </div>
    57  </form>
    58  `);
    59  
    60  // Number of open dialogs.
    61  let numDialogs = 0;
    62  
    63  // Creates and shows a modal dialog filled with the supplied template.
    64  // |className| is added to the <dialog>. The <dialog> element is returned, and
    65  // the shadow root can be accessed via |dialog.firstElementChild.shadowRoot|.
    66  export function createDialog(
    67    template: HTMLTemplateElement,
    68    className: string | null = null
    69  ): HTMLDialogElement {
    70    const dialog = createElement(
    71      'dialog',
    72      'dialog',
    73      document.body
    74    ) as HTMLDialogElement;
    75    if (className) dialog.classList.add(className);
    76  
    77    dialog.addEventListener('close', () => {
    78      document.body.removeChild(dialog);
    79      numDialogs--;
    80    });
    81  
    82    // It seems like it isn't possible to attach a shadow root directly to
    83    // <dialog>, so add a wrapper element first. See
    84    // https://dom.spec.whatwg.org/#dom-element-attachshadow and
    85    // https://github.com/WICG/webcomponents/issues/110.
    86    const wrapper = createElement('span', null, dialog);
    87    const shadow = createShadow(wrapper, dialogStyle);
    88    shadow.adoptedStyleSheets = [commonStyles];
    89    shadow.appendChild(template.content.cloneNode(true));
    90  
    91    dialog.showModal();
    92    numDialogs++;
    93    return dialog;
    94  }
    95  
    96  // Creates and shows a modal dialog with the supplied title and text.
    97  // The dialog is not returned.
    98  export function showMessageDialog(title: string, message: string) {
    99    const dialog = createDialog(msgTemplate);
   100    const shadow = dialog.firstElementChild!.shadowRoot!;
   101    $('title', shadow).innerText = title;
   102    $('message', shadow).innerText = message;
   103    $('ok-button', shadow).addEventListener('click', () => dialog.close());
   104  }
   105  
   106  // Returns true if a dialog is currently shown.
   107  export const isDialogShown = () => numDialogs > 0;