# Modal - Code

> Code examples and API documentation for the ix-modal-header

import ModalConfig from '@site/docs/autogenerated/utils/modal-config.mdx';
import ModalInstance from '@site/docs/autogenerated/utils/modal-instance.mdx';

import ModalService from '@site/docs/autogenerated/utils/modal.service.mdx';

# Modal - Code

Our modals support the following sizes:

- `360`
- `480`
- `600`
- `720`
- `840`
- `full-width` - Modal extends to fill entire screen width (modal will still have some horizontal margin)
- `full-screen` - Modal extends to fill entire screen

The `size` can be configured over the configuration object of the `showModal` function.

### React Examples

#### modal-sizes.tsx
```tsx
import './modal-sizes.scoped.css';

import { IxModalSize } from '@siemens/ix';
import { IxButton, Modal, ModalRef, showModal } from '@siemens/ix-react';
import { useRef } from 'react';

export default () => {
  const modalRef = useRef<ModalRef>(null);

  const open = (size: IxModalSize) => {
    showModal({
      size: size,
      content: (
        <Modal ref={modalRef}>
          <IxButton onClick={() => modalRef.current?.close(null)}>
            Modal with size {size}
          </IxButton>
        </Modal>
      ),
    });
  };

  return (
    <div className="modal-sizes">
      <IxButton onClick={() => open('360')}>Show modal size 360</IxButton>
      <IxButton onClick={() => open('480')}>Show modal size 480</IxButton>
      <IxButton onClick={() => open('600')}>Show modal size 600</IxButton>
      <IxButton onClick={() => open('720')}>Show modal size 720</IxButton>
      <IxButton onClick={() => open('840')}>Show modal size 840</IxButton>
      <IxButton onClick={() => open('full-width')}>
        Show modal size full-width
      </IxButton>
      <IxButton onClick={() => open('full-screen')}>
        Show modal size full-screen
      </IxButton>
    </div>
  );
};
```

#### modal-sizes.scoped.css
```css
.modal-sizes {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.modal-sizes > ix-button {
  width: auto;
  margin: 0.25rem;
}
```

### Angular Examples

#### modal-sizes.ts
```ts
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { IxModalSize, ModalService } from '@siemens/ix-angular';

@Component({
  standalone: false,
  selector: 'app-example',
  styleUrls: ['./modal-sizes.css'],
  templateUrl: './modal-sizes.html',
})
export default class ModalSizes {
  @ViewChild('customModal', { read: TemplateRef })
  customModalRef!: TemplateRef<any>;

  constructor(private readonly modalService: ModalService) {}

  async open(size: IxModalSize) {
    await this.modalService.open({
      content: this.customModalRef,
      data: size,
      size: size,
    });
  }
}
```

#### modal-sizes.html
```html
<div class="modal-sizes">
  <ix-button (click)="open('360')">Show modal size 360</ix-button>
  <ix-button (click)="open('480')">Show modal size 480</ix-button>
  <ix-button (click)="open('600')">Show modal size 600</ix-button>
  <ix-button (click)="open('720')">Show modal size 720</ix-button>
  <ix-button (click)="open('840')">Show modal size 840</ix-button>
  <ix-button (click)="open('full-width')">Show modal size full-width</ix-button>
  <ix-button (click)="open('full-screen')">
    Show modal size full-screen
  </ix-button>
</div>

<ng-template #customModal let-modal>
  <ix-button (click)="modal.dismiss('dismiss')">Modal with size {{ modal.data }}</ix-button>
</ng-template>
```

#### modal-sizes.css
```css
.modal-sizes {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.modal-sizes > ix-button {
  width: auto;
  margin: 0.25rem;
}
```

### Angular Standalone Examples

#### modal-sizes.ts
```ts
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { IxButton, ModalService } from '@siemens/ix-angular/standalone';

import { IxModalSize } from '@siemens/ix-angular';

@Component({
  selector: 'app-example',
  imports: [IxButton],
  styleUrls: ['./modal-sizes.css'],
  templateUrl: './modal-sizes.html',
})
export default class ModalSizes {
  @ViewChild('customModal', { read: TemplateRef })
  customModalRef!: TemplateRef<any>;

  constructor(private readonly modalService: ModalService) {}

  async open(size: IxModalSize) {
    await this.modalService.open({
      content: this.customModalRef,
      data: size,
      size: size,
    });
  }
}
```

#### modal-sizes.html
```html
<div class="modal-sizes">
  <ix-button (click)="open('360')">Show modal size 360</ix-button>
  <ix-button (click)="open('480')">Show modal size 480</ix-button>
  <ix-button (click)="open('600')">Show modal size 600</ix-button>
  <ix-button (click)="open('720')">Show modal size 720</ix-button>
  <ix-button (click)="open('840')">Show modal size 840</ix-button>
  <ix-button (click)="open('full-width')">Show modal size full-width</ix-button>
  <ix-button (click)="open('full-screen')">
    Show modal size full-screen
  </ix-button>
</div>

<ng-template #customModal let-modal>
  <ix-button (click)="modal.dismiss('dismiss')"
    >Modal with size {{ modal.data }}</ix-button
  >
</ng-template>
```

