Skip to content

Commit

Permalink
Merge pull request #96 from subquery/feat/markdown
Browse files Browse the repository at this point in the history
feat: markdown
  • Loading branch information
HuberTRoy authored Oct 30, 2023
2 parents 34ca234 + 66a4a80 commit 4cb8b25
Show file tree
Hide file tree
Showing 10 changed files with 766 additions and 9 deletions.
3 changes: 2 additions & 1 deletion components/common/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export * from './steps';
export * from './card';
export { default as TextInput } from './textInput';
export { default as Toast } from './toast';
export * from './typography';
export { Typography } from './typography';
export * from './notification';
export type { IGraphiQL } from './GraphiQL';
export { default as SubqlProvider } from './provider';
export { default as Markdown } from './markdown/Markdown';
82 changes: 82 additions & 0 deletions components/common/markdown/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

import React, { FC, useState } from 'react';
import { createBEM } from 'components/utilities/createBem';
import clsx from 'clsx';
import MarkdownCompiler, { Options } from 'react-markdown';
import { attachPropertiesToComponent } from '../../utilities/attachPropertiesToCompnent';
import { Input, Radio } from 'antd';
import { Typography } from '../typography';
import { usePropsValue } from 'components/utilities/usePropsValue';
import { TextAreaProps } from 'antd/es/input';

import './markdown.less';

const bem = createBEM('subql-markdown');
const previewBem = createBEM('subql-markdown-preview');

export interface SubqlMarkdown {
value?: string | undefined;
onChange?: (val: string | undefined) => void;
inputProps: TextAreaProps;
previewProps: Options;
}

const MarkdownPreview: FC<Options> = (props) => {
return (
<div className={clsx(previewBem())}>
<MarkdownCompiler {...props}></MarkdownCompiler>
</div>
);
};

const Markdown: FC<SubqlMarkdown> = (props) => {
const [tabVal, setTabVal] = useState<'edit' | 'preview'>('edit');
const [markdownVal, setMarkdownVal] = usePropsValue({
value: props.value,
defaultValue: '',
onChange: props.onChange,
});
return (
<div className={clsx(bem())}>
<div className={clsx(bem('header'))}>
<Radio.Group
value={tabVal}
onChange={(val) => {
setTabVal(val.target.value);
}}
optionType="button"
>
<Radio value={'edit'}>Markdown Edit</Radio>
<Radio value={'preview'}>Preview</Radio>
</Radio.Group>
<span style={{ flex: 1 }}></span>
<Typography type="secondary">
This entry supports{' '}
<Typography.Link href="https://commonmark.org/help/" active>
&#32;basic markdown
</Typography.Link>
</Typography>
</div>

<div className={clsx(bem('main'))}>
{tabVal === 'edit' && (
<Input.TextArea
rows={20}
{...props.inputProps}
value={markdownVal}
onChange={(val) => {
setMarkdownVal(val.target.value);
}}
></Input.TextArea>
)}
{tabVal === 'preview' && <MarkdownPreview {...props.previewProps}>{markdownVal}</MarkdownPreview>}
</div>
</div>
);
};

export default attachPropertiesToComponent(Markdown, {
Preview: MarkdownPreview,
});
76 changes: 76 additions & 0 deletions components/common/markdown/markdown.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.subql-markdown {
width: 100%;
border: 1px solid var(--sq-graylight);
border-radius: 8px;
padding: 10px 8px;
background: #fff;

.ant-radio-group {
background: var(--sq-gray200);
padding: 4px;
border-radius: 4px;
.ant-radio-button-wrapper {
border: none;
border-radius: 4px;
background: var(--sq-gray200);
color: var(--sq-gray600);
font-family: var(--sq-font-family);
&:not(:first-child)::before {
display: none;
}

&-checked {
background: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.05);
color: var(--sq-gray800);

&:hover {
background: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.05);
color: var(--sq-gray800);
}
}

& + .ant-radio-button-wrapper {
margin-left: 8px;
}
}
}

&__header {
display: flex;
border-bottom: 1px solid var(--sq-gray200);
margin-bottom: 10px;
padding-bottom: 10px;
}

&__main {
min-height: 440px;
.ant-input {
padding: 0;
border: none;
resize: none;
color: var(--sq-gray500);
&:focus {
box-shadow: none;
}
}
}

&-preview {
p {
margin: 1em 0;
}
ul {
margin-left: 22px;
}
li {
margin: 0.5em 0;
}

img {
max-width: 100%;
margin: 1em 0;
}
}
}
20 changes: 19 additions & 1 deletion components/common/typography/Typography.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
margin: 0;
padding: 0;
font-family: var(--sq-font-family);

display: inline;

