Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a8dd014
chore: initial changes
kbangelov Feb 27, 2026
a193715
Merge branch 'develop' into task/uepr-518-manual-thumbnail-project-sa…
kbangelov Mar 12, 2026
45c2935
chore: moved button in editor and added modal
kbangelov Mar 12, 2026
84d2901
chore: added alerts
kbangelov Mar 13, 2026
942bb59
chore: updated focus, restricted access to button to owner
kbangelov Mar 13, 2026
42dedfc
chore: reverted some changes
kbangelov Mar 13, 2026
d858e2c
chore: fixed undefined prop
kbangelov Mar 13, 2026
cc1264b
chore: tooltip changes and more
kbangelov Mar 18, 2026
4c0d824
chore: some UI and UX improvements
kbangelov Mar 18, 2026
f4698f8
chore: addressed some comments
kbangelov Mar 19, 2026
6c06180
chore: addressed comments
kbangelov Mar 19, 2026
8934776
chore: addressed copilot comments
kbangelov Mar 19, 2026
725e94e
chore: move method into lib folder
kbangelov Mar 19, 2026
c3addb3
chore: omit unused logic
kbangelov Mar 19, 2026
4780722
chore: addressed comments
kbangelov Mar 24, 2026
bb35c3e
chore: merged components to use popup with arrow common logic
kbangelov Mar 25, 2026
5d39186
chore: remove async logic
kbangelov Mar 25, 2026
26759dd
chore: addressed comments, renamed component more appropriately
kbangelov Mar 25, 2026
02c594c
chore: refactored delete confirmation prompt and other code
kbangelov Mar 26, 2026
68ac1ed
chore: modal with arrow refactoring
kbangelov Mar 26, 2026
e792842
chore: made delete confirmation prompt use confirmation prompt
kbangelov Mar 27, 2026
b8cc0c3
chore: refactored props
kbangelov Mar 27, 2026
6356ca7
chore: removed unnecessary code
kbangelov Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions packages/scratch-gui/src/components/alerts/alert.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
body .alert {
width: 100%;
display: flex;
box-sizing: border-box;
flex-direction: row;
overflow: hidden;
justify-content: flex-start;
Expand All @@ -19,8 +20,8 @@ body .alert {
}

.alert.warn {
background: #FFF0DF;
border: 1px solid #FF8C1A;
background: $ui-alert-orange;
border: 1px solid $data-primary;
box-shadow: 0px 0px 0px 2px rgba(255, 140, 26, 0.25);
}

Expand All @@ -30,6 +31,11 @@ body .alert {
box-shadow: 0px 0px 0px 2px $extensions-light;
}

.alert.info-blue {
border: 1px solid $motion-primary;
background: $ui-primary;
}

.alert-spinner {
self-align: center;
}
Expand Down Expand Up @@ -76,7 +82,7 @@ body .alert {
width: 6.5rem;
padding: 0.55rem 0.9rem;
border-radius: 0.35rem;
background: #FF8C1A;
background: $data-primary;
color: white;
font-weight: 700;
font-size: 0.77rem;
Expand Down
8 changes: 7 additions & 1 deletion packages/scratch-gui/src/components/button/button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ButtonComponent = ({
iconSrc,
onClick,
children,
componentRef,
...props
}) => {

Expand All @@ -33,6 +34,7 @@ const ButtonComponent = ({
className
)}
onClick={onClick}
ref={componentRef}
{...props}
>
{icon}
Expand All @@ -47,7 +49,11 @@ ButtonComponent.propTypes = {
disabled: PropTypes.bool,
iconClassName: PropTypes.string,
iconSrc: PropTypes.string,
onClick: PropTypes.func
onClick: PropTypes.func,
componentRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({current: PropTypes.instanceOf(Element)})
])
};

export default ButtonComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@import "../../css/colors.css";
@import "../../css/units.css";

.modal-container {
display: flex;
width: 12.5rem;
padding: 0.75rem;
flex-direction: column;
align-items: flex-start;
border-radius: 0.5rem;
background: $looks-secondary;
gap: 0.9375rem;
box-sizing: border-box;
}

.label {
align-self: stretch;
color: $ui-white;
text-align: center;
font-size: 1rem;
font-weight: 700;
line-height: 1.5rem;
}

.button-row {
font-weight: 700;
display: flex;
gap: 0.5rem;
width: 100%;
}

.button-row button {
all: unset;
display: flex;
padding: 0.5rem 1rem;
justify-content: center;
align-items: center;
gap: 0.5rem;
flex: 1;
border-radius: 2.5rem;
background: inherit;
cursor: pointer;
}

.button-row button:focus {
outline: auto;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently yes because of the earlier all: unset; on the button

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if that's a sign not to use all:unset but rather change what actually needs to be changed? My concern is that we may unset some default button styles that we haven't though of testing. I don't have a strong preference though, that's just a thought.

}

.button-row button span {
color: $ui-white;
font-size: 0.75rem;
font-weight: 700;
line-height: 1rem;
}

.button-row button.confirm-button {
background: $ui-white;
}

.button-row button.confirm-button span {
color: $looks-secondary;
}

.button-row button.cancel-button {
border: 0.0625rem solid $ui-white;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactModal from 'react-modal';
import {defineMessages, FormattedMessage} from 'react-intl';

import Box from '../box/box.jsx';
import PopupWithArrow from '../popup-with-arrow/popup-with-arrow.jsx';

import arrowDownIcon from './icon--arrow-down.svg';
import arrowUpIcon from './icon--arrow-up.svg';
import arrowLeftIcon from './icon--arrow-left.svg';
import arrowRightIcon from './icon--arrow-right.svg';

import styles from './confirmation-prompt.css';
import {PopupAlign, PopupSide} from '../../lib/calculatePopupPosition.js';

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 defaultConfig = {
modalWidth: 200,
spaceForArrow: 16,
counterOffset: 7,
arrowOffsetFromBottom: 2,
arrowWidth: 29,
arrowHeight: 13
};

const arrowConfig = {
arrowDownIcon,
arrowUpIcon,
arrowLeftIcon,
arrowRightIcon
};

const ConfirmationPrompt = ({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we:

  • pass the positioning params (width, arrow sizes, ..) as a configuration object to the ConfirmationPrompt
  • reuse this component for the delete confirmation prompt, since it already contains a less complex version of the logic used here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I find slightly confusing about the current implementation is the following structure:

  • PopupWithArrow is a generic component used by ConfirmationPrompt, DeleteConfirmationPrompt, and FeatureCallout.
  • ConfirmationPrompt is also a generic component, but it's currently only used for the thumbnail confirmation prompt and not for the delete confirmation prompt.

I think what we actually need is a clearer separation of responsibilities:

  • SetTooltipConfirmationPrompt should use ConfirmationPrompt and pass in specific content such as title (if needed), description, className, layout, etc.
  • DeleteConfirmationPrompt should also use ConfirmationPrompt and provide its own specific content.
  • ConfirmationPrompt should use PopupWithArrow, which handles alignment and positioning.
  • FeatureCallout should use PopupWithArrow for alignment and positioning.
  • PopupWithArrow should use ReactModal to leverage its built-in behavior for handling outside clicks, escape key presses, etc.

This structure would simplify the overall design and ensure that each component has a clear, single responsibility. We may still need to refine the naming to better reflect each component's purpose, but this is the general direction I think we should take.

title,
message,
confirmLabel,
cancelLabel,
onConfirm,
onCancel,
isOpen,
relativeElementRef,
side,
align,
layoutConfig
}) => {
const {modalWidth, spaceForArrow, counterOffset, arrowOffsetFromBottom, arrowHeight, arrowWidth} =
{...defaultConfig, ...layoutConfig};

return (
<PopupWithArrow
isOpen={isOpen}
relativeElementRef={relativeElementRef}
side={side}
align={align}
layoutConfig={{
popupWidth: modalWidth,
spaceForArrow,
counterOffset,
arrowOffsetFromBottom,
arrowHeight,
arrowWidth
}}
arrowConfig={arrowConfig}
>
{({popupRef, pos}) => (
<ReactModal
isOpen
onRequestClose={onCancel}
contentLabel={title}
style={{
content: {
top: pos.top,
left: pos.left,
width: modalWidth,
border: 'none',
height: 'fit-content',
backgroundColor: 'transparent',
padding: 0,
margin: 0,
position: 'fixed',
overflowX: 'hidden',
zIndex: 1000
},
overlay: {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 510,
backgroundColor: 'transparent'
}
}}
>
<Box
className={styles.modalContainer}
componentRef={popupRef}
>
<Box className={styles.label}>
{message}
</Box>
<Box className={styles.buttonRow}>
<button
onClick={onCancel}
className={styles.cancelButton}
>
{cancelLabel ?? <FormattedMessage {...messages.defaultCancelLabel} />}
</button>
<button
onClick={onConfirm}
className={styles.confirmButton}
>
{confirmLabel ?? <FormattedMessage {...messages.defaultConfirmLabel} />}
</button>
</Box>
</Box>
</ReactModal>
)}
</PopupWithArrow>
);
};

ConfirmationPrompt.propTypes = {
isOpen: PropTypes.bool.isRequired,
title: PropTypes.string,
message: PropTypes.node.isRequired,
confirmLabel: PropTypes.node,
cancelLabel: PropTypes.node,
onConfirm: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
relativeElementRef: PropTypes.shape({current: PropTypes.instanceOf(Element)}),
side: PropTypes.oneOf(Object.values(PopupSide)).isRequired,
align: PropTypes.oneOf(Object.values(PopupAlign)),
layoutConfig: PropTypes.shape({
modalWidth: PropTypes.number,
spaceForArrow: PropTypes.number,
arrowOffsetFromBottom: PropTypes.number,
counterOffset: PropTypes.number,
arrowHeight: PropTypes.number,
arrowWidth: PropTypes.number
})
};

export default ConfirmationPrompt;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this one bigger than the others? What method did you use for exporting them from Figma?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was some gap on the originally downloaded one. I wonder if it's worth resolving, since it has the same proportions and same length in the end.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be, at least size-wise the stored SVG will be smaller. Though I'm not sure if the difference is significant enough to make a difference.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 3 additions & 5 deletions packages/scratch-gui/src/components/gui/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,6 @@ const GUIComponent = props => {
isRendererSupported={isRendererSupported}
isRtl={isRtl}
loading={loading}
manuallySaveThumbnails={
manuallySaveThumbnails &&
userOwnsProject
}
onUpdateProjectThumbnail={onUpdateProjectThumbnail}
stageSize={STAGE_SIZE_MODES.large}
vm={vm}
>
Expand Down Expand Up @@ -545,6 +540,9 @@ const GUIComponent = props => {
vm={vm}
ariaRole="region"
ariaLabel={intl.formatMessage(ariaMessages.stage)}
manuallySaveThumbnails={manuallySaveThumbnails}
userOwnsProject={userOwnsProject}
onUpdateProjectThumbnail={onUpdateProjectThumbnail}
/>
<Box
className={styles.targetWrapper}
Expand Down
Loading
Loading