#### modal-sizes.css
```css
.modal-sizes {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.modal-sizes > ix-button {
  width: auto;
  margin: 0.25rem;
}
```

### Vue Examples

#### modal-sizes.vue
```vue
<script setup lang="tsx">
import type { IxModalSize } from '@siemens/ix';
import {
  IxButton,
  Modal,
  type ModalSlotProps,
  showModal,
} from '@siemens/ix-vue';

const sizes: IxModalSize[] = [
  '360',
  '480',
  '600',
  '720',
  '840',
  'full-width',
  'full-screen',
];

const open = (size: IxModalSize) => {
  showModal({
    size,
    content: (
      <Modal>
        {({ closeModal }: ModalSlotProps) => (
          <IxButton onClick={closeModal}> Modal with size {size} </IxButton>
        )}
      </Modal>
    ),
  });
};
</script>

<style scoped src="./modal-sizes.css"></style>

<template>
  <div class="modal-sizes">
    <IxButton v-for="size in sizes" :key="size" @click="open(size)">
      Show modal size {{ size }}
    </IxButton>
  </div>
</template>
```

#### modal-sizes.css
```css
.modal-sizes {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.modal-sizes > ix-button {
  width: auto;
  margin: 0.25rem;
}
```

## Custom

How to open a modal depends on the framework in use. Note that you will not instantiate `ix-modal` on your own.
Select the appropriate section below for the respective usage information.

### Angular

#### By template

#### modal-by-template.ts
```ts
import { Component, TemplateRef, ViewChild } from '@angular/core';
import { ModalService } from '@siemens/ix-angular';

@Component({
  standalone: false,
  selector: 'app-example',
  template: `
    <ix-button (click)="openModal()">Show modal</ix-button>

    <ng-template #customModal let-modal>
      <ix-modal>
        <ix-modal-header> Message headline </ix-modal-header>
        <ix-modal-content
          >Message text lorem ipsum: {{ modal.data }}</ix-modal-content
        >
        <ix-modal-footer>
          <ix-button variant="subtle-primary" class="dismiss-modal" (click)="modal.dismiss('dismiss')">
            Cancel
          </ix-button>
          <ix-button class="close-modal" (click)="modal.close('okay')">
            OK
          </ix-button>
        </ix-modal-footer>
      </ix-modal>
    </ng-template>
  `,
})
export default class Modal {
  @ViewChild('customModal', { read: TemplateRef })
  customModalRef!: TemplateRef<any>;

  constructor(private readonly modalService: ModalService) {}

  async openModal() {
    const instance = await this.modalService.open({
      content: this.customModalRef,
      data: 'Some data',
    });

    instance.onClose.on((a) => {
      console.log(a);
    });

    instance.htmlElement.addEventListener(
      'keydown',
      (keyboardEvent: KeyboardEvent) => {
        console.log(keyboardEvent.key);
      }
    );
  }
}
```

#### By instance

#### modal-by-instance.ts
```ts
import { Component } from '@angular/core';
import { ModalService } from '@siemens/ix-angular';
import ModalByInstanceExample from './modal-by-instance-content';

@Component({
  standalone: false,
  selector: 'app-example',
  template: '<ix-button (click)="openModal()">Show modal</ix-button>',
})
export default class ModalByInstance {
  constructor(private readonly modalService: ModalService) {}

  async openModal() {
    await this.modalService.open({
      content: ModalByInstanceExample,
      data: 'Some data',
    });
  }
}
```

#### modal-by-instance-content.ts
```ts
import { Component } from '@angular/core';
import { IxActiveModal } from '@siemens/ix-angular';

@Component({
  standalone: false,
  selector: 'app-example-content',
  template: `
    <ix-modal-header> Message headline </ix-modal-header>
    <ix-modal-content>
      Message text lorem ipsum: {{ activeModal.data }}
    </ix-modal-content>
    <ix-modal-footer>
      <ix-button variant="subtle-primary" class="dismiss-modal" (click)="activeModal.dismiss('dismiss')">
        Cancel
      </ix-button>
      <ix-button
        autofocus
        class="close-modal"
        (click)="activeModal.close('okay')"
      >
        OK
      </ix-button>
    </ix-modal-footer>
  `,
})
export default class ModalByInstanceContent {
  constructor(readonly activeModal: IxActiveModal) {}

  close() {
    this.activeModal.close('My close response');
  }
}
```

