chore: update marked helper
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 16 KiB |
@ -1,4 +1,4 @@
|
|||||||
import { IMAGE_URL_REG } from "../helpers/consts";
|
import { IMAGE_URL_REG } from "../helpers/marked";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import useI18n from "../hooks/useI18n";
|
import useI18n from "../hooks/useI18n";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
|
@ -10,6 +10,7 @@ export interface EditorRefActions {
|
|||||||
insertText: (text: string) => void;
|
insertText: (text: string) => void;
|
||||||
setContent: (text: string) => void;
|
setContent: (text: string) => void;
|
||||||
getContent: () => string;
|
getContent: () => string;
|
||||||
|
getCursorPosition: () => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditorProps {
|
interface EditorProps {
|
||||||
@ -64,14 +65,17 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prevValue = editorRef.current.value;
|
const prevValue = editorRef.current.value;
|
||||||
editorRef.current.value =
|
const cursorPosition = editorRef.current.selectionStart;
|
||||||
prevValue.slice(0, editorRef.current.selectionStart) + rawText + prevValue.slice(editorRef.current.selectionStart);
|
editorRef.current.value = prevValue.slice(0, cursorPosition) + rawText + prevValue.slice(cursorPosition);
|
||||||
|
editorRef.current.focus();
|
||||||
|
editorRef.current.selectionEnd = cursorPosition + rawText.length;
|
||||||
handleContentChangeCallback(editorRef.current.value);
|
handleContentChangeCallback(editorRef.current.value);
|
||||||
refresh();
|
refresh();
|
||||||
},
|
},
|
||||||
setContent: (text: string) => {
|
setContent: (text: string) => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.value = text;
|
editorRef.current.value = text;
|
||||||
|
editorRef.current.focus();
|
||||||
handleContentChangeCallback(editorRef.current.value);
|
handleContentChangeCallback(editorRef.current.value);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@ -79,6 +83,9 @@ const Editor = forwardRef((props: EditorProps, ref: React.ForwardedRef<EditorRef
|
|||||||
getContent: (): string => {
|
getContent: (): string => {
|
||||||
return editorRef.current?.value ?? "";
|
return editorRef.current?.value ?? "";
|
||||||
},
|
},
|
||||||
|
getCursorPosition: (): number => {
|
||||||
|
return editorRef.current?.selectionStart ?? 0;
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
@ -4,8 +4,8 @@ import dayjs from "dayjs";
|
|||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
import "dayjs/locale/zh";
|
import "dayjs/locale/zh";
|
||||||
import useI18n from "../hooks/useI18n";
|
import useI18n from "../hooks/useI18n";
|
||||||
import { IMAGE_URL_REG, UNKNOWN_ID } from "../helpers/consts";
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
import { DONE_BLOCK_REG, formatMemoContent, TODO_BLOCK_REG } from "../helpers/marked";
|
import { DONE_BLOCK_REG, formatMemoContent, IMAGE_URL_REG, TODO_BLOCK_REG } from "../helpers/marked";
|
||||||
import { editorStateService, locationService, memoService, userService } from "../services";
|
import { editorStateService, locationService, memoService, userService } from "../services";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
import { editorStateService, memoService, userService } from "../services";
|
import { editorStateService, memoService, userService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import { IMAGE_URL_REG, MEMO_LINK_REG, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts";
|
import { UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { formatMemoContent, parseHtmlToRawText } from "../helpers/marked";
|
import { formatMemoContent, IMAGE_URL_REG, MEMO_LINK_REG, parseHtmlToRawText } from "../helpers/marked";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
|
@ -179,6 +179,34 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
setEditorContentCache(content);
|
setEditorContentCache(content);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleCheckBoxBtnClick = () => {
|
||||||
|
if (!editorRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cursorPosition = editorRef.current.getCursorPosition();
|
||||||
|
const prevValue = editorRef.current.getContent().slice(0, cursorPosition);
|
||||||
|
if (prevValue === "" || prevValue.endsWith("\n")) {
|
||||||
|
editorRef.current?.insertText("- [ ] ");
|
||||||
|
} else {
|
||||||
|
editorRef.current?.insertText("\n- [ ] ");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCodeBlockBtnClick = () => {
|
||||||
|
if (!editorRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cursorPosition = editorRef.current.getCursorPosition();
|
||||||
|
const prevValue = editorRef.current.getContent().slice(0, cursorPosition);
|
||||||
|
if (prevValue === "" || prevValue.endsWith("\n")) {
|
||||||
|
editorRef.current?.insertText("```\n\n```");
|
||||||
|
} else {
|
||||||
|
editorRef.current?.insertText("\n```\n\n```");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleUploadFileBtnClick = useCallback(() => {
|
const handleUploadFileBtnClick = useCallback(() => {
|
||||||
const inputEl = document.createElement("input");
|
const inputEl = document.createElement("input");
|
||||||
inputEl.type = "file";
|
inputEl.type = "file";
|
||||||
@ -258,6 +286,12 @@ const MemoEditor: React.FC<Props> = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<button className="action-btn">
|
||||||
|
<Icon.CheckSquare className="icon-img" onClick={handleCheckBoxBtnClick} />
|
||||||
|
</button>
|
||||||
|
<button className="action-btn">
|
||||||
|
<Icon.Code className="icon-img" onClick={handleCodeBlockBtnClick} />
|
||||||
|
</button>
|
||||||
<button className="action-btn">
|
<button className="action-btn">
|
||||||
<Icon.Image className="icon-img" onClick={handleUploadFileBtnClick} />
|
<Icon.Image className="icon-img" onClick={handleUploadFileBtnClick} />
|
||||||
<span className={`tip-text ${state.isUploadingResource ? "!block" : ""}`}>Uploading</span>
|
<span className={`tip-text ${state.isUploadingResource ? "!block" : ""}`}>Uploading</span>
|
||||||
|
@ -2,7 +2,7 @@ import { useEffect, useRef } from "react";
|
|||||||
import { memoService, shortcutService } from "../services";
|
import { memoService, shortcutService } from "../services";
|
||||||
import useI18n from "../hooks/useI18n";
|
import useI18n from "../hooks/useI18n";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts";
|
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/marked";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
import { checkShouldShowMemoWithFilters } from "../helpers/filter";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
|
@ -2,8 +2,8 @@ import { globalService, userService } from "../../services";
|
|||||||
import { useAppSelector } from "../../store";
|
import { useAppSelector } from "../../store";
|
||||||
import { VISIBILITY_SELECTOR_ITEMS } from "../../helpers/consts";
|
import { VISIBILITY_SELECTOR_ITEMS } from "../../helpers/consts";
|
||||||
import useI18n from "../../hooks/useI18n";
|
import useI18n from "../../hooks/useI18n";
|
||||||
import BetaBadge from "../BetaBadge";
|
|
||||||
import Selector from "../common/Selector";
|
import Selector from "../common/Selector";
|
||||||
|
import BetaBadge from "../BetaBadge";
|
||||||
import "../../less/settings/preferences-section.less";
|
import "../../less/settings/preferences-section.less";
|
||||||
|
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { userService } from "../services";
|
import { userService } from "../services";
|
||||||
import toImage from "../labs/html2image";
|
import toImage from "../labs/html2image";
|
||||||
import { ANIMATION_DURATION, IMAGE_URL_REG } from "../helpers/consts";
|
import { ANIMATION_DURATION } from "../helpers/consts";
|
||||||
import useI18n from "../hooks/useI18n";
|
import useI18n from "../hooks/useI18n";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { formatMemoContent } from "../helpers/marked";
|
import { formatMemoContent, IMAGE_URL_REG } from "../helpers/marked";
|
||||||
import Only from "./common/OnlyWhen";
|
import Only from "./common/OnlyWhen";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { DAILY_TIMESTAMP } from "../../helpers/consts";
|
import { DAILY_TIMESTAMP } from "../../helpers/consts";
|
||||||
import "../../less/common/date-picker.less";
|
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
|
import "../../less/common/date-picker.less";
|
||||||
|
|
||||||
interface DatePickerProps {
|
interface DatePickerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -10,18 +10,6 @@ export const TOAST_ANIMATION_DURATION = 400;
|
|||||||
// millisecond in a day
|
// millisecond in a day
|
||||||
export const DAILY_TIMESTAMP = 3600 * 24 * 1000;
|
export const DAILY_TIMESTAMP = 3600 * 24 * 1000;
|
||||||
|
|
||||||
// tag regex
|
|
||||||
export const TAG_REG = /#([^\s#]+?) /g;
|
|
||||||
|
|
||||||
// markdown image regex
|
|
||||||
export const IMAGE_URL_REG = /!\[.*?\]\((.+?)\)/g;
|
|
||||||
|
|
||||||
// markdown link regex
|
|
||||||
export const LINK_URL_REG = /\[(.*?)\]\((.+?)\)/g;
|
|
||||||
|
|
||||||
// linked memo regex
|
|
||||||
export const MEMO_LINK_REG = /@\[(.+?)\]\((.+?)\)/g;
|
|
||||||
|
|
||||||
export const VISIBILITY_SELECTOR_ITEMS = [
|
export const VISIBILITY_SELECTOR_ITEMS = [
|
||||||
{ text: "PUBLIC", value: "PUBLIC" },
|
{ text: "PUBLIC", value: "PUBLIC" },
|
||||||
{ text: "PROTECTED", value: "PROTECTED" },
|
{ text: "PROTECTED", value: "PROTECTED" },
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "./consts";
|
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "./marked";
|
||||||
|
|
||||||
export const relationConsts = [
|
export const relationConsts = [
|
||||||
{ text: "And", value: "AND" },
|
{ text: "And", value: "AND" },
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
import { escape } from "lodash-es";
|
import { escape } from "lodash-es";
|
||||||
import { IMAGE_URL_REG, LINK_URL_REG, MEMO_LINK_REG, TAG_REG } from "./consts";
|
|
||||||
|
|
||||||
const CODE_BLOCK_REG = /```([\s\S]*?)```/g;
|
const CODE_BLOCK_REG = /```([\s\S]*?)```\n?/g;
|
||||||
const BOLD_TEXT_REG = /\*\*(.+?)\*\*/g;
|
const BOLD_TEXT_REG = /\*\*(.+?)\*\*/g;
|
||||||
const EM_TEXT_REG = /\*(.+?)\*/g;
|
const EM_TEXT_REG = /\*(.+?)\*/g;
|
||||||
export const TODO_BLOCK_REG = /- \[ \] /g;
|
|
||||||
export const DONE_BLOCK_REG = /- \[x\] /g;
|
|
||||||
const DOT_LI_REG = /[*-] /g;
|
const DOT_LI_REG = /[*-] /g;
|
||||||
const NUM_LI_REG = /(\d+)\. /g;
|
const NUM_LI_REG = /(\d+)\. /g;
|
||||||
|
export const TODO_BLOCK_REG = /- \[ \] /g;
|
||||||
|
export const DONE_BLOCK_REG = /- \[x\] /g;
|
||||||
|
// tag regex
|
||||||
|
export const TAG_REG = /#([^\s#]+?) /g;
|
||||||
|
// markdown image regex
|
||||||
|
export const IMAGE_URL_REG = /!\[.*?\]\((.+?)\)\n?/g;
|
||||||
|
// markdown link regex
|
||||||
|
export const LINK_URL_REG = /\[(.*?)\]\((.+?)\)/g;
|
||||||
|
// linked memo regex
|
||||||
|
export const MEMO_LINK_REG = /@\[(.+?)\]\((.+?)\)/g;
|
||||||
|
|
||||||
const parseMarkedToHtml = (markedStr: string): string => {
|
const parseMarkedToHtml = (markedStr: string): string => {
|
||||||
const htmlText = markedStr
|
const htmlText = markedStr
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
@apply flex flex-col justify-start items-start w-full mb-4;
|
@apply flex flex-col justify-start items-start w-full mb-4;
|
||||||
|
|
||||||
> .title-container {
|
> .title-container {
|
||||||
@apply w-full flex flex-row justify-between items-center mb-2;
|
@apply w-full flex flex-row justify-between items-center;
|
||||||
|
|
||||||
> .logo-img {
|
> .logo-img {
|
||||||
@apply h-16 w-auto;
|
@apply h-20 w-auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,11 @@
|
|||||||
@apply mb-1 w-full flex flex-row justify-start items-center text-xs leading-6;
|
@apply mb-1 w-full flex flex-row justify-start items-center text-xs leading-6;
|
||||||
|
|
||||||
> .tip-text {
|
> .tip-text {
|
||||||
@apply text-blue-600 mr-2;
|
@apply text-gray-400 mr-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .cancel-btn {
|
> .cancel-btn {
|
||||||
@apply px-2 border rounded-full leading-5 text-gray-600 hover:border-gray-600;
|
@apply px-2 border rounded-full leading-5 text-blue-600 hover:border-blue-600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|