Skip to content

Commit

Permalink
Merge pull request #9586 from hicommonwealth/burton/editor-v4-with-po…
Browse files Browse the repository at this point in the history
…pover

Handle mobile toolbar activation / focus on mobile
  • Loading branch information
burtonator authored Oct 23, 2024
2 parents ceb5d76 + 63c3aab commit f9b0308
Show file tree
Hide file tree
Showing 37 changed files with 854 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ body,
}

.mdxeditor-container-disabled {
background-color: $neutral-50;
cursor: not-allowed !important;

.DesktopEditorFooter {
background-color: $neutral-50;
}

* {
pointer-events: none;
}

.mdxeditor-root-contenteditable {
// TODO: disable editing here...
}
Expand All @@ -51,7 +62,7 @@ body,
border-top: 1px solid $neutral-200;

min-height: 150px;
max-height: 450px;
//max-height: 450px;
}

.mdxeditor-toolbar {
Expand Down Expand Up @@ -93,6 +104,13 @@ body,
font-size: inherit !important;
}

div[data-editor-block-type='image'] {
// this selects and hides the settings button
button:nth-of-type(2) {
display: none;
}
}

button {
outline: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import React, {
useRef,
useState,
} from 'react';
import { TooltipIndicator } from 'views/components/MarkdownEditor/indicators/TooltipIndicator';
import { MarkdownEditorModeContext } from 'views/components/MarkdownEditor/MarkdownEditorModeContext';
import { useDeviceProfile } from 'views/components/MarkdownEditor/useDeviceProfile';
import { MarkdownEditorMethods } from 'views/components/MarkdownEditor/useMarkdownEditorMethods';
Expand Down Expand Up @@ -82,19 +83,28 @@ export type MarkdownEditorProps = Readonly<{
placeholder?: string;
imageHandler?: ImageHandler;
SubmitButton?: () => ReactNode;
tooltip?: ReactNode;
tooltip?: string;
onMarkdownEditorMethods?: (methods: MarkdownEditorMethods) => void;
onChange?: (markdown: MarkdownStr) => void;
autoFocus?: boolean;
}>;

export const MarkdownEditor = memo(function MarkdownEditor(
props: MarkdownEditorProps,
) {
const { SubmitButton, onMarkdownEditorMethods, disabled, onChange } = props;
const {
SubmitButton,
onMarkdownEditorMethods,
disabled,
onChange,
autoFocus,
tooltip,
} = props;
const errorHandler = useMarkdownEditorErrorHandler();
const [dragging, setDragging] = useState(false);
const [uploading, setUploading] = useState(false);
const [active, setActive] = useState(false);
const [hovering, setHovering] = useState(false);

const dragCounterRef = useRef(0);

Expand Down Expand Up @@ -274,11 +284,19 @@ export const MarkdownEditor = memo(function MarkdownEditor(
const doFocus = useCallback(() => {
if (mdxEditorRef.current) {
mdxEditorRef.current.focus();
} else {
console.warn('No markdown editor ref');
}
}, []);

const handleRef = useCallback(
(methods: MDXEditorMethods) => {
if (methods && mdxEditorRef.current === null) {
// on startup, the mdx editor places the cursor at the END of the
// document when loading content but this doesn't make a ton of sense.
methods.focus(undefined, { defaultSelection: 'rootStart' });
}

mdxEditorRef.current = methods;
onMarkdownEditorMethods?.(methods);
},
Expand All @@ -291,6 +309,17 @@ export const MarkdownEditor = memo(function MarkdownEditor(
};
}, []);

const handleKeyDown = useCallback(
(event: React.KeyboardEvent) => {
if (disabled) {
// needed to prevent the user from typing into the contenteditable
event.preventDefault();
event.stopPropagation();
}
},
[disabled],
);

return (
<MarkdownEditorModeContext.Provider value={mode}>
<MarkdownEditorContext.Provider value={mdxEditorMethods}>
Expand All @@ -304,13 +333,17 @@ export const MarkdownEditor = memo(function MarkdownEditor(
active ? 'mdxeditor-container-active' : null,
disabled ? 'mdxeditor-container-disabled' : null,
)}
onKeyDownCapture={handleKeyDown}
onDrop={handleDrop}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onPaste={(event) => handlePaste(event)}
onFocus={() => setActive(true)}
onBlur={() => setActive(false)}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
autoFocus={autoFocus}
>
<MDXEditor
onError={errorHandler}
Expand All @@ -327,10 +360,12 @@ export const MarkdownEditor = memo(function MarkdownEditor(
mode === 'mobile' ? (
<ToolbarForMobile
SubmitButton={SubmitButton}
onImage={imageUploadHandlerWithMarkdownInsertion}
focus={doFocus}
/>
) : (
<ToolbarForDesktop
focus={doFocus}
onImage={imageUploadHandlerWithMarkdownInsertion}
/>
),
Expand Down Expand Up @@ -364,6 +399,9 @@ export const MarkdownEditor = memo(function MarkdownEditor(
/>
)}

{disabled && hovering && tooltip && (
<TooltipIndicator label={tooltip} />
)}
{dragging && <DragIndicator />}
{uploading && <UploadIndicator />}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
.mdxeditor-container-mode-desktop .MarkdownSubmitButton button {
.MarkdownSubmitButton {
border: none;
outline: none;
cursor: pointer;
}

.mdxeditor-container-mode-desktop .MarkdownSubmitButton {
font-size: 1.2em;
padding: 8px 16px;
cursor: pointer;
border-radius: 8px;
display: inline-block;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import clsx from 'clsx';
import React from 'react';
import { useMarkdownEditorMode } from 'views/components/MarkdownEditor/useMarkdownEditorMode';
import './MarkdownSubmitButton.scss';

export type MarkdownSubmitButtonProps = Readonly<{
label: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import '../../../../../styles/shared';

.TooltipIndicator {
// cover the parent
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;

// needed so that drag operations don't get hijacked by the indicator which
// would prevent dropping images and markdown files into the editor.
pointer-events: none;

// the z-index is needed because the 'select' in MDXEditor has its own z-index
// which we have to sit on top of.
z-index: 10;

// needed so that the .inner progress indicator can be centered
display: flex;

.inner {
// center the contnts
margin: auto auto;

font-size: 12px;
letter-spacing: 0.24px;
border-radius: 4px;
background-color: $neutral-900;
padding: 4px 8px;
color: $white;
text-align: center;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import './TooltipIndicator.scss';

type TooltipIndicatorProps = Readonly<{
label: string;
}>;

export const TooltipIndicator = (props: TooltipIndicatorProps) => {
const { label } = props;
return (
<div className="TooltipIndicator">
<div className="inner">{label}</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.BlockSelectorButton {
button {
outline: none;
border: none;
cursor: pointer;
}
.DropdownIndicator {
display: flex;
gap: 2px;
}
}

.FormattingPopover {
padding: 4px;

margin-bottom: 8px;
margin-top: 8px;

button {
outline: none;
border: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import ClickAwayListener from '@mui/base/ClickAwayListener';
import { currentBlockType$, useCellValue } from 'commonwealth-mdxeditor';
import React, { useCallback } from 'react';
import { HeadingButton } from 'views/components/MarkdownEditor/toolbars/HeadingButton';
import { PlaceholderIcon } from 'views/components/MarkdownEditor/toolbars/PlaceholderIcon';
import { blockTypeToIconName } from 'views/components/MarkdownEditor/toolbars/blockTypeToIconName';
import { CWIcon } from 'views/components/component_kit/cw_icons/cw_icon';
import CWPopover, {
usePopover,
} from 'views/components/component_kit/new_designs/CWPopover';
import './BlockSelectorButton.scss';

type BlockSelectorButtonProps = Readonly<{
focus: () => void;
}>;

export const BlockSelectorButton = (props: BlockSelectorButtonProps) => {
const { focus } = props;

const popoverProps = usePopover();

const currentBlockType = useCellValue(currentBlockType$);

const handleClick = useCallback(
(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
popoverProps.handleInteraction(event);
focus();
},
[focus, popoverProps],
);

const handleFormatButtonClick = useCallback(
(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
popoverProps.handleInteraction(event);
},
[popoverProps],
);

const handleClickAway = useCallback(() => {
popoverProps.dispose();
}, [popoverProps]);

const iconName = blockTypeToIconName(currentBlockType);

return (
<ClickAwayListener onClickAway={handleClickAway}>
<div className="BlockSelectorButton">
<button onClick={handleClick}>
<div className="DropdownIndicator">
{iconName && <CWIcon iconName={iconName} />}
{!iconName && <PlaceholderIcon />}
<CWIcon iconName="caretDown" iconSize="xs" />
</div>
</button>

<CWPopover
className="FormattingPopover"
body={
<div onMouseLeave={popoverProps.handleInteraction}>
<HeadingButton blockType="p" onClick={handleFormatButtonClick} />
<HeadingButton blockType="h1" onClick={handleFormatButtonClick} />
<HeadingButton blockType="h2" onClick={handleFormatButtonClick} />
<HeadingButton blockType="h3" onClick={handleFormatButtonClick} />
<HeadingButton
blockType="quote"
onClick={handleFormatButtonClick}
/>
</div>
}
{...popoverProps}
/>
</div>
</ClickAwayListener>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { openLinkEditDialog$, usePublisher } from 'commonwealth-mdxeditor';
import React, { useCallback } from 'react';
import CWIconButton from 'views/components/component_kit/new_designs/CWIconButton';
import { CWTooltip } from 'views/components/component_kit/new_designs/CWTooltip';
import './HeadingButton.scss';

export type CWCreateLinkButtonProps = Readonly<{
onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
}>;

export const CreateLinkButton = (props: CWCreateLinkButtonProps) => {
const { onClick } = props;
const openLinkDialog = usePublisher(openLinkEditDialog$);

const handleClick = useCallback(
(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
openLinkDialog();
onClick?.(event);
},
[onClick, openLinkDialog],
);

return (
<CWTooltip
content="Create link"
renderTrigger={(handleInteraction) => (
<CWIconButton
buttonSize="lg"
iconName="linkPhosphor"
onMouseEnter={handleInteraction}
onMouseLeave={handleInteraction}
onClick={handleClick}
/>
)}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

.FilePickerInput {
visibility: hidden;
width: 0;
height: 0;
width: 1px;
height: 1px;
}

.FilePickerButton {
Expand Down
Loading

0 comments on commit f9b0308

Please sign in to comment.