`@siemens/ix-angular` provides an injectable service that allows to open modal dialogs based on a `ng-template` reference or by component type.
If you want to pass arbitrary data to the modal use the `data`-property. In order to access that data inside the modal template, use `let-modal` as seen in the angular example above.

**ModalService**

```ts
open(config: ModalConfig<TData: any, TReason: any>): Promise<ModalInstance<TData>>
```

### React

`@siemens/ix-react` provides an function that allows to open modal dialogs based on a `JSXElement`.

#### modal.tsx
```tsx
import {
  IxButton,
  IxModalContent,
  IxModalFooter,
  IxModalHeader,
  Modal,
  ModalRef,
  showModal,
} from '@siemens/ix-react';
import { useRef } from 'react';

function CustomModal() {
  const modalRef = useRef<ModalRef>(null);

  const close = () => {
    modalRef.current?.close('close payload!');
  };
  const dismiss = () => {
    modalRef.current?.dismiss('dismiss payload');
  };

  return (
    <Modal ref={modalRef}>
      <IxModalHeader onCloseClick={() => dismiss()}>
        Message headline
      </IxModalHeader>
      <IxModalContent>Message text lorem ipsum</IxModalContent>
      <IxModalFooter>
        <IxButton variant="subtle-primary" onClick={() => dismiss()}>
          Cancel
        </IxButton>
        <IxButton autoFocus onClick={() => close()}>
          OK
        </IxButton>
      </IxModalFooter>
    </Modal>
  );
}

export default () => {
  async function show() {
    await showModal({
      content: <CustomModal />,
    });
  }

  return (
    <>
      <IxButton onClick={show}>Show modal</IxButton>
    </>
  );
};
```

:::info Use context

It is highly recommended to provide the `IxApplicationContext` as part of your application.

:::

```tsx
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <IxApplicationContext>
    {/*
      <BrowserRouter>
        <App />
      </BrowserRouter>
    */}
  </IxApplicationContext>
);
```

### Vue

`@siemens/ix-vue` provides a function that allows to open modal dialogs based on a `VNode`.

#### modal.vue
```vue
<script setup lang="tsx">
import {
  IxButton,
  IxModalHeader,
  IxModalContent,
  IxModalFooter,
  Modal,
  ModalSlotProps,
  showModal
} from '@siemens/ix-vue';

function show() {
  showModal({
    content: <Modal>{
      ({ closeModal, dismissModal }: ModalSlotProps) => [
        <IxModalHeader>Message headline</IxModalHeader>,
        <IxModalContent>Message text lorem ipsum</IxModalContent>,
        <IxModalFooter>
          <IxButton variant="subtle-primary" onClick={() => dismissModal()}>Cancel</IxButton>
          <IxButton onClick={() => closeModal()}>OK</IxButton>
        </IxModalFooter>
      ]
    }
    </Modal >
  })
}

</script>

<template>
  <IxButton @click="show()">Show modal</IxButton>
</template>
```

:::info JSX/TSX support

