Skip to content

Commit

Permalink
feat: md handle add upload image setting
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanyxh committed May 18, 2024
1 parent c985f45 commit ad61923
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 22 deletions.
3 changes: 3 additions & 0 deletions src/components/Dialog/Dialog.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
}

.header .bar {
display: flex;
align-items: center;
justify-content: start;
flex: 1;
height: 100%;
cursor: move;
Expand Down
8 changes: 6 additions & 2 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export type DialogProps = Omit<
>;

export interface IDialogProps extends DialogProps {
draggable?: boolean;
open: boolean;
draggable?: boolean;
toolbar?: React.ReactNode;
onMinimize?(): any;
onClose?(): any;
}
Expand Down Expand Up @@ -93,6 +94,7 @@ const Dialog = forwardRef<IDialogExpose, Readonly<IDialogProps>>(
function Dialog(props, ref) {
const {
open,
toolbar = null,
draggable = true,
className = '',
children,
Expand Down Expand Up @@ -218,7 +220,9 @@ const Dialog = forwardRef<IDialogExpose, Readonly<IDialogProps>>(
className={styles.bar}
style={{ cursor: draggable ? void 0 : 'default' }}
onMouseDownCapture={handleStart}
></div>
>
{toolbar}
</div>

<div className={styles.operator}>
<Icon
Expand Down
6 changes: 4 additions & 2 deletions src/filehandle/components/BackgroundMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { createPortal } from 'react-dom';
import classNames from 'classnames';

import styles from './styles/BackgroundMenu.module.less';
import type { BackgroundProgram } from '../BackgroundManager';
import type { BackgroundManager } from '../BackgroundManager';
import type {
BackgroundManager,
BackgroundProgram
} from '../BackgroundManager';

interface IBackgroundMenuProps {
backgroundManager: BackgroundManager;
Expand Down
66 changes: 64 additions & 2 deletions src/filehandle/md_editor/component/MDEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type { FH } from '@/filehandle/utils/fileManager';

import styles from './styles/MDEditor.module.less';
import '@/assets/styles/prism-one-dark.css';
import type { UploadInfo } from '../store/useMDStore';
import { useMDStore } from '../store/useMDStore';
import { reduce } from '../theme-reduce';
import { toFormData } from '../utils';

import { defaultValueCtx, Editor, rootCtx } from '@milkdown/core';
import { clipboard } from '@milkdown/plugin-clipboard';
Expand All @@ -13,7 +16,8 @@ import { history } from '@milkdown/plugin-history';
import { indent } from '@milkdown/plugin-indent';
import { listener, listenerCtx } from '@milkdown/plugin-listener';
import { prism, prismConfig } from '@milkdown/plugin-prism';
import { upload } from '@milkdown/plugin-upload';
import type { Uploader } from '@milkdown/plugin-upload';
import { upload, uploadConfig } from '@milkdown/plugin-upload';
import {
blockquoteAttr,
bulletListAttr,
Expand Down Expand Up @@ -58,6 +62,58 @@ const blockClass = { class: styles.typography };

const getMDString = getMarkdown();

let uploadInfo: UploadInfo | null = null;
const selfUpload = async (file: File) => {
if (!uploadInfo || uploadInfo.url.trim() === '') {
return window.URL.createObjectURL(file);
}

const body = uploadInfo.body.trim() ? JSON.parse(uploadInfo.body) : {};
const data = toFormData({ ...body, [uploadInfo.field]: file });

const navigation = uploadInfo.navigation;

return fetch(uploadInfo.url, {
method: 'POST',
body: data
})
.then((res) => res.json())
.then((value) =>
navigation.split('.').reduce((prev, curr) => prev[curr], value)
);
};

const uploader: Uploader = async (files, schema) => {
const images: File[] = [];

for (let i = 0; i < files.length; i++) {
const file = files.item(i);
if (!file) {
continue;
}

// You can handle whatever the file type you want, we handle image here.
if (!file.type.includes('image')) {
continue;
}

images.push(file);
}

const nodes = await Promise.all(
images.map(async (image) => {
const src = await selfUpload(image);
const alt = image.name;
return schema.nodes.image.createAndFill({
src,
alt
})!;
})
);

return nodes;
};

function createMDEditor(
el: HTMLElement,
value = '',
Expand All @@ -68,6 +124,11 @@ function createMDEditor(
ctx.set(rootCtx, el);
ctx.set(defaultValueCtx, value);

ctx.update(uploadConfig.key, (prev) => ({
...prev,
uploader
}));

ctx.set(prismConfig.key, {
configureRefractor: (r) => {
r.alias('shell', 'sh');
Expand Down Expand Up @@ -102,10 +163,11 @@ const MDEditor = forwardRef<IMDEditorExpose, IMDEditorProps>(
function MDEditor(props, ref) {
const { currentHandle, changed, onChanged, onSave } = props;

uploadInfo = useMDStore().uploadInfo;

const editorContainerRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<Editor>();
const mdStringRef = useRef('');

const creatingRef = useRef(false);

useImperativeHandle(ref, () => ({
Expand Down
104 changes: 104 additions & 0 deletions src/filehandle/md_editor/component/MDHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useEffect, useId, useRef, useState } from 'react';

import { Button, Form, Input, Modal, Row } from 'antd';

import { error } from '@/utils';

import type { BackgroundManager } from '@/filehandle/BackgroundManager';
Expand All @@ -10,6 +12,8 @@ import type { IMDContentExpose } from './MDContent';
import { MDContent } from './MDContent';
import styles from './styles/MDHandle.module.less';
import type { DH, FH } from '../../utils/fileManager';
import type { UploadInfo } from '../store/useMDStore';
import { useMDStore } from '../store/useMDStore';

export interface IMDHandle {
handle: DH | FH;
Expand All @@ -18,6 +22,105 @@ export interface IMDHandle {
update(): void;
}

const Toolbar = () => {
const [uploadSettingModal, setUploadSettingModal] = useState(false);

const { uploadInfo, setUploadInfo } = useMDStore();

const [form] = Form.useForm<UploadInfo>();

const handleSetImageUpload = () => {
setUploadSettingModal(true);
};

const handleCancel = () => {
setUploadSettingModal(false);

form.resetFields();
};

const handleSetUploadSetting = () => {
form
.validateFields()
.then((value) => {
setUploadInfo(value);

setUploadSettingModal(false);

form.resetFields();
})
.catch(() => {
/* empty */
});
};

return (
<>
<Row style={{ marginLeft: 5 }} gutter={20}>
<Button
type="text"
size="small"
style={{ color: 'var(--color-info)' }}
onClick={handleSetImageUpload}
>
图片上传设置
</Button>
</Row>

<Modal
title="图片上传设置"
style={{ top: '15vh' }}
destroyOnClose
open={uploadSettingModal}
onCancel={handleCancel}
onOk={handleSetUploadSetting}
>
<Form
name="markdown-upload-setting"
layout="vertical"
autoComplete="off"
initialValues={uploadInfo}
form={form}
>
<Form.Item<UploadInfo>
label="上传地址"
name="url"
rules={[{ required: true, message: 'Please input upload url!' }]}
>
<Input placeholder="请输入上传的目标地址" />
</Form.Item>

<Form.Item<UploadInfo>
label="文件字段"
name="field"
rules={[{ required: true, message: 'Please input file field!' }]}
>
<Input placeholder="请输入文件对应的字段" />
</Form.Item>

<Form.Item<UploadInfo>
label="响应路径"
name="navigation"
rules={[
{ required: true, message: 'Please input response navigation!' }
]}
>
<Input placeholder="请输入图片地址在响应中的路径,以 . 分割" />
</Form.Item>

{/* TODO: JSON editor tool */}
<Form.Item<UploadInfo> label="额外请求体" name="body">
<Input.TextArea
rows={5}
placeholder="额外请求参数,请输入 JSON 格式"
/>
</Form.Item>
</Form>
</Modal>
</>
);
};

const MDHandle: React.FC<IMDHandle> = (props) => {
const { handle, backgroundManager, update, destroy } = props;

Expand Down Expand Up @@ -74,6 +177,7 @@ const MDHandle: React.FC<IMDHandle> = (props) => {
className={styles.mdHandle}
open={open}
draggable={false}
toolbar={<Toolbar />}
onAnimationEnd={onAnimationEnd}
onMinimize={handleMinimize}
onClose={onClose}
Expand Down
38 changes: 38 additions & 0 deletions src/filehandle/md_editor/store/useMDStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getStorage, setStorage } from '@/utils';

import { create } from 'zustand';

export interface UploadInfo {
url: string;
field: string;
navigation: string;
body: string;
}

export interface MDState {
uploadInfo: UploadInfo;
}

export interface MDActions {
setUploadInfo(uploadInfo: UploadInfo): void;
}

export const useMDStore = create<MDState & MDActions>((set) => ({
...getStorage<MDState>('filesystem/md', {
uploadInfo: {
url: '',
field: '',
navigation: '',
body: ''
}
}),
setUploadInfo(uploadInfo) {
set(() => {
const store = { uploadInfo };

setStorage('filesystem/md', store);

return store;
});
}
}));
18 changes: 18 additions & 0 deletions src/filehandle/md_editor/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const toFormData = (
params: Record<string, string | Blob>,
data?: FormData
) => {
data = data || new FormData();

const strategy: Record<string, () => string | Blob> = {
$timestamp() {
return Date.now().toString();
}
};

for (const key in params) {
data.append(key, strategy[params[key].toString()]?.() || params[key]);
}

return data;
};
2 changes: 1 addition & 1 deletion src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as useResizeObserver } from './useResizeObserver';
export * from './useResizeObserver';
2 changes: 1 addition & 1 deletion src/hooks/useResizeObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const map: {

const sizeMap: { [key: string]: { width: number; height: number } } = {};

export default function useResizeObserver(selector: string) {
export function useResizeObserver(selector: string) {
const [size, setSize] = useState(
sizeMap[selector] || {
width: 0,
Expand Down
8 changes: 4 additions & 4 deletions src/router/components/Loading.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
interface ILoadignProps {}
interface ILoadingProps {}

const Loadign: React.FC<Readonly<ILoadignProps>> = (props) => {
const Loading: React.FC<Readonly<ILoadingProps>> = (props) => {
console.log(props);

return <div>Loadign</div>;
return <div>Loading</div>;
};

export default Loadign;
export default Loading;
3 changes: 2 additions & 1 deletion src/utils/localStorageTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { merge } from 'lodash-es';
export const KEYS = {
/** Website configuration key */
APP_KEY: 'app',
USER_KEY: 'user'
USER_KEY: 'user',
FILE_SYSTEM_MD: 'filesystem/md'
} as const;

type TKEYS = (typeof KEYS)[keyof typeof KEYS];
Expand Down
2 changes: 1 addition & 1 deletion src/viewer/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Icon } from '@/components';

import LogoImage from '@/assets/images/main.webp';

import Feedback from './components/Feedback';
import { Feedback } from './components/Feedback';
import languageData from './data/language.json';
import navbarData from './data/navbar.json';
import styles from './styles/Layout.module.less';
Expand Down
Loading

0 comments on commit ad61923

Please sign in to comment.