mirror of
https://github.com/usememos/memos.git
synced 2024-09-20 06:25:56 +08:00
feat: add eslint to frontend
This commit is contained in:
parent
493391bb03
commit
73812cd58d
|
@ -1,3 +1,24 @@
|
|||
{
|
||||
"extends": ["prettier"]
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint", "prettier"],
|
||||
"ignorePatterns": ["node_modules", "dist", "public"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"@typescript-eslint/no-empty-interface": ["off"],
|
||||
"@typescript-eslint/no-explicit-any": ["off"],
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"@typescript-eslint/no-namespace": "off"
|
||||
}
|
||||
}
|
||||
|
|
8
web/.vscode/setting.json
vendored
Normal file
8
web/.vscode/setting.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"serve": "vite preview"
|
||||
"serve": "vite preview",
|
||||
"lint": "eslint --ext .js,.ts,.tsx, src"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^17.0.2",
|
||||
|
@ -14,8 +15,15 @@
|
|||
"devDependencies": {
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.6.0",
|
||||
"@typescript-eslint/parser": "^5.6.0",
|
||||
"@vitejs/plugin-react": "^1.0.0",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.27.1",
|
||||
"less": "^4.1.1",
|
||||
"prettier": "2.5.1",
|
||||
"typescript": "^4.3.2",
|
||||
"vite": "^2.6.14"
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { memo, useCallback, useEffect, useState } from "react";
|
||||
import { memo, useCallback, useEffect, useState } from "react";
|
||||
import { memoService, queryService } from "../services";
|
||||
import { checkShouldShowMemoWithFilters, filterConsts, getDefaultFilter, relationConsts } from "../helpers/filter";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
|
@ -143,7 +143,7 @@ interface MemoFilterInputerProps {
|
|||
handleFilterRemove: (index: number) => void;
|
||||
}
|
||||
|
||||
const MemoFilterInputer: React.FC<MemoFilterInputerProps> = memo((props: MemoFilterInputerProps) => {
|
||||
const FilterInputer: React.FC<MemoFilterInputerProps> = (props: MemoFilterInputerProps) => {
|
||||
const { index, filter, handleFilterChange, handleFilterRemove } = props;
|
||||
const { type } = filter;
|
||||
const [inputElements, setInputElements] = useState<JSX.Element>(<></>);
|
||||
|
@ -294,7 +294,9 @@ const MemoFilterInputer: React.FC<MemoFilterInputerProps> = memo((props: MemoFil
|
|||
<img className="remove-btn" src="/icons/close.svg" onClick={handleRemoveBtnClick} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const MemoFilterInputer: React.FC<MemoFilterInputerProps> = memo(FilterInputer);
|
||||
|
||||
export default function showCreateQueryDialog(queryId?: string): void {
|
||||
showDialog(
|
||||
|
|
|
@ -25,6 +25,7 @@ interface Props {
|
|||
onContentChange: (content: string) => void;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefActions>) => {
|
||||
const {
|
||||
globalState: { useTinyUndoHistoryCache },
|
||||
|
@ -45,16 +46,21 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefAction
|
|||
const refresh = useRefresh();
|
||||
|
||||
useEffect(() => {
|
||||
if (initialContent) {
|
||||
editorRef.current!.value = initialContent;
|
||||
if (initialContent && editorRef.current) {
|
||||
editorRef.current.value = initialContent;
|
||||
refresh();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (useTinyUndoHistoryCache) {
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { tinyUndoActionsCache, tinyUndoIndexCache } = storage.get(["tinyUndoActionsCache", "tinyUndoIndexCache"]);
|
||||
tinyUndoRef.current = new TinyUndo(editorRef.current!, {
|
||||
|
||||
tinyUndoRef.current = new TinyUndo(editorRef.current, {
|
||||
interval: 5000,
|
||||
initialActions: tinyUndoActionsCache,
|
||||
initialIndex: tinyUndoIndexCache,
|
||||
|
@ -88,17 +94,23 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefAction
|
|||
ref,
|
||||
() => ({
|
||||
focus: () => {
|
||||
editorRef.current!.focus();
|
||||
editorRef.current?.focus();
|
||||
},
|
||||
insertText: (rawText: string) => {
|
||||
const prevValue = editorRef.current!.value;
|
||||
editorRef.current!.value = prevValue + rawText;
|
||||
handleContentChangeCallback(editorRef.current!.value);
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevValue = editorRef.current.value;
|
||||
editorRef.current.value = prevValue + rawText;
|
||||
handleContentChangeCallback(editorRef.current.value);
|
||||
refresh();
|
||||
},
|
||||
setContent: (text: string) => {
|
||||
editorRef.current!.value = text;
|
||||
refresh();
|
||||
if (editorRef.current) {
|
||||
editorRef.current.value = text;
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
getContent: (): string => {
|
||||
return editorRef.current?.value ?? "";
|
||||
|
@ -108,7 +120,7 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefAction
|
|||
);
|
||||
|
||||
const handleEditorInput = useCallback(() => {
|
||||
handleContentChangeCallback(editorRef.current!.value);
|
||||
handleContentChangeCallback(editorRef.current?.value ?? "");
|
||||
refresh();
|
||||
}, []);
|
||||
|
||||
|
@ -124,8 +136,12 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefAction
|
|||
}, []);
|
||||
|
||||
const handleCommonConfirmBtnClick = useCallback(() => {
|
||||
handleConfirmBtnClickCallback(editorRef.current!.value);
|
||||
editorRef.current!.value = "";
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleConfirmBtnClickCallback(editorRef.current.value);
|
||||
editorRef.current.value = "";
|
||||
refresh();
|
||||
// After confirm btn clicked, tiny-undo should reset state(clear actions and index)
|
||||
tinyUndoRef.current?.resetState();
|
||||
|
|
|
@ -14,7 +14,6 @@ const MemoList: React.FC<Props> = () => {
|
|||
const {
|
||||
locationState: { query },
|
||||
memoState: { memos },
|
||||
globalState,
|
||||
} = useContext(appContext);
|
||||
const [isFetching, setFetchStatus] = useState(true);
|
||||
const wrapperElement = useRef<HTMLDivElement>(null);
|
||||
|
|
|
@ -23,7 +23,6 @@ const MyAccountSection: React.FC<Props> = () => {
|
|||
const [username, setUsername] = useState<string>(user.username);
|
||||
const [showEditUsernameInputs, setShowEditUsernameInputs] = useState(false);
|
||||
const [showConfirmUnbindGithubBtn, setShowConfirmUnbindGithubBtn] = useState(false);
|
||||
const [showConfirmUnbindWxBtn, setShowConfirmUnbindWxBtn] = useState(false);
|
||||
|
||||
const handleUsernameChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const nextUsername = e.target.value as string;
|
||||
|
|
|
@ -11,7 +11,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||
const { globalState } = useContext(appContext);
|
||||
const { useTinyUndoHistoryCache, shouldHideImageUrl, shouldSplitMemoWord, shouldUseMarkdownParser } = globalState;
|
||||
|
||||
const demoMemoContent = `👋 你好呀~\n我是一个demo:\n* 👏 欢迎使用memos;`;
|
||||
const demoMemoContent = "👋 你好呀~\n我是一个demo:\n* 👏 欢迎使用memos;";
|
||||
|
||||
const handleOpenTinyUndoChanged = () => {
|
||||
globalStateService.setAppSetting({
|
||||
|
@ -87,7 +87,7 @@ const PreferencesSection: React.FC<Props> = () => {
|
|||
<label className="form-label checkbox-form-label" onClick={handleOpenTinyUndoChanged}>
|
||||
<span className="normal-text">
|
||||
启用{" "}
|
||||
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()}>
|
||||
<a target="_blank" href="https://github.com/boojack/tiny-undo" onClick={(e) => e.stopPropagation()} rel="noreferrer">
|
||||
tiny-undo
|
||||
</a>
|
||||
</span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext, useEffect } from "react";
|
||||
import { useContext, useEffect } from "react";
|
||||
import appContext from "../stores/appContext";
|
||||
import useToggle from "../hooks/useToggle";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { memo, useEffect, useRef } from "react";
|
||||
import { memo, useEffect, useRef } from "react";
|
||||
import useToggle from "../../hooks/useToggle";
|
||||
import "../../less/common/selector.less";
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ async function request<T>(method: string, url: string, data?: any): Promise<Resp
|
|||
"Content-Type": "application/json",
|
||||
};
|
||||
if (data !== null) {
|
||||
requestConfig.body = JSON.stringify(data);
|
||||
}
|
||||
requestConfig.body = JSON.stringify(data);
|
||||
}
|
||||
|
||||
const response = await fetch(url, requestConfig);
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace utils {
|
|||
finalObject[key] = temp;
|
||||
}
|
||||
} else {
|
||||
if (Boolean(val)) {
|
||||
if (val) {
|
||||
finalObject[key] = val;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from "react";
|
||||
|
||||
function useLoading(initialState: boolean = true) {
|
||||
function useLoading(initialState = true) {
|
||||
const [state, setState] = useState({ isLoading: initialState, isFailed: false, isSucceed: false });
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useState } from "react";
|
||||
|
||||
function useRefresh() {
|
||||
const [_, setBoolean] = useState<Boolean>(false);
|
||||
const [, setBoolean] = useState<boolean>(false);
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
setBoolean((ps) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export type State = Readonly<Object>;
|
||||
export type State = Readonly<Record<string, any>>;
|
||||
export type Action = {
|
||||
type: string;
|
||||
payload: any;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
const cachedResourceMap = new Map<string, string>();
|
||||
|
||||
const convertResourceToDataURL = (url: string, useCache = true): Promise<string> => {
|
||||
const convertResourceToDataURL = async (url: string, useCache = true): Promise<string> => {
|
||||
if (useCache && cachedResourceMap.has(url)) {
|
||||
return Promise.resolve(cachedResourceMap.get(url) as string);
|
||||
}
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const res = await fetch(url);
|
||||
const blob = await res.blob();
|
||||
var reader = new FileReader();
|
||||
const res = await fetch(url);
|
||||
const blob = await res.blob();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
const base64Url = reader.result as string;
|
||||
cachedResourceMap.set(url, base64Url);
|
||||
|
|
|
@ -21,7 +21,7 @@ const getFontsStyleElement = async (element: HTMLElement) => {
|
|||
const resourceUrls = src.split(",").map((s) => {
|
||||
return s.replace(/url\("?(.+?)"?\)/, "$1");
|
||||
});
|
||||
let base64Urls = [];
|
||||
const base64Urls: string[] = [];
|
||||
|
||||
for (const url of resourceUrls) {
|
||||
try {
|
||||
|
|
|
@ -73,11 +73,16 @@ export const toCanvas = async (element: HTMLElement, options?: Options): Promise
|
|||
const imageEl = new Image();
|
||||
imageEl.src = url;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d")!;
|
||||
const ratio = options?.pixelRatio || 1;
|
||||
const { width, height } = getElementSize(element);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
if (!context) {
|
||||
return Promise.reject("Canvas error");
|
||||
}
|
||||
|
||||
canvas.width = width * ratio;
|
||||
canvas.height = height * ratio;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
type State = Readonly<Object>;
|
||||
type State = Readonly<Record<string, any>>;
|
||||
interface Action {
|
||||
type: string;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ export default defineConfig({
|
|||
cors: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8080/",
|
||||
// target: "https://memos.justsven.top/",
|
||||
// target: "http://localhost:8080/",
|
||||
target: "https://memos.justsven.top/",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
|
|
1470
web/yarn.lock
1470
web/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue