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

POC: Editor: Multipage Canvas #11669

Merged
merged 13 commits into from
Jun 30, 2022
11 changes: 11 additions & 0 deletions includes/Experiments.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,17 @@ public function get_experiments(): array {
'group' => 'editor',
'default' => true,
],
/**
* Author: @barklund
* Issue: #9643
* Creation date: 2022-06-21
*/
[
'name' => 'extraPages',
'label' => __( 'Context Pages', 'web-stories' ),
'description' => __( 'Show extra pages for context before and after the current canvas page. Note: This might come with a performance penalty.', 'web-stories' ),
'group' => 'editor',
],
];
}

Expand Down
12 changes: 12 additions & 0 deletions packages/story-editor/src/app/layout/useZoomSetting.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,15 @@ function calculateViewportProperties(workspaceSize, zoomSetting, zoomLevel) {
const viewportHeight = hasAnyOverflow
? workspaceSize.availableHeight
: fullbleedHeight + pagePadding;

// Calculate if extra pages can be seen before/after current page and if so how many
const extraSpace =
(workspaceSize.width - (pageWidth + 2 * PAGE_NAV_WIDTH)) / 2;
const extraPageWidth = Math.round(0.9 * pageWidth);
const hasExtraPages = !hasAnyOverflow && hasPageNavigation && extraSpace > 50;
const extraPageCount = hasExtraPages
? Math.ceil(extraSpace / extraPageWidth)
: 0;
return {
pageWidth,
pageHeight,
Expand All @@ -169,6 +178,9 @@ function calculateViewportProperties(workspaceSize, zoomSetting, zoomLevel) {
pagePadding,
viewportWidth,
viewportHeight,
hasExtraPages,
extraPageWidth,
extraPageCount,
};
}

Expand Down
161 changes: 161 additions & 0 deletions packages/story-editor/src/components/canvas/extraPages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* 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 styled from 'styled-components';
import { memo } from '@googleforcreators/react';
import { PAGE_RATIO } from '@googleforcreators/units';
import PropTypes from 'prop-types';
import { __, sprintf } from '@googleforcreators/i18n';
import { useFeature } from 'flagged';

/**
* Internal dependencies
*/
import { useStory, useLayout } from '../../app';
import PagePreview from '../footer/pagepreview';

const GAP = 32;

const ExtraPageWrapper = styled.div`
display: flex;
overflow: hidden;
justify-content: ${({ isPrevious }) =>
isPrevious ? 'flex-end' : 'flex-start'};
align-items: center;
height: 100%;
`;
const ExtraPageList = styled.ol`
display: flex;
flex-direction: ${({ isPrevious }) => (isPrevious ? 'row-reverse' : 'row')};
width: ${({ listWidth }) => listWidth}px;
height: ${({ extraPageHeight }) => extraPageHeight}px;
margin: 0;
padding: 0 ${GAP}px;
gap: ${GAP}px;
`;
const ExtraPage = styled.li`
display: block;
width: ${({ extraPageWidth }) => extraPageWidth - GAP}px;
height: 100%;
border-radius: 4px;
background-color: white;

opacity: 0.5;
transition: opacity 0.2s ease;
&:hover {
opacity: 1;
}
`;
const ExtraPagePreview = styled(PagePreview)`
cursor: pointer;
`;

function range(from, to) {
return from < to ? Array.from(Array(to - from)).map((k, v) => v + from) : [];
}

function getPagesToShow({
isPrevious,
hasExtraPages,
currentPageIndex,
extraPageCount,
pageCount,
}) {
if (!hasExtraPages) {
return [];
}
// Showing pages before the current one
if (isPrevious) {
const from = Math.max(0, currentPageIndex - extraPageCount);
const to = currentPageIndex;
return range(from, to).reverse();
}
// Showing pages after the current one
const from = currentPageIndex + 1;
const to = Math.min(pageCount, currentPageIndex + extraPageCount + 2);
return range(from, to);
}

function ExtraPages({ isPrevious = false }) {
const { currentPageIndex, pages, setCurrentPage } = useStory(
({ state: { pages, currentPageIndex }, actions: { setCurrentPage } }) => ({
currentPageIndex,
pages,
setCurrentPage,
})
);
const { hasExtraPages, extraPageWidth, extraPageCount } = useLayout(
({ state: { hasExtraPages, extraPageWidth, extraPageCount } }) => ({
hasExtraPages,
extraPageWidth,
extraPageCount,
})
);
const isExtraPagesEnabled = useFeature('extraPages');
const pageCount = pages?.length;
if (!pageCount || !isExtraPagesEnabled) {
return null;
}
const pagesToShow = getPagesToShow({
isPrevious,
hasExtraPages,
currentPageIndex,
extraPageCount,
pageCount,
});
if (pagesToShow.length === 0) {
return null;
}
const listWidth = pagesToShow.length * extraPageWidth;
const extraPageHeight = (extraPageWidth - GAP) / PAGE_RATIO;

const clickPage = (pageId) => () => setCurrentPage({ pageId });

return (
<ExtraPageWrapper isPrevious={isPrevious}>
<ExtraPageList
isPrevious={isPrevious}
listWidth={listWidth}
extraPageHeight={extraPageHeight}
>
{pagesToShow.map((pageNum) => (
<ExtraPage key={pageNum} extraPageWidth={extraPageWidth}>
<ExtraPagePreview
page={pages[pageNum]}
onClick={clickPage(pages[pageNum].id)}
aria-label={sprintf(
/* translators: %s: page number. */
__('Go to page %s', 'web-stories'),
pageNum + 1
)}
width={extraPageWidth - GAP}
height={extraPageHeight}
/>
</ExtraPage>
))}
</ExtraPageList>
</ExtraPageWrapper>
);
}

ExtraPages.propTypes = {
isPrevious: PropTypes.bool,
};

export default memo(ExtraPages);
16 changes: 15 additions & 1 deletion packages/story-editor/src/components/canvas/framesLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ import {
useRightClickMenu,
} from '../../app';
import useCanvasKeys from '../../app/canvas/useCanvasKeys';
import { Layer, NavNextArea, NavPrevArea, PageArea } from './layout';
import {
Layer,
NavNextArea,
NavPrevArea,
PageArea,
PageBeforeArea,
PageAfterArea,
} from './layout';
import ExtraPages from './extraPages';
import FrameElement from './frameElement';
import Selection from './selection';
import PageNav from './pagenav';
Expand Down Expand Up @@ -128,6 +136,12 @@ function FramesNavAndSelection({ children }) {
aria-label={__('Frames layer', 'web-stories')}
>
{children}
<PageBeforeArea>
<ExtraPages isPrevious />
</PageBeforeArea>
<PageAfterArea>
<ExtraPages />
</PageAfterArea>
<NavPrevArea>
<PageNav isNext={false} />
</NavPrevArea>
Expand Down
24 changes: 14 additions & 10 deletions packages/story-editor/src/components/canvas/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ const LayerGrid = styled.section`
/*
. = empty space
h = header
p = previous page
n = next page
b = back navigation
f = forward navigation
p = canvas page
c = canvas page
m = page action menu
w = workspace footer
t = canvas page title
Expand All @@ -84,7 +86,7 @@ const LayerGrid = styled.section`
grid:
'h h h h h h h' ${HEADER_HEIGHT}px
'. . . t . . .' minmax(16px, 1fr)
'. b . p . f .' var(--viewport-height-px)
'p b . c . f n' var(--viewport-height-px)
'. . . . . . .' 1fr
'w w w w w w w' ${({ footerHeight }) => footerHeight}px
'. . . . . . .' ${FOOTER_BOTTOM_MARGIN}px
Expand Down Expand Up @@ -113,7 +115,7 @@ const Area = styled.div`
// Page area is not `overflow:hidden` by default to allow different clipping
// mechanisms.
const PageAreaContainer = styled(Area).attrs({
area: 'p',
area: 'c',
})`
position: relative;
display: flex;
Expand Down Expand Up @@ -259,16 +261,16 @@ const NavArea = styled(Area)`
justify-content: center;
`;

const NavPrevArea = styled(NavArea).attrs({
area: 'b',
})``;
const NavPrevArea = styled(NavArea).attrs({ area: 'b' })``;

const NavNextArea = styled(NavArea).attrs({
area: 'f',
})``;
const NavNextArea = styled(NavArea).attrs({ area: 'f' })``;

const PageBeforeArea = styled(Area).attrs({ area: 'p' })``;

const PageAfterArea = styled(Area).attrs({ area: 'n' })``;

const PageMenuArea = styled.div`
grid-area: p;
grid-area: c;
position: absolute;
right: calc(-24px + var(--page-padding-px));
top: calc(0.5 * var(--page-padding-px));
Expand Down Expand Up @@ -484,6 +486,8 @@ PageArea.propTypes = {
export {
Layer,
PageArea,
PageBeforeArea,
PageAfterArea,
PageTitleContainer as PageTitleArea,
HeadArea,
MenuArea,
Expand Down