@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
232 lines (230 loc) • 7.59 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { FormattedMessage } from 'react-intl';
import { isBlank } from '../../utils/string';
import React, { useEffect, useState } from 'react';
import { makeStyles } from 'tss-react/mui';
import { useDispatch } from 'react-redux';
import IconButton from '@mui/material/IconButton';
import RefreshRounded from '@mui/icons-material/RefreshRounded';
import Paper from '@mui/material/Paper';
import ItemDisplay from '../ItemDisplay';
import PagesSearchAhead from '../PagesSearchAhead';
import SingleItemSelector from '../SingleItemSelector';
import MoreRounded from '@mui/icons-material/MoreVertRounded';
import { getOffsetLeft, getOffsetTop } from '@mui/material/Popover';
import { withIndex } from '../../utils/path';
import { showItemMegaMenu } from '../../state/actions/dialogs';
import { getNumOfMenuOptionsForItem } from '../../utils/content';
import Tooltip from '@mui/material/Tooltip';
import { changeCurrentUrl, reloadRequest } from '../../state/actions/preview';
import { getHostToGuestBus } from '../../utils/subjects';
import PreviewBackButton from '../PreviewBackButton';
import PreviewForwardButton from '../PreviewForwardButton';
import { usePreviewNavigation } from '../../hooks/usePreviewNavigation';
const useAddressBarStyles = makeStyles()((theme) => ({
toolbar: {
placeContent: 'center space-between'
},
addressBarInput: {
width: 300,
padding: '2px 2px 2px 10px',
display: 'flex',
alignItems: 'center',
backgroundColor: theme.palette.background.default
},
addressBarInputFocused: {
border: `2px solid ${theme.palette.primary.main}`,
backgroundColor: theme.palette.background.paper
},
inputContainer: {
marginLeft: theme.spacing(1)
},
input: {
border: 'none',
background: 'transparent',
'&:focus:invalid, &:focus': {
border: 'none',
boxShadow: 'none'
}
},
divider: {
height: 28,
margin: 4
},
selectorPopoverRoot: {
width: 400,
marginLeft: '4px'
},
hidden: {
visibility: 'hidden'
},
itemActionSkeleton: {
width: 40,
margin: '0 5px'
},
itemDisplayWrapper: {
width: '100%',
overflow: 'hidden',
cursor: 'pointer',
display: 'flex'
},
itemPreviewUrl: {
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
marginLeft: '4px'
},
itemDisplaySkeleton: {
marginLeft: '5px',
width: '100%'
}
}));
export function PreviewAddressBar(props) {
const { classes, cx } = useAddressBarStyles();
const { site = '', item } = props;
const noSiteSet = isBlank(site);
const { currentUrlPath = '' } = usePreviewNavigation();
const [internalUrl, setInternalUrl] = useState(currentUrlPath);
const [openSelector, setOpenSelector] = useState(false);
const [focus, setFocus] = useState(false);
const disabled = noSiteSet || !item;
const dispatch = useDispatch();
const onOptions = (e) => {
const anchorRect = e.currentTarget.getBoundingClientRect();
const top = anchorRect.top + getOffsetTop(anchorRect, 'top');
const left = anchorRect.left + getOffsetLeft(anchorRect, 'left');
let path = item.path;
if (path === '/site/website') {
path = withIndex(item.path);
}
dispatch(
showItemMegaMenu({
path: path,
anchorReference: 'anchorPosition',
anchorPosition: { top, left },
loaderItems: getNumOfMenuOptionsForItem(item)
})
);
};
const onUrlChange = (url) => {
dispatch(changeCurrentUrl(url));
};
const onRefresh = () => {
getHostToGuestBus().next({ type: reloadRequest.type });
};
useEffect(() => {
currentUrlPath && setInternalUrl(currentUrlPath);
}, [currentUrlPath]);
return React.createElement(
React.Fragment,
null,
React.createElement(PreviewBackButton, null),
React.createElement(PreviewForwardButton, null),
React.createElement(
Tooltip,
{
title: React.createElement(FormattedMessage, {
id: 'previewAddressBar.reloadButtonLabel',
defaultMessage: 'Reload this page'
})
},
React.createElement(
'span',
null,
React.createElement(
IconButton,
{ onClick: onRefresh, size: 'large', disabled: disabled },
React.createElement(RefreshRounded, null)
)
)
),
React.createElement(
Paper,
{
variant: focus ? 'elevation' : 'outlined',
elevation: focus ? 2 : 0,
className: cx(classes.addressBarInput, focus && classes.addressBarInputFocused)
},
!focus &&
item &&
React.createElement(
'div',
{ className: classes.itemDisplayWrapper, onClick: () => setFocus(true) },
React.createElement(ItemDisplay, {
item: item,
styles: { root: { maxWidth: '100%' } },
showNavigableAsLinks: false
})
),
(focus || !item) &&
React.createElement(PagesSearchAhead, {
autoFocus: focus,
value: internalUrl,
placeholder: noSiteSet ? '' : '/',
onEnter: onUrlChange,
classes: { input: classes.input },
onFocus: () => setFocus(true),
onBlur: () => setFocus(false)
}),
React.createElement(SingleItemSelector, {
rootPath: '/site/website/index.xml',
selectedItem: item,
open: openSelector,
onClose: () => setOpenSelector(false),
onDropdownClick: () => setOpenSelector(!openSelector),
onItemClicked: (item) => {
setOpenSelector(false);
setInternalUrl(item.previewUrl);
onUrlChange(item.previewUrl);
},
hideUI: true,
classes: {
popoverRoot: classes.selectorPopoverRoot
}
})
),
React.createElement(
Tooltip,
{
title: Boolean(item)
? React.createElement(FormattedMessage, { id: 'words.options', defaultMessage: 'Options' })
: ''
},
React.createElement(
IconButton,
{ onClick: onOptions, disabled: !item, size: 'large', id: 'previewAddressBarActionsMenuButton' },
React.createElement(MoreRounded, null)
)
)
);
}
export default PreviewAddressBar;