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

[DEV-13254] Implement settings menu for variables with optional fallback #561

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
63bb867
Add fallback property to VariableNode
kudlajz Aug 5, 2024
7d90a73
Implement light version of the dropdown
kudlajz Aug 6, 2024
2952877
Add withFallback property to Variable type and export it
kudlajz Aug 6, 2024
5ef1a6c
Make fallback property optional
kudlajz Aug 6, 2024
2a355d6
Implement settings menu for variables
kudlajz Aug 6, 2024
327d11b
Fix prettier issues
kudlajz Aug 6, 2024
3534db1
Close menu after saving
kudlajz Aug 6, 2024
8998d91
Improve the dropdown light variant
kudlajz Aug 6, 2024
41f97ca
Change menu title
kudlajz Aug 6, 2024
bb66fba
Remove element.fallback from effect dependencies
kudlajz Aug 6, 2024
5d1de0c
Attach both Slate's ref as well as ours
kudlajz Aug 6, 2024
bdc740a
Do not allow closing settings menu and only show it if only variable …
kudlajz Aug 6, 2024
b3464b3
Fixed prettier issues
kudlajz Aug 6, 2024
82ed4a2
Use Popper options context to properly position the settings menu
kudlajz Aug 6, 2024
69a7280
Only show the dropdown if there's more than one option
kudlajz Aug 6, 2024
b96db7c
Add tooltip to Fallback section with explanation
kudlajz Aug 6, 2024
8110dfb
Extract variable menu to its own component
kudlajz Aug 6, 2024
80f657f
Add border around the dropdown
kudlajz Aug 7, 2024
db71925
Do not nullify fallback otherwise Slate removes it
kudlajz Aug 7, 2024
087ff82
Add empty fallback property to new variable nodes
kudlajz Aug 7, 2024
0683089
Allow closing the variable menu
kudlajz Aug 7, 2024
d7db464
Select the variable upon inserting
kudlajz Aug 7, 2024
825edab
Fixed prettier issues
kudlajz Aug 7, 2024
df1a3fd
Fixed tests
kudlajz Aug 7, 2024
7b404d2
Make the parameter optional, default to true
kudlajz Aug 7, 2024
fa220f9
Update packages/slate-editor/src/extensions/variables/VariableElement…
kudlajz Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function getMenuPopperModifiers(
enabled: true,
options: {
padding: 19,
...modifiers?.arrow,
},
} satisfies Partial<ArrowModifier>,
{
Expand Down
1 change: 1 addition & 0 deletions packages/slate-editor/src/components/EditorBlock/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { CloseButton } from './CloseButton';
export { EditorBlock } from './EditorBlock';
export { Menu } from './Menu';
export { ResizableEditorBlock } from './ResizableEditorBlock';
47 changes: 47 additions & 0 deletions packages/slate-editor/src/components/Menu/Dropdown.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,52 @@
width: 180px;
}

&.light {
.DropdownToggle {
border-radius: $border-radius-base;

&[data-headlessui-state="open"] {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}

&.enabled,
&.enabled:hover {
color: $black;
background-color: $white;
}

&.enabled .Caret {
color: $black;
}
}

.DropdownMenu {
width: 100%;
margin: 0;
background-color: $white;
border: 1px solid $grey-200;
box-shadow: none;
}

.MenuItem {
color: $black;

&.selected,
&.active {
color: $black;

&::before {
background-color: $grey-200;
}
}

&.active:not(.selected) {
color: $black;
}
}
}

&:last-child {
border-right: none;
}
Expand Down Expand Up @@ -83,6 +129,7 @@
background-color: $toolbar-dark-theme-bg;
border-radius: 0 0 $toolbar-dark-theme-border-radius $toolbar-dark-theme-border-radius;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.11), inset 0 1px 0 $toolbar-dark-theme-border-color;
z-index: 2;
}

