Skip to content
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

Background element refactor (+ drop targets and fixes) #693

Merged
merged 11 commits into from
Mar 24, 2020
60 changes: 60 additions & 0 deletions assets/src/edit-story/app/story/effects/usePageBackgrounds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import { useEffect } from 'react';
/**
* Internal dependencies
*/
import { createNewElement } from '../../../elements';
import theme from '../../../theme';
import { PAGE_WIDTH } from '../../../constants';
import { MaskTypes } from '../../../masks';
import createSolidFromString from '../../../utils/createSolidFromString';

// By default, the element should be 33% of the page.
const DEFAULT_ELEMENT_WIDTH = PAGE_WIDTH / 3;

// Make sure pages have background elements at all times
function usePageBackgrounds({ currentPage, setBackgroundElement, addElement }) {
useEffect(() => {
if (!currentPage || currentPage?.backgroundElementId) {
return;
}
const element = createNewElement('shape', {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we update the model itself, I think it'd work better in the story reducer rather than a disconnected effect. For one, we wouldn't have history problems with this approach. This might be an important thing to fix right away.

x: (PAGE_WIDTH / 4) * Math.random(),
y: (PAGE_WIDTH / 4) * Math.random(),
width: DEFAULT_ELEMENT_WIDTH,
height: DEFAULT_ELEMENT_WIDTH,
rotationAngle: 0,
mask: {
type: MaskTypes.RECTANGLE,
},
flip: {
vertical: false,
horizontal: false,
},
isBackground: true,
backgroundColor: createSolidFromString(theme.colors.fg.v1),
});
addElement({ element });
setBackgroundElement({ elementId: element.id });
}, [addElement, currentPage, setBackgroundElement]);
}

export default usePageBackgrounds;
8 changes: 8 additions & 0 deletions assets/src/edit-story/app/story/storyProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import useLoadStory from './effects/useLoadStory';
import useSaveStory from './actions/useSaveStory';
import useHistoryEntry from './effects/useHistoryEntry';
import useHistoryReplay from './effects/useHistoryReplay';
import usePageBackgrounds from './effects/usePageBackgrounds';
import useStoryReducer from './useStoryReducer';
import useDeleteStory from './actions/useDeleteStory';

