Skip to content

Commit

Permalink
[migrate] replace Markdown IME with HTML editor (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
luojiyin1987 committed Jul 12, 2024
1 parent 915905a commit fe685c2
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 64 deletions.
22 changes: 22 additions & 0 deletions components/Form/HTMLEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { FC } from 'react';
import {
AudioTool,
Editor,
EditorProps,
IFrameTool,
ImageTool,
OriginalTools,
VideoTool,
} from 'react-bootstrap-editor';
import { Constructor } from 'web-utility';

const ExcludeTools = [IFrameTool, AudioTool, VideoTool];

const CustomTools = OriginalTools.filter(
Tool => !ExcludeTools.includes(Tool as Constructor<IFrameTool>),
);

const HTMLEditor: FC<EditorProps> = props => (
<Editor tools={CustomTools} {...props} />
);
export default HTMLEditor;
100 changes: 36 additions & 64 deletions components/Git/ArticleEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@ import { computed, observable } from 'mobx';
import { GitContent } from 'mobx-github';
import { observer } from 'mobx-react';
import { DataObject } from 'mobx-restful';
import {
ChangeEvent,
Component,
createRef,
FormEvent,
MouseEvent,
} from 'react';
import dynamic from 'next/dynamic';
import { ChangeEvent, Component, FormEvent, MouseEvent } from 'react';
import { Button, Col, Form } from 'react-bootstrap';
import { blobOf, formatDate, uniqueID } from 'web-utility';
import YAML from 'yaml';

import { GitRepositoryModel, userStore } from '../../models/Repository';
import { i18n } from '../../models/Translation';
import { ListField } from '../Form/JSONEditor';
import { MarkdownEditor } from '../Form/MarkdownEditor';
import { PathSelect } from './PathSelect';
import { RepositorySelect } from './RepositorySelect';

const { t } = i18n;
const HTMLEditor = dynamic(() => import('../Form/HTMLEditor'), { ssr: false });

export const fileType = {
MarkDown: ['md', 'markdown'],
JSON: ['json'],
Expand All @@ -39,6 +37,7 @@ export type HyperLink = HTMLAnchorElement | HTMLImageElement;
export class ArticleEditor extends Component {
@observable
accessor repository = '';
editorContent = '';

@computed
get currentRepository() {
Expand All @@ -59,18 +58,9 @@ export class ArticleEditor extends Component {
path = '';
URL = '';

private Core = createRef<MarkdownEditor>();

get core() {
return this.Core.current;
}

@observable
accessor meta: PostMeta | null = null;

@observable
accessor copied = false;

static contentFilter({ type, name }: GitContent) {
return (
type === 'dir' ||
Expand Down Expand Up @@ -123,31 +113,25 @@ export class ArticleEditor extends Component {
if (meta[1]) this.setPostMeta(meta[1]);
}

if (this.core) this.core.raw = content;
this.editorContent = content;
};

reset = () => {
this.meta = null;

if (this.core) this.core.raw = '';
this.editorContent = '';
};

onPathClear = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
if (value.trim()) return;

this.meta = null;

if (this.core) this.core.raw = '';
if (!value.trim()) this.reset();
};

fixURL = debounce(() => {
const { repository } = this,
pageURL = window.location.href.split('?')[0];
pageURL = window.location.href.split('?')[0],
root = document.querySelector('div[contenteditable]');

if (this.core && this.core.root)
for (let element of this.core.root.querySelectorAll<HyperLink>(
'[href], [src]',
)) {
if (root)
for (let element of root.querySelectorAll<HyperLink>('[href], [src]')) {
let URI =
element instanceof HTMLAnchorElement ? element.href : element.src;

Expand All @@ -166,36 +150,37 @@ export class ArticleEditor extends Component {

getContent() {
const type = this.URL.split('.').slice(-1)[0],
{ meta, core } = this;
{ meta, editorContent } = this;

if (fileType.JSON.includes(type)) return JSON.stringify(meta);

if (fileType.YAML.includes(type)) return YAML.stringify(meta);

if (fileType.MarkDown.includes(type) && core) {
if (!meta) return core.raw;
if (fileType.MarkDown.includes(type) && editorContent) {
if (!meta) return editorContent;
// @ts-ignore
meta.updated = formatDate();

return `---
${YAML.stringify(meta)}
---
${core.raw}`;
${editorContent}`;
}
}

submit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();

const { currentRepository, repositoryStore, core } = this,
const { currentRepository, repositoryStore, editorContent } = this,
// @ts-ignore
{ message } = event.currentTarget.elements;

if (!core?.root) return;
if (!editorContent) return;

const root = document.querySelector('div[contenteditable]');
const media: HTMLMediaElement[] = [].filter.call(
core.root.querySelectorAll('img[src], audio[src], video[src]'),
root!.querySelectorAll('img[src], audio[src], video[src]'),
({ src }) => new URL(src).protocol === 'blob:',
);

Expand Down Expand Up @@ -224,16 +209,6 @@ export class ArticleEditor extends Component {
window.alert('Submitted');
};

copyMarkdown = async (event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault();

if (this.core) {
await navigator.clipboard.writeText(this.core.raw);

this.copied = true;
}
};

loadFile = async (path: string) => {
const type = path.split('.').at(-1)?.toLowerCase();

Expand All @@ -251,7 +226,7 @@ export class ArticleEditor extends Component {
};

render() {
const { repository, meta, copied } = this;
const { repository, meta } = this;

return (
<Form
Expand All @@ -260,39 +235,41 @@ export class ArticleEditor extends Component {
onSubmit={this.submit}
>
<Form.Group className="row">
<label className="col-sm-2 col-form-label">Repository</label>
<label className="col-sm-2 col-form-label">{t('repository')}</label>
<RepositorySelect
onChange={({ owner, name }) =>
(this.repository = `${owner}/${name}`)
}
/>
</Form.Group>
<Form.Group className="row">
<label className="col-sm-2 col-form-label">File path</label>
<label className="col-sm-2 col-form-label">{t('file_path')}</label>

{repository && (
<PathSelect repository={repository} onChange={this.loadFile} />
)}
</Form.Group>
<Form.Group className="row align-items-center">
<label className="col-sm-2 col-form-label">Commit message</label>
<label className="col-sm-2 col-form-label">
{t('commit_message')}
</label>
<Col sm={7}>
<Form.Control as="textarea" name="message" required />
</Col>
<Col
sm={3}
className="d-flex flex-wrap gap-2 justify-content-around align-items-center"
>
<Button type="submit">Commit</Button>
<Button type="submit">{t('commit')}</Button>
<Button type="reset" variant="danger">
Clear
{t('clear')}
</Button>
</Col>
</Form.Group>

{meta && (
<Form.Group>
<label>Meta</label>
<label>{t('meta')}</label>
<ListField
value={meta}
onChange={({ currentTarget: { value } }) =>
Expand All @@ -303,17 +280,12 @@ export class ArticleEditor extends Component {
)}
<Form.Group onInput={this.fixURL}>
<div className="d-flex justify-content-between align-items-center my-2">
<label>Content</label>
<Button
variant="secondary"
size="sm"
onClick={this.copyMarkdown}
onBlur={() => (this.copied = false)}
>
{copied ? '√' : ''} Copy MarkDown
</Button>
<label>{t('content')}</label>
</div>
<MarkdownEditor ref={this.Core} />
<HTMLEditor
defaultValue={this.editorContent}
onChange={value => (this.editorContent = value)}
/>
</Form.Group>
</Form>
);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"primereact": "^10.6.6",
"react": "^18.3.1",
"react-bootstrap": "^2.10.2",
"react-bootstrap-editor": "^2.0.4",
"react-dom": "^18.3.1",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
Expand Down
50 changes: 50 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions translation/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ export default {
range_module: 'module',
last_step: 'back',

// Git pager
repository: 'repository',
file_path: 'file path',
commit_message: 'commit message',
commit: 'commit',
clear: 'clear',
meta: 'meta',
content: 'content',
copy_MarkDown: 'copy Markdown',

//Polyfill page
select_compatible_browser: 'select Compatible Browser',
select_features: 'select features',
Expand Down
Loading

1 comment on commit fe685c2

@github-actions
Copy link

@github-actions github-actions bot commented on fe685c2 Jul 12, 2024

Choose a reason for hiding this comment

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

Deploy preview for oss-toolbox ready!

✅ Preview
https://oss-toolbox-hna72dxo4-techquerys-projects.vercel.app

Built with commit fe685c2.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.