sanity-plugin-markdown 2.0.4 → 3.0.0-studio-v3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -2
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +215 -0
- package/lib/index.js.map +1 -0
- package/lib/index.modern.js +208 -0
- package/lib/index.modern.js.map +1 -0
- package/package.json +58 -26
- package/src/components/Editor.tsx +164 -0
- package/src/hooks/useDebounce.ts +14 -0
- package/src/index.ts +18 -0
- package/lib/components/Editor.js +0 -145
- package/lib/components/Editor.js.map +0 -1
- package/lib/hooks/useDebounce.js +0 -40
- package/lib/hooks/useDebounce.js.map +0 -1
- package/lib/schemaType.js +0 -18
- package/lib/schemaType.js.map +0 -1
- package/sanity.json +0 -12
package/README.md
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
# sanity-plugin-markdown
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
> **NOTE**
|
|
4
|
+
>
|
|
5
|
+
> This is the **Sanity Studio v3 version** of sanity-plugin-markdown.
|
|
6
|
+
>
|
|
7
|
+
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-markdown).
|
|
8
|
+
|
|
9
|
+
## What is it?
|
|
10
|
+
|
|
11
|
+
A Markdown editor with preview for Sanity Studio.
|
|
12
|
+
|
|
13
|
+
Supports Github flavored markdown and image uploads. You can either drag image(s) into the editor or click the bottom bar to bring up a file selector. The resulting image URL(s) are inserted with a default width parameter which you can change to your liking using the [Sanity image pipeline parameters](https://www.sanity.io/docs/image-urls).
|
|
14
|
+
|
|
15
|
+

|
|
3
16
|
|
|
4
17
|
## Installation
|
|
5
18
|
|
|
6
19
|
```
|
|
7
|
-
|
|
20
|
+
npm install --save sanity-plugin-markdown@studio-v3
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
or
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
yarn add sanity-plugin-markdown@studio-v3
|
|
8
27
|
```
|
|
9
28
|
|
|
10
29
|
## Usage
|
|
@@ -32,3 +51,25 @@ const myDocument = {
|
|
|
32
51
|
|
|
33
52
|
MIT © Sanity.io
|
|
34
53
|
See LICENSE
|
|
54
|
+
|
|
55
|
+
## Develop & test
|
|
56
|
+
|
|
57
|
+
Make sure to run `npm run build` once, then run
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm run link-watch
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
In another shell, `cd` to your test studio and run:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx yalc add sanity-plugin-markdown --link && yarn install
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Now, changes in this repo will be automatically built and pushed to the studio,
|
|
70
|
+
triggering hotreload. Yalc avoids issues with react-hooks that are typical when using yarn/npm link.
|
|
71
|
+
|
|
72
|
+
### About build & watch
|
|
73
|
+
|
|
74
|
+
This plugin uses [@sanity/plugin-sdk](https://github.com/sanity-io/plugin-sdk)
|
|
75
|
+
with default configuration for build & watch scripts.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"AEGA,OAAO,MAAM,6CAcX,CAAA","sources":["src/src/hooks/useDebounce.ts","src/src/components/Editor.tsx","src/src/index.ts","src/index.ts"],"sourcesContent":[null,null,null,"import {MarkdownEditor} from './components/Editor'\nimport {createPlugin, defineType} from 'sanity'\n\nexport const markdownSchema = createPlugin({\n name: 'markdown-editor',\n schema: {\n types: [\n defineType({\n type: 'string',\n name: 'markdown',\n title: 'Markdown',\n components: {\n input: MarkdownEditor,\n },\n }),\n ],\n },\n})\n"],"names":[],"version":3,"file":"index.d.ts.map","sourceRoot":"../"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var $3KLdq$sanity = require("sanity");
|
|
2
|
+
var $3KLdq$reactjsxruntime = require("react/jsx-runtime");
|
|
3
|
+
var $3KLdq$reactmde = require("react-mde");
|
|
4
|
+
var $3KLdq$reactmarkdown = require("react-markdown");
|
|
5
|
+
var $3KLdq$remarkgfm = require("remark-gfm");
|
|
6
|
+
var $3KLdq$reachautoid = require("@reach/auto-id");
|
|
7
|
+
require("react-mde/lib/styles/css/react-mde-all.css");
|
|
8
|
+
var $3KLdq$sanityform = require("sanity/form");
|
|
9
|
+
var $3KLdq$react = require("react");
|
|
10
|
+
var $3KLdq$styledcomponents = require("styled-components");
|
|
11
|
+
var $3KLdq$sanityui = require("@sanity/ui");
|
|
12
|
+
|
|
13
|
+
function $parcel$export(e, n, v, s) {
|
|
14
|
+
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
15
|
+
}
|
|
16
|
+
function $parcel$interopDefault(a) {
|
|
17
|
+
return a && a.__esModule ? a.default : a;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
$parcel$export(module.exports, "markdownSchema", () => $244e63ca53592e4d$export$f225414958dd8925);
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
function $84fd43422be26bd7$export$2e2bcd8739ae039(value, delay) {
|
|
28
|
+
const [debouncedValue, setDebouncedValue] = (0, $3KLdq$react.useState)(value);
|
|
29
|
+
(0, $3KLdq$react.useEffect)(()=>{
|
|
30
|
+
const handler = setTimeout(()=>{
|
|
31
|
+
setDebouncedValue(value);
|
|
32
|
+
}, delay);
|
|
33
|
+
return ()=>clearTimeout(handler);
|
|
34
|
+
}, [
|
|
35
|
+
value,
|
|
36
|
+
delay
|
|
37
|
+
]);
|
|
38
|
+
return debouncedValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
const $067e75d167fd097d$var$Preview = ({ markdown: markdown })=>{
|
|
49
|
+
return /*#__PURE__*/ (0, $3KLdq$reactjsxruntime.jsx)((0, ($parcel$interopDefault($3KLdq$reactmarkdown))), {
|
|
50
|
+
plugins: [
|
|
51
|
+
(0, ($parcel$interopDefault($3KLdq$remarkgfm)))
|
|
52
|
+
],
|
|
53
|
+
children: markdown
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const $067e75d167fd097d$var$defaultToolbarCommands = [
|
|
57
|
+
[
|
|
58
|
+
"header",
|
|
59
|
+
"bold",
|
|
60
|
+
"italic",
|
|
61
|
+
"strikethrough"
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"link",
|
|
65
|
+
"quote",
|
|
66
|
+
"code"
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
"unordered-list",
|
|
70
|
+
"ordered-list",
|
|
71
|
+
"checked-list"
|
|
72
|
+
],
|
|
73
|
+
];
|
|
74
|
+
// !important is used whenever we need to override inline styles for an element
|
|
75
|
+
const $067e75d167fd097d$var$MarkdownTheme = (0, ($parcel$interopDefault($3KLdq$styledcomponents))).div`
|
|
76
|
+
& .react-mde {
|
|
77
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
& .mde-text {
|
|
81
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.input.default.enabled.bg} !important;
|
|
82
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.input.default.enabled.fg} !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
& .mde-header {
|
|
86
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.bg};
|
|
87
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.fg};
|
|
88
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
89
|
+
|
|
90
|
+
& button,
|
|
91
|
+
.mde-header-item.mde-header-item.mde-header-item button {
|
|
92
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.button.bleed.default.enabled.fg};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
& button.selected {
|
|
96
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
& .image-tip {
|
|
101
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.bg};
|
|
102
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.muted.default.enabled.fg};
|
|
103
|
+
font-family: ${({ studioTheme: studioTheme })=>studioTheme.fonts.text.family};
|
|
104
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
const $067e75d167fd097d$export$18a9461578883ec4 = /*#__PURE__*/ (0, $3KLdq$react.forwardRef)(function MarkdownEditor(props, ref) {
|
|
108
|
+
const { schemaType: type , value: value = "" , onBlur: onBlur , onChange: onChange , onFocus: onFocus , readOnly: readOnly } = props;
|
|
109
|
+
const options = type.options;
|
|
110
|
+
const [selectedTab, setSelectedTab] = (0, $3KLdq$react.useState)("write");
|
|
111
|
+
const [editedValue, setEditedValue] = (0, $3KLdq$react.useState)(value);
|
|
112
|
+
const debouncedValue = (0, $84fd43422be26bd7$export$2e2bcd8739ae039)(editedValue, 100);
|
|
113
|
+
const textarea = (0, $3KLdq$react.useRef)(null);
|
|
114
|
+
const sanityClient = (0, $3KLdq$sanity.useClient)();
|
|
115
|
+
// Conditionally update textarea styles based on read only access.
|
|
116
|
+
// It's also possible to define inline styles via `childProps.textArea.style` on `<ReactMde />`,
|
|
117
|
+
// but this will override any dynamically created styles provided by the component itself.
|
|
118
|
+
(0, $3KLdq$react.useEffect)(()=>{
|
|
119
|
+
if (textarea.current) textarea.current.style.backgroundColor = readOnly ? "rgba(240,240,240)" : "rgba(255,255,255)";
|
|
120
|
+
}, [
|
|
121
|
+
textarea,
|
|
122
|
+
readOnly
|
|
123
|
+
]);
|
|
124
|
+
(0, $3KLdq$react.useEffect)(()=>{
|
|
125
|
+
setEditedValue(value);
|
|
126
|
+
}, [
|
|
127
|
+
value
|
|
128
|
+
]);
|
|
129
|
+
(0, $3KLdq$react.useEffect)(()=>{
|
|
130
|
+
if (!debouncedValue && value) onChange((0, $3KLdq$sanityform.PatchEvent).from([
|
|
131
|
+
(0, $3KLdq$sanityform.unset)()
|
|
132
|
+
]));
|
|
133
|
+
else if (debouncedValue !== value) onChange((0, $3KLdq$sanityform.PatchEvent).from([
|
|
134
|
+
(0, $3KLdq$sanityform.set)(debouncedValue)
|
|
135
|
+
]));
|
|
136
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
137
|
+
}, [
|
|
138
|
+
debouncedValue,
|
|
139
|
+
onChange
|
|
140
|
+
]);
|
|
141
|
+
const saveImage = async function*(data) {
|
|
142
|
+
const client = sanityClient.withConfig({
|
|
143
|
+
apiVersion: "2021-03-25"
|
|
144
|
+
});
|
|
145
|
+
let success = true;
|
|
146
|
+
const result = await client.assets.upload("image", data).then((doc)=>`${doc.url}?w=450`).catch(()=>{
|
|
147
|
+
success = false;
|
|
148
|
+
return `Error: Could not upload file. Only images are supported.`;
|
|
149
|
+
});
|
|
150
|
+
yield result;
|
|
151
|
+
return success;
|
|
152
|
+
};
|
|
153
|
+
const generatePreview = (0, $3KLdq$react.useCallback)((markdown)=>Promise.resolve(/*#__PURE__*/ (0, $3KLdq$reactjsxruntime.jsx)($067e75d167fd097d$var$Preview, {
|
|
154
|
+
markdown: markdown
|
|
155
|
+
})), []);
|
|
156
|
+
const inputId = (0, $3KLdq$reachautoid.useId)();
|
|
157
|
+
const { sanity: studioTheme } = (0, $3KLdq$sanityui.useTheme)();
|
|
158
|
+
/*
|
|
159
|
+
Assign our forwarded ref to a wrapper element.
|
|
160
|
+
Ideally, this should be assigned to the textarea inside <ReactMde /> component.
|
|
161
|
+
Whilst `react-mde` does allow you to target individual sub-components' refs,
|
|
162
|
+
it will complain if you try and assign a forwarded ref.
|
|
163
|
+
*/ return /*#__PURE__*/ (0, $3KLdq$reactjsxruntime.jsx)("div", {
|
|
164
|
+
ref: ref,
|
|
165
|
+
children: /*#__PURE__*/ (0, $3KLdq$reactjsxruntime.jsx)($067e75d167fd097d$var$MarkdownTheme, {
|
|
166
|
+
studioTheme: studioTheme,
|
|
167
|
+
children: /*#__PURE__*/ (0, $3KLdq$reactjsxruntime.jsx)((0, ($parcel$interopDefault($3KLdq$reactmde))), {
|
|
168
|
+
toolbarCommands: options?.toolbar || $067e75d167fd097d$var$defaultToolbarCommands,
|
|
169
|
+
value: editedValue,
|
|
170
|
+
onChange: setEditedValue,
|
|
171
|
+
selectedTab: selectedTab,
|
|
172
|
+
onTabChange: setSelectedTab,
|
|
173
|
+
readOnly: readOnly,
|
|
174
|
+
generateMarkdownPreview: generatePreview,
|
|
175
|
+
childProps: {
|
|
176
|
+
textArea: {
|
|
177
|
+
id: inputId,
|
|
178
|
+
onBlur: onBlur,
|
|
179
|
+
onFocus: onFocus
|
|
180
|
+
},
|
|
181
|
+
writeButton: {
|
|
182
|
+
tabIndex: -1
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
paste: {
|
|
186
|
+
saveImage: saveImage
|
|
187
|
+
},
|
|
188
|
+
refs: {
|
|
189
|
+
textarea: textarea
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
const $244e63ca53592e4d$export$f225414958dd8925 = (0, $3KLdq$sanity.createPlugin)({
|
|
199
|
+
name: "markdown-editor",
|
|
200
|
+
schema: {
|
|
201
|
+
types: [
|
|
202
|
+
(0, $3KLdq$sanity.defineType)({
|
|
203
|
+
type: "string",
|
|
204
|
+
name: "markdown",
|
|
205
|
+
title: "Markdown",
|
|
206
|
+
components: {
|
|
207
|
+
input: (0, $067e75d167fd097d$export$18a9461578883ec4)
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["src/index.ts","src/components/Editor.tsx","src/hooks/useDebounce.ts"],"sourcesContent":["import {MarkdownEditor} from './components/Editor'\nimport {createPlugin, defineType} from 'sanity'\n\nexport const markdownSchema = createPlugin({\n name: 'markdown-editor',\n schema: {\n types: [\n defineType({\n type: 'string',\n name: 'markdown',\n title: 'Markdown',\n components: {\n input: MarkdownEditor,\n },\n }),\n ],\n },\n})\n","import ReactMde from 'react-mde'\nimport ReactMarkdown from 'react-markdown'\nimport gfm from 'remark-gfm'\nimport {useId} from '@reach/auto-id'\n\nimport useDebounce from '../hooks/useDebounce'\nimport 'react-mde/lib/styles/css/react-mde-all.css'\nimport {PatchEvent, set, unset} from 'sanity/form'\nimport {StringInputProps, useClient} from 'sanity'\nimport React, {\n FC,\n forwardRef,\n PropsWithChildren,\n Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react'\nimport {ToolbarCommands} from 'react-mde/lib/definitions/types'\nimport styled from 'styled-components'\nimport {Theme, useTheme} from '@sanity/ui'\n\nconst Preview = ({markdown}: {markdown: string}) => {\n return <ReactMarkdown plugins={[gfm]}>{markdown}</ReactMarkdown>\n}\n\nconst defaultToolbarCommands = [\n ['header', 'bold', 'italic', 'strikethrough'],\n ['link', 'quote', 'code'],\n ['unordered-list', 'ordered-list', 'checked-list'],\n]\n\ntype SanityTheme = Theme['sanity']\n\ntype Style = PropsWithChildren<{\n studioTheme: SanityTheme\n}>\n\n// !important is used whenever we need to override inline styles for an element\nconst MarkdownTheme: FC<Style> = styled.div`\n & .react-mde {\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n\n & .mde-text {\n background: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.bg} !important;\n color: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.fg} !important;\n }\n\n & .mde-header {\n background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};\n color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.fg};\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n\n & button,\n .mde-header-item.mde-header-item.mde-header-item button {\n color: ${({studioTheme}: Style) => studioTheme.color.button.bleed.default.enabled.fg};\n }\n\n & button.selected {\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n }\n\n & .image-tip {\n background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};\n color: ${({studioTheme}: Style) => studioTheme.color.muted.default.enabled.fg};\n font-family: ${({studioTheme}: Style) => studioTheme.fonts.text.family};\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n`\n\nexport const MarkdownEditor = forwardRef(function MarkdownEditor(\n props: StringInputProps,\n ref: Ref<any>\n) {\n const {schemaType: type, value = '', onBlur, onChange, onFocus, readOnly} = props\n const options: {toolbar?: ToolbarCommands} | undefined = type.options as any\n const [selectedTab, setSelectedTab] = useState<'write' | 'preview'>('write')\n const [editedValue, setEditedValue] = useState(value)\n const debouncedValue = useDebounce(editedValue, 100)\n const textarea = useRef<HTMLTextAreaElement>(null)\n const sanityClient = useClient()\n\n // Conditionally update textarea styles based on read only access.\n // It's also possible to define inline styles via `childProps.textArea.style` on `<ReactMde />`,\n // but this will override any dynamically created styles provided by the component itself.\n useEffect(() => {\n if (textarea.current) {\n textarea.current.style.backgroundColor = readOnly ? 'rgba(240,240,240)' : 'rgba(255,255,255)'\n }\n }, [textarea, readOnly])\n\n useEffect(() => {\n setEditedValue(value)\n }, [value])\n\n useEffect(() => {\n if (!debouncedValue && value) {\n onChange(PatchEvent.from([unset()]))\n } else if (debouncedValue !== value) {\n onChange(PatchEvent.from([set(debouncedValue)]))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debouncedValue, onChange])\n\n const saveImage = async function* (data: any) {\n const client = sanityClient.withConfig({apiVersion: '2021-03-25'})\n\n let success = true\n const result = await client.assets\n .upload('image', data)\n .then((doc) => `${doc.url}?w=450`)\n .catch(() => {\n success = false\n return `Error: Could not upload file. Only images are supported.`\n })\n\n yield result\n return success\n }\n\n const generatePreview = useCallback(\n (markdown: string) => Promise.resolve(<Preview markdown={markdown} />),\n []\n )\n\n const inputId = useId()\n const {sanity: studioTheme} = useTheme()\n /*\n Assign our forwarded ref to a wrapper element.\n Ideally, this should be assigned to the textarea inside <ReactMde /> component.\n Whilst `react-mde` does allow you to target individual sub-components' refs,\n it will complain if you try and assign a forwarded ref.\n */\n return (\n <div ref={ref}>\n <MarkdownTheme studioTheme={studioTheme}>\n <ReactMde\n toolbarCommands={options?.toolbar || defaultToolbarCommands}\n value={editedValue}\n onChange={setEditedValue}\n selectedTab={selectedTab}\n onTabChange={setSelectedTab}\n readOnly={readOnly}\n generateMarkdownPreview={generatePreview}\n childProps={{\n textArea: {\n id: inputId,\n onBlur,\n onFocus,\n },\n writeButton: {\n tabIndex: -1,\n },\n }}\n paste={{saveImage}}\n refs={{textarea}}\n />\n </MarkdownTheme>\n </div>\n )\n})\n","import {useState, useEffect} from 'react'\n\nexport default function useDebounce(value: unknown, delay: number) {\n const [debouncedValue, setDebouncedValue] = useState(value)\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n return () => clearTimeout(handler)\n }, [value, delay])\n\n return debouncedValue\n}\n"],"names":[],"version":3,"file":"index.js.map","sourceRoot":"../"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {createPlugin as $jPEKE$createPlugin, defineType as $jPEKE$defineType, useClient as $jPEKE$useClient} from "sanity";
|
|
2
|
+
import {jsx as $jPEKE$jsx} from "react/jsx-runtime";
|
|
3
|
+
import $jPEKE$reactmde from "react-mde";
|
|
4
|
+
import $jPEKE$reactmarkdown from "react-markdown";
|
|
5
|
+
import $jPEKE$remarkgfm from "remark-gfm";
|
|
6
|
+
import {useId as $jPEKE$useId} from "@reach/auto-id";
|
|
7
|
+
import "react-mde/lib/styles/css/react-mde-all.css";
|
|
8
|
+
import {PatchEvent as $jPEKE$PatchEvent, unset as $jPEKE$unset, set as $jPEKE$set} from "sanity/form";
|
|
9
|
+
import {forwardRef as $jPEKE$forwardRef, useState as $jPEKE$useState, useRef as $jPEKE$useRef, useEffect as $jPEKE$useEffect, useCallback as $jPEKE$useCallback} from "react";
|
|
10
|
+
import $jPEKE$styledcomponents from "styled-components";
|
|
11
|
+
import {useTheme as $jPEKE$useTheme} from "@sanity/ui";
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function $a9f5cf746146e5f0$export$2e2bcd8739ae039(value, delay) {
|
|
20
|
+
const [debouncedValue, setDebouncedValue] = (0, $jPEKE$useState)(value);
|
|
21
|
+
(0, $jPEKE$useEffect)(()=>{
|
|
22
|
+
const handler = setTimeout(()=>{
|
|
23
|
+
setDebouncedValue(value);
|
|
24
|
+
}, delay);
|
|
25
|
+
return ()=>clearTimeout(handler);
|
|
26
|
+
}, [
|
|
27
|
+
value,
|
|
28
|
+
delay
|
|
29
|
+
]);
|
|
30
|
+
return debouncedValue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
const $483dd42bf797352b$var$Preview = ({ markdown: markdown })=>{
|
|
41
|
+
return /*#__PURE__*/ (0, $jPEKE$jsx)((0, $jPEKE$reactmarkdown), {
|
|
42
|
+
plugins: [
|
|
43
|
+
(0, $jPEKE$remarkgfm)
|
|
44
|
+
],
|
|
45
|
+
children: markdown
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const $483dd42bf797352b$var$defaultToolbarCommands = [
|
|
49
|
+
[
|
|
50
|
+
"header",
|
|
51
|
+
"bold",
|
|
52
|
+
"italic",
|
|
53
|
+
"strikethrough"
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
"link",
|
|
57
|
+
"quote",
|
|
58
|
+
"code"
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"unordered-list",
|
|
62
|
+
"ordered-list",
|
|
63
|
+
"checked-list"
|
|
64
|
+
],
|
|
65
|
+
];
|
|
66
|
+
// !important is used whenever we need to override inline styles for an element
|
|
67
|
+
const $483dd42bf797352b$var$MarkdownTheme = (0, $jPEKE$styledcomponents).div`
|
|
68
|
+
& .react-mde {
|
|
69
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
& .mde-text {
|
|
73
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.input.default.enabled.bg} !important;
|
|
74
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.input.default.enabled.fg} !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
& .mde-header {
|
|
78
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.bg};
|
|
79
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.fg};
|
|
80
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
81
|
+
|
|
82
|
+
& button,
|
|
83
|
+
.mde-header-item.mde-header-item.mde-header-item button {
|
|
84
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.button.bleed.default.enabled.fg};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
& button.selected {
|
|
88
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
& .image-tip {
|
|
93
|
+
background: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.bg};
|
|
94
|
+
color: ${({ studioTheme: studioTheme })=>studioTheme.color.muted.default.enabled.fg};
|
|
95
|
+
font-family: ${({ studioTheme: studioTheme })=>studioTheme.fonts.text.family};
|
|
96
|
+
border-color: ${({ studioTheme: studioTheme })=>studioTheme.color.card.enabled.border};
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
const $483dd42bf797352b$export$18a9461578883ec4 = /*#__PURE__*/ (0, $jPEKE$forwardRef)(function MarkdownEditor(props, ref) {
|
|
100
|
+
const { schemaType: type , value: value = "" , onBlur: onBlur , onChange: onChange , onFocus: onFocus , readOnly: readOnly } = props;
|
|
101
|
+
const options = type.options;
|
|
102
|
+
const [selectedTab, setSelectedTab] = (0, $jPEKE$useState)("write");
|
|
103
|
+
const [editedValue, setEditedValue] = (0, $jPEKE$useState)(value);
|
|
104
|
+
const debouncedValue = (0, $a9f5cf746146e5f0$export$2e2bcd8739ae039)(editedValue, 100);
|
|
105
|
+
const textarea = (0, $jPEKE$useRef)(null);
|
|
106
|
+
const sanityClient = (0, $jPEKE$useClient)();
|
|
107
|
+
// Conditionally update textarea styles based on read only access.
|
|
108
|
+
// It's also possible to define inline styles via `childProps.textArea.style` on `<ReactMde />`,
|
|
109
|
+
// but this will override any dynamically created styles provided by the component itself.
|
|
110
|
+
(0, $jPEKE$useEffect)(()=>{
|
|
111
|
+
if (textarea.current) textarea.current.style.backgroundColor = readOnly ? "rgba(240,240,240)" : "rgba(255,255,255)";
|
|
112
|
+
}, [
|
|
113
|
+
textarea,
|
|
114
|
+
readOnly
|
|
115
|
+
]);
|
|
116
|
+
(0, $jPEKE$useEffect)(()=>{
|
|
117
|
+
setEditedValue(value);
|
|
118
|
+
}, [
|
|
119
|
+
value
|
|
120
|
+
]);
|
|
121
|
+
(0, $jPEKE$useEffect)(()=>{
|
|
122
|
+
if (!debouncedValue && value) onChange((0, $jPEKE$PatchEvent).from([
|
|
123
|
+
(0, $jPEKE$unset)()
|
|
124
|
+
]));
|
|
125
|
+
else if (debouncedValue !== value) onChange((0, $jPEKE$PatchEvent).from([
|
|
126
|
+
(0, $jPEKE$set)(debouncedValue)
|
|
127
|
+
]));
|
|
128
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
129
|
+
}, [
|
|
130
|
+
debouncedValue,
|
|
131
|
+
onChange
|
|
132
|
+
]);
|
|
133
|
+
const saveImage = async function*(data) {
|
|
134
|
+
const client = sanityClient.withConfig({
|
|
135
|
+
apiVersion: "2021-03-25"
|
|
136
|
+
});
|
|
137
|
+
let success = true;
|
|
138
|
+
const result = await client.assets.upload("image", data).then((doc)=>`${doc.url}?w=450`).catch(()=>{
|
|
139
|
+
success = false;
|
|
140
|
+
return `Error: Could not upload file. Only images are supported.`;
|
|
141
|
+
});
|
|
142
|
+
yield result;
|
|
143
|
+
return success;
|
|
144
|
+
};
|
|
145
|
+
const generatePreview = (0, $jPEKE$useCallback)((markdown)=>Promise.resolve(/*#__PURE__*/ (0, $jPEKE$jsx)($483dd42bf797352b$var$Preview, {
|
|
146
|
+
markdown: markdown
|
|
147
|
+
})), []);
|
|
148
|
+
const inputId = (0, $jPEKE$useId)();
|
|
149
|
+
const { sanity: studioTheme } = (0, $jPEKE$useTheme)();
|
|
150
|
+
/*
|
|
151
|
+
Assign our forwarded ref to a wrapper element.
|
|
152
|
+
Ideally, this should be assigned to the textarea inside <ReactMde /> component.
|
|
153
|
+
Whilst `react-mde` does allow you to target individual sub-components' refs,
|
|
154
|
+
it will complain if you try and assign a forwarded ref.
|
|
155
|
+
*/ return /*#__PURE__*/ (0, $jPEKE$jsx)("div", {
|
|
156
|
+
ref: ref,
|
|
157
|
+
children: /*#__PURE__*/ (0, $jPEKE$jsx)($483dd42bf797352b$var$MarkdownTheme, {
|
|
158
|
+
studioTheme: studioTheme,
|
|
159
|
+
children: /*#__PURE__*/ (0, $jPEKE$jsx)((0, $jPEKE$reactmde), {
|
|
160
|
+
toolbarCommands: options?.toolbar || $483dd42bf797352b$var$defaultToolbarCommands,
|
|
161
|
+
value: editedValue,
|
|
162
|
+
onChange: setEditedValue,
|
|
163
|
+
selectedTab: selectedTab,
|
|
164
|
+
onTabChange: setSelectedTab,
|
|
165
|
+
readOnly: readOnly,
|
|
166
|
+
generateMarkdownPreview: generatePreview,
|
|
167
|
+
childProps: {
|
|
168
|
+
textArea: {
|
|
169
|
+
id: inputId,
|
|
170
|
+
onBlur: onBlur,
|
|
171
|
+
onFocus: onFocus
|
|
172
|
+
},
|
|
173
|
+
writeButton: {
|
|
174
|
+
tabIndex: -1
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
paste: {
|
|
178
|
+
saveImage: saveImage
|
|
179
|
+
},
|
|
180
|
+
refs: {
|
|
181
|
+
textarea: textarea
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
const $df9eabe9bda49ea8$export$f225414958dd8925 = (0, $jPEKE$createPlugin)({
|
|
191
|
+
name: "markdown-editor",
|
|
192
|
+
schema: {
|
|
193
|
+
types: [
|
|
194
|
+
(0, $jPEKE$defineType)({
|
|
195
|
+
type: "string",
|
|
196
|
+
name: "markdown",
|
|
197
|
+
title: "Markdown",
|
|
198
|
+
components: {
|
|
199
|
+
input: (0, $483dd42bf797352b$export$18a9461578883ec4)
|
|
200
|
+
}
|
|
201
|
+
}),
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
export {$df9eabe9bda49ea8$export$f225414958dd8925 as markdownSchema};
|
|
208
|
+
//# sourceMappingURL=index.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["src/index.ts","src/components/Editor.tsx","src/hooks/useDebounce.ts"],"sourcesContent":["import {MarkdownEditor} from './components/Editor'\nimport {createPlugin, defineType} from 'sanity'\n\nexport const markdownSchema = createPlugin({\n name: 'markdown-editor',\n schema: {\n types: [\n defineType({\n type: 'string',\n name: 'markdown',\n title: 'Markdown',\n components: {\n input: MarkdownEditor,\n },\n }),\n ],\n },\n})\n","import ReactMde from 'react-mde'\nimport ReactMarkdown from 'react-markdown'\nimport gfm from 'remark-gfm'\nimport {useId} from '@reach/auto-id'\n\nimport useDebounce from '../hooks/useDebounce'\nimport 'react-mde/lib/styles/css/react-mde-all.css'\nimport {PatchEvent, set, unset} from 'sanity/form'\nimport {StringInputProps, useClient} from 'sanity'\nimport React, {\n FC,\n forwardRef,\n PropsWithChildren,\n Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react'\nimport {ToolbarCommands} from 'react-mde/lib/definitions/types'\nimport styled from 'styled-components'\nimport {Theme, useTheme} from '@sanity/ui'\n\nconst Preview = ({markdown}: {markdown: string}) => {\n return <ReactMarkdown plugins={[gfm]}>{markdown}</ReactMarkdown>\n}\n\nconst defaultToolbarCommands = [\n ['header', 'bold', 'italic', 'strikethrough'],\n ['link', 'quote', 'code'],\n ['unordered-list', 'ordered-list', 'checked-list'],\n]\n\ntype SanityTheme = Theme['sanity']\n\ntype Style = PropsWithChildren<{\n studioTheme: SanityTheme\n}>\n\n// !important is used whenever we need to override inline styles for an element\nconst MarkdownTheme: FC<Style> = styled.div`\n & .react-mde {\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n\n & .mde-text {\n background: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.bg} !important;\n color: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.fg} !important;\n }\n\n & .mde-header {\n background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};\n color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.fg};\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n\n & button,\n .mde-header-item.mde-header-item.mde-header-item button {\n color: ${({studioTheme}: Style) => studioTheme.color.button.bleed.default.enabled.fg};\n }\n\n & button.selected {\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n }\n\n & .image-tip {\n background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};\n color: ${({studioTheme}: Style) => studioTheme.color.muted.default.enabled.fg};\n font-family: ${({studioTheme}: Style) => studioTheme.fonts.text.family};\n border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};\n }\n`\n\nexport const MarkdownEditor = forwardRef(function MarkdownEditor(\n props: StringInputProps,\n ref: Ref<any>\n) {\n const {schemaType: type, value = '', onBlur, onChange, onFocus, readOnly} = props\n const options: {toolbar?: ToolbarCommands} | undefined = type.options as any\n const [selectedTab, setSelectedTab] = useState<'write' | 'preview'>('write')\n const [editedValue, setEditedValue] = useState(value)\n const debouncedValue = useDebounce(editedValue, 100)\n const textarea = useRef<HTMLTextAreaElement>(null)\n const sanityClient = useClient()\n\n // Conditionally update textarea styles based on read only access.\n // It's also possible to define inline styles via `childProps.textArea.style` on `<ReactMde />`,\n // but this will override any dynamically created styles provided by the component itself.\n useEffect(() => {\n if (textarea.current) {\n textarea.current.style.backgroundColor = readOnly ? 'rgba(240,240,240)' : 'rgba(255,255,255)'\n }\n }, [textarea, readOnly])\n\n useEffect(() => {\n setEditedValue(value)\n }, [value])\n\n useEffect(() => {\n if (!debouncedValue && value) {\n onChange(PatchEvent.from([unset()]))\n } else if (debouncedValue !== value) {\n onChange(PatchEvent.from([set(debouncedValue)]))\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debouncedValue, onChange])\n\n const saveImage = async function* (data: any) {\n const client = sanityClient.withConfig({apiVersion: '2021-03-25'})\n\n let success = true\n const result = await client.assets\n .upload('image', data)\n .then((doc) => `${doc.url}?w=450`)\n .catch(() => {\n success = false\n return `Error: Could not upload file. Only images are supported.`\n })\n\n yield result\n return success\n }\n\n const generatePreview = useCallback(\n (markdown: string) => Promise.resolve(<Preview markdown={markdown} />),\n []\n )\n\n const inputId = useId()\n const {sanity: studioTheme} = useTheme()\n /*\n Assign our forwarded ref to a wrapper element.\n Ideally, this should be assigned to the textarea inside <ReactMde /> component.\n Whilst `react-mde` does allow you to target individual sub-components' refs,\n it will complain if you try and assign a forwarded ref.\n */\n return (\n <div ref={ref}>\n <MarkdownTheme studioTheme={studioTheme}>\n <ReactMde\n toolbarCommands={options?.toolbar || defaultToolbarCommands}\n value={editedValue}\n onChange={setEditedValue}\n selectedTab={selectedTab}\n onTabChange={setSelectedTab}\n readOnly={readOnly}\n generateMarkdownPreview={generatePreview}\n childProps={{\n textArea: {\n id: inputId,\n onBlur,\n onFocus,\n },\n writeButton: {\n tabIndex: -1,\n },\n }}\n paste={{saveImage}}\n refs={{textarea}}\n />\n </MarkdownTheme>\n </div>\n )\n})\n","import {useState, useEffect} from 'react'\n\nexport default function useDebounce(value: unknown, delay: number) {\n const [debouncedValue, setDebouncedValue] = useState(value)\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n return () => clearTimeout(handler)\n }, [value, delay])\n\n return debouncedValue\n}\n"],"names":[],"version":3,"file":"index.modern.js.map","sourceRoot":"../"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-markdown",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-studio-v3.0",
|
|
4
4
|
"description": "Markdown fields in Sanity Studio. Supports Github flavored Markdown and image uploads.",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
6
|
+
"clean": "rimraf lib",
|
|
7
|
+
"prebuild": "npm run clean && plugin-kit verify-package --silent",
|
|
8
|
+
"build": "parcel build --no-cache",
|
|
9
|
+
"watch": "parcel watch",
|
|
10
|
+
"link-watch": "plugin-kit link-watch",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"format": "prettier src -w",
|
|
13
|
+
"prepare": "husky install",
|
|
14
|
+
"compile": "tsc --noEmit",
|
|
15
|
+
"prepublishOnly": " npm run build"
|
|
9
16
|
},
|
|
17
|
+
"source": "./src/index.ts",
|
|
18
|
+
"main": "./lib/index.js",
|
|
19
|
+
"module": "./lib/index.modern.js",
|
|
20
|
+
"types": "./lib/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"require": "./lib/index.js",
|
|
24
|
+
"default": "./lib/index.modern.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"lib"
|
|
30
|
+
],
|
|
10
31
|
"keywords": [
|
|
11
32
|
"sanity",
|
|
12
33
|
"sanity-plugin",
|
|
@@ -17,35 +38,42 @@
|
|
|
17
38
|
"author": "Sanity.io <hello@sanity.io>",
|
|
18
39
|
"license": "MIT",
|
|
19
40
|
"dependencies": {
|
|
41
|
+
"@reach/auto-id": "^0.17.0",
|
|
42
|
+
"@sanity/incompatible-plugin": "^0.0.1-studio-v3.3",
|
|
43
|
+
"@sanity/ui": "^0.37.9",
|
|
20
44
|
"react-markdown": "^5.0.3",
|
|
21
45
|
"react-mde": "^11.5.0",
|
|
22
46
|
"remark-gfm": "^1.0.0"
|
|
23
47
|
},
|
|
24
48
|
"devDependencies": {
|
|
25
|
-
"
|
|
49
|
+
"@commitlint/cli": "^16.1.0",
|
|
50
|
+
"@commitlint/config-conventional": "^15.0.0",
|
|
51
|
+
"@parcel/packager-ts": "^2.6.0",
|
|
52
|
+
"@parcel/transformer-typescript-types": "^2.6.0",
|
|
53
|
+
"@sanity/plugin-kit": "^0.0.1-studio-v3.4",
|
|
54
|
+
"@types/styled-components": "^5.1.25",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^5.12.0",
|
|
56
|
+
"@typescript-eslint/parser": "^5.12.0",
|
|
57
|
+
"eslint": "^8.7.0",
|
|
26
58
|
"eslint-config-prettier": "^8.3.0",
|
|
27
|
-
"eslint-config-sanity": "5.1.0",
|
|
28
|
-
"eslint-plugin-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
59
|
+
"eslint-config-sanity": "^5.1.0",
|
|
60
|
+
"eslint-plugin-prettier": "4.0.0",
|
|
61
|
+
"eslint-plugin-react": "^7.28.0",
|
|
62
|
+
"eslint-plugin-react-hooks": "^4.5.0",
|
|
63
|
+
"husky": "^7.0.0",
|
|
64
|
+
"lint-staged": "^12.3.3",
|
|
65
|
+
"parcel": "^2.6.0",
|
|
66
|
+
"prettier": "^2.5.1",
|
|
67
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
68
|
+
"rimraf": "^3.0.2",
|
|
69
|
+
"sanity": "^2.30.1-purple-unicorn.964",
|
|
70
|
+
"styled-components": "^5.2.0",
|
|
71
|
+
"typescript": "^4.7.3"
|
|
31
72
|
},
|
|
32
73
|
"peerDependencies": {
|
|
33
|
-
"react": "^17.0.0"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"semi": false,
|
|
37
|
-
"printWidth": 100,
|
|
38
|
-
"bracketSpacing": false,
|
|
39
|
-
"singleQuote": true
|
|
40
|
-
},
|
|
41
|
-
"eslintConfig": {
|
|
42
|
-
"parser": "sanipack/babel/eslint-parser",
|
|
43
|
-
"extends": [
|
|
44
|
-
"sanity",
|
|
45
|
-
"sanity/react",
|
|
46
|
-
"prettier",
|
|
47
|
-
"prettier/react"
|
|
48
|
-
]
|
|
74
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
75
|
+
"sanity": "purple-unicorn",
|
|
76
|
+
"styled-components": "^5.2.0"
|
|
49
77
|
},
|
|
50
78
|
"repository": {
|
|
51
79
|
"type": "git",
|
|
@@ -54,5 +82,9 @@
|
|
|
54
82
|
"bugs": {
|
|
55
83
|
"url": "https://github.com/sanity-io/sanity-plugin-markdown/issues"
|
|
56
84
|
},
|
|
57
|
-
"homepage": "https://sanity.io"
|
|
85
|
+
"homepage": "https://sanity.io",
|
|
86
|
+
"sanityExchangeUrl": "https://www.sanity.io/plugins/sanity-plugin-markdown",
|
|
87
|
+
"engines": {
|
|
88
|
+
"node": ">=14.0.0"
|
|
89
|
+
}
|
|
58
90
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import ReactMde from 'react-mde'
|
|
2
|
+
import ReactMarkdown from 'react-markdown'
|
|
3
|
+
import gfm from 'remark-gfm'
|
|
4
|
+
import {useId} from '@reach/auto-id'
|
|
5
|
+
|
|
6
|
+
import useDebounce from '../hooks/useDebounce'
|
|
7
|
+
import 'react-mde/lib/styles/css/react-mde-all.css'
|
|
8
|
+
import {PatchEvent, set, unset} from 'sanity/form'
|
|
9
|
+
import {StringInputProps, useClient} from 'sanity'
|
|
10
|
+
import React, {
|
|
11
|
+
FC,
|
|
12
|
+
forwardRef,
|
|
13
|
+
PropsWithChildren,
|
|
14
|
+
Ref,
|
|
15
|
+
useCallback,
|
|
16
|
+
useEffect,
|
|
17
|
+
useRef,
|
|
18
|
+
useState,
|
|
19
|
+
} from 'react'
|
|
20
|
+
import {ToolbarCommands} from 'react-mde/lib/definitions/types'
|
|
21
|
+
import styled from 'styled-components'
|
|
22
|
+
import {Theme, useTheme} from '@sanity/ui'
|
|
23
|
+
|
|
24
|
+
const Preview = ({markdown}: {markdown: string}) => {
|
|
25
|
+
return <ReactMarkdown plugins={[gfm]}>{markdown}</ReactMarkdown>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultToolbarCommands = [
|
|
29
|
+
['header', 'bold', 'italic', 'strikethrough'],
|
|
30
|
+
['link', 'quote', 'code'],
|
|
31
|
+
['unordered-list', 'ordered-list', 'checked-list'],
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
type SanityTheme = Theme['sanity']
|
|
35
|
+
|
|
36
|
+
type Style = PropsWithChildren<{
|
|
37
|
+
studioTheme: SanityTheme
|
|
38
|
+
}>
|
|
39
|
+
|
|
40
|
+
// !important is used whenever we need to override inline styles for an element
|
|
41
|
+
const MarkdownTheme: FC<Style> = styled.div`
|
|
42
|
+
& .react-mde {
|
|
43
|
+
border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
& .mde-text {
|
|
47
|
+
background: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.bg} !important;
|
|
48
|
+
color: ${({studioTheme}: Style) => studioTheme.color.input.default.enabled.fg} !important;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
& .mde-header {
|
|
52
|
+
background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};
|
|
53
|
+
color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.fg};
|
|
54
|
+
border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};
|
|
55
|
+
|
|
56
|
+
& button,
|
|
57
|
+
.mde-header-item.mde-header-item.mde-header-item button {
|
|
58
|
+
color: ${({studioTheme}: Style) => studioTheme.color.button.bleed.default.enabled.fg};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
& button.selected {
|
|
62
|
+
border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
& .image-tip {
|
|
67
|
+
background: ${({studioTheme}: Style) => studioTheme.color.card.enabled.bg};
|
|
68
|
+
color: ${({studioTheme}: Style) => studioTheme.color.muted.default.enabled.fg};
|
|
69
|
+
font-family: ${({studioTheme}: Style) => studioTheme.fonts.text.family};
|
|
70
|
+
border-color: ${({studioTheme}: Style) => studioTheme.color.card.enabled.border};
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
export const MarkdownEditor = forwardRef(function MarkdownEditor(
|
|
75
|
+
props: StringInputProps,
|
|
76
|
+
ref: Ref<any>
|
|
77
|
+
) {
|
|
78
|
+
const {schemaType: type, value = '', onBlur, onChange, onFocus, readOnly} = props
|
|
79
|
+
const options: {toolbar?: ToolbarCommands} | undefined = type.options as any
|
|
80
|
+
const [selectedTab, setSelectedTab] = useState<'write' | 'preview'>('write')
|
|
81
|
+
const [editedValue, setEditedValue] = useState(value)
|
|
82
|
+
const debouncedValue = useDebounce(editedValue, 100)
|
|
83
|
+
const textarea = useRef<HTMLTextAreaElement>(null)
|
|
84
|
+
const sanityClient = useClient()
|
|
85
|
+
|
|
86
|
+
// Conditionally update textarea styles based on read only access.
|
|
87
|
+
// It's also possible to define inline styles via `childProps.textArea.style` on `<ReactMde />`,
|
|
88
|
+
// but this will override any dynamically created styles provided by the component itself.
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (textarea.current) {
|
|
91
|
+
textarea.current.style.backgroundColor = readOnly ? 'rgba(240,240,240)' : 'rgba(255,255,255)'
|
|
92
|
+
}
|
|
93
|
+
}, [textarea, readOnly])
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
setEditedValue(value)
|
|
97
|
+
}, [value])
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!debouncedValue && value) {
|
|
101
|
+
onChange(PatchEvent.from([unset()]))
|
|
102
|
+
} else if (debouncedValue !== value) {
|
|
103
|
+
onChange(PatchEvent.from([set(debouncedValue)]))
|
|
104
|
+
}
|
|
105
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
+
}, [debouncedValue, onChange])
|
|
107
|
+
|
|
108
|
+
const saveImage = async function* (data: any) {
|
|
109
|
+
const client = sanityClient.withConfig({apiVersion: '2021-03-25'})
|
|
110
|
+
|
|
111
|
+
let success = true
|
|
112
|
+
const result = await client.assets
|
|
113
|
+
.upload('image', data)
|
|
114
|
+
.then((doc) => `${doc.url}?w=450`)
|
|
115
|
+
.catch(() => {
|
|
116
|
+
success = false
|
|
117
|
+
return `Error: Could not upload file. Only images are supported.`
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
yield result
|
|
121
|
+
return success
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const generatePreview = useCallback(
|
|
125
|
+
(markdown: string) => Promise.resolve(<Preview markdown={markdown} />),
|
|
126
|
+
[]
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const inputId = useId()
|
|
130
|
+
const {sanity: studioTheme} = useTheme()
|
|
131
|
+
/*
|
|
132
|
+
Assign our forwarded ref to a wrapper element.
|
|
133
|
+
Ideally, this should be assigned to the textarea inside <ReactMde /> component.
|
|
134
|
+
Whilst `react-mde` does allow you to target individual sub-components' refs,
|
|
135
|
+
it will complain if you try and assign a forwarded ref.
|
|
136
|
+
*/
|
|
137
|
+
return (
|
|
138
|
+
<div ref={ref}>
|
|
139
|
+
<MarkdownTheme studioTheme={studioTheme}>
|
|
140
|
+
<ReactMde
|
|
141
|
+
toolbarCommands={options?.toolbar || defaultToolbarCommands}
|
|
142
|
+
value={editedValue}
|
|
143
|
+
onChange={setEditedValue}
|
|
144
|
+
selectedTab={selectedTab}
|
|
145
|
+
onTabChange={setSelectedTab}
|
|
146
|
+
readOnly={readOnly}
|
|
147
|
+
generateMarkdownPreview={generatePreview}
|
|
148
|
+
childProps={{
|
|
149
|
+
textArea: {
|
|
150
|
+
id: inputId,
|
|
151
|
+
onBlur,
|
|
152
|
+
onFocus,
|
|
153
|
+
},
|
|
154
|
+
writeButton: {
|
|
155
|
+
tabIndex: -1,
|
|
156
|
+
},
|
|
157
|
+
}}
|
|
158
|
+
paste={{saveImage}}
|
|
159
|
+
refs={{textarea}}
|
|
160
|
+
/>
|
|
161
|
+
</MarkdownTheme>
|
|
162
|
+
</div>
|
|
163
|
+
)
|
|
164
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {useState, useEffect} from 'react'
|
|
2
|
+
|
|
3
|
+
export default function useDebounce(value: unknown, delay: number) {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState(value)
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const handler = setTimeout(() => {
|
|
7
|
+
setDebouncedValue(value)
|
|
8
|
+
}, delay)
|
|
9
|
+
|
|
10
|
+
return () => clearTimeout(handler)
|
|
11
|
+
}, [value, delay])
|
|
12
|
+
|
|
13
|
+
return debouncedValue
|
|
14
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {MarkdownEditor} from './components/Editor'
|
|
2
|
+
import {createPlugin, defineType} from 'sanity'
|
|
3
|
+
|
|
4
|
+
export const markdownSchema = createPlugin({
|
|
5
|
+
name: 'markdown-editor',
|
|
6
|
+
schema: {
|
|
7
|
+
types: [
|
|
8
|
+
defineType({
|
|
9
|
+
type: 'string',
|
|
10
|
+
name: 'markdown',
|
|
11
|
+
title: 'Markdown',
|
|
12
|
+
components: {
|
|
13
|
+
input: MarkdownEditor,
|
|
14
|
+
},
|
|
15
|
+
}),
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
})
|
package/lib/components/Editor.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = Editor;
|
|
7
|
-
|
|
8
|
-
var React = _interopRequireWildcard(require("react"));
|
|
9
|
-
|
|
10
|
-
var _reactMde = _interopRequireDefault(require("react-mde"));
|
|
11
|
-
|
|
12
|
-
var _reactMarkdown = _interopRequireDefault(require("react-markdown"));
|
|
13
|
-
|
|
14
|
-
var _remarkGfm = _interopRequireDefault(require("remark-gfm"));
|
|
15
|
-
|
|
16
|
-
var _patchEvent = _interopRequireWildcard(require("part:@sanity/form-builder/patch-event"));
|
|
17
|
-
|
|
18
|
-
var _client = _interopRequireDefault(require("part:@sanity/base/client"));
|
|
19
|
-
|
|
20
|
-
var _default = _interopRequireDefault(require("part:@sanity/components/fieldsets/default"));
|
|
21
|
-
|
|
22
|
-
var _useDebounce = _interopRequireDefault(require("../hooks/useDebounce"));
|
|
23
|
-
|
|
24
|
-
require("react-mde/lib/styles/css/react-mde-all.css?raw");
|
|
25
|
-
|
|
26
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
|
-
|
|
28
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
29
|
-
|
|
30
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
31
|
-
|
|
32
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
33
|
-
|
|
34
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
35
|
-
|
|
36
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
37
|
-
|
|
38
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
39
|
-
|
|
40
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
41
|
-
|
|
42
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
43
|
-
|
|
44
|
-
function _awaitAsyncGenerator(value) { return new _AwaitValue(value); }
|
|
45
|
-
|
|
46
|
-
function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerator(fn.apply(this, arguments)); }; }
|
|
47
|
-
|
|
48
|
-
function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; var wrappedAwait = value instanceof _AwaitValue; Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) { if (wrappedAwait) { resume(key === "return" ? "return" : "next", arg); return; } settle(result.done ? "return" : "normal", arg); }, function (err) { resume("throw", err); }); } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } }
|
|
49
|
-
|
|
50
|
-
_AsyncGenerator.prototype[typeof Symbol === "function" && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; };
|
|
51
|
-
|
|
52
|
-
_AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); };
|
|
53
|
-
|
|
54
|
-
_AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); };
|
|
55
|
-
|
|
56
|
-
_AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); };
|
|
57
|
-
|
|
58
|
-
function _AwaitValue(value) { this.wrapped = value; }
|
|
59
|
-
|
|
60
|
-
var Preview = _ref2 => {
|
|
61
|
-
var markdown = _ref2.markdown;
|
|
62
|
-
return /*#__PURE__*/React.createElement(_reactMarkdown.default, {
|
|
63
|
-
plugins: [_remarkGfm.default]
|
|
64
|
-
}, markdown);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
var defaultToolbarCommands = [['header', 'bold', 'italic', 'strikethrough'], ['link', 'quote', 'code'], ['unordered-list', 'ordered-list', 'checked-list']];
|
|
68
|
-
|
|
69
|
-
function Editor(props) {
|
|
70
|
-
var type = props.type,
|
|
71
|
-
_props$value = props.value,
|
|
72
|
-
value = _props$value === void 0 ? '' : _props$value;
|
|
73
|
-
var _type$options = type.options,
|
|
74
|
-
options = _type$options === void 0 ? {} : _type$options;
|
|
75
|
-
|
|
76
|
-
var _React$useState = React.useState('write'),
|
|
77
|
-
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
78
|
-
selectedTab = _React$useState2[0],
|
|
79
|
-
setSelectedTab = _React$useState2[1];
|
|
80
|
-
|
|
81
|
-
var _React$useState3 = React.useState(value),
|
|
82
|
-
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
83
|
-
editedValue = _React$useState4[0],
|
|
84
|
-
setEditedValue = _React$useState4[1];
|
|
85
|
-
|
|
86
|
-
var debouncedValue = (0, _useDebounce.default)(editedValue, 100);
|
|
87
|
-
React.useEffect(() => {
|
|
88
|
-
setEditedValue(value);
|
|
89
|
-
}, [value]);
|
|
90
|
-
React.useEffect(() => {
|
|
91
|
-
if (!debouncedValue && value) {
|
|
92
|
-
props.onChange(_patchEvent.default.from([(0, _patchEvent.unset)()]));
|
|
93
|
-
} else if (debouncedValue !== value) {
|
|
94
|
-
props.onChange(_patchEvent.default.from([(0, _patchEvent.set)(debouncedValue)]));
|
|
95
|
-
}
|
|
96
|
-
}, [debouncedValue]);
|
|
97
|
-
|
|
98
|
-
var saveImage = /*#__PURE__*/function () {
|
|
99
|
-
var _ref = _wrapAsyncGenerator(function* (data) {
|
|
100
|
-
var client = _client.default.withConfig({
|
|
101
|
-
apiVersion: '2021-03-25'
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
var success = true;
|
|
105
|
-
var result = yield _awaitAsyncGenerator(client.assets.upload('image', data).then(doc => "".concat(doc.url, "?w=450")).catch(() => {
|
|
106
|
-
success = false;
|
|
107
|
-
return "Error: Could not upload file. Only images are supported.";
|
|
108
|
-
}));
|
|
109
|
-
yield result;
|
|
110
|
-
return success;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
return function saveImage(_x) {
|
|
114
|
-
return _ref.apply(this, arguments);
|
|
115
|
-
};
|
|
116
|
-
}();
|
|
117
|
-
|
|
118
|
-
return /*#__PURE__*/React.createElement(_default.default, {
|
|
119
|
-
markers: props.markers,
|
|
120
|
-
presence: props.presence,
|
|
121
|
-
legend: props.type.title,
|
|
122
|
-
description: props.type.description,
|
|
123
|
-
level: props.level
|
|
124
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
125
|
-
className: "container"
|
|
126
|
-
}, /*#__PURE__*/React.createElement(_reactMde.default, {
|
|
127
|
-
toolbarCommands: options['toolbar'] || defaultToolbarCommands,
|
|
128
|
-
value: editedValue,
|
|
129
|
-
onChange: setEditedValue,
|
|
130
|
-
selectedTab: selectedTab,
|
|
131
|
-
onTabChange: setSelectedTab,
|
|
132
|
-
generateMarkdownPreview: markdown => Promise.resolve( /*#__PURE__*/React.createElement(Preview, {
|
|
133
|
-
markdown: markdown
|
|
134
|
-
})),
|
|
135
|
-
childProps: {
|
|
136
|
-
writeButton: {
|
|
137
|
-
tabIndex: -1
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
paste: {
|
|
141
|
-
saveImage
|
|
142
|
-
}
|
|
143
|
-
})));
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=Editor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Editor.jsx"],"names":["Preview","markdown","gfm","defaultToolbarCommands","Editor","props","type","value","options","React","useState","selectedTab","setSelectedTab","editedValue","setEditedValue","debouncedValue","useEffect","onChange","PatchEvent","from","saveImage","data","client","sanityClient","withConfig","apiVersion","success","result","assets","upload","then","doc","url","catch","markers","presence","title","description","level","Promise","resolve","writeButton","tabIndex"],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport ReactMde from 'react-mde'\nimport ReactMarkdown from 'react-markdown'\nimport gfm from 'remark-gfm'\n\nimport PatchEvent, {set, unset} from 'part:@sanity/form-builder/patch-event'\nimport sanityClient from 'part:@sanity/base/client'\n\nimport Fieldset from 'part:@sanity/components/fieldsets/default'\n\nimport useDebounce from '../hooks/useDebounce'\n\nimport 'react-mde/lib/styles/css/react-mde-all.css?raw'\n\nconst Preview = ({markdown}) => {\n return <ReactMarkdown plugins={[gfm]}>{markdown}</ReactMarkdown>\n}\n\nconst defaultToolbarCommands = [\n ['header', 'bold', 'italic', 'strikethrough'],\n ['link', 'quote', 'code'],\n ['unordered-list', 'ordered-list', 'checked-list'],\n]\n\nexport default function Editor(props) {\n const {type, value = ''} = props\n const {options = {}} = type\n const [selectedTab, setSelectedTab] = React.useState('write')\n const [editedValue, setEditedValue] = React.useState(value)\n const debouncedValue = useDebounce(editedValue, 100)\n\n React.useEffect(() => {\n setEditedValue(value)\n }, [value])\n\n React.useEffect(() => {\n if (!debouncedValue && value) {\n props.onChange(PatchEvent.from([unset()]))\n } else if (debouncedValue !== value) {\n props.onChange(PatchEvent.from([set(debouncedValue)]))\n }\n }, [debouncedValue])\n\n const saveImage = async function* (data) {\n const client = sanityClient.withConfig({apiVersion: '2021-03-25'})\n\n let success = true\n const result = await client.assets\n .upload('image', data)\n .then((doc) => `${doc.url}?w=450`)\n .catch(() => {\n success = false\n return `Error: Could not upload file. Only images are supported.`\n })\n\n yield result\n return success\n }\n\n return (\n <Fieldset\n markers={props.markers}\n presence={props.presence}\n legend={props.type.title}\n description={props.type.description}\n level={props.level}\n >\n <div className=\"container\">\n <ReactMde\n toolbarCommands={options['toolbar'] || defaultToolbarCommands}\n value={editedValue}\n onChange={setEditedValue}\n selectedTab={selectedTab}\n onTabChange={setSelectedTab}\n generateMarkdownPreview={(markdown) => Promise.resolve(<Preview markdown={markdown} />)}\n childProps={{\n writeButton: {\n tabIndex: -1,\n },\n }}\n paste={{saveImage}}\n />\n </div>\n </Fieldset>\n )\n}\n"],"file":"Editor.js"}
|
package/lib/hooks/useDebounce.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = useDebounce;
|
|
7
|
-
|
|
8
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
-
|
|
10
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
11
|
-
|
|
12
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
-
|
|
14
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
15
|
-
|
|
16
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
17
|
-
|
|
18
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
19
|
-
|
|
20
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
21
|
-
|
|
22
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
23
|
-
|
|
24
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
25
|
-
|
|
26
|
-
function useDebounce(value, delay) {
|
|
27
|
-
var _useState = (0, _react.useState)(value),
|
|
28
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
29
|
-
debouncedValue = _useState2[0],
|
|
30
|
-
setDebouncedValue = _useState2[1];
|
|
31
|
-
|
|
32
|
-
(0, _react.useEffect)(() => {
|
|
33
|
-
var handler = setTimeout(() => {
|
|
34
|
-
setDebouncedValue(value);
|
|
35
|
-
}, delay);
|
|
36
|
-
return () => clearTimeout(handler);
|
|
37
|
-
}, [value]);
|
|
38
|
-
return debouncedValue;
|
|
39
|
-
}
|
|
40
|
-
//# sourceMappingURL=useDebounce.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/useDebounce.js"],"names":["useDebounce","value","delay","debouncedValue","setDebouncedValue","handler","setTimeout","clearTimeout"],"mappings":";;;;;;;AAAA;;;;;;;;;;;;;;;;;;AACe,SAASA,WAAT,CAAqBC,KAArB,EAA4BC,KAA5B,EAAmC;AAChD,kBAA4C,qBAASD,KAAT,CAA5C;AAAA;AAAA,MAAOE,cAAP;AAAA,MAAuBC,iBAAvB;;AACA,wBAAU,MAAM;AACd,QAAMC,OAAO,GAAGC,UAAU,CAAC,MAAM;AAC/BF,MAAAA,iBAAiB,CAACH,KAAD,CAAjB;AACD,KAFyB,EAEvBC,KAFuB,CAA1B;AAIA,WAAO,MAAMK,YAAY,CAACF,OAAD,CAAzB;AACD,GAND,EAMG,CAACJ,KAAD,CANH;AAQA,SAAOE,cAAP;AACD","sourcesContent":["import React, {useState, useEffect} from 'react'\nexport default function useDebounce(value, delay) {\n const [debouncedValue, setDebouncedValue] = useState(value)\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n return () => clearTimeout(handler)\n }, [value])\n\n return debouncedValue\n}\n"],"file":"useDebounce.js"}
|
package/lib/schemaType.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = void 0;
|
|
7
|
-
|
|
8
|
-
var _Editor = _interopRequireDefault(require("./components/Editor"));
|
|
9
|
-
|
|
10
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
-
|
|
12
|
-
var _default = {
|
|
13
|
-
type: 'string',
|
|
14
|
-
name: 'markdown',
|
|
15
|
-
inputComponent: _Editor.default
|
|
16
|
-
};
|
|
17
|
-
exports.default = _default;
|
|
18
|
-
//# sourceMappingURL=schemaType.js.map
|
package/lib/schemaType.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schemaType.js"],"names":["type","name","inputComponent","Editor"],"mappings":";;;;;;;AAAA;;;;eAEe;AACbA,EAAAA,IAAI,EAAE,QADO;AAEbC,EAAAA,IAAI,EAAE,UAFO;AAGbC,EAAAA,cAAc,EAAEC;AAHH,C","sourcesContent":["import Editor from './components/Editor'\n\nexport default {\n type: 'string',\n name: 'markdown',\n inputComponent: Editor,\n}\n"],"file":"schemaType.js"}
|