@royallondon/design-system-component-modal 0.0.0-stage → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,54 @@
1
+ Authorised Use Licence v1.0
2
+
3
+ Copyright © 2026 Royal London.
4
+ All rights reserved.
5
+
6
+ 1. Grant of Licence
7
+
8
+ The Licensor grants a non-exclusive, non-transferable,
9
+ revocable, royalty-free licence to use the Software only where:
10
+
11
+ (a) the individual, organisation, or business has been
12
+ expressly authorised in writing by the Licensor; and
13
+
14
+ (b) such authorisation remains in force.
15
+
16
+ 2. Restrictions
17
+
18
+ The Software may not be:
19
+
20
+ (a) used by any unauthorised person or organisation;
21
+
22
+ (b) redistributed, sublicensed, sold, rented or made
23
+ available to third parties;
24
+
25
+ (c) incorporated into another product or service without
26
+ written approval;
27
+
28
+ (d) reverse engineered except where prohibited by law.
29
+
30
+ 3. Ownership
31
+
32
+ The Software remains the exclusive property of the Licensor.
33
+ No ownership rights are transferred under this Licence.
34
+
35
+ 4. Termination
36
+
37
+ The Licensor may revoke authorisation at any time by
38
+ written notice.
39
+
40
+ Upon termination the Licensee must immediately cease use
41
+ of the Software and destroy all copies.
42
+
43
+ 5. No Warranty
44
+
45
+ The Software is provided "as is" without warranty of any kind.
46
+
47
+ 6. Limitation of Liability
48
+
49
+ The Licensor shall not be liable for any damages arising
50
+ from use of the Software.
51
+
52
+ 7. Governing Law
53
+
54
+ This Licence shall be governed by the laws of England and Wales.
package/README.md CHANGED
@@ -1,3 +1,69 @@
1
- # Temporary Holding Version
1
+ ### Installation
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Install the Modal package from npm
4
+
5
+ ```bash
6
+ npm install @royallondon/design-system-component-modal
7
+ ```
8
+
9
+ Import the required CSS file
10
+
11
+ ```css
12
+ @import '@royallondon/design-system-component-modal/dist/modal';
13
+ ```
14
+
15
+ Import the required JS file, add the html and modal trigger element then initalise the modal function
16
+
17
+ ```js
18
+ import { createModal } from '@royallondon/design-system-component-modal/dist/modal';
19
+
20
+ const innerHTML = `<div class="rl-c-modal-content" id="modal-content" >
21
+ <div id="modal-heading" class="rl-c-modal-heading">
22
+ <h2 class="rl-t-title-large">Are you sure?</h2>
23
+ </div>
24
+ <button class="rl-c-modal-close rl-c-modal-close--primary" aria-label="Close">
25
+ <svg class="rl-f-icon rl-f-icon--xs" aria-hidden="true">
26
+ <use href="/assets/icons.svg#semantic-danger"></use>
27
+ </svg>
28
+ </button>
29
+ <div class="rl-u__margin--b-6">
30
+ <p>The pension plan won't be saved and any information you have provided for this request will be lost.</p>
31
+ </div>
32
+ <div class="rl-form__row rl-form__row--action">
33
+ <button class="rl-c-button--primary">Yes, remove</button>
34
+ <button class="rl-c-button--ghost">No, don't remove</button>
35
+ </div>
36
+ </div>`;
37
+ createModal({
38
+ innerHTML,
39
+ modalTriggerElement: element_id | element_classname,
40
+ modalSize: xsmall | small | medium | large,
41
+ });
42
+ ```
43
+
44
+ ### Support
45
+
46
+ Issues and questions are handled through the [Royal London Design System support page](https://design.royallondon.com/support/).
47
+
48
+ ### Licence
49
+
50
+ #### @royallondon/design-system-component-modal
51
+
52
+ This package is published primarily for use in Royal London products. It is made available publicly so that partners and suppliers building Royal London experiences can consume it directly.
53
+
54
+ Use of this package is subject to the Royal London Authorised Use Licence. See the `LICENSE.md` file included in this package.
55
+
56
+ The Modal component for the [Royal London Design System](https://design.royallondon.com). For design guidance, examples and accessibility notes, see the [Modal page](https://design.royallondon.com/components/modal/).
57
+
58
+ ### Release log
59
+
60
+ | Version | Release notes |
61
+ | ------- | ---------------------------------------------------------------------------------------------------- |
62
+ | 2.3.0 | Prepare for public npm release: add licence, publish `dist` only (`src` removed), remove source maps |
63
+ | 2.2.5 | Move to new feed |
64
+ | 2.2.0 | Apply new icons and latest tokens added to foundations |
65
+ | 2.1.1 | Fix spacing between elements and improve responsive experience |
66
+ | 2.1.0 | Update foundations dependency version to ensure new tokens |
67
+ | 2.0.1 | Fix issue with media query processing without postcss |
68
+ | 2.0.0 | Upgrade for compatibility with foundations v2 |
69
+ | 1.0.0 | Initial release of modal component |
package/dist/modal.css ADDED
@@ -0,0 +1 @@
1
+ @custom-media --mq-xsmall (max-width: 31.9375rem);@custom-media --mq-small (min-width: 32rem);@custom-media --mq-medium (min-width: 48rem);@custom-media --mq-large (min-width: 64rem);@custom-media --mq-medium-max (max-width: 47.9375rem);@custom-media --mq-large-max (max-width: 63.9375rem);@custom-media --mq-xs-max (max-width: 31.9375rem);@custom-media --mq-sm (min-width: 32rem);@custom-media --mq-sm-max (max-width: 47.9375rem);@custom-media --mq-md (min-width: 48rem);@custom-media --mq-md-max (max-width: 63.9375rem);@custom-media --mq-lg (min-width: 64rem);@custom-media --mq-lg-max (max-width: 79.9375rem);@custom-media --mq-xl (min-width: 80rem);@custom-media --mq-xl-max (max-width: 95.9375rem);@custom-media --mq-xxl (min-width: 96rem);:root{--xsmall-modal-maxwidth:calc(100% - var(--spacing-05)*2);--xsmall-modal-width:288px;--small-modal-width:432px;--medium-modal-width:576px;--large-modal-width:816px}[class*=rl-c-modal--]{background-color:var(--modal-fill-box);display:none;left:50%;padding:var(--spacing-04);position:fixed;top:var(--spacing-08);transform:translate(-50%,-100%);width:576px;width:var(--medium-modal-width);z-index:1000}@media (min-width:32rem){[class*=rl-c-modal--]{width:432px;width:var(--small-modal-width)}}@media (--mq-small ){[class*=rl-c-modal--]{width:432px;width:var(--small-modal-width)}}@media (min-width:48rem){[class*=rl-c-modal--]{width:576px;width:var(--medium-modal-width)}}@media (--mq-medium ){[class*=rl-c-modal--]{width:576px;width:var(--medium-modal-width)}}@media (min-width:64rem){[class*=rl-c-modal--]{width:816px;width:var(--large-modal-width)}}@media (--mq-large ){[class*=rl-c-modal--]{width:816px;width:var(--large-modal-width)}}@media (min-width:32rem){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large,.rl-c-modal--medium,.rl-c-modal--small{width:432px;width:var(--small-modal-width)}}@media (--mq-small ){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large,.rl-c-modal--medium,.rl-c-modal--small{width:432px;width:var(--small-modal-width)}}@media (min-width:48rem){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large,.rl-c-modal--medium{width:576px;width:var(--medium-modal-width)}}@media (--mq-medium ){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large,.rl-c-modal--medium{width:576px;width:var(--medium-modal-width)}}@media (min-width:64rem){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large{width:816px;width:var(--large-modal-width)}}@media (--mq-large ){.rl-c-modal--xsmall{width:288px;width:var(--xsmall-modal-width)}.rl-c-modal--large{width:816px;width:var(--large-modal-width)}}@media (max-width:31.9375rem){.rl-c-modal--large,.rl-c-modal--medium,.rl-c-modal--small,.rl-c-modal--xsmall,[class*=rl-c-modal--]{width:var(--xsmall-modal-maxwidth)}}@media (--mq-xsmall ){.rl-c-modal--large,.rl-c-modal--medium,.rl-c-modal--small,.rl-c-modal--xsmall,[class*=rl-c-modal--]{width:var(--xsmall-modal-maxwidth)}}[class*=rl-c-modal--].open{animation:slideDown .3s ease forwards;display:block}@keyframes slideDown{0%{transform:translate(-50%,-100%)}to{transform:translate(-50%)}}.rl-c-modal-heading{color:var(--modal-text-title);margin-bottom:var(--spacing-03)}.rl-c-modal-heading>:last-child{margin-bottom:0}.rl-c-modal-content{color:var(--modal-text-body);margin:calc(var(--spacing-01)*-1);max-height:80vh;overflow-y:auto;padding:var(--spacing-01);position:relative}.rl-c-modal-close--primary{align-items:center;display:flex;height:1.5rem;justify-content:center;padding:0;position:absolute;right:var(--spacing-01);top:var(--spacing-02);width:1.5rem}.rl-c-modal-backdrop{background:color-mix(in srgb,var(--modal-fill-overlay),transparent 80%);height:100%;left:0;position:fixed;top:0;width:100%;z-index:999}
@@ -0,0 +1,12 @@
1
+ interface ModalOptions {
2
+ innerHTML?: string;
3
+ modalTriggerElement: HTMLElement;
4
+ modalSize?: string;
5
+ customButtons?: {
6
+ selector: string;
7
+ closeAfter?: boolean;
8
+ callback: (event: MouseEvent) => void;
9
+ }[];
10
+ }
11
+ export declare function createModal(options?: ModalOptions): void;
12
+ export {};
package/dist/modal.js ADDED
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createModal = createModal;
7
+ function createModal(options = {}) {
8
+ const {
9
+ innerHTML,
10
+ modalTriggerElement,
11
+ modalSize = '',
12
+ customButtons
13
+ } = options;
14
+ const modalBackdrop = document.createElement('div');
15
+ modalBackdrop.classList.add('rl-c-modal-backdrop');
16
+ let modalElement;
17
+ let triggeringElement;
18
+ let focusableElements = [];
19
+ let nextFocusElement;
20
+ modalTriggerElement.addEventListener('click', event => {
21
+ event.preventDefault();
22
+ if (!modalElement) {
23
+ create(innerHTML);
24
+ }
25
+ open();
26
+ const focusableElements2 = document.body.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
27
+ let matchedIndex = -1;
28
+ for (let i = 0; i < focusableElements2.length; i++) {
29
+ const element = focusableElements2[i];
30
+ const match = modalTriggerElement;
31
+ if (element.outerHTML === match.outerHTML) {
32
+ matchedIndex = i;
33
+ break;
34
+ }
35
+ }
36
+ nextFocusElement = focusableElements2[matchedIndex + 1];
37
+ triggeringElement = modalTriggerElement;
38
+ });
39
+ function create(customInnerHTML) {
40
+ modalElement = document.createElement('div');
41
+ modalElement.classList.add(`rl-c-modal--${modalSize}`);
42
+ modalElement.setAttribute('role', 'dialog');
43
+ modalElement.setAttribute('aria-labelledby', 'modal-heading');
44
+ modalElement.innerHTML = customInnerHTML;
45
+ document.body.appendChild(modalElement);
46
+ const closeButton = modalElement.querySelectorAll('.rl-c-modal-close');
47
+ closeButton.forEach(closeBtn => {
48
+ closeBtn.addEventListener('click', close);
49
+ });
50
+ if (customButtons) {
51
+ customButtons.forEach(({
52
+ selector,
53
+ closeAfter = true,
54
+ callback
55
+ }) => {
56
+ const button = modalElement.querySelector(selector);
57
+ button.addEventListener('click', event => {
58
+ callback(event);
59
+ if (closeAfter) {
60
+ close();
61
+ }
62
+ });
63
+ });
64
+ }
65
+ focusableElements = modalElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
66
+ }
67
+ function open() {
68
+ document.body.appendChild(modalBackdrop);
69
+ modalElement.classList.add('open');
70
+ const closeButton = modalElement.querySelector('.rl-c-modal-close');
71
+ closeButton.focus();
72
+ const backgroundContent = document.querySelectorAll('body > *:not([class*="rl-c-modal--"])');
73
+ backgroundContent.forEach(element => {
74
+ element.setAttribute('aria-hidden', 'true');
75
+ if (!element.classList.contains(`rl-c-modal--${modalSize}`)) {
76
+ element.setAttribute('tabindex', '-1');
77
+ }
78
+ });
79
+ document.addEventListener('keydown', handleKeyDown);
80
+ modalElement.addEventListener('keydown', handleModalTab);
81
+ }
82
+ function close() {
83
+ document.body.removeChild(modalBackdrop);
84
+ modalElement.classList.remove('open');
85
+ if (triggeringElement.offsetParent) {
86
+ triggeringElement.focus();
87
+ } else {
88
+ nextFocusElement.focus();
89
+ }
90
+ const backgroundContent = document.querySelectorAll('[aria-hidden="true"]');
91
+ backgroundContent.forEach(element => {
92
+ element.removeAttribute('aria-hidden');
93
+ element.removeAttribute('tabindex');
94
+ });
95
+ }
96
+ function handleKeyDown(event) {
97
+ if (event.key === 'Escape' && modalElement.classList.contains('open')) {
98
+ close();
99
+ }
100
+ }
101
+ function handleModalTab(event) {
102
+ const firstFocusableElement = focusableElements[0];
103
+ const lastFocusableElement = focusableElements[focusableElements.length - 1];
104
+ if (event.key === 'Tab') {
105
+ if (event.shiftKey && document.activeElement === firstFocusableElement) {
106
+ event.preventDefault();
107
+ lastFocusableElement.focus();
108
+ } else if (!event.shiftKey && document.activeElement === lastFocusableElement) {
109
+ event.preventDefault();
110
+ firstFocusableElement.focus();
111
+ }
112
+ }
113
+ }
114
+ }
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@royallondon/design-system-component-modal",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "2.3.0",
4
+ "description": "Royal London Design System - Modal package",
5
+ "homepage": "https://design.royallondon.com/components/modal/",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://dev.azure.com/RLG-UK/GTC/_git/design-system",
9
+ "directory": "ds-packages/packages/design-system-component-modal"
10
+ },
11
+ "bugs": {
12
+ "url": "https://design.royallondon.com/support/"
13
+ },
14
+ "keywords": [
15
+ "royal-london-design-system-component-modal",
16
+ "rlds-modal",
17
+ "royal-london"
18
+ ],
19
+ "author": "Design System Squad <DesignSystem@royallondon.com>",
20
+ "license": "SEE LICENSE IN LICENSE.md",
21
+ "typings": "dist/modal.d.ts",
22
+ "main": "dist/modal.css",
23
+ "style": "dist/modal.css",
24
+ "scripts": {
25
+ "build:clean": "npx rimraf dist",
26
+ "build:lint": "stylelint src/**/*.{css,pcss} --fix",
27
+ "build:css": "postcss src/**/modal.pcss --base src --dir dist/ --ext css",
28
+ "build:js": "npm-run-all --serial build:babel build:types",
29
+ "build:babel": "babel src/ --out-dir dist/ --config-file ../../babel.config",
30
+ "build:types": "npx copyfiles -u 1 \"src/**/*.d.ts\" dist",
31
+ "build": "npm-run-all --serial build:clean build:css build:js"
32
+ },
33
+ "devDependencies": {
34
+ "@royallondon/design-system-foundations": "^2.23.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@royallondon/design-system-foundations": ">= 2.15.0 < 3"
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
6
45
  }