| 1 | const Spacer = require('./Spacer.js');
|
|---|
| 2 | const theme = require('../theme.js');
|
|---|
| 3 |
|
|---|
| 4 | /**
|
|---|
| 5 | * @typedef {Object} RuntimeErrorFooterProps
|
|---|
| 6 | * @property {string} [initialFocus]
|
|---|
| 7 | * @property {boolean} multiple
|
|---|
| 8 | * @property {function(MouseEvent): void} onClickCloseButton
|
|---|
| 9 | * @property {function(MouseEvent): void} onClickNextButton
|
|---|
| 10 | * @property {function(MouseEvent): void} onClickPrevButton
|
|---|
| 11 | */
|
|---|
| 12 |
|
|---|
| 13 | /**
|
|---|
| 14 | * A fixed footer that handles pagination of runtime errors.
|
|---|
| 15 | * @param {Document} document
|
|---|
| 16 | * @param {HTMLElement} root
|
|---|
| 17 | * @param {RuntimeErrorFooterProps} props
|
|---|
| 18 | * @returns {void}
|
|---|
| 19 | */
|
|---|
| 20 | function RuntimeErrorFooter(document, root, props) {
|
|---|
| 21 | const footer = document.createElement('div');
|
|---|
| 22 | footer.style.backgroundColor = '#' + theme.dimgrey;
|
|---|
| 23 | footer.style.bottom = '0';
|
|---|
| 24 | footer.style.boxShadow = '0 -1px 4px rgba(0, 0, 0, 0.3)';
|
|---|
| 25 | footer.style.height = '2.5rem';
|
|---|
| 26 | footer.style.left = '0';
|
|---|
| 27 | footer.style.right = '0';
|
|---|
| 28 | footer.style.lineHeight = '2.5rem';
|
|---|
| 29 | footer.style.paddingBottom = '0';
|
|---|
| 30 | footer.style.paddingBottom = 'env(safe-area-inset-bottom)';
|
|---|
| 31 | footer.style.position = 'fixed';
|
|---|
| 32 | footer.style.textAlign = 'center';
|
|---|
| 33 | footer.style.zIndex = '2';
|
|---|
| 34 |
|
|---|
| 35 | const BUTTON_CONFIGS = {
|
|---|
| 36 | prev: {
|
|---|
| 37 | id: 'prev',
|
|---|
| 38 | label: '◀ Prev',
|
|---|
| 39 | onClick: props.onClickPrevButton,
|
|---|
| 40 | },
|
|---|
| 41 | close: {
|
|---|
| 42 | id: 'close',
|
|---|
| 43 | label: '× Close',
|
|---|
| 44 | onClick: props.onClickCloseButton,
|
|---|
| 45 | },
|
|---|
| 46 | next: {
|
|---|
| 47 | id: 'next',
|
|---|
| 48 | label: 'Next ▶',
|
|---|
| 49 | onClick: props.onClickNextButton,
|
|---|
| 50 | },
|
|---|
| 51 | };
|
|---|
| 52 |
|
|---|
| 53 | let buttons = [BUTTON_CONFIGS.close];
|
|---|
| 54 | if (props.multiple) {
|
|---|
| 55 | buttons = [BUTTON_CONFIGS.prev, BUTTON_CONFIGS.close, BUTTON_CONFIGS.next];
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 | /** @type {HTMLButtonElement | undefined} */
|
|---|
| 59 | let initialFocusButton;
|
|---|
| 60 | for (let i = 0; i < buttons.length; i += 1) {
|
|---|
| 61 | const buttonConfig = buttons[i];
|
|---|
| 62 |
|
|---|
| 63 | const button = document.createElement('button');
|
|---|
| 64 | button.id = buttonConfig.id;
|
|---|
| 65 | button.innerHTML = buttonConfig.label;
|
|---|
| 66 | button.tabIndex = 1;
|
|---|
| 67 | button.style.backgroundColor = '#' + theme.dimgrey;
|
|---|
| 68 | button.style.border = 'none';
|
|---|
| 69 | button.style.color = '#' + theme.white;
|
|---|
| 70 | button.style.cursor = 'pointer';
|
|---|
| 71 | button.style.fontSize = 'inherit';
|
|---|
| 72 | button.style.height = '100%';
|
|---|
| 73 | button.style.padding = '0.5rem 0.75rem';
|
|---|
| 74 | button.style.width = (100 / buttons.length).toString(10) + '%';
|
|---|
| 75 | button.addEventListener('click', buttonConfig.onClick);
|
|---|
| 76 |
|
|---|
| 77 | if (buttonConfig.id === props.initialFocus) {
|
|---|
| 78 | initialFocusButton = button;
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | footer.appendChild(button);
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | root.appendChild(footer);
|
|---|
| 85 |
|
|---|
| 86 | Spacer(document, root, { space: '2.5rem' });
|
|---|
| 87 |
|
|---|
| 88 | if (initialFocusButton) {
|
|---|
| 89 | initialFocusButton.focus();
|
|---|
| 90 | }
|
|---|
| 91 | }
|
|---|
| 92 |
|
|---|
| 93 | module.exports = RuntimeErrorFooter;
|
|---|