Update overlay buttons

This commit is contained in:
Enrico Ros
2024-04-25 11:36:58 -07:00
parent 861a037321
commit 7b99bd71da
6 changed files with 76 additions and 52 deletions
+6 -6
View File
@@ -1,14 +1,14 @@
import * as React from 'react';
import type { SxProps } from '@mui/joy/styles/types';
import { Box, Button, IconButton, Tooltip, Typography } from '@mui/joy';
import { Box, Button, Tooltip, Typography } from '@mui/joy';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import WebIcon from '@mui/icons-material/Web';
import { copyToClipboard } from '~/common/util/clipboardUtils';
import type { HtmlBlock } from './blocks';
import { overlayButtonsSx } from './code/RenderCode';
import { OverlayButton, overlayButtonsSx } from './code/RenderCode';
// this is used by the blocks parser (for full text detection) and by the Code component (for inline rendering)
@@ -99,14 +99,14 @@ export function RenderHtml(props: { htmlBlock: HtmlBlock, sx?: SxProps }) {
{/* External HTML Buttons */}
<Box className='overlay-buttons' sx={{ ...overlayButtonsSx, p: 1.5 }}>
<Tooltip title={showHTML ? 'Hide' : 'Show Web Page'} variant='solid'>
<IconButton variant={showHTML ? 'solid' : 'outlined'} color='danger' onClick={() => setShowHTML(!showHTML)}>
<OverlayButton variant={showHTML ? 'solid' : 'outlined'} color='danger' onClick={() => setShowHTML(!showHTML)}>
<WebIcon />
</IconButton>
</OverlayButton>
</Tooltip>
<Tooltip title='Copy Code' variant='solid'>
<IconButton variant='outlined' onClick={handleCopyToClipboard}>
<OverlayButton variant='outlined' onClick={handleCopyToClipboard}>
<ContentCopyIcon />
</IconButton>
</OverlayButton>
</Tooltip>
</Box>
+7 -7
View File
@@ -12,7 +12,7 @@ import { GoodTooltip } from '~/common/components/GoodTooltip';
import { Link } from '~/common/components/Link';
import type { ImageBlock } from './blocks';
import { overlayButtonsSx } from './code/RenderCode';
import { OverlayButton, overlayButtonsSx } from './code/RenderCode';
const mdImageReferenceRegex = /^!\[([^\]]*)]\(([^)]+)\)$/;
@@ -128,24 +128,24 @@ export const RenderImage = (props: {
<Box className='overlay-buttons' sx={{ ...overlayButtonsSx, pt: 0.5, px: 0.5, gap: 0.5 }}>
{!!props.onRunAgain && (
<GoodTooltip title='Draw again'>
<IconButton variant='outlined' onClick={props.onRunAgain}>
<OverlayButton variant='outlined' onClick={props.onRunAgain}>
<ReplayIcon />
</IconButton>
</OverlayButton>
</GoodTooltip>
)}
{!!alt && (
<GoodTooltip title={infoOpen ? 'Hide Prompt' : 'Show Prompt'}>
<IconButton variant={infoOpen ? 'solid' : 'soft'} onClick={() => setInfoOpen(open => !open)}>
<OverlayButton variant={infoOpen ? 'solid' : 'outlined'} onClick={() => setInfoOpen(open => !open)}>
<InfoOutlinedIcon />
</IconButton>
</OverlayButton>
</GoodTooltip>
)}
<GoodTooltip title='Open in new tab'>
<IconButton variant='soft' component={Link} href={url} download={alt || 'image'} target='_blank'>
<OverlayButton variant='outlined' component={Link} href={url} download={alt || 'image'} target='_blank'>
<OpenInNewIcon />
</IconButton>
</OverlayButton>
</GoodTooltip>
</Box>
</Sheet>
+5 -3
View File
@@ -1,11 +1,13 @@
import * as React from 'react';
import { IconButton, Tooltip } from '@mui/joy';
import { Tooltip } from '@mui/joy';
import { Brand } from '~/common/app.config';
import { CodePenIcon } from '~/common/components/icons/3rdparty/CodePenIcon';
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
import { OverlayButton } from './RenderCode';
// CodePen is a web-based HTML, CSS, and JavaScript code editor
const _languages = ['html', 'css', 'javascript', 'json', 'typescript'];
@@ -48,9 +50,9 @@ const handleOpenInCodePen = (code: string, language: string) => {
export function ButtonCodePen(props: { code: string, language: string }): React.JSX.Element {
return (
<Tooltip title='Open in CodePen' variant='solid'>
<IconButton variant='outlined' color='neutral' onClick={() => handleOpenInCodePen(props.code, props.language)}>
<OverlayButton variant='outlined' onClick={() => handleOpenInCodePen(props.code, props.language)}>
<CodePenIcon />
</IconButton>
</OverlayButton>
</Tooltip>
);
}
+5 -3
View File
@@ -1,6 +1,8 @@
import * as React from 'react';
import { IconButton, Tooltip } from '@mui/joy';
import { Tooltip } from '@mui/joy';
import { OverlayButton } from './RenderCode';
// JSFiidle is a web-based HTML, CSS, and JavaScript code editor
@@ -49,9 +51,9 @@ const handleOpenInJsFiddle = (code: string, language: string) => {
export function ButtonJsFiddle(props: { code: string, language: string }): React.JSX.Element {
return (
<Tooltip title='Open in JSFiddle' variant='solid'>
<IconButton onClick={() => handleOpenInJsFiddle(props.code, props.language)}>
<OverlayButton variant='outlined' onClick={() => handleOpenInJsFiddle(props.code, props.language)}>
JS
</IconButton>
</OverlayButton>
</Tooltip>
);
}
+5 -3
View File
@@ -1,11 +1,13 @@
import * as React from 'react';
import { IconButton, Tooltip } from '@mui/joy';
import { Tooltip } from '@mui/joy';
import { Brand } from '~/common/app.config';
import { StackBlitzIcon } from '~/common/components/icons/3rdparty/StackBlitzIcon';
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
import { OverlayButton } from './RenderCode';
const _languages = [
'typescript',
@@ -77,9 +79,9 @@ const handleOpenInStackBlitz = (code: string, language: string, title?: string)
export function ButtonStackBlitz(props: { code: string, language: string, title?: string }): React.JSX.Element {
return (
<Tooltip title='Open in StackBlitz' variant='solid'>
<IconButton variant='outlined' color='neutral' onClick={() => handleOpenInStackBlitz(props.code, props.language, props.title)}>
<OverlayButton variant='outlined' onClick={() => handleOpenInStackBlitz(props.code, props.language, props.title)}>
<StackBlitzIcon />
</IconButton>
</OverlayButton>
</Tooltip>
);
}
+48 -30
View File
@@ -2,7 +2,7 @@ import * as React from 'react';
import { useQuery } from '@tanstack/react-query';
import type { SxProps } from '@mui/joy/styles/types';
import { Box, ButtonGroup, IconButton, Sheet, Tooltip, Typography } from '@mui/joy';
import { Box, ButtonGroup, IconButton, Sheet, styled, Tooltip, Typography } from '@mui/joy';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import FitScreenIcon from '@mui/icons-material/FitScreen';
import HtmlIcon from '@mui/icons-material/Html';
@@ -19,11 +19,13 @@ import { ButtonStackBlitz, isStackBlitzSupported } from './ButtonStackBlitz';
import { heuristicIsHtml, IFrameComponent } from '../RenderHtml';
import { patchSvgString, RenderCodeMermaid } from './RenderCodeMermaid';
export function getPlantUmlServerUrl(): string {
// set at nextjs build time
return process.env.NEXT_PUBLIC_PLANTUML_SERVER_URL || 'https://www.plantuml.com/plantuml/svg/';
}
async function fetchPlantUmlSvg(plantUmlCode: string): Promise<string | null> {
// Get the PlantUML server from inline env var
let plantUmlServerUrl = getPlantUmlServerUrl();
@@ -64,13 +66,19 @@ async function fetchPlantUmlSvg(plantUmlCode: string): Promise<string | null> {
}
export const OverlayButton = styled(IconButton)(({ theme, variant }) => ({
backgroundColor: variant === 'outlined' ? theme.palette.background.surface : undefined,
'--Icon-fontSize': theme.fontSize.lg,
})) as typeof IconButton;
export const overlayButtonsSx: SxProps = {
position: 'absolute', top: 0, right: 0, zIndex: 2, /* top of message and its chips */
display: 'flex', flexDirection: 'row', gap: 1,
opacity: 0, transition: 'opacity 0.2s cubic-bezier(.17,.84,.44,1)',
// buttongroup: background
'& > div > button': {
backgroundColor: 'background.surface',
// backgroundColor: 'background.surface',
// backdropFilter: 'blur(12px)',
},
};
@@ -206,53 +214,63 @@ function RenderCodeImpl(props: RenderCodeImplProps) {
{/* Buttons */}
<Box className='overlay-buttons' sx={{ ...overlayButtonsSx, p: 0.5 }}>
{/* Show HTML */}
{isHTML && (
<Tooltip title={optimizeLightweight ? null : renderHTML ? 'Hide' : 'Show Web Page'}>
<IconButton variant={renderHTML ? 'solid' : 'soft'} color='danger' onClick={() => setShowHTML(!showHTML)}>
<HtmlIcon />
</IconButton>
</Tooltip>
)}
{isMermaid && (
<Tooltip title={optimizeLightweight ? null : renderMermaid ? 'Show Code' : 'Render Mermaid'}>
<IconButton variant={renderMermaid ? 'solid' : 'soft'} onClick={() => setShowMermaid(!showMermaid)}>
<SchemaIcon />
</IconButton>
</Tooltip>
)}
{isPlantUML && (
<Tooltip title={optimizeLightweight ? null : renderPlantUML ? 'Show Code' : 'Render PlantUML'}>
<IconButton variant={renderPlantUML ? 'solid' : 'soft'} onClick={() => setShowPlantUML(!showPlantUML)}>
<SchemaIcon />
</IconButton>
<OverlayButton variant={renderHTML ? 'solid' : 'outlined'} color='danger' onClick={() => setShowHTML(!showHTML)}>
<HtmlIcon sx={{ fontSize: 'xl2' }} />
</OverlayButton>
</Tooltip>
)}
{/* Show SVG */}
{isSVG && (
<Tooltip title={optimizeLightweight ? null : renderSVG ? 'Show Code' : 'Render SVG'}>
<IconButton variant={renderSVG ? 'solid' : 'soft'} onClick={() => setShowSVG(!showSVG)}>
<OverlayButton variant={renderSVG ? 'solid' : 'outlined'} onClick={() => setShowSVG(!showSVG)}>
<ShapeLineOutlinedIcon />
</IconButton>
</OverlayButton>
</Tooltip>
)}
{((isMermaid && showMermaid) || (isPlantUML && showPlantUML && !plantUmlError) || (isSVG && showSVG && canScaleSVG)) && (
<Tooltip title={optimizeLightweight ? null : fitScreen ? 'Original Size' : 'Fit Screen'}>
<IconButton variant={fitScreen ? 'solid' : 'soft'} onClick={() => setFitScreen(on => !on)}>
<FitScreenIcon />
</IconButton>
</Tooltip>
{/* Show Diagrams */}
{(isMermaid || isPlantUML) && (
<ButtonGroup aria-label='Diagram'>
{/* Toggle rendering */}
<Tooltip title={optimizeLightweight ? null : (renderMermaid || renderPlantUML) ? 'Show Code' : 'Render Mermaid'}>
<OverlayButton variant={(renderMermaid || renderPlantUML) ? 'solid' : 'outlined'} onClick={() => {
if (isMermaid) setShowMermaid(on => !on);
if (isPlantUML) setShowPlantUML(on => !on);
}}>
<SchemaIcon />
</OverlayButton>
</Tooltip>
{/* Fit-To-Screen */}
{((isMermaid && showMermaid) || (isPlantUML && showPlantUML && !plantUmlError) || (isSVG && showSVG && canScaleSVG)) && (
<Tooltip title={optimizeLightweight ? null : fitScreen ? 'Original Size' : 'Fit Screen'}>
<OverlayButton variant={fitScreen ? 'solid' : 'outlined'} onClick={() => setFitScreen(on => !on)}>
<FitScreenIcon />
</OverlayButton>
</Tooltip>
)}
</ButtonGroup>
)}
{/* New Code Window Buttons */}
{(canJSFiddle || canCodePen || canStackBlitz) && (
<ButtonGroup aria-label='Open code in external editors' sx={{ cornerRadius: 'md' }}>
<ButtonGroup aria-label='Open code in external editors'>
{canJSFiddle && <ButtonJsFiddle code={blockCode} language={inferredCodeLanguage!} />}
{canCodePen && <ButtonCodePen code={blockCode} language={inferredCodeLanguage!} />}
{canStackBlitz && <ButtonStackBlitz code={blockCode} title={blockTitle} language={inferredCodeLanguage!} />}
</ButtonGroup>
)}
{/* Copy */}
{props.noCopyButton !== true && (
<Tooltip title={optimizeLightweight ? null : 'Copy Code'}>
<IconButton variant='soft' onClick={handleCopyToClipboard}>
<OverlayButton variant='outlined' onClick={handleCopyToClipboard}>
<ContentCopyIcon />
</IconButton>
</OverlayButton>
</Tooltip>
)}
</Box>