&__h1 {
font-family: var(--sq-font-family-header);
Expand Down Expand Up @@ -139,4 +139,22 @@
color: var(--sq-gray400);
}
}

&-link {
.subql-typography {
transition: .1s all linear;
color: var(--sq-gray600);
}
&:hover {
.subql-typography {
color: var(--sq-blue400);
}
}

&--active {
.subql-typography {
color: var(--sq-blue400);
}
}
}
}
31 changes: 27 additions & 4 deletions components/common/typography/Typography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import './Typography.less';
import { Space, Tooltip } from 'antd';
import { createBEM } from 'components/utilities/createBem';
import { Context } from '../provider';
import { attachPropertiesToComponent } from 'components/utilities/attachPropertiesToCompnent';

type Props = {
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'large' | 'text' | 'medium' | 'small' | 'overline';
Expand All @@ -17,7 +18,16 @@ type Props = {
tooltipIcon?: React.ReactNode;
} & React.HTMLProps<HTMLParagraphElement>;

export const Typography: React.FC<Props> = ({
export interface LinkProps {
href: string;
children?: React.ReactNode;
active?: boolean;
}

const bem = createBEM('subql-typography');
const linkBem = createBEM('subql-typography-link');

const TypographyInner: React.FC<Props> = ({
children,
variant = 'text',
type = 'default',
Expand All @@ -27,11 +37,10 @@ export const Typography: React.FC<Props> = ({
className,
...htmlProps
}) => {
const bem = createBEM('subql-typography');
const { theme } = React.useContext(Context);

const inner = () => (
<p
<article
{...htmlProps}
className={clsx(
bem(),
Expand All @@ -43,7 +52,7 @@ export const Typography: React.FC<Props> = ({
)}
>
{children}
</p>
</article>
);
if (!tooltip) {
return <Space>{inner()}</Space>;
Expand All @@ -58,3 +67,17 @@ export const Typography: React.FC<Props> = ({
</Tooltip>
);
};

const Link: React.FC<LinkProps> = (props) => {
const { href, children, active = false } = props;

return (
<a href={href} className={clsx(linkBem({ active }))}>
<Typography>{children}</Typography>
</a>
);
};

export const Typography = attachPropertiesToComponent(TypographyInner, {
Link: Link,
});
1 change: 1 addition & 0 deletions components/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
--sq-gray700: #454f58;
--sq-gray800: #212b36;
--sq-gray900: #1a202c;
--sq-graylight: #c2c9d1;

/* Semantic */
--sq-success: #65cd45;
Expand Down
12 changes: 12 additions & 0 deletions components/utilities/attachPropertiesToCompnent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

export function attachPropertiesToComponent<C, P extends Record<string, any>>(component: C, properties: P): C & P {
const ret = component as any;
for (const key in properties) {
if (properties.hasOwnProperty(key)) {
ret[key] = properties[key];
}
}
return ret;
}
33 changes: 33 additions & 0 deletions components/utilities/usePropsValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: Apache-2.0

// Copy from antd-mobile
import { SetStateAction, useRef } from 'react';
import { useMemoizedFn, useUpdate } from 'ahooks';

type Options<T> = {
value?: T;
defaultValue: T;
onChange?: (v: T) => void;
};

export function usePropsValue<T>(options: Options<T>) {
const { value, defaultValue, onChange } = options;

const update = useUpdate();

const stateRef = useRef<T>(value !== undefined ? value : defaultValue);
if (value !== undefined) {
stateRef.current = value;
}

const setState = useMemoizedFn((v: SetStateAction<T>, forceTrigger = false) => {
// `forceTrigger` means trigger `onChange` even if `v` is the same as `stateRef.current`
const nextValue = typeof v === 'function' ? (v as (prevState: T) => T)(stateRef.current) : v;
if (!forceTrigger && nextValue === stateRef.current) return;
stateRef.current = nextValue;
update();
return onChange?.(nextValue);
});
return [stateRef.current, setState] as const;
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"dependencies": {
"@graphiql/plugin-explorer": "^0.3.4",
"@graphiql/toolkit": "^0.9.1",
"ahooks": "^3.7.8",
"antd": "^5.8.4",
"clsx": "^1.2.1",
"graphiql": "^3.0.5",
Expand All @@ -69,6 +70,7 @@
"ra-data-graphql": "^4.11.3",
"react-icons": "^4.8.0",
"react-jazzicon": "^0.1.3",
"react-markdown": "^9.0.0",
"react-router-dom": "^6.4.2",
"rollup-plugin-copy": "^3.4.0",
"string-width": "4.2.3",
Expand Down
Loading

0 comments on commit 4cb8b25

Please sign in to comment.