mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-13 02:30:29 +03:00
Room input improvements (#1502)
* prevent context menu when editing message * send sticker body (#1479) * update emojiboard search text reaction input label * stop generating upload image thumbnail (#1475) * maintain upload order * Fix message options spinner variant * add markdown toggle in editor toolbar * fix heading toggle icon update with cursor move * add hotkeys for heading * change editor markdown btn style * use Ctrl + Enter to send message (#1470) * fix reaction tooltip word-break * add shift in editor hokeys with number * stop parsing markdown in link
This commit is contained in:
parent
c7e5c1fce8
commit
2957a45c4b
13 changed files with 162 additions and 71 deletions
|
|
@ -66,3 +66,7 @@ export const EditorToolbarBase = style({
|
|||
export const EditorToolbar = style({
|
||||
padding: config.space.S100,
|
||||
});
|
||||
|
||||
export const MarkdownBtnBox = style({
|
||||
paddingRight: config.space.S100,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
import React, { ReactNode, useState } from 'react';
|
||||
import { ReactEditor, useSlate } from 'slate-react';
|
||||
import {
|
||||
headingLevel,
|
||||
isAnyMarkActive,
|
||||
isBlockActive,
|
||||
isMarkActive,
|
||||
|
|
@ -31,6 +32,8 @@ import { BlockType, MarkType } from './types';
|
|||
import { HeadingLevel } from './slate';
|
||||
import { isMacOS } from '../../utils/user-agent';
|
||||
import { KeySymbol } from '../../utils/key-symbol';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
|
||||
function BtnTooltip({ text, shortCode }: { text: string; shortCode?: string }) {
|
||||
return (
|
||||
|
|
@ -115,13 +118,13 @@ export function BlockButton({ format, icon, tooltip }: BlockButtonProps) {
|
|||
|
||||
export function HeadingBlockButton() {
|
||||
const editor = useSlate();
|
||||
const [level, setLevel] = useState<HeadingLevel>(1);
|
||||
const level = headingLevel(editor);
|
||||
const [open, setOpen] = useState(false);
|
||||
const isActive = isBlockActive(editor, BlockType.Heading);
|
||||
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
||||
|
||||
const handleMenuSelect = (selectedLevel: HeadingLevel) => {
|
||||
setOpen(false);
|
||||
setLevel(selectedLevel);
|
||||
toggleBlock(editor, BlockType.Heading, { level: selectedLevel });
|
||||
ReactEditor.focus(editor);
|
||||
};
|
||||
|
|
@ -130,7 +133,6 @@ export function HeadingBlockButton() {
|
|||
<PopOut
|
||||
open={open}
|
||||
offset={5}
|
||||
align="Start"
|
||||
position="Top"
|
||||
content={
|
||||
<FocusTrap
|
||||
|
|
@ -145,15 +147,51 @@ export function HeadingBlockButton() {
|
|||
>
|
||||
<Menu style={{ padding: config.space.S100 }}>
|
||||
<Box gap="100">
|
||||
<IconButton onClick={() => handleMenuSelect(1)} size="400" radii="300">
|
||||
<Icon size="200" src={Icons.Heading1} />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleMenuSelect(2)} size="400" radii="300">
|
||||
<Icon size="200" src={Icons.Heading2} />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => handleMenuSelect(3)} size="400" radii="300">
|
||||
<Icon size="200" src={Icons.Heading3} />
|
||||
</IconButton>
|
||||
<TooltipProvider
|
||||
tooltip={<BtnTooltip text="Heading 1" shortCode={`${modKey} + Shift + 1`} />}
|
||||
delay={500}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={() => handleMenuSelect(1)}
|
||||
size="400"
|
||||
radii="300"
|
||||
>
|
||||
<Icon size="200" src={Icons.Heading1} />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
<TooltipProvider
|
||||
tooltip={<BtnTooltip text="Heading 2" shortCode={`${modKey} + Shift + 2`} />}
|
||||
delay={500}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={() => handleMenuSelect(2)}
|
||||
size="400"
|
||||
radii="300"
|
||||
>
|
||||
<Icon size="200" src={Icons.Heading2} />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
<TooltipProvider
|
||||
tooltip={<BtnTooltip text="Heading 3" shortCode={`${modKey} + Shift + 3`} />}
|
||||
delay={500}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
onClick={() => handleMenuSelect(3)}
|
||||
size="400"
|
||||
radii="300"
|
||||
>
|
||||
<Icon size="200" src={Icons.Heading3} />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
</Box>
|
||||
</Menu>
|
||||
</FocusTrap>
|
||||
|
|
@ -169,7 +207,7 @@ export function HeadingBlockButton() {
|
|||
size="400"
|
||||
radii="300"
|
||||
>
|
||||
<Icon size="200" src={Icons[`Heading${level}`]} />
|
||||
<Icon size="200" src={level ? Icons[`Heading${level}`] : Icons.Heading1} />
|
||||
<Icon size="200" src={isActive ? Icons.Cross : Icons.ChevronBottom} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
|
@ -210,8 +248,10 @@ export function ExitFormatting({ tooltip }: ExitFormattingProps) {
|
|||
export function Toolbar() {
|
||||
const editor = useSlate();
|
||||
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
||||
const disableInline = isBlockActive(editor, BlockType.CodeBlock);
|
||||
|
||||
const canEscape = isAnyMarkActive(editor) || !isBlockActive(editor, BlockType.Paragraph);
|
||||
const [isMarkdown, setIsMarkdown] = useSetting(settingsAtom, 'isMarkdown');
|
||||
|
||||
return (
|
||||
<Box className={css.EditorToolbarBase}>
|
||||
|
|
@ -271,12 +311,12 @@ export function Toolbar() {
|
|||
<BlockButton
|
||||
format={BlockType.OrderedList}
|
||||
icon={Icons.OrderList}
|
||||
tooltip={<BtnTooltip text="Ordered List" shortCode={`${modKey} + 7`} />}
|
||||
tooltip={<BtnTooltip text="Ordered List" shortCode={`${modKey} + Shift + 7`} />}
|
||||
/>
|
||||
<BlockButton
|
||||
format={BlockType.UnorderedList}
|
||||
icon={Icons.UnorderList}
|
||||
tooltip={<BtnTooltip text="Unordered List" shortCode={`${modKey} + 8`} />}
|
||||
tooltip={<BtnTooltip text="Unordered List" shortCode={`${modKey} + Shift + 8`} />}
|
||||
/>
|
||||
<HeadingBlockButton />
|
||||
</Box>
|
||||
|
|
@ -292,6 +332,28 @@ export function Toolbar() {
|
|||
</Box>
|
||||
</>
|
||||
)}
|
||||
<Box className={css.MarkdownBtnBox} shrink="No" grow="Yes" justifyContent="End">
|
||||
<TooltipProvider
|
||||
align="End"
|
||||
tooltip={<BtnTooltip text="Inline Markdown" />}
|
||||
delay={500}
|
||||
>
|
||||
{(triggerRef) => (
|
||||
<IconButton
|
||||
ref={triggerRef}
|
||||
variant="SurfaceVariant"
|
||||
onClick={() => setIsMarkdown(!isMarkdown)}
|
||||
aria-pressed={isMarkdown}
|
||||
size="300"
|
||||
radii="300"
|
||||
disabled={disableInline || !!isAnyMarkActive(editor)}
|
||||
>
|
||||
<Icon size="200" src={Icons.Markdown} filled={isMarkdown} />
|
||||
</IconButton>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
<span />
|
||||
</Box>
|
||||
</Box>
|
||||
</Scroll>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -15,12 +15,15 @@ export const INLINE_HOTKEYS: Record<string, MarkType> = {
|
|||
const INLINE_KEYS = Object.keys(INLINE_HOTKEYS);
|
||||
|
||||
export const BLOCK_HOTKEYS: Record<string, BlockType> = {
|
||||
'mod+7': BlockType.OrderedList,
|
||||
'mod+8': BlockType.UnorderedList,
|
||||
'mod+shift+7': BlockType.OrderedList,
|
||||
'mod+shift+8': BlockType.UnorderedList,
|
||||
"mod+'": BlockType.BlockQuote,
|
||||
'mod+;': BlockType.CodeBlock,
|
||||
};
|
||||
const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
|
||||
const isHeading1 = isKeyHotkey('mod+shift+1');
|
||||
const isHeading2 = isKeyHotkey('mod+shift+2');
|
||||
const isHeading3 = isKeyHotkey('mod+shift+3');
|
||||
|
||||
/**
|
||||
* @return boolean true if shortcut is toggled.
|
||||
|
|
@ -86,6 +89,18 @@ export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Elem
|
|||
return false;
|
||||
});
|
||||
if (blockToggled) return true;
|
||||
if (isHeading1(event)) {
|
||||
toggleBlock(editor, BlockType.Heading, { level: 1 });
|
||||
return true;
|
||||
}
|
||||
if (isHeading2(event)) {
|
||||
toggleBlock(editor, BlockType.Heading, { level: 2 });
|
||||
return true;
|
||||
}
|
||||
if (isHeading3(event)) {
|
||||
toggleBlock(editor, BlockType.Heading, { level: 3 });
|
||||
return true;
|
||||
}
|
||||
|
||||
const inlineToggled = isBlockActive(editor, BlockType.CodeBlock)
|
||||
? false
|
||||
|
|
|
|||
|
|
@ -52,6 +52,16 @@ export const isBlockActive = (editor: Editor, format: BlockType) => {
|
|||
return !!match;
|
||||
};
|
||||
|
||||
export const headingLevel = (editor: Editor): HeadingLevel | undefined => {
|
||||
const [nodeEntry] = Editor.nodes(editor, {
|
||||
match: (node) => Element.isElement(node) && node.type === BlockType.Heading,
|
||||
});
|
||||
const [node] = nodeEntry ?? [];
|
||||
if (!node) return undefined;
|
||||
if ('level' in node) return node.level;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
type BlockOption = { level: HeadingLevel };
|
||||
const NESTED_BLOCK = [
|
||||
BlockType.OrderedList,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue