diff --git a/app/assets/stylesheets/components/modals.scss b/app/assets/stylesheets/components/modals.scss index 53c635ea9..0f4fc5ec3 100644 --- a/app/assets/stylesheets/components/modals.scss +++ b/app/assets/stylesheets/components/modals.scss @@ -1,26 +1,22 @@ @import '../config/import'; .crayons-modal { - --modal-max-width: 768px; - --modal-body-padding: var(--su-3); - --modal-header-padding: var(--su-2) var(--su-2) var(--su-2) var(--su-3); - --modal-header-font-size: var(--fs-l); - --modal-body-font-size: var(--fs-base); + --modal-max-width: 640px; + --modal-padding: var(--su-3); + --modal-backdrop-pointer-events: none; + --modal-border: none; + --modal-header-border: 1px solid var(--divider); + --modal-header-padding: var(--su-2) var(--su-2) var(--su-2) + var(--modal-padding); + --modal-alignment: unset; + --modal-header-alignment: space-between; @media (min-width: $breakpoint-s) { - --modal-header-padding: var(--su-2) var(--su-2) var(--su-2) var(--su-4); - --modal-body-padding: var(--su-4); + --modal-padding: var(--su-4); } @media (min-width: $breakpoint-m) { - --modal-header-padding: var(--su-3) var(--su-3) var(--su-3) var(--su-6); - --modal-body-padding: var(--su-6); - --modal-header-font-size: var(--fs-xl); - --modal-body-font-size: var(--fs-l); - } - - @media (min-width: $breakpoint-l) { - --modal-body-padding: var(--su-8); + --modal-padding: var(--su-7); } position: fixed; @@ -34,82 +30,103 @@ z-index: var(--z-modal); pointer-events: none; - &__overlay { - background: rgba(0, 0, 0, 0.6); - opacity: 0.5; + &--bg-dismissible { + --modal-backdrop-pointer-events: auto; + } + + &--prompt { + --modal-header-border: none; + --modal-header-padding: var(--modal-padding) var(--su-8) 0 + var(--modal-padding); + --modal-body-margin: 0; + + @media (min-width: $breakpoint-m) { + --modal-body-margin: calc((var(--modal-padding) * -1) + var(--su-4)); + } + + .crayons-modal__dismiss { + position: absolute; + right: var(--su-2); + top: var(--su-2); + } + } + + &--centered { + --modal-header-padding: var(--su-7) var(--su-8) 0 var(--su-8); + --modal-alignment: center; + --modal-header-alignment: center; + } + + &--danger { + --modal-border: 4px solid var(--modal-danger-border-color); + } + + &__backdrop { + background: var(--modal-backdrop); position: absolute; left: 0; right: 0; top: 0; bottom: 0; pointer-events: auto; - - &.background-clickable { - pointer-events: none; - } } &__box { display: grid; grid-template-rows: auto 1fr; max-width: var(--modal-max-width); - background: var(--card-bg); - color: var(--card-color); + background: var(--modal-bg); z-index: var(--z-elevate); max-height: 100%; width: 100%; height: 100%; overflow: hidden; + position: relative; pointer-events: auto; border-radius: var(--radius-large-auto); + border: var(--modal-border); + text-align: var(--modal-alignment); box-shadow: var(--shadow-2); @media (min-width: $breakpoint-s) { - background: var(--card-bg); - color: var(--card-color); max-height: calc(100% - var(--su-6)); height: auto; width: 90%; } &__header { - border-bottom: 1px solid var(--base-a5); - display: flex; - align-items: center; - justify-content: space-between; + border-bottom: var(--modal-header-border); padding: var(--modal-header-padding); flex-shrink: 0; - - &__title { - font-size: var(--modal-header-font-size); - } + display: flex; + justify-content: var(--modal-header-alignment); + align-items: center; } &__body { - font-size: var(--modal-body-font-size); - padding: var(--modal-body-padding); + padding: var(--modal-padding); + margin-top: var(--modal-body-margin); max-height: 100%; overflow-y: auto; grid-auto-rows: min-content; } } - &--s { + &--small { @media (min-width: $breakpoint-s) { --modal-max-width: 480px; - --modal-body-padding: var(--su-6); - --modal-header-font-size: var(--fs-l); - --modal-body-font-size: var(--fs-base); } } - &--m { + &--large { @media (min-width: $breakpoint-m) { - --modal-max-width: 640px; - --modal-body-padding: var(--su-7); - --modal-header-padding: var(--su-3) var(--su-5); - --modal-header-font-size: var(--fs-xl); - --modal-body-font-size: var(--fs-l); + --modal-max-width: 768px; + } + } + + &--prompt { + @media (min-width: $breakpoint-s) { + --modal-max-width: 480px; } } } diff --git a/app/assets/stylesheets/config/_colors.css b/app/assets/stylesheets/config/_colors.css index 05dfe8679..a6e65d90d 100644 --- a/app/assets/stylesheets/config/_colors.css +++ b/app/assets/stylesheets/config/_colors.css @@ -276,6 +276,14 @@ --indicator-info-bg: var(--accent-brand); --indicator-info-color: rgb(var(--white)); + /*********************************************** + ** Modals ************************************** + ***********************************************/ + + --modal-bg: rgb(var(--white)); + --modal-backdrop: rgba(var(--black), 0.6); + --modal-danger-border-color: rgb(var(--red-500)); + /*********************************************** ** Colors ************************************** ***********************************************/ @@ -301,6 +309,9 @@ /* Focus */ --focus: var(--accent-brand); + /* Divider */ + --divider: rgb(var(--grey-200)); + /* Shadows */ --shadow-1: 0 10px 15px -3px rgba(var(--black), 0.1), 0 4px 6px -2px rgba(var(--black), 0.05), 0 0 0 1px rgba(var(--black), 0.1); diff --git a/app/assets/stylesheets/themes/dark.css b/app/assets/stylesheets/themes/dark.css index a218de70d..9eec08da6 100644 --- a/app/assets/stylesheets/themes/dark.css +++ b/app/assets/stylesheets/themes/dark.css @@ -284,6 +284,14 @@ --indicator-info-bg: rgb(var(--indigo-700)); --indicator-info-color: rgb(var(--indigo-100)); + /*********************************************** + ** Modals ************************************** + ***********************************************/ + + --modal-bg: rgb(var(--grey-900)); + --modal-backdrop: rgba(var(--black), 0.6); + --modal-danger-border-color: rgb(var(--red-400)); + /*********************************************** ** Colors ************************************** ***********************************************/ @@ -308,6 +316,9 @@ /* Focus */ --focus: var(--accent-brand-lighter); + /* Divider */ + --divider: rgb(var(--grey-700)); + /* Shadows */ --shadow-1: 0 10px 15px -3px rgba(var(--black), 0.5), 0 4px 6px -2px rgba(var(--black), 0.25), inset 0 0 0 1px rgba(var(--white), 0.1); diff --git a/app/javascript/admin/adminModal.js b/app/javascript/admin/adminModal.js index d29a1a620..87110f097 100644 --- a/app/javascript/admin/adminModal.js +++ b/app/javascript/admin/adminModal.js @@ -31,7 +31,7 @@ export const adminModal = function ({ rightCustomDataAttr = null, }) { return ` -
${title}
diff --git a/app/javascript/crayons/Modal/Modal.jsx b/app/javascript/crayons/Modal/Modal.jsx index 88e77ece7..30c2717e4 100644 --- a/app/javascript/crayons/Modal/Modal.jsx +++ b/app/javascript/crayons/Modal/Modal.jsx @@ -1,102 +1,59 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; +import classNames from 'classnames/bind'; import { FocusTrap } from '../../shared/components/focusTrap'; import { defaultChildrenPropTypes } from '../../common-prop-types'; -import { Button } from '@crayons'; +import { ButtonNew as Button } from '@crayons'; import CloseIcon from '@images/x.svg'; -function getAdditionalClassNames({ size, className }) { - let additionalClassNames = ''; - - if (size && size.length > 0 && size !== 'default') { - additionalClassNames += ` crayons-modal--${size}`; - } - - if (className && className.length > 0) { - additionalClassNames += ` ${className}`; - } - - return additionalClassNames; -} - -/** - * A modal component which can be presented with or without an overlay. - * The modal is presented within a focus trap for accessibility purposes - please note that the selector used for the focusTrap must be unique on the given page, otherwise focus may be trapped on the wrong element. - * - * @param {Object} props - * @param {Array} props.children The content to be displayed inside the Modal. Can be provided by composition (see example). - * @param {string} props.size The desired modal size ('s', 'm' or 'default') - * @param {string} props.className Optional additional classnames to apply to the modal container - * @param {string} props.title The title to be displayed in the modal heading. If provided, a title bar with a close button will be displayed. - * @param {boolean} props.overlay Whether or not to show a semi-opaque overlay behind the modal - * @param {Function} props.onClose Callback for any function to be executed on close button click or Escape - * @param {boolean} props.closeOnClickOutside Whether the modal should close if the user clicks outside of it - * @param {string} props.focusTrapSelector The CSS selector for where to trap the user's focus. This should be unique to the page in which the modal is presented. - * - * @example - *Some modal content
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse - odio est, ultricies vel euismod ut, fringilla quis tellus. Sed at - dui mi. Fusce cursus nibh lectus, vitae lobortis orci volutpat quis.{' '} -
++ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Suspendisse odio est, ultricies vel euismod ut, fringilla quis + tellus. Sed at dui mi. Fusce cursus nibh lectus, vitae lobortis + orci volutpat quis. +
+ {!args.prompt && ( ++ Sed at dui mi. Fusce cursus nibh lectus, vitae lobortis orci + volutpat quis. Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Suspendisse odio est, ultricies vel euismod ut, + fringilla quis tellus. Sed at dui mi. Fusce cursus nibh lectus, + vitae lobortis orci volutpat quis. +
+ )} +