Dialog Example

The following example web component creates a metadata panel section containing a series of forms that you can use to display demo dialogs. It has no practical function, it is simply a demonstration of how the various dialog methods work, and what kind of dialogs they display. The web component is created using the cue.core.webcomponents.StorylineEditorMetadataPanel class and is configured as follows:

customComponents:
  - name: "simple-dialog"
    tagName: "simple-dialog"
    modulePath: "webcomponents/simple-dialog/simple-dialog.js"
    attributes:
      title: "Simple Dialog"
      icon: "simple-dialog-icon"

Here is the web component code:

class SimpleDialog extends cue.core.webcomponents.StorylineEditorMetadataPanel {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
        <style>
        :host { margin:0; padding: 0; width: 100%; display:block; }
        h1, h2, h3, h4 {
            color: #9c9c9c;
        }

        /* Panel specific inputs */
        .text-field {
            height: 32px;
            vertical-align: middle;
            font-family: 'Hind', Helvetica Neue, Helvetica, Arial, Sans-serif;
            font-weight: lighter;
            font-size: 18px;
            line-height: 1.3;
            color: #444444;
            cursor: pointer;
            border-radius: 3px;
            width: 100%;
        }

        .text-area {
            height: 128px;
            font-family: 'Hind', Helvetica Neue, Helvetica, Arial, Sans-serif;
            font-weight: lighter;
            font-size: 18px;
            color: #444444;
            cursor: pointer;
            border-radius: 3px;
            width: 100%;
        }

        button,
        input[type=submit],
        input[type=cancel],
        input[type=button] {
            height: 32px;
            border: none;
            background: #d3d3d3;
            vertical-align: middle;
            font-family: 'Hind', Helvetica Neue, Helvetica, Arial, Sans-serif;
            font-weight: lighter;
            font-size: 18px;
            line-height: 1.3;
            color: #444444;
            padding: 2px 10px 0 10px;
            cursor: pointer;
            border-radius: 3px;
            width: 100%;
        }
        button:hover {
            background: #e5e5e5;
        }
        .group {
            padding: 20px;
        }
        .spacer {
            padding:10px
        }
        p {
            margin: 0;
        }
        </style>
        <div>
            <h1>Simple Dialog API</h1>
            <h2>A Web Component</h2>
        </div>
        <hr>
        <div class="group">
            <h3>Simple Customizable Dialogs</h3>
            <p></p>
            <div class="spacer"></div>
            <p>Title</p>
            <input class ="text-field" type="text" id="simple-title" value="Simple Dialog">
            <p>Message</p>
            <textarea class="text-area" id="simple-message" rows="5" >description</textarea>
            <p>Button Label</p>
            <input class ="text-field" type="text" id="simple-button-label" value="Ok">
            <div class="spacer"></div>
            <button id="simple-button">Show One Button Dialog</button>
        </div>
        <hr>
        <div class="group">
            <h3>Customizable Reactive Dialogs</h3>
            <p>The dialog API exposes customizable dialogs with positve and negative options. The answer is a Promise that is resolved or rejected.</p>
            <div class="spacer"></div>
            <p>Title</p>
            <input class ="text-field" type="text" id="two-button-title" value="OK or Cancel dialog">
            <p>Message</p>
            <textarea class="text-area" id="two-button-message" rows="5" >Using this dialog one can promt the user for a positive or negative answer. The answer can be used for further action.</textarea>
            <p>Cancel label</p>
            <input class ="text-field" type="text" id="two-button-cancel-label" value="Cancel">
            <p>Ok Label</p>
            <input class ="text-field" type="text" id="two-button-ok-label" value="Ok">
            <label for="two-button-use-default-buttons"> Use default buttons</label>
            <input type="checkbox" id="two-button-use-default-buttons" checked>
            <div class="spacer"></div>
            <div class="spacer"></div>
            <button id="two-button-button">Show Two Button Dialog</button>
            <div class="spacer"></div>

            <p>output:</p>
            <input disabled class ="text-field" type="text" id="two-button-output" value="">
        </div>
        <hr>
        <div class="group">

            <h3>Input validation using Dialogs</h3>
            <p>The Dialos API can be used to validate input from the user.</p>
            <div class="spacer"></div>
            <p>Which City was the European Capital of Culture 2017?</p>
            <input class ="text-field" type="text" id="answer" value="">
            <div class="spacer"></div>
            <button id="quiz-button">Answer question</button>

