foam3

The FOAM3 framework has a clear hierarchy of modal components, with Popup serving as the base class for all modal dialogs.

Base Component: Popup

Popup is the foundational modal component that provides core modal functionality:

StyledModal: Standardized Modal Design

StyledModal extends Popup to provide a consistent, styled modal interface:

Key Features:

ConfirmationModal: Action-Oriented Dialogs

ConfirmationModal extends StyledModal for yes/no confirmation scenarios:

Specialized Features:

ApplicationPopup: Wizard and Flow Context

ApplicationPopup extends Popup for complex application flows like wizards:

Advanced Features:

Usage Patterns

The framework registers these components in the build system, making them available throughout the application.

When to use each:

Notes

There’s also a legacy PopupView component that appears to be an older implementation with different positioning logic, but the dialog system primarily uses the Popup hierarchy described above. The framework also includes specialized modals like PropertyModal, a ConfirmationModal subclass for property editing scenarios.

Citations

File: src/foam/u2/dialog/Popup.js (L18-21)

foam.CLASS({
  package: 'foam.u2.dialog',
  name: 'Popup',
  extends: 'foam.u2.borders.ControlBorder',

File: src/foam/u2/dialog/Popup.js (L23-26)

  documentation: `This is a container for a whole-screen, modal overlay. It
    fills the viewport with a transparent grey background, and then
    centers the "content" element. Clicking the background closes the
    dialog. Exports itself as "overlay", for use by OK and CANCEL buttons.`,

File: src/foam/u2/dialog/Popup.js (L40-96)

  css: `
    << {
      display: flex;
      flex-direction: column;
      align-items: center;
      bottom: 0;
      height: 100%;
      justify-content: space-around;
      left: 0;
      position: fixed;
      right: 0;
      top: 0;
      width: 100%;
      z-index: $z-modal;
    }
    <<X {
      position: absolute;
      top: min(10%, 16px);
      right: min(10%, 16px);
      z-index: $z-10;
      cursor: pointer;
      transition: all ease-in 0.1s;
      padding: 0;
    }
    <<X:hover{
      transform: scale(1.1)
    }
    <<background {
      background-color: $backgroundInverse;
      bottom: 0;
      left: 0;
      opacity: 0.4;
      position: absolute;
      right: 0;
      top: 0;
    }
    <<inner {
      height: auto;
      width: auto;
      display: flex;
      justify-content: center;
      z-index: 3;
      position: relative;
      border-radius: 3px;
      box-shadow: 0 24px 24px 0 rgba(0, 0, 0, 0.12), 0 0 24px 0 rgba(0, 0, 0, 0.15);
      overflow: auto;
      background: $backgroundDefault;
      /* The following line fixes a stacking problem in certain browsers. */
      will-change: opacity;
    }

    <<fullscreen <<inner {
      height: 100%;
      width: 100%;
      border-radius: 0;
    }
 `,

File: src/foam/u2/dialog/Popup.js (L170-184)

  actions: [
    {
      name: 'closeModal',
      icon: 'images/ic-cancelblack.svg',
      label: '',
      keyboardShortcuts: [ 27 /* Escape */ ],
      code: function() {
        if ( this.onClose ) this.onClose();
        this.closedLatch?.resolve(this.data);
        // Delay removal by 32ms (two animation frames) so the action.closeModal
        // topic has a chance to be published
        this.hide();
        this.setTimeout(() => {this.remove()}, 32);
      }
    }

File: src/foam/u2/dialog/StyledModal.js (L7-25)

foam.ENUM({
  package: 'foam.u2.dialog',
  name: 'ModalStyles',

  values: [
    {
      name: 'DEFAULT',
      color: '$backgroundDefault'
    },
    {
      name: 'DESTRUCTIVE',
      color: '$destructive400'
    },
    {
      name: 'WARN',
      color: '$warn400'
    }
  ]
});

File: src/foam/u2/dialog/StyledModal.js (L27-33)

foam.CLASS({
  package: 'foam.u2.dialog',
  name: 'StyledModal',
  extends: 'foam.u2.dialog.Popup',
  documentation: `
    This view is a simple styled modal with a title and ability to add content/strings and actions
  `,

File: src/foam/u2/dialog/StyledModal.js (L73-78)

    <<actionBar {
      display: flex;
      justify-content: flex-end;
      padding: 16px 0px;
      gap: 8px;
    }

File: src/foam/u2/dialog/StyledModal.js (L108-118)

  properties: [
    {
      class: 'String',
      name: 'maxHeight',
      value: '65vh'
    },
    {
      class: 'String',
      name: 'maxWidth',
      value: 'min(90vw, 400px)'
    },

File: src/foam/u2/dialog/StyledModal.js (L119-125)

    {
      class: 'Enum',
      of: 'foam.u2.dialog.ModalStyles',
      name: 'modalStyle',
      value: 'DEFAULT',
      documentation: 'Setting modal styles adds a coloured bar at the top of the modal'
    },

File: src/foam/u2/dialog/StyledModal.js (L126-129)

    {
      name: 'title',
      class: 'String'
    },

File: src/foam/u2/dialog/StyledModal.js (L142-145)

    {
      class: 'String',
      name: 'description'
    },

File: src/foam/u2/dialog/ConfirmationModal.js (L7-14)

foam.CLASS({
  package: 'foam.u2.dialog',
  name: 'ConfirmationModal',
  extends: 'foam.u2.dialog.StyledModal',
  documentation: `
    Extension of styled modal with a primary and secondary action, mainly to be used for conifrmations and yes/no modals.
    Clicking on any action closes performs the action and closes the dialog
  `,

File: src/foam/u2/dialog/ConfirmationModal.js (L29-45)

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.lang.Action',
      name: 'primaryAction',
      documentation: 'The primary action for this modal dialog (Save/Submit/Continue)',
    },
    {
      class: 'FObjectProperty',
      of: 'foam.lang.Action',
      name: 'secondaryAction',
      documentation: `The secondary action for this modal dialog (Close/Cancel)
      can be turned off using the 'showCancel' property`,
    },
    ['showCancel', true],
    'data'
  ],

File: src/foam/u2/dialog/ConfirmationModal.js (L58-90)

  actions: [
    {
      name: 'confirm',
      buttonStyle: 'PRIMARY',
      code: async function(X) {
        if ( ! this.primaryAction ) {
          X.closeDialog();
          return;
        }

        var self = this;
        return await this.primaryAction.maybeCall(X, this.data).then((result) => {
          self.data = result;
          X.closeDialog();
        });
      }
    },
    {
      name: 'cancel',
      buttonStyle: 'TERTIARY',
      code: async function(X) {
        if ( ! this.secondaryAction ) {
          X.closeDialog();
          return;
        }

        var self = this;
        return await this.secondaryAction?.maybeCall(X, this.data).then((result) => {
          self.data = result;
          X.closeDialog();
        })
      }
    }

File: src/foam/u2/dialog/ApplicationPopup.js (L7-13)

foam.CLASS({
  package: 'foam.u2.dialog',
  name: 'ApplicationPopup',
  extends: 'foam.u2.dialog.Popup',
  documentation: `
    A full-featured popup with the application's branding on it.
  `,

File: src/foam/u2/dialog/ApplicationPopup.js (L251-258)

    {
      class: 'Array',
      name: 'leadingActions'
    },
    {
      class: 'Array',
      name: 'primaryActions'
    },

File: src/foam/u2/dialog/ApplicationPopup.js (L259-263)

    {
      class: 'foam.u2.ViewSpec',
      name: 'progressView',
      value: { class: 'foam.u2.ProgressView' }
    },

File: src/foam/u2/dialog/ApplicationPopup.js (L264-273)

    {
      class: 'foam.u2.ViewSpec',
      name: 'dynamicFooter',
      documentation: 'Content rendered below the body content. Still a part of the body and can change based on the content of the body'
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'dynamicHeader',
      documentation: 'Content rendered above the body content. Still a part of the body and can change based on the content of the body'
    },

File: src/foam/u2/dialog/ApplicationPopup.js (L287-295)

      const updateWidth = () => {
        if ( this.displayWidth?.ordinal <= foam.u2.layout.DisplayWidth.MD.ordinal ) {
          this.forceFullscreen = true;
        } else {
          this.forceFullscreen = false;
        }
      }
      updateWidth();
      this.onDetach(this.displayWidth$.sub(updateWidth))

File: src/foam/u2/dialog/ApplicationPopup.js (L343-348)

            .start()
              .addClass(this.myClass('header-center'))
              .start({ class: 'foam.core.u2.navigation.ApplicationLogoView' })
                .addClass(this.myClass('logo'))
              .end()
            .end()

File: src/foam/u2/pom.js (L289-294)

    { name: "dialog/Popup",                                       flags: "web" },
    { name: "dialog/ApplicationPopup",                            flags: "web" },
    { name: "dialog/DialogActionsView",                           flags: "web" },
    { name: "dialog/StyledModal",                                 flags: "web" },
    { name: "dialog/ConfirmationModal",                           flags: "web" },
    { name: "Dialog",                                             flags: "web" },

File: src/foam/u2/PopupView.js (L18-21)

foam.CLASS({
  package: 'foam.u2',
  name: 'PopupView',
  extends: 'foam.u2.Element',

File: src/foam/u2/PropertyModal.js (L7-10)

foam.CLASS({
  package: 'foam.u2',
  name: 'PropertyModal',
  extends: 'foam.u2.dialog.ConfirmationModal',