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

Issue 1524/text with bullets #1534

Merged
merged 12 commits into from
Sep 20, 2023
Merged
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
"remark-gfm": "^3.0.1",
"remark-parse": "^10.0.1",
"remark-slate": "^1.8.6",
"slate": "^0.78.0",
"slate": "^0.94.1",
"slate-history": "^0.66.0",
"slate-react": "^0.77.4",
"slate-react": "^0.98.3",
"slugify": "^1.6.5",
"tsconfig-paths": "^4.1.2",
"unified": "^10.1.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ export default class CallerInstructionsModel extends ModelBase {
return false;
}

const lsInstructions = localStorage.getItem(this._key) || '';
const lsInstructions = localStorage.getItem(this._key)?.trim() || '';
const dataInstructions = data.instructions.trim();

return data.instructions != lsInstructions;
return dataInstructions != lsInstructions;
}

get isSaving(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/zui/ZUITextEditor/TextElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const TextElement: React.FunctionComponent<RenderElementProps> = ({
if (element.type === 'bulleted-list') {
return (
<ul style={style} {...attributes}>
{children}{' '}
{children}
</ul>
);
}
Expand Down
22 changes: 21 additions & 1 deletion src/zui/ZUITextEditor/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import isUrl from 'is-url';
import { BlockType, LeafType, NodeTypes, serialize } from 'remark-slate';
import {
Ancestor,
Descendant,
Editor,
NodeEntry,
Range,
Element as SlateElement,
Text,
Transforms,
} from 'slate';
import { BlockType, LeafType, NodeTypes, serialize } from 'remark-slate';
import isHotkey, { isKeyHotkey } from 'is-hotkey';

const LIST_TYPES = ['numbered-list', 'bulleted-list'];
Expand Down Expand Up @@ -225,6 +227,23 @@ const convertSlateToRemarked = (
return convertedChildren;
};

const shouldBeRemoved = (node: NodeEntry<Ancestor>): boolean => {
if (node && Object.prototype.hasOwnProperty.call(node[0], 'children')) {
if (
'children' in node[0].children[0] &&
Object.prototype.hasOwnProperty.call(node[0].children[0], 'children')
) {
Comment on lines +231 to +235
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unnecessarily complex to me. I realize you probably found this solution in the Slate issue tracker, but I'm curious if you know the reason for using hasOwnProperty.call(node[0]… instead of node[0]?.hasOwnProperty(…, etc?

Copy link
Contributor Author

@rebecarubio rebecarubio Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the reason I was implemented so complex it's because Ancestor and the following types are a mixed of types. The reason to use hasOwnProperty.call it's to avoid a lint error. If I use node[0]?.hasOwnProperty(…, I get the error Do not access Object.prototype method 'hasOwnProperty' from target object.

if (
node[0].children[0].children[0] &&
node[0].children[0].children[0].text === ''
) {
return true;
}
}
}
return false;
};

const slateToMarkdown = (slateArray: Descendant[]): string => {
const nodeTypes = {
block_quote: 'block-quote',
Expand Down Expand Up @@ -269,6 +288,7 @@ export {
isMarkActive,
keyDownHandler,
LIST_TYPES,
shouldBeRemoved,
slateToMarkdown,
withInlines,
unwrapLink,
Expand Down
70 changes: 62 additions & 8 deletions src/zui/ZUITextEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { makeStyles } from '@mui/styles';
import { markdownToSlate } from './utils/markdownToSlate';
import { withHistory } from 'slate-history';
import { Box, ClickAwayListener, Collapse } from '@mui/material';
import { createEditor, Descendant, Editor, Transforms } from 'slate';
import {
createEditor,
deleteBackward,
Descendant,
Editor,
Node,
Transforms,
} from 'slate';
import {
Editable,
ReactEditor,
Expand All @@ -13,15 +20,26 @@ import {
Slate,
withReact,
} from 'slate-react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';

import './types';
import { FileUpload } from 'features/files/hooks/useFileUploads';
import TextElement from './TextElement';
import theme from 'theme';
import Toolbar from './Toolbar';
import { ZetkinFileUploadChip } from 'zui/ZUIFileChip';
import { keyDownHandler, slateToMarkdown, withInlines } from './helpers';
import {
keyDownHandler,
shouldBeRemoved,
slateToMarkdown,
withInlines,
} from './helpers';

const emptySlate = [
{
Expand Down Expand Up @@ -86,15 +104,45 @@ const ZUITextEditor: React.FunctionComponent<ZUITextEditorProps> = ({
() => withInlines(withHistory(withReact(createEditor()))),
[]
);

//fixes deleting the missing bullet point in empty list in root
editor.deleteBackward = (...args) => {
deleteBackward(editor, ...args);

const bulletListNode = Editor.above(editor, {
match: (n: Node) =>
'type' in n &&
n.type === 'list-item' &&
Object.prototype.hasOwnProperty.call(n, 'children'),
});

if (bulletListNode) {
if (shouldBeRemoved(bulletListNode)) {
Transforms.setNodes(
editor,
{ type: 'paragraph' },
{
at: bulletListNode[1],
match: (n) => 'type' in n && n.type === 'list-item',
}
);
}
}
return editor;
};

const markdownValue = useRef('');
const [initialValueSlate, setInitialValueSlate] = useState<
Descendant[] | null
>(null);

useEffect(() => {
(async () => {
if (initialValue) {
const slate = await markdownToSlate(initialValue as string);
setInitialValueSlate(slate as Descendant[]);
if (initialValue !== markdownValue.current) {
const slate = await markdownToSlate(initialValue);
setInitialValueSlate(slate as Descendant[]);
}
} else {
setInitialValueSlate(emptySlate);
}
Expand Down Expand Up @@ -123,8 +171,11 @@ const ZUITextEditor: React.FunctionComponent<ZUITextEditorProps> = ({
{initialValueSlate && (
<Slate
editor={editor}
onChange={(slateArray) => onChange(slateToMarkdown(slateArray))}
value={initialValueSlate}
initialValue={initialValueSlate}
onChange={(slateArray) => {
markdownValue.current = slateToMarkdown(slateArray);
onChange(markdownValue.current);
}}
>
<Editable
autoFocus
Expand All @@ -134,7 +185,10 @@ const ZUITextEditor: React.FunctionComponent<ZUITextEditorProps> = ({
renderElement={renderElement}
renderLeaf={renderLeaf}
spellCheck
style={{ overflowY: 'scroll' }}
style={{
outline: 'none',
overflowY: 'scroll',
}}
/>
<Collapse in={active} sx={{ flexShrink: 0 }}>
<Toolbar onClickAttach={onClickAttach} />
Expand Down
45 changes: 28 additions & 17 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2221,6 +2221,11 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"

"@juggle/resize-observer@^3.4.0":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==

"@mdx-js/mdx@^1.6.22":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
Expand Down Expand Up @@ -4362,7 +4367,12 @@
dependencies:
"@types/geojson" "*"

"@types/lodash@^4.14.149", "@types/lodash@^4.14.167":
"@types/lodash@^4.14.149":
version "4.14.198"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c"
integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==

"@types/lodash@^4.14.167":
version "4.14.182"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
Expand Down Expand Up @@ -6582,10 +6592,10 @@ compression@^1.7.4:
safe-buffer "5.1.2"
vary "~1.1.2"

compute-scroll-into-view@^1.0.17:
version "1.0.17"
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
compute-scroll-into-view@^1.0.20:
version "1.0.20"
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43"
integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==

concat-map@0.0.1:
version "0.0.1"
Expand Down Expand Up @@ -14275,11 +14285,11 @@ schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
ajv-keywords "^3.5.2"

scroll-into-view-if-needed@^2.2.20:
version "2.2.29"
resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
version "2.2.31"
resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587"
integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==
dependencies:
compute-scroll-into-view "^1.0.17"
compute-scroll-into-view "^1.0.20"

"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
Expand Down Expand Up @@ -14488,11 +14498,12 @@ slate-history@^0.66.0:
dependencies:
is-plain-object "^5.0.0"

slate-react@^0.77.4:
version "0.77.4"
resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.77.4.tgz#b2532b57e47c4e088b1583ed8befc6e238a15ad5"
integrity sha512-e3gYuEhjbEX4IhydC4kWZgcvH5fgJkJMuu9paarO6gdW5vRRSWnys/EbOs5ALp4e9NWr5u1XzQkJnbpyW5AqBA==
slate-react@^0.98.3:
version "0.98.3"
resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.98.3.tgz#5090d269d69186f3ec2a6b5862d2645f01772eda"
integrity sha512-p1BnF9eRyRM0i5hkgOb11KgmpWLQm9Zyp6jVkOAj5fPdIGheKhg48Z7aWKrayeJ4nmRyi/NjRZz/io5hQcphmw==
dependencies:
"@juggle/resize-observer" "^3.4.0"
"@types/is-hotkey" "^0.1.1"
"@types/lodash" "^4.14.149"
direction "^1.0.3"
Expand All @@ -14502,10 +14513,10 @@ slate-react@^0.77.4:
scroll-into-view-if-needed "^2.2.20"
tiny-invariant "1.0.6"

slate@^0.78.0:
version "0.78.0"
resolved "https://registry.yarnpkg.com/slate/-/slate-0.78.0.tgz#cd0328d22b0a99c543987d2a2dd30903bb950ee9"
integrity sha512-VwQ0RafT3JPf9SFrXI02Dh3S4Iz9en7d1nn50C/LJjjqjfgv+a2ORbgWMdYjhycPYldaxJwcI3OpP9D1g4SXEg==
slate@^0.94.1:
version "0.94.1"
resolved "https://registry.yarnpkg.com/slate/-/slate-0.94.1.tgz#13b0ba7d0a7eeb0ec89a87598e9111cbbd685696"
integrity sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==
dependencies:
immer "^9.0.6"
is-plain-object "^5.0.0"
Expand Down
Loading