Expand Down Expand Up @@ -90,6 +91,13 @@ function StoryProvider({ storyId, children }) {
useHistoryEntry({ pages, current, selection, story, capabilities });
useHistoryReplay({ restore });

// Ensure all pages have a background element at all times
usePageBackgrounds({
currentPage,
setBackgroundElement: api.setBackgroundElement,
addElement: api.addElement,
});

// This action allows the user to save the story
// (and it will have side-effects because saving can update url and status,
// thus the need for `updateStory`)
Expand Down
5 changes: 4 additions & 1 deletion assets/src/edit-story/components/canvas/canvasProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ function CanvasProvider({ children }) {
setSelectedElementsById({ elementIds: [elId] });
}
evt.currentTarget.focus();
evt.stopPropagation();
if (currentPage?.backgroundElementId !== elId) {
evt.stopPropagation();
}

if ('mousedown' === evt.type) {
evt.persist();
Expand All @@ -94,6 +96,7 @@ function CanvasProvider({ children }) {
},
[
editingElement,
currentPage,
clearEditing,
toggleElementInSelection,
setSelectedElementsById,
Expand Down
22 changes: 11 additions & 11 deletions assets/src/edit-story/components/canvas/displayLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@ import styled from 'styled-components';
* Internal dependencies
*/
import { useStory } from '../../app';
import generatePatternStyles from '../../utils/generatePatternStyles';
import createSolid from '../../utils/createSolid';
import useCanvas from './useCanvas';
import DisplayElement from './displayElement';
import { Layer, PageArea } from './layout';

const DEFAULT_COLOR = createSolid(255, 255, 255);

const DisplayPageArea = styled(PageArea).attrs(({ backgroundColor }) => ({
const DisplayPageArea = styled(PageArea).attrs({
className: 'container',
overflowAllowed: false,
style: generatePatternStyles(backgroundColor || DEFAULT_COLOR),
}))``;
})`
background-color: white;
background-image: linear-gradient(45deg, #999999 25%, transparent 25%),
linear-gradient(-45deg, #999999 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #999999 75%),
linear-gradient(-45deg, transparent 75%, #999999 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
`;

function DisplayLayer() {
const {
Expand All @@ -48,10 +51,7 @@ function DisplayLayer() {

return (
<Layer pointerEvents="none">
<DisplayPageArea
backgroundColor={currentPage?.backgroundColor}
ref={setPageContainer}
>
<DisplayPageArea ref={setPageContainer}>
{currentPage &&
currentPage.elements.map(({ id, ...rest }) => {
if (editingElement === id) {
Expand Down
7 changes: 5 additions & 2 deletions assets/src/edit-story/components/canvas/frameElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function FrameElement({ element }) {
actions: { setNodeForElement, handleSelectElement },
} = useCanvas();
const {
state: { selectedElementIds },
state: { selectedElementIds, currentPage },
} = useStory();
const {
actions: { getBox },
Expand All @@ -77,6 +77,7 @@ function FrameElement({ element }) {
}, [id, setNodeForElement]);
const isSelected = selectedElementIds.includes(id);
const box = getBox(element);
const isBackground = currentPage?.backgroundElementId === id;

return (
<Wrapper
Expand All @@ -87,7 +88,9 @@ function FrameElement({ element }) {
if (!isSelected) {
handleSelectElement(id, evt);
}
evt.stopPropagation();
if (!isBackground) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this? Could you add a comment in code. This seems like an important behavior involving background elements. As such, I'm also wondering if it'd be safer to roll it all inside the handleSelectElement? Otherwise we're fragmenting the behavior a bit between two places. It used to be easier with a simple stopPropagation, but this could be more nuanced.

evt.stopPropagation();
}
}}
onFocus={(evt) => {
if (!isSelected) {
Expand Down
17 changes: 9 additions & 8 deletions assets/src/edit-story/components/canvas/pagepreview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import PropTypes from 'prop-types';
* Internal dependencies
*/
import useStory from '../../../app/story/useStory';
import generatePatternStyles from '../../../utils/generatePatternStyles';
import createSolid from '../../../utils/createSolid';
import { TransformProvider } from '../../transform';
import { UnitsProvider } from '../../../units';
import DisplayElement from '../displayElement';
Expand Down Expand Up @@ -58,14 +56,17 @@ const Page = styled.button`
`}
`;

const DEFAULT_COLOR = createSolid(255, 255, 255);

const PreviewWrapper = styled.div.attrs(({ backgroundColor }) => ({
style: generatePatternStyles(backgroundColor || DEFAULT_COLOR),
}))`
const PreviewWrapper = styled.div`
height: 100%;
position: relative;
overflow: hidden;
background-color: white;
background-image: linear-gradient(45deg, #999999 25%, transparent 25%),
linear-gradient(-45deg, #999999 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #999999 75%),
linear-gradient(-45deg, transparent 75%, #999999 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
`;

function PagePreview({ index, forwardedRef, ...props }) {
Expand All @@ -78,7 +79,7 @@ function PagePreview({ index, forwardedRef, ...props }) {
<UnitsProvider pageSize={{ width, height }}>
<TransformProvider>
<Page {...props} ref={forwardedRef}>
<PreviewWrapper backgroundColor={page.backgroundColor}>
<PreviewWrapper>
{page.elements.map(({ id, ...rest }) => (
<DisplayElement key={id} element={{ id, ...rest }} />
))}
Expand Down
7 changes: 5 additions & 2 deletions assets/src/edit-story/components/canvas/selectionCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const Lasso = styled.div`
function SelectionCanvas({ children }) {
const {
actions: { clearSelection },
state: { selectedElements },
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use selectedElementIds instead? They are updating a lot less often.

} = useStory();
const {
state: { pageContainer },
Expand Down Expand Up @@ -98,8 +99,10 @@ function SelectionCanvas({ children }) {
};

const onMouseDown = (evt) => {
clearSelection();
clearEditing();
if (selectedElements.length) {
clearSelection();
clearEditing();
}

const overlay = overlayRef.current;
let offsetX = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const DEFAULT_FLIP = { horizontal: false, vertical: false };

function BackgroundSizePositionPanel({ selectedElements, pushUpdate }) {
// Background can only have one selected element.
const flip = selectedElements[0].flip || DEFAULT_FLIP;
const flip = selectedElements[0]?.flip || DEFAULT_FLIP;

const {
actions: { setBackgroundElement },
Expand Down
19 changes: 9 additions & 10 deletions assets/src/edit-story/components/panels/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
* Internal dependencies
*/
import { elementTypes } from '../../elements';
import PageBackgroundPanel from './pageBackground';
import BackgroundSizePositionPanel from './backgroundSizePosition';
import BackgroundOverlayPanel from './backgroundOverlay';
import ImageAccessibilityPanel from './imageAccessibility';
import LinkPanel from './link';
import LayerStylePanel from './layerStyle';
import ShapeStylePanel from './shapeStyle';
import SizePositionPanel from './sizePosition';
import TextStylePanel from './textStyle';
import VideoAccessibilityPanel from './videoAccessibility';
Expand All @@ -41,19 +41,18 @@ const LAYER_STYLE = 'layerStyle';
const LINK = 'link';
const TEXT = 'text';
const SIZE_POSITION = 'sizePosition';
const STYLE = 'style';
const TEXT_STYLE = 'textStyle';
const SHAPE_STYLE = 'shapeStyle';
const VIDEO_OPTIONS = 'videoOptions';
const VIDEO_ACCESSIBILITY = 'videoAccessibility';
const PAGE = 'page';
const NO_SELECTION = 'noselection';

export const PanelTypes = {
BACKGROUND_SIZE_POSITION,
BACKGROUND_DISPLAY,
BACKGROUND_OVERLAY,
SIZE_POSITION,
STYLE,
SHAPE_STYLE,
LAYER_STYLE,
TEXT,
TEXT_STYLE,
Expand All @@ -71,25 +70,23 @@ function intersect(a, b) {

export function getPanels(elements) {
if (elements.length === 0) {
return [
{ type: PAGE, Panel: PageBackgroundPanel },
{ type: NO_SELECTION, Panel: NoSelectionPanel },
];
return [{ type: NO_SELECTION, Panel: NoSelectionPanel }];
}

const isBackground = elements.length === 1 && elements[0].isBackground;

// Only display background panel in case of background element.
if (isBackground) {
const panels = [
{ type: PAGE, Panel: PageBackgroundPanel },
{ type: BACKGROUND_SIZE_POSITION, Panel: BackgroundSizePositionPanel },
{ type: LAYER_STYLE, Panel: LayerStylePanel },
{ type: BACKGROUND_OVERLAY, Panel: BackgroundOverlayPanel },
{ type: BACKGROUND_DISPLAY, Panel: BackgroundDisplayPanel },
];
// If the selected element's type is video / image , display accessibility panel, too.
if ('video' === elements[0].type) {
if ('shape' === elements[0].type) {
panels.unshift({ type: SHAPE_STYLE, Panel: ShapeStylePanel });
} else if ('video' === elements[0].type) {
panels.push({ type: VIDEO_OPTIONS, Panel: VideoOptionsPanel });
panels.push({
type: VIDEO_ACCESSIBILITY,
Expand Down Expand Up @@ -129,6 +126,8 @@ export function getPanels(elements) {
return { type, Panel: LinkPanel };
case TEXT_STYLE:
return { type, Panel: TextStylePanel };
case SHAPE_STYLE:
return { type, Panel: ShapeStylePanel };
case VIDEO_OPTIONS:
return { type, Panel: VideoOptionsPanel };
case VIDEO_ACCESSIBILITY:
Expand Down
Loading