The example above is using TSX. To add JSX/TSX support in your Vue project,
please refer to the [Vue documentation](https://vuejs.org/guide/extras/render-function.html#jsx-tsx).
It is not required to use JSX and as an alternative you can use
the [`h()`](https://vuejs.org/guide/extras/render-function.html#creating-vnodes) function to create the modal's VNode.

:::

:::warning Use context

It is required to provide the `IxApplicationContext` as part of your application in order to use the `showModal` function.

:::

```vue
<script setup lang="ts">
import { IxApplicationContext } from '@siemens/ix-vue';
</script>

<template>
  <IxApplicationContext>
    <!-- <App /> -->
  </IxApplicationContext>
</template>
```

### Javascript

#### modal.html
```html
<!DOCTYPE html>
<html lang="en" data-ix-theme="classic" data-ix-color-schema="system">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Modal example</title>
  </head>
  <body>
    <ix-button>Show modal</ix-button>
    <template id="modal-example-template">
      <ix-modal-header>Message headline</ix-modal-header>
      <ix-modal-content>Message text lorem ipsum</ix-modal-content>
      <ix-modal-footer>
        <ix-button variant="subtle-primary" data-cancel>Cancel</ix-button>
        <ix-button data-okay>OK</ix-button>
      </ix-modal-footer>
    </template>

    <script type="module">
      import { showModal, closeModal, dismissModal } from '@siemens/ix';

      function createExampleModal() {
        const name = 'modal-example';
        window.customElements.define(
          name,
          class extends HTMLElement {
            isInitalRender = false;

            constructor() {
              super();
            }

            connectedCallback() {
              if (this.isInitalRender) {
                return;
              }

              this.isInitalRender = true;
              this.firstRender();
            }

            firstRender() {
              const modalTemplate = document.getElementById(
                'modal-example-template'
              );
              const template = modalTemplate.content.cloneNode(true);

              const cancelButton = template.querySelector('[data-cancel]');
              const okayButton = template.querySelector('[data-okay]');

              cancelButton.addEventListener('click', () => {
                dismissModal(this);
              });
              okayButton.addEventListener('click', () => {
                closeModal(this);
              });

              this.append(template);
            }
          }
        );

        return name;
      }

      (async function () {
        const exampleModalName = createExampleModal();

        await window.customElements.whenDefined('ix-button');
        const button = document.querySelector('ix-button');

        button.addEventListener('click', async () => {
          const customModal = document.createElement(exampleModalName);

          const modal = await showModal({
            content: customModal,
          });
        });
      })();
    </script>
    <script type="module" src="./init.js"></script>
  </body>
</html>
```

## API for ix-modal-header

### Properties

| Name | Description | Attribute | Type | Default |
| --- | --- | --- | --- | --- |
| ariaLabelCloseIconButton | { "ARIA label for the close icon button\n\nWill be set as aria-label on the nested HTML button element" } | aria-label-close-icon-button | string \| undefined | 'Close modal' |
| ariaLabelIcon | { "ARIA label for the icon" } | aria-label-icon | string \| undefined |  |
| hideClose | { "Hide the close button" } | hide-close | boolean | false |
| icon | { "Icon of the header" } | icon | string \| undefined |  |
| iconColor | { "Icon color" } | icon-color | string \| undefined |  |

### Events

| Name | Description | Event | Detail |
| --- | --- | --- | --- |
| closeClick | { "Emits when the close icon is clicked and closes the modal\n\nCan be prevented, in which case only the event is triggered, and the modal remains open" } | closeClick | MouseEvent |

### Slot

| Name | Description |
| --- | --- |
| default | { "Modal header content." } |

## API for ix-modal-config

### Properties

| Name | Description | Attribute | Type |
| --- | --- | --- | --- |
| animation | { "Enable modal animation" } | animation | boolean |
| ariaDescribedby | { "ID of element describing the modal" } | ariaDescribedby | string |
| ariaLabelledby | { "ID of element labeling the modal" } | ariaLabelledby | string |
| backdrop | { "Show backdrop behind modal" } | backdrop | boolean |
| beforeDismiss | { "Called before modal is dismissed" } | beforeDismiss | unknown |
| centered | { "Center modal vertically" } | centered | boolean |
| closeOnBackdropClick | { "Dismiss modal on backdrop click (ignored when **isNonBlocking** is ``true``)" } | closeOnBackdropClick | boolean |
| content | { "Modal content" } | content | string \| CONTENT |
| isNonBlocking | { "Non-modal dialog: page stays interactive, no lightbox or focus trap; ``aria-modal`` is ``false``.\n\nSet before calling ``showModal()``; changing while open is unsupported." } | isNonBlocking | boolean |
| size | { "Modal size" } | size | IxModalSize |

## API for ix-modal-instance

### Properties

| Name | Description | Attribute | Type |
| --- | --- | --- | --- |
| htmlElement | { "The Modal HTML Element" } | htmlElement | HTMLIxModalElement |
| onClose | { "Event that fires when closing the modal" } | onClose | TypedEvent |
| onDismiss | { "Event that fires when dismissing the modal" } | onDismiss | TypedEvent |

## API for modal utils (JavaScript, React, Vue)

### Functions

#### closeModal&lt;TClose &#x3D; any&gt;

```ts
closeModal&lt;TClose &#x3D; any&gt;(element: Element, closeResult: TClose): void;
```

{ "Close closest ix-modal relative to a provided element" }

#### dismissModal

```ts
dismissModal(element: Element, dismissResult: any): void;
```

{ "Dismiss closest ix-modal relative to a provided element" }

#### setA11yAttributes

```ts
setA11yAttributes(element: HTMLElement, config: ModalConfig): void;
```

{ "Set accessibility attributes on modal element" }

#### showModal&lt;T&gt;

```ts
showModal&lt;T&gt;(config: ModalConfig&lt;T&gt;): Promise&lt;ModalInstance&lt;T&gt;&gt;;
```

{ "Show modal with given configuration" }

## API for ModalService (Angular)

### Functions

#### close&lt;TReason &#x3D; any&gt;

```ts
close&lt;TReason &#x3D; any&gt;(instance: ModalInstance&lt;TReason&gt;, reason: TReason): void;
```

{ "Closes a modal based on a ModalInstance" }

#### open&lt;TData &#x3D; any, TReason &#x3D; any&gt;

```ts
open&lt;TData &#x3D; any, TReason &#x3D; any&gt;(config: ModalConfig&lt;TData&gt;): Promise&lt;ModalInstance&lt;TReason&gt;&gt;;
```

{ "Opens a modal based on ModalConfig" }
