-
Notifications
You must be signed in to change notification settings - Fork 161
[UEPR-518] Implement in-editor Manual Save Project Thumbnail logic #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 5 commits
a8dd014
a193715
45c2935
84d2901
942bb59
42dedfc
d858e2c
cc1264b
4c0d824
f4698f8
6c06180
8934776
725e94e
c3addb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| @import "../../css/colors.css"; | ||
| @import "../../css/units.css"; | ||
|
|
||
| .modal-container { | ||
| display: flex; | ||
| width: 200px; | ||
KManolov3 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| padding: 12px; | ||
| flex-direction: column; | ||
| align-items: flex-start; | ||
| border-radius: 8px; | ||
| background: #855CD6; | ||
| gap: 15px; | ||
| box-sizing: border-box; | ||
| } | ||
|
|
||
| .label { | ||
| align-self: stretch; | ||
| color: var(--White-White, #FFF); | ||
KManolov3 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
KManolov3 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| text-align: center; | ||
| font-family: "Helvetica Neue"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably redundant
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why so? |
||
| font-size: 16px; | ||
| font-style: normal; | ||
kbangelov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| font-weight: 700; | ||
| line-height: 24px; | ||
| } | ||
|
|
||
| .button-row { | ||
| font-weight: bolder; | ||
kbangelov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| display: flex; | ||
| gap: 0.5rem; | ||
| width: 100%; | ||
| } | ||
|
|
||
| .button-row button { | ||
| all: unset; | ||
KManolov3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| display: flex; | ||
| padding: 8px 16px; | ||
| justify-content: center; | ||
| align-items: center; | ||
| gap: 8px; | ||
| flex: 1 0 0; | ||
KManolov3 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| border-radius: 40px; | ||
| background: inherit; | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| .button-row button:focus { | ||
| outline: auto; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. currently yes because of the earlier all: unset; on the button |
||
| } | ||
|
|
||
| .button-row button span { | ||
| color: var(--white-white-100, #FFF); | ||
| font-family: "Helvetica Neue"; | ||
| font-size: 12px; | ||
| font-style: normal; | ||
| font-weight: 700; | ||
| line-height: 16px; | ||
| } | ||
|
|
||
| .button-row button.confirm-button { | ||
| background: var(--White-White, #FFF); | ||
| } | ||
|
|
||
| .button-row button.confirm-button span { | ||
| color: var(--Looks-Purple-2, #855CD6); | ||
| } | ||
|
|
||
| .button-row button.cancel-button { | ||
| border: 1px solid var(--white-white-100, #FFF); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,256 @@ | ||
| import React, {useRef, useCallback, useEffect} from 'react'; | ||
| import debounce from 'lodash.debounce'; | ||
| import PropTypes from 'prop-types'; | ||
| import ReactModal from 'react-modal'; | ||
| import {defineMessages, FormattedMessage, useIntl} from 'react-intl'; | ||
|
|
||
| import Box from '../box/box.jsx'; | ||
|
|
||
| import arrowLeftIcon from './icon--arrow-left.svg'; | ||
| import arrowRightIcon from './icon--arrow-right.svg'; | ||
| import arrowDownIcon from './icon--arrow-down.svg'; | ||
| import arrowUpIcon from './icon--arrow-up.svg'; | ||
|
|
||
| import styles from './confirmation-prompt.css'; | ||
|
|
||
| const messages = defineMessages({ | ||
| defaultConfirmLabel: { | ||
| defaultMessage: 'yes', | ||
| description: 'Label for confirm button in confirmation prompt', | ||
| id: 'gui.confirmationPrompt.confirm' | ||
| }, | ||
| defaultCancelLabel: { | ||
| defaultMessage: 'no', | ||
| description: 'Label for cancel button in confirmation prompt', | ||
| id: 'gui.confirmationPrompt.cancel' | ||
| } | ||
| }); | ||
|
|
||
| const modalWidth = 200; | ||
| const spaceForArrow = 16; | ||
| const arrowOffsetFromEnd = 7; | ||
| const arrowLongSide = 29; | ||
| const arrowShortSide = 13; | ||
|
||
|
|
||
| const calculateModalPosition = (relativeElemRef, modalRef, modalPosition) => { | ||
KManolov3 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const arrowHeight = (modalPosition === 'left' || modalPosition === 'right') ? | ||
| arrowLongSide : arrowShortSide; | ||
| const arrowWidth = (modalPosition === 'left' || modalPosition === 'right') ? | ||
| arrowShortSide : arrowLongSide; | ||
|
|
||
| const el = relativeElemRef?.current; | ||
| const modalEl = modalRef?.current; | ||
| if (!el || !modalEl) { | ||
| return {}; | ||
| } | ||
|
|
||
| const buttonRect = el.getBoundingClientRect(); | ||
| const modalRect = modalEl.getBoundingClientRect(); | ||
| const modalHeight = modalRect.height; | ||
|
|
||
| let top = 0; | ||
| let left = 0; | ||
| let arrowIcon = null; | ||
| let arrowTop = 0; | ||
| let arrowLeft = 0; | ||
|
|
||
| switch (modalPosition) { | ||
| case 'left': | ||
| top = buttonRect.top - (modalHeight / 2) + (buttonRect.height / 2); | ||
| left = buttonRect.left - modalWidth - spaceForArrow; | ||
| arrowIcon = arrowRightIcon; | ||
| arrowTop = buttonRect.top + (buttonRect.height / 2) - (arrowHeight / 2); | ||
| arrowLeft = left + modalWidth; | ||
| break; | ||
| case 'right': | ||
| top = buttonRect.top - (modalHeight / 2) + (buttonRect.height / 2); | ||
| left = buttonRect.right + spaceForArrow; | ||
| arrowIcon = arrowLeftIcon; | ||
| arrowTop = buttonRect.top + (buttonRect.height / 2) - (arrowHeight / 2); | ||
| arrowLeft = left - arrowWidth; | ||
| break; | ||
| case 'up': | ||
| top = buttonRect.top - modalHeight - spaceForArrow; | ||
| left = buttonRect.left - ((modalWidth - buttonRect.width) / 2); | ||
| arrowIcon = arrowDownIcon; | ||
| arrowTop = top + modalHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| case 'down': | ||
| top = buttonRect.bottom + spaceForArrow; | ||
| left = buttonRect.left - ((modalWidth - buttonRect.width) / 2); | ||
| arrowIcon = arrowUpIcon; | ||
| arrowTop = top - arrowHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| case 'down left': | ||
| top = buttonRect.bottom + spaceForArrow; | ||
| left = buttonRect.left - modalWidth + buttonRect.width + arrowOffsetFromEnd; | ||
| arrowIcon = arrowUpIcon; | ||
| arrowTop = top - arrowHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| case 'down right': | ||
| top = buttonRect.bottom + spaceForArrow; | ||
| left = buttonRect.left - arrowOffsetFromEnd; | ||
| arrowIcon = arrowUpIcon; | ||
| arrowTop = top - arrowHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| case 'up left': | ||
| top = buttonRect.top - modalHeight - spaceForArrow; | ||
| left = buttonRect.left - modalWidth + buttonRect.width + arrowOffsetFromEnd; | ||
| arrowIcon = arrowDownIcon; | ||
| arrowTop = top + modalHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| case 'up right': | ||
| top = buttonRect.top - modalHeight - spaceForArrow; | ||
| left = buttonRect.left - arrowOffsetFromEnd; | ||
| arrowIcon = arrowDownIcon; | ||
| arrowTop = top + modalHeight; | ||
| arrowLeft = buttonRect.left + (buttonRect.width / 2) - (arrowWidth / 2); | ||
| break; | ||
| } | ||
|
|
||
| return {top, left, arrowIcon, arrowTop, arrowLeft}; | ||
| }; | ||
|
|
||
| const ConfirmationPrompt = ({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we reuse this new ConfirmationPrompt for the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we:
|
||
| title, | ||
| message, | ||
| confirmLabel, | ||
| cancelLabel, | ||
| onConfirm, | ||
| onCancel, | ||
| isOpen, | ||
| relativeElemRef, | ||
| modalPosition | ||
| }) => { | ||
| const intl = useIntl(); | ||
|
|
||
| const modalRef = useRef(null); | ||
| const [modalPositionValues, setModalPositionValues] = React.useState({}); | ||
|
|
||
| const updatePosition = useCallback(() => { | ||
| if (relativeElemRef.current && modalRef.current) { | ||
| const pos = calculateModalPosition(relativeElemRef, modalRef, modalPosition); | ||
| setModalPositionValues(pos); | ||
| } | ||
| }, [relativeElemRef, modalPosition]); | ||
|
|
||
| useEffect(() => { | ||
| if (!isOpen) return; | ||
|
|
||
| const debouncedUpdate = debounce(updatePosition, 50, {leading: true}); | ||
|
|
||
| debouncedUpdate(); | ||
|
|
||
| window.addEventListener('resize', debouncedUpdate); | ||
| return () => window.removeEventListener('resize', debouncedUpdate); | ||
| }, [isOpen, relativeElemRef, modalPosition]); | ||
|
|
||
| const onModalMount = useCallback(el => { | ||
| if (!el || !isOpen) return; | ||
| modalRef.current = el; | ||
|
|
||
| updatePosition(); | ||
| }, [isOpen, relativeElemRef, modalPosition]); | ||
kbangelov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return ( | ||
| isOpen && ( | ||
| <ReactModal | ||
| isOpen | ||
| onRequestClose={onCancel} | ||
| contentLabel={intl.formatMessage(title)} | ||
| style={{ | ||
| content: { | ||
kbangelov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| top: modalPositionValues.top, | ||
| left: modalPositionValues.left, | ||
| width: modalWidth, | ||
kbangelov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| border: 'none', | ||
| height: 'fit-content', | ||
| backgroundColor: 'transparent', | ||
| padding: 0, | ||
| margin: 0, | ||
| position: 'absolute', | ||
| overflowX: 'hidden', | ||
| zIndex: 1000 | ||
| }, | ||
| overlay: { | ||
| position: 'fixed', | ||
| top: 0, | ||
| left: 0, | ||
| right: 0, | ||
| bottom: 0, | ||
| zIndex: 510, | ||
| backgroundColor: 'transparent' | ||
| } | ||
| }} | ||
| > | ||
| {modalPositionValues.arrowIcon && ( | ||
| <img | ||
| src={modalPositionValues.arrowIcon} | ||
kbangelov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| style={{ | ||
| position: 'fixed', | ||
| top: modalPositionValues.arrowTop, | ||
| left: modalPositionValues.arrowLeft, | ||
| width: (modalPosition === 'left' || modalPosition === 'right') ? | ||
| arrowShortSide : arrowLongSide, | ||
| height: (modalPosition === 'left' || modalPosition === 'right') ? | ||
| arrowLongSide : arrowShortSide, | ||
| zIndex: 1001 | ||
| }} | ||
| /> | ||
| )} | ||
| <Box | ||
| className={styles.modalContainer} | ||
| componentRef={onModalMount} | ||
| > | ||
| <Box className={styles.label}> | ||
| <FormattedMessage {...message} /> | ||
| </Box> | ||
|
|
||
| <Box className={styles.buttonRow}> | ||
| <button | ||
| onClick={onCancel} | ||
| className={styles.cancelButton} | ||
| > | ||
| <FormattedMessage {...(cancelLabel ?? messages.defaultCancelLabel)} /> | ||
| </button> | ||
|
|
||
| <button | ||
| onClick={onConfirm} | ||
| className={styles.confirmButton} | ||
| > | ||
| <FormattedMessage {...(confirmLabel ?? messages.defaultConfirmLabel)} /> | ||
| </button> | ||
| </Box> | ||
| </Box> | ||
| </ReactModal> | ||
| ) | ||
| ); | ||
| }; | ||
|
|
||
| ConfirmationPrompt.propTypes = { | ||
| isOpen: PropTypes.bool.isRequired, | ||
| title: PropTypes.string.isRequired, | ||
| message: PropTypes.string.isRequired, | ||
| confirmLabel: PropTypes.string, | ||
| cancelLabel: PropTypes.string, | ||
kbangelov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| onConfirm: PropTypes.func.isRequired, | ||
| onCancel: PropTypes.func.isRequired, | ||
| relativeElemRef: PropTypes.shape({current: PropTypes.instanceOf(Element)}), | ||
| modalPosition: PropTypes.oneOf([ | ||
| 'left', | ||
| 'right', | ||
| 'up', | ||
| 'down', | ||
| 'down left', | ||
| 'down right', | ||
| 'up left', | ||
| 'up right' | ||
| ]).isRequired | ||
| }; | ||
|
|
||
| export default ConfirmationPrompt; | ||
Uh oh!
There was an error while loading. Please reload this page.