            <div class="spacer"></div>
            <p>Insert a number between 5 and 10</p>
            <input class ="text-field" type="number" id="numberField" value="">
            <div class="spacer"></div>
            <button type="submit"id="numValidation">Submit</button>
            <div class="spacer"></div>

        </div>
        <hr>
        <div class="group">
            <h3>Alert Dialogs</h3>
            <p>The dialog API exposes two levels of alert dialogs; Warning and Error. A description of the problem can be passed to the dialog.</p>
            <div class="spacer"></div>
            <p>Type alert message:</p>
            <textarea class="text-area" id="error-msg" rows="5" >A error dialog can be used to notify the user, who is about to do something is not permitted</textarea>
            <div class="spacer"></div>
            <button id="openWarningDialog" class="buttons">Warning Dialog</button>
            <div class="spacer"></div>
            <button id="openErrorDialog" class="buttons">Error Dialog</button>
            <div class="spacer"></div>
        </div>

        <hr>
        <h2>Advanced Dialog API</h2>
        <p>The advanced API is based on VDF models.</p>
        <div class="group">
            <h3>A dialog editor</h3>
            <p>Values can be passed to the dialog</p>
            <p>Top</p>
            <input class="text-field" type="text" id="editor-top" value="top">
            <p>Titel</p>
            <input class="text-field" type="text" id="editor-title" value="title">
            <p>Cancel label</p>
            <input class ="text-field" type="text" id="editor-cancel-label" value="Cancel">
            <p>Ok Label</p>
            <input class ="text-field" type="text" id="editor-ok-label" value="Ok">
            <label for="editor-use-default-buttons"> Use default buttons</label>
            <input type="checkbox" id="editor-use-default-buttons" checked>
            <div class="spacer"></div>
            <button id="editor">Editor Dialog</button>
            <div class="spacer"></div>
            <p>body output:</p>
            <span class="text-field" type="text" id="editor-output"></span>
        </div>
        `;
  }

  connectedCallback() {
    this.shadowRoot
      .getElementById('simple-button')
      .addEventListener('click', () => {
        // Get the dialog configurations
        const top = this.shadowRoot.querySelector('#simple-title').value;
        const description = this.shadowRoot.querySelector('#simple-message')
          .value;
        const buttonLabel = this.shadowRoot.querySelector(
          '#simple-button-label'
        ).value
          ? this.shadowRoot.querySelector('#simple-button-label').value
          : undefined;
        // Call the dialog api
        this.dialog.showOneButton(description, top, buttonLabel);
      });

    this.shadowRoot
      .getElementById('two-button-button')
      .addEventListener('click', () => {
        // Get the dialog configurations
        const top = this.shadowRoot.querySelector('#two-button-title').value;
        const description = this.shadowRoot.querySelector('#two-button-message')
          .value;
        const okButtonLabel = this.shadowRoot.querySelector(
          'input[id="two-button-ok-label"]'
        ).value
          ? this.shadowRoot.querySelector('input[id="two-button-ok-label"]')
              .value
          : undefined;
        const cancelButtonLabel = this.shadowRoot.querySelector(
          'input[id="two-button-cancel-label"]'
        ).value
          ? this.shadowRoot.querySelector('input[id="two-button-cancel-label"]')
              .value
          : undefined;
        const useDefaultButtons = this.shadowRoot.querySelector(
          '#two-button-use-default-buttons'
        ).checked;

        // Call the dialog api
        this.dialog
          .showTwoButton(
            description,
            top,
            okButtonLabel,
            cancelButtonLabel,
            useDefaultButtons
          )
          .then(
            // if promise is resolved
            () => {
              this.shadowRoot.querySelector(
                'input[id="two-button-output"]'
              ).value = okButtonLabel ? okButtonLabel : 'OK';
            },
            //if promise is rejected
            () => {
              this.shadowRoot.querySelector(
                'input[id="two-button-output"]'
              ).value = cancelButtonLabel ? cancelButtonLabel : 'Cancel';
            }
          );
      });

    this.shadowRoot
      .getElementById('quiz-button')
      .addEventListener('click', () => {
        const answer = this.shadowRoot.querySelector('input[id="answer"]')
          .value;
        try {
          const possibleAnswers = ['aarhus', 'århus'];
          let correct = false;
          possibleAnswers.forEach(ans => {
            if (answer.toLowerCase().localeCompare(ans) === 0) correct = true;
          });

          if (!correct) {
            throw Error(
              'No "' +
                answer +
                '"' +
                ' was not the European Capital of Culture 2017'
            );
          }
          this.dialog.showOneButton(
            'Yes, it is correct that the European Capital of Culture 2017 is "Århus"',
            'Correct',
            'Yaay',
            true
          );
        } catch (error) {
          this.dialog.showOneButton(error.message, 'Incorrect', 'Oops', true);
        }
      });

    this.shadowRoot
      .getElementById('numValidation')
      .addEventListener('click', () => {
        let x = this.shadowRoot.querySelector('input[id="numberField"]').value;
        try {
          if (x == '') throw new Error('field is empty');
          if (isNaN(x)) throw new Error('input is not a number');
          x = Number(x);
          if (x < 5) throw new Error('input is too low');
          if (x > 10) throw new Error('input is too high');
          this.dialog.showOneButton(
            'Yes, number is between 5 an 10',
            'Good Job',
            'Thanks',
            true
          );
        } catch (error) {
          this.dialog.showOneButton(
            error.message,
            'Invalid Input',
            "I'll try again",
            'Please try again'
          );
        }
      });

    this.shadowRoot
      .getElementById('openWarningDialog')
      .addEventListener('click', () => {
        const description = this.shadowRoot.querySelector('#error-msg').value;
        this.dialog.showWarning(description);
      });

    this.shadowRoot
      .getElementById('openErrorDialog')
      .addEventListener('click', () => {
        const description = this.shadowRoot.querySelector('#error-msg').value;
        this.dialog.showError(description);
      });

    this.shadowRoot.getElementById('editor').addEventListener('click', () => {
      const top = this.shadowRoot.querySelector('#editor-top').value;
      const title = this.shadowRoot.querySelector('#editor-title').value;
      const okButtonLabel = this.shadowRoot.querySelector(
        'input[id="editor-ok-label"]'
      ).value
        ? this.shadowRoot.querySelector('input[id="editor-ok-label"]').value
        : undefined;
      const cancelButtonLabel = this.shadowRoot.querySelector(
        'input[id="editor-cancel-label"]'
      ).value
        ? this.shadowRoot.querySelector('input[id="editor-cancel-label"]').value
        : undefined;
      const useDefaultButtons = this.shadowRoot.querySelector(
        '#editor-use-default-buttons'
      ).checked;
      // Call the dialog api
      this.dialog
        .showVdf(
          `
          <vdf:payload xmlns:vdf="http://www.vizrt.com/types" model="webcomponents/simple-dialog/vdfEditorModel.xml">
              <vdf:field name="title">
                  <vdf:value>` +
            title +
            `</vdf:value>
              </vdf:field>
              <vdf:field name="body">
                  <vdf:value>
                      <div xmlns="http://www.w3.org/1999/xhtml">
                          <p>this is body</p>
                      </div>
                  </vdf:value>
              </vdf:field>
          </vdf:payload>`,
          top,
          okButtonLabel,
          cancelButtonLabel,
          useDefaultButtons
        )
        .then(
          // if promise is resolved
          res => {
            this.shadowRoot.querySelector('#editor-output').innerHTML =
              res.values.body;
          },
          //if promise is rejected
          res => {
            console.log(res);
          }
        );
    });
  }
}
customElements.define('simple-dialog', SimpleDialog);

class SimpleDialogIcon extends cue.core.webcomponents
  .StorylineEditorMetadataPanel {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
          <style>
            :host { margin: 0 0px 0 0px; width: 26px; display: inline; float: left; margin-right: 18px; }
            img { width: 20px; position: relative; }
          </style>
          <img class="icon">
        `;

    this.activeIconPath = 'simple-dialog-icon.png';
    this.inactiveIconPath = 'simple-dialog-icon.png';
  }
  connectedCallback() {
    this.activeStateChanged(this.active);
    this.addActiveWatcher(active => {
      this.activeStateChanged(active);
    });
  }

  activeStateChanged(active) {
    let img = this.shadowRoot.querySelector('img.icon');
    if (active) {
      img.src = this.getAbsolutePath(this.activeIconPath);
    } else {
      img.src = this.getAbsolutePath(this.inactiveIconPath);
    }
  }
  getAbsolutePath(path) {
    const baseURI = import.meta.url;
    return baseURI.substring(0, baseURI.lastIndexOf('/') + 1) + path;
  }
}
customElements.define('simple-dialog-icon', SimpleDialogIcon);