.MenuItem {
Expand Down
3 changes: 3 additions & 0 deletions packages/slate-editor/src/components/Menu/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export namespace Dropdown {
disabled?: boolean;
renderOption?: ComponentType<{ option: Option<Value>; selected: boolean }>;
value?: Value;
variant?: 'dark' | 'light';
className?: string;
}
}
Expand All @@ -32,6 +33,7 @@ export function Dropdown<Value extends string = string>({
disabled = false,
renderOption = PlainLabel,
value,
variant = 'dark',
...props
}: Dropdown.Props<Value>): ReturnType<FunctionComponent<Dropdown.Props<Value>>> {
const RenderOption = renderOption;
Expand All @@ -50,6 +52,7 @@ export function Dropdown<Value extends string = string>({
<Listbox
className={classNames(styles.Dropdown, className, {
[styles.enabled]: !disabled,
[styles.light]: variant === 'light',
})}
as="div"
{...props}
Expand Down
5 changes: 3 additions & 2 deletions packages/slate-editor/src/components/Toolbox/Caption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import styles from './Toolbox.module.scss';

type Props = {
children: React.ReactNode;
className?: string;
withFullOpacity?: boolean;
};

export function Caption({ children, withFullOpacity = false }: Props) {
export function Caption({ children, className, withFullOpacity = false }: Props) {
return (
<span
className={classNames(styles.caption, {
className={classNames(styles.caption, className, {
[styles['caption--full-opacity']]: withFullOpacity,
})}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { Transforms } from 'slate';

import type { MentionElementType } from '../types';

export function insertMention(editor: Editor, element: MentionElementType) {
export function insertMention(
editor: Editor,
element: MentionElementType,
moveCursorAfterInsert = true,
) {
Transforms.insertNodes(editor, element);
Transforms.move(editor, { distance: 1, unit: 'offset' });

if (moveCursorAfterInsert) {
Transforms.move(editor, { distance: 1, unit: 'offset' });
}
}
4 changes: 3 additions & 1 deletion packages/slate-editor/src/extensions/mentions/useMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { MentionElementType, Option } from './types';
interface Parameters<V> {
createMentionElement: (option: Option<V>) => MentionElementType;
isEnabled?: (target: Range | null) => boolean;
moveCursorAfterInsert?: boolean;
options: Option<V>[];
trigger: string;
}
Expand All @@ -28,6 +29,7 @@ export interface Mentions<V> {
export function useMentions<V>({
createMentionElement,
isEnabled = stubTrue,
moveCursorAfterInsert = true,
options,
trigger,
}: Parameters<V>): Mentions<V> {
Expand All @@ -44,7 +46,7 @@ export function useMentions<V>({
if (target) {
Transforms.select(editor, target);
const mentionElement = createMentionElement(option);
insertMention(editor, mentionElement);
insertMention(editor, mentionElement, moveCursorAfterInsert);
setTarget(null);
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@import "styles/helpers";
@import "styles/variables";

.VariableElement {
/*
* Selection in Slate 0.50+ does not work as expected if a void block is selectable.
* It could lead to an error when Slate is resolving the node which crashes the editor.
*
* Also, it's not possible to drag a node when it's selectable.
*
* see: https://github.com/prezly/clubhouse-archive/blob/master/archive/stories/19824.md
* see: https://github.com/prezly/clubhouse-archive/blob/master/archive/stories/20456.md
*/
user-select: none;

padding: 0 2px;
background-color: $legacy-alert-info-bg;
border: 1px solid $legacy-alert-info-border;
border-radius: 3px;
color: darken($legacy-alert-info-text, 5%);

&:hover {
color: #004492;
}

&.selected {
border-color: #004492;
}
}
61 changes: 61 additions & 0 deletions packages/slate-editor/src/extensions/variables/VariableElement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { isVariableNode, type VariableNode } from '@prezly/slate-types';
import classNames from 'classnames';
import React, { useEffect, useState } from 'react';
import type { RenderElementProps } from 'slate-react';
import { useSelected, useSlateStatic } from 'slate-react';

import type { Variable } from './types';
import styles from './VariableElement.module.scss';
import { VariableMenu } from './VariableMenu';

interface Props extends RenderElementProps {
element: VariableNode;
variables: Variable[];
}

export function VariableElement({ attributes, children, element, variables }: Props) {
const selected = useSelected();
const editor = useSlateStatic();

const [isMenuOpen, setMenuOpen] = useState(false);
const [container, setContainer] = useState<HTMLSpanElement | null>(null);

const variable = variables.find(({ key }) => key === element.key);
const selectedNodes = Array.from(editor.nodes({ mode: 'lowest' }));
const isOnlyVariableSelected =
selectedNodes.length === 1 && selectedNodes.every(([node]) => isVariableNode(node));

useEffect(() => {
if (selected) {
setMenuOpen(true);
}
}, [selected]);

return (
<>
{selected && isOnlyVariableSelected && container && isMenuOpen && (
<VariableMenu
container={container}
element={element}
onClose={() => setMenuOpen(false)}
variables={variables}
/>
)}
<span
{...attributes}
className={classNames(styles.VariableElement, {
[styles.selected]: selected,
})}
onClick={() => setMenuOpen(true)}
ref={(ref) => {
setContainer(ref);
attributes.ref(ref);
}}
>
{variable?.text}
{element.fallback && ` or "${element.fallback}"`}
{children}
</span>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import "styles/helpers";
@import "styles/variables";

.Dropdown {
width: 100% !important;
}

.Caption {
display: flex;
align-items: center;
justify-content: space-between;

.Icon {
display: flex;
align-items: center;

> svg {
width: 16px;
height: 16px;
}
}
}
Loading
Loading