sanity-plugin-markdown 3.0.0-v3-studio.7 → 3.0.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 +4 -7
- package/lib/index.esm.js +1 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/src/index.d.ts +4 -4
- package/package.json +6 -7
- package/src/components/Editor.tsx +80 -17
package/README.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# sanity-plugin-markdown
|
|
2
2
|
|
|
3
|
-
> **
|
|
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).
|
|
3
|
+
> This is a **Sanity Studio v3** plugin.
|
|
4
|
+
> For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-markdown/tree/studio-v2).
|
|
8
5
|
|
|
9
6
|
## What is it?
|
|
10
7
|
|
|
@@ -23,13 +20,13 @@ You can still add markdown image tags, but you will have to get the image url yo
|
|
|
23
20
|
## Installation
|
|
24
21
|
|
|
25
22
|
```
|
|
26
|
-
npm install --save sanity-plugin-markdown
|
|
23
|
+
npm install --save sanity-plugin-markdown
|
|
27
24
|
```
|
|
28
25
|
|
|
29
26
|
or
|
|
30
27
|
|
|
31
28
|
```
|
|
32
|
-
yarn add sanity-plugin-markdown
|
|
29
|
+
yarn add sanity-plugin-markdown
|
|
33
30
|
```
|
|
34
31
|
|
|
35
32
|
## Usage
|
package/lib/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,
|
|
1
|
+
function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}import{jsx as r}from"react/jsx-runtime";import n from"@uiw/react-md-editor";import{useState as o,useEffect as a,forwardRef as i,useCallback as c}from"react";import{useClient as s,PatchEvent as l,unset as u,set as p,defineType as m,definePlugin as f}from"sanity";import{useTheme as d,Card as y}from"@sanity/ui";import b from"rehype-sanitize";const g=i((function(e,t){const{value:i="",onChange:m,elementProps:{onBlur:f,onFocus:g},readOnly:w}=e,[h,P]=o(i),j=function(e,t){const[r,n]=o(e);return a((()=>{const r=setTimeout((()=>{n(e)}),t);return()=>clearTimeout(r)}),[e,t]),r}(h,100),v=s({apiVersion:"2022-01-01"});a((()=>{P(i)}),[i]),a((()=>{!j&&i?m(l.from([u()])):j!==i&&m(l.from([p(j)]))}),[j,m]);const{sanity:k}=d(),D=c((async e=>{await O(e.clipboardData,P,v)}),[P,v]),E=c((async e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&await O(e.dataTransfer,P,v)}),[P,v]);return r("div",{ref:t,"data-color-mode":k.color.dark?"dark":"light",children:w?r(y,{border:!0,padding:3,children:r(n.Markdown,{source:i,rehypePlugins:[[b]]})}):r(n,{value:h,onChange:P,onBlur:f,onFocus:g,previewOptions:{rehypePlugins:[[b]]},preview:"edit",onPaste:D,onDrop:E})})}));async function O(e,t,r){const n=[];for(let t=0;t<e.items.length;t+=1){const r=e.files.item(t);r&&n.push(r)}await Promise.all(n.map((async e=>{const n=await r.assets.upload("image",e).then((e=>"".concat(e.url,"?w=450"))),o=function(e){const t=document.querySelector("textarea");if(!t)return null;let r=t.value;const n=r.length,o=t.selectionStart,a=t.selectionEnd,i=r.slice(0,o),c=r.slice(o,n);return r=i+e+c,t.value=r,t.selectionEnd=a+e.length,r}(""));o&&t(o)})))}const w=m(function(r){for(var n=1;n<arguments.length;n++){var o=null!=arguments[n]?arguments[n]:{};n%2?e(Object(o),!0).forEach((function(e){t(r,e,o[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):e(Object(o)).forEach((function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))}))}return r}({type:"string",name:"markdown",title:"Markdown"},{components:{input:g}})),h=g,P=f({name:"markdown-editor",schema:{types:[w]}});export{h as MarkdownEditor,P as markdownSchema,w as markdownSchemaType};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/lib/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/components/Editor.tsx","../src/hooks/useDebounce.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import MDEditor from '@uiw/react-md-editor'\nimport useDebounce from '../hooks/useDebounce'\nimport {
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/components/Editor.tsx","../src/hooks/useDebounce.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import MDEditor from '@uiw/react-md-editor'\nimport useDebounce from '../hooks/useDebounce'\nimport {PatchEvent, SanityClient, set, StringInputProps, unset, useClient} from 'sanity'\nimport React, {\n ClipboardEvent,\n forwardRef,\n Ref,\n SetStateAction,\n useCallback,\n useEffect,\n useState,\n DragEvent,\n} from 'react'\nimport {Card, useTheme} from '@sanity/ui'\nimport rehypeSanitize from 'rehype-sanitize'\n\nexport const MarkdownEditor = forwardRef(function MarkdownEditor(\n props: StringInputProps,\n ref: Ref<any>\n) {\n const {\n value = '',\n onChange,\n elementProps: {onBlur, onFocus},\n readOnly,\n } = props\n const [editedValue, setEditedValue] = useState<string | undefined>(value)\n const debouncedValue = useDebounce(editedValue, 100)\n // const client = useClient({apiVersion: '2021-03-25'})\n const client = useClient({apiVersion: '2022-01-01'})\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 {sanity: studioTheme} = useTheme()\n const onPaste = useCallback(\n async (event: ClipboardEvent<HTMLDivElement>) => {\n await onImagePasted(event.clipboardData, setEditedValue, client)\n },\n [setEditedValue, client]\n )\n\n const onDrop = useCallback(\n async (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer) {\n await onImagePasted(event.dataTransfer, setEditedValue, client)\n }\n },\n [setEditedValue, client]\n )\n\n return (\n <div ref={ref} data-color-mode={studioTheme.color.dark ? 'dark' : 'light'}>\n {readOnly ? (\n <Card border padding={3}>\n <MDEditor.Markdown source={value} rehypePlugins={[[rehypeSanitize]]} />\n </Card>\n ) : (\n <MDEditor\n value={editedValue}\n onChange={setEditedValue}\n onBlur={onBlur}\n onFocus={onFocus}\n previewOptions={{\n rehypePlugins: [[rehypeSanitize]],\n }}\n preview=\"edit\"\n onPaste={onPaste}\n onDrop={onDrop}\n />\n )}\n </div>\n )\n})\n\n// https://github.com/uiwjs/react-md-editor/issues/83#issuecomment-1185471844\nasync function onImagePasted(\n dataTransfer: DataTransfer,\n setMarkdown: (value: SetStateAction<string | undefined>) => void,\n client: SanityClient\n) {\n const files: File[] = []\n for (let index = 0; index < dataTransfer.items.length; index += 1) {\n const file = dataTransfer.files.item(index)\n\n if (file) {\n files.push(file)\n }\n }\n\n await Promise.all(\n files.map(async (file) => {\n const url = await client.assets.upload('image', file).then((doc) => `${doc.url}?w=450`)\n const insertedMarkdown = insertToTextArea(``)\n if (!insertedMarkdown) {\n return\n }\n setMarkdown(insertedMarkdown)\n })\n )\n}\n\nfunction insertToTextArea(insertString: string) {\n const textarea = document.querySelector('textarea')\n if (!textarea) {\n return null\n }\n\n let sentence = textarea.value\n const len = sentence.length\n const pos = textarea.selectionStart\n const end = textarea.selectionEnd\n\n const front = sentence.slice(0, pos)\n const back = sentence.slice(pos, len)\n\n sentence = front + insertString + back\n\n textarea.value = sentence\n textarea.selectionEnd = end + insertString.length\n\n return sentence\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","import {defineType, StringDefinition} from 'sanity'\nimport {MarkdownEditor} from './components/Editor'\n\nconst markdownTypeName = 'markdown' as const\n\n/**\n * @public\n */\nexport interface MarkdownDefinition extends Omit<StringDefinition, 'type' | 'fields' | 'options'> {\n type: typeof markdownTypeName\n}\n\ndeclare module '@sanity/types' {\n // makes type: 'markdown' narrow correctly when using defineType/defineField/defineArrayMember\n export interface IntrinsicDefinitions {\n markdown: MarkdownDefinition\n }\n}\n\nexport const markdownSchemaType = defineType({\n type: 'string',\n name: markdownTypeName,\n title: 'Markdown',\n ...({components: {input: MarkdownEditor}} as {}), //TODO revert when rc.1 ships\n})\n","import {MarkdownEditor as Editor} from './components/Editor'\nimport {definePlugin} from 'sanity'\nimport {markdownSchemaType, MarkdownDefinition} from './schema'\n\n// re-exporting MarkdownEditor directly explodes @parcel/transformer-typescript-types :shrug:\nconst MarkdownEditor = Editor\n\nexport {MarkdownEditor, markdownSchemaType}\nexport type {MarkdownDefinition}\n\nexport const markdownSchema = definePlugin({\n name: 'markdown-editor',\n schema: {\n types: [markdownSchemaType],\n },\n})\n"],"names":["MarkdownEditor","forwardRef","props","ref","value","onChange","elementProps","onBlur","onFocus","readOnly","editedValue","setEditedValue","useState","debouncedValue","delay","setDebouncedValue","useEffect","handler","setTimeout","clearTimeout","useDebounce","client","useClient","apiVersion","PatchEvent","from","unset","set","sanity","studioTheme","useTheme","onPaste","useCallback","async","onImagePasted","event","clipboardData","onDrop","preventDefault","stopPropagation","dataTransfer","jsx","color","dark","children","Card","border","padding","MDEditor","Markdown","source","rehypePlugins","rehypeSanitize","previewOptions","preview","setMarkdown","files","index","items","length","file","item","push","Promise","all","map","url","assets","upload","then","doc","insertedMarkdown","insertString","textarea","document","querySelector","sentence","len","pos","selectionStart","end","selectionEnd","front","slice","back","insertToTextArea","concat","markdownSchemaType","defineType","_objectSpread","type","name","title","components","input","Editor","markdownSchema","definePlugin","schema","types"],"mappings":"2qBAgBO,MAAMA,EAAiBC,GAAW,SACvCC,EACAC,GAEM,MAAAC,MACJA,EAAQ,GAAAC,SACRA,EACAC,cAAcC,OAACA,EAAAC,QAAQA,GAAOC,SAC9BA,GACEP,GACGQ,EAAaC,GAAkBC,EAA6BR,GAC7DS,ECzBgB,SAAYT,EAAgBU,GAClD,MAAOD,EAAgBE,GAAqBH,EAASR,GAS9C,OARPY,GAAU,KACF,MAAAC,EAAUC,YAAW,KACzBH,EAAkBX,EAAK,GACtBU,GAEI,MAAA,IAAMK,aAAaF,EAAO,GAChC,CAACb,EAAOU,IAEJD,CACT,CDcyBO,CAAYV,EAAa,KAE1CW,EAASC,EAAU,CAACC,WAAY,eACtCP,GAAU,KACRL,EAAeP,EAAK,GACnB,CAACA,IAEJY,GAAU,MACHH,GAAkBT,EACrBC,EAASmB,EAAWC,KAAK,CAACC,OACjBb,IAAmBT,GAC5BC,EAASmB,EAAWC,KAAK,CAACE,EAAId,KAChC,GAEC,CAACA,EAAgBR,IAEpB,MAAOuB,OAAQC,GAAeC,IACxBC,EAAUC,GACdC,gBACQC,EAAcC,EAAMC,cAAezB,EAAgBU,EAAM,GAEjE,CAACV,EAAgBU,IAGbgB,EAASL,GACbC,UACEE,EAAMG,iBACNH,EAAMI,kBACFJ,EAAMK,oBACFN,EAAcC,EAAMK,aAAc7B,EAAgBU,EAC1D,GAEF,CAACV,EAAgBU,IAGnB,OACGoB,EAAA,MAAA,CAAItC,MAAU,kBAAiB0B,EAAYa,MAAMC,KAAO,OAAS,QAC/DC,WACEH,EAAAI,EAAA,CAAKC,QAAM,EAACC,QAAS,EACpBH,SAAAH,EAACO,EAASC,SAAT,CAAkBC,OAAQ9C,EAAO+C,cAAe,CAAC,CAACC,QAGpDX,EAAAO,EAAA,CACC5C,MAAOM,EACPL,SAAUM,EACVJ,SACAC,UACA6C,eAAgB,CACdF,cAAe,CAAC,CAACC,KAEnBE,QAAQ,OACRvB,UACAM,YAKV,IAGAJ,eAAeC,EACbM,EACAe,EACAlC,GAEA,MAAMmC,EAAgB,GACtB,IAAA,IAASC,EAAQ,EAAGA,EAAQjB,EAAakB,MAAMC,OAAQF,GAAS,EAAG,CACjE,MAAMG,EAAOpB,EAAagB,MAAMK,KAAKJ,GAEjCG,GACFJ,EAAMM,KAAKF,EAEf,OAEMG,QAAQC,IACZR,EAAMS,KAAIhC,UACR,MAAMiC,QAAY7C,EAAO8C,OAAOC,OAAO,QAASR,GAAMS,MAAMC,aAAWA,EAAIJ,IAAW,YAChFK,EASZ,SAA0BC,GAClB,MAAAC,EAAWC,SAASC,cAAc,YACxC,IAAKF,EACI,OAAA,KAGT,IAAIG,EAAWH,EAASrE,MACxB,MAAMyE,EAAMD,EAASjB,OACfmB,EAAML,EAASM,eACfC,EAAMP,EAASQ,aAEfC,EAAQN,EAASO,MAAM,EAAGL,GAC1BM,EAAOR,EAASO,MAAML,EAAKD,GAO1B,OALPD,EAAWM,EAAQV,EAAeY,EAElCX,EAASrE,MAAQwE,EACRH,EAAAQ,aAAeD,EAAMR,EAAab,OAEpCiB,CACT,CA7B+BS,CAAiB,OAAAC,OAAOpB,EAAM,MAClDK,GAGLhB,EAAYgB,EAAgB,IAGlC,CE5GA,MAgBagB,EAAqBC,iWAAWC,CAAA,CAC3CC,KAAM,SACNC,KAlBuB,WAmBvBC,MAAO,YACH,CAACC,WAAY,CAACC,MAAO9F,MClBrBA,EAAiB+F,EAKVC,EAAiBC,EAAa,CACzCN,KAAM,kBACNO,OAAQ,CACNC,MAAO,CAACZ"}
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function e(e,
|
|
1
|
+
"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}Object.defineProperty(exports,"__esModule",{value:!0});var r=require("react/jsx-runtime"),n=require("@uiw/react-md-editor"),o=require("react"),a=require("sanity"),i=require("@sanity/ui"),c=require("rehype-sanitize");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var u=s(n),l=s(c);const f=o.forwardRef((function(e,t){const{value:n="",onChange:c,elementProps:{onBlur:s,onFocus:f},readOnly:p}=e,[y,m]=o.useState(n),b=function(e,t){const[r,n]=o.useState(e);return o.useEffect((()=>{const r=setTimeout((()=>{n(e)}),t);return()=>clearTimeout(r)}),[e,t]),r}(y,100),h=a.useClient({apiVersion:"2022-01-01"});o.useEffect((()=>{m(n)}),[n]),o.useEffect((()=>{!b&&n?c(a.PatchEvent.from([a.unset()])):b!==n&&c(a.PatchEvent.from([a.set(b)]))}),[b,c]);const{sanity:w}=i.useTheme(),g=o.useCallback((async e=>{await d(e.clipboardData,m,h)}),[m,h]),O=o.useCallback((async e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&await d(e.dataTransfer,m,h)}),[m,h]);return r.jsx("div",{ref:t,"data-color-mode":w.color.dark?"dark":"light",children:p?r.jsx(i.Card,{border:!0,padding:3,children:r.jsx(u.default.Markdown,{source:n,rehypePlugins:[[l.default]]})}):r.jsx(u.default,{value:y,onChange:m,onBlur:s,onFocus:f,previewOptions:{rehypePlugins:[[l.default]]},preview:"edit",onPaste:g,onDrop:O})})}));async function d(e,t,r){const n=[];for(let t=0;t<e.items.length;t+=1){const r=e.files.item(t);r&&n.push(r)}await Promise.all(n.map((async e=>{const n=await r.assets.upload("image",e).then((e=>"".concat(e.url,"?w=450"))),o=function(e){const t=document.querySelector("textarea");if(!t)return null;let r=t.value;const n=r.length,o=t.selectionStart,a=t.selectionEnd,i=r.slice(0,o),c=r.slice(o,n);return r=i+e+c,t.value=r,t.selectionEnd=a+e.length,r}(""));o&&t(o)})))}const p=a.defineType(function(r){for(var n=1;n<arguments.length;n++){var o=null!=arguments[n]?arguments[n]:{};n%2?e(Object(o),!0).forEach((function(e){t(r,e,o[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):e(Object(o)).forEach((function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))}))}return r}({type:"string",name:"markdown",title:"Markdown"},{components:{input:f}})),y=f,m=a.definePlugin({name:"markdown-editor",schema:{types:[p]}});exports.MarkdownEditor=y,exports.markdownSchema=m,exports.markdownSchemaType=p;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/components/Editor.tsx","../src/hooks/useDebounce.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import MDEditor from '@uiw/react-md-editor'\nimport useDebounce from '../hooks/useDebounce'\nimport {
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/Editor.tsx","../src/hooks/useDebounce.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import MDEditor from '@uiw/react-md-editor'\nimport useDebounce from '../hooks/useDebounce'\nimport {PatchEvent, SanityClient, set, StringInputProps, unset, useClient} from 'sanity'\nimport React, {\n ClipboardEvent,\n forwardRef,\n Ref,\n SetStateAction,\n useCallback,\n useEffect,\n useState,\n DragEvent,\n} from 'react'\nimport {Card, useTheme} from '@sanity/ui'\nimport rehypeSanitize from 'rehype-sanitize'\n\nexport const MarkdownEditor = forwardRef(function MarkdownEditor(\n props: StringInputProps,\n ref: Ref<any>\n) {\n const {\n value = '',\n onChange,\n elementProps: {onBlur, onFocus},\n readOnly,\n } = props\n const [editedValue, setEditedValue] = useState<string | undefined>(value)\n const debouncedValue = useDebounce(editedValue, 100)\n // const client = useClient({apiVersion: '2021-03-25'})\n const client = useClient({apiVersion: '2022-01-01'})\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 {sanity: studioTheme} = useTheme()\n const onPaste = useCallback(\n async (event: ClipboardEvent<HTMLDivElement>) => {\n await onImagePasted(event.clipboardData, setEditedValue, client)\n },\n [setEditedValue, client]\n )\n\n const onDrop = useCallback(\n async (event: DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n if (event.dataTransfer) {\n await onImagePasted(event.dataTransfer, setEditedValue, client)\n }\n },\n [setEditedValue, client]\n )\n\n return (\n <div ref={ref} data-color-mode={studioTheme.color.dark ? 'dark' : 'light'}>\n {readOnly ? (\n <Card border padding={3}>\n <MDEditor.Markdown source={value} rehypePlugins={[[rehypeSanitize]]} />\n </Card>\n ) : (\n <MDEditor\n value={editedValue}\n onChange={setEditedValue}\n onBlur={onBlur}\n onFocus={onFocus}\n previewOptions={{\n rehypePlugins: [[rehypeSanitize]],\n }}\n preview=\"edit\"\n onPaste={onPaste}\n onDrop={onDrop}\n />\n )}\n </div>\n )\n})\n\n// https://github.com/uiwjs/react-md-editor/issues/83#issuecomment-1185471844\nasync function onImagePasted(\n dataTransfer: DataTransfer,\n setMarkdown: (value: SetStateAction<string | undefined>) => void,\n client: SanityClient\n) {\n const files: File[] = []\n for (let index = 0; index < dataTransfer.items.length; index += 1) {\n const file = dataTransfer.files.item(index)\n\n if (file) {\n files.push(file)\n }\n }\n\n await Promise.all(\n files.map(async (file) => {\n const url = await client.assets.upload('image', file).then((doc) => `${doc.url}?w=450`)\n const insertedMarkdown = insertToTextArea(``)\n if (!insertedMarkdown) {\n return\n }\n setMarkdown(insertedMarkdown)\n })\n )\n}\n\nfunction insertToTextArea(insertString: string) {\n const textarea = document.querySelector('textarea')\n if (!textarea) {\n return null\n }\n\n let sentence = textarea.value\n const len = sentence.length\n const pos = textarea.selectionStart\n const end = textarea.selectionEnd\n\n const front = sentence.slice(0, pos)\n const back = sentence.slice(pos, len)\n\n sentence = front + insertString + back\n\n textarea.value = sentence\n textarea.selectionEnd = end + insertString.length\n\n return sentence\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","import {defineType, StringDefinition} from 'sanity'\nimport {MarkdownEditor} from './components/Editor'\n\nconst markdownTypeName = 'markdown' as const\n\n/**\n * @public\n */\nexport interface MarkdownDefinition extends Omit<StringDefinition, 'type' | 'fields' | 'options'> {\n type: typeof markdownTypeName\n}\n\ndeclare module '@sanity/types' {\n // makes type: 'markdown' narrow correctly when using defineType/defineField/defineArrayMember\n export interface IntrinsicDefinitions {\n markdown: MarkdownDefinition\n }\n}\n\nexport const markdownSchemaType = defineType({\n type: 'string',\n name: markdownTypeName,\n title: 'Markdown',\n ...({components: {input: MarkdownEditor}} as {}), //TODO revert when rc.1 ships\n})\n","import {MarkdownEditor as Editor} from './components/Editor'\nimport {definePlugin} from 'sanity'\nimport {markdownSchemaType, MarkdownDefinition} from './schema'\n\n// re-exporting MarkdownEditor directly explodes @parcel/transformer-typescript-types :shrug:\nconst MarkdownEditor = Editor\n\nexport {MarkdownEditor, markdownSchemaType}\nexport type {MarkdownDefinition}\n\nexport const markdownSchema = definePlugin({\n name: 'markdown-editor',\n schema: {\n types: [markdownSchemaType],\n },\n})\n"],"names":["MarkdownEditor","forwardRef","props","ref","value","onChange","elementProps","onBlur","onFocus","readOnly","editedValue","setEditedValue","useState","debouncedValue","delay","setDebouncedValue","useEffect","handler","setTimeout","clearTimeout","useDebounce","client","useClient","apiVersion","PatchEvent","from","unset","set","sanity","studioTheme","useTheme","onPaste","useCallback","async","onImagePasted","event","clipboardData","onDrop","preventDefault","stopPropagation","dataTransfer","jsx","color","dark","children","Card","border","padding","MDEditor","Markdown","source","rehypePlugins","rehypeSanitize","previewOptions","preview","setMarkdown","files","index","items","length","file","item","push","Promise","all","map","url","assets","upload","then","doc","insertedMarkdown","insertString","textarea","document","querySelector","sentence","len","pos","selectionStart","end","selectionEnd","front","slice","back","insertToTextArea","concat","markdownSchemaType","defineType","_objectSpread","type","name","title","components","input","Editor","markdownSchema","definePlugin","schema","types"],"mappings":"qpBAgBO,MAAMA,EAAiBC,EAAAA,YAAW,SACvCC,EACAC,GAEM,MAAAC,MACJA,EAAQ,GAAAC,SACRA,EACAC,cAAcC,OAACA,EAAAC,QAAQA,GAAOC,SAC9BA,GACEP,GACGQ,EAAaC,GAAkBC,WAA6BR,GAC7DS,ECzBgB,SAAYT,EAAgBU,GAClD,MAAOD,EAAgBE,GAAqBH,WAASR,GAS9C,OARPY,EAAAA,WAAU,KACF,MAAAC,EAAUC,YAAW,KACzBH,EAAkBX,EAAK,GACtBU,GAEI,MAAA,IAAMK,aAAaF,EAAO,GAChC,CAACb,EAAOU,IAEJD,CACT,CDcyBO,CAAYV,EAAa,KAE1CW,EAASC,EAAAA,UAAU,CAACC,WAAY,eACtCP,EAAAA,WAAU,KACRL,EAAeP,EAAK,GACnB,CAACA,IAEJY,EAAAA,WAAU,MACHH,GAAkBT,EACrBC,EAASmB,aAAWC,KAAK,CAACC,EAAAA,WACjBb,IAAmBT,GAC5BC,EAASmB,aAAWC,KAAK,CAACE,MAAId,KAChC,GAEC,CAACA,EAAgBR,IAEpB,MAAOuB,OAAQC,GAAeC,EAASA,WACjCC,EAAUC,EAAAA,aACdC,gBACQC,EAAcC,EAAMC,cAAezB,EAAgBU,EAAM,GAEjE,CAACV,EAAgBU,IAGbgB,EAASL,EAAAA,aACbC,UACEE,EAAMG,iBACNH,EAAMI,kBACFJ,EAAMK,oBACFN,EAAcC,EAAMK,aAAc7B,EAAgBU,EAC1D,GAEF,CAACV,EAAgBU,IAGnB,OACGoB,EAAAA,IAAA,MAAA,CAAItC,MAAU,kBAAiB0B,EAAYa,MAAMC,KAAO,OAAS,QAC/DC,WACEH,EAAAA,IAAAI,OAAA,CAAKC,QAAM,EAACC,QAAS,EACpBH,SAAAH,EAAAA,IAACO,UAASC,SAAT,CAAkBC,OAAQ9C,EAAO+C,cAAe,CAAC,CAACC,EAAAA,cAGpDX,EAAAA,IAAAO,UAAA,CACC5C,MAAOM,EACPL,SAAUM,EACVJ,SACAC,UACA6C,eAAgB,CACdF,cAAe,CAAC,CAACC,EAAAA,WAEnBE,QAAQ,OACRvB,UACAM,YAKV,IAGAJ,eAAeC,EACbM,EACAe,EACAlC,GAEA,MAAMmC,EAAgB,GACtB,IAAA,IAASC,EAAQ,EAAGA,EAAQjB,EAAakB,MAAMC,OAAQF,GAAS,EAAG,CACjE,MAAMG,EAAOpB,EAAagB,MAAMK,KAAKJ,GAEjCG,GACFJ,EAAMM,KAAKF,EAEf,OAEMG,QAAQC,IACZR,EAAMS,KAAIhC,UACR,MAAMiC,QAAY7C,EAAO8C,OAAOC,OAAO,QAASR,GAAMS,MAAMC,aAAWA,EAAIJ,IAAW,YAChFK,EASZ,SAA0BC,GAClB,MAAAC,EAAWC,SAASC,cAAc,YACxC,IAAKF,EACI,OAAA,KAGT,IAAIG,EAAWH,EAASrE,MACxB,MAAMyE,EAAMD,EAASjB,OACfmB,EAAML,EAASM,eACfC,EAAMP,EAASQ,aAEfC,EAAQN,EAASO,MAAM,EAAGL,GAC1BM,EAAOR,EAASO,MAAML,EAAKD,GAO1B,OALPD,EAAWM,EAAQV,EAAeY,EAElCX,EAASrE,MAAQwE,EACRH,EAAAQ,aAAeD,EAAMR,EAAab,OAEpCiB,CACT,CA7B+BS,CAAiB,OAAAC,OAAOpB,EAAM,MAClDK,GAGLhB,EAAYgB,EAAgB,IAGlC,CE5GA,MAgBagB,EAAqBC,EAAAA,0WAAWC,CAAA,CAC3CC,KAAM,SACNC,KAlBuB,WAmBvBC,MAAO,YACH,CAACC,WAAY,CAACC,MAAO9F,MClBrBA,EAAiB+F,EAKVC,EAAiBC,EAAAA,aAAa,CACzCN,KAAM,kBACNO,OAAQ,CACNC,MAAO,CAACZ"}
|
package/lib/src/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-markdown",
|
|
3
|
-
"version": "3.0.0
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Markdown fields in Sanity Studio. Supports Github flavored Markdown and image uploads.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -52,17 +52,16 @@
|
|
|
52
52
|
"watch": "pkg-utils watch"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@reach/auto-id": "^0.17.0",
|
|
56
55
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
57
56
|
"@sanity/ui": "1.0.0-beta.32",
|
|
58
|
-
"@uiw/react-md-editor": "^3.
|
|
57
|
+
"@uiw/react-md-editor": "^3.19.7",
|
|
59
58
|
"rehype-sanitize": "^5.0.1"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|
|
62
61
|
"@commitlint/cli": "^17.2.0",
|
|
63
62
|
"@commitlint/config-conventional": "^17.2.0",
|
|
64
|
-
"@sanity/pkg-utils": "^1.
|
|
65
|
-
"@sanity/plugin-kit": "^2.1.
|
|
63
|
+
"@sanity/pkg-utils": "^1.18.0",
|
|
64
|
+
"@sanity/plugin-kit": "^2.1.16",
|
|
66
65
|
"@sanity/semantic-release-preset": "^2.0.2",
|
|
67
66
|
"@types/styled-components": "^5.1.26",
|
|
68
67
|
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
|
@@ -79,13 +78,13 @@
|
|
|
79
78
|
"prettier-plugin-packagejson": "^2.3.0",
|
|
80
79
|
"react": "^18",
|
|
81
80
|
"rimraf": "^3.0.2",
|
|
82
|
-
"sanity": "
|
|
81
|
+
"sanity": "3.0.0-rc.2",
|
|
83
82
|
"styled-components": "^5.3.6",
|
|
84
83
|
"typescript": "^4.8.4"
|
|
85
84
|
},
|
|
86
85
|
"peerDependencies": {
|
|
87
86
|
"react": "^18",
|
|
88
|
-
"sanity": "dev-preview || 3.0.0-rc.
|
|
87
|
+
"sanity": "dev-preview || 3.0.0-rc.2",
|
|
89
88
|
"styled-components": "^5.2"
|
|
90
89
|
},
|
|
91
90
|
"engines": {
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import MDEditor from '@uiw/react-md-editor'
|
|
2
2
|
import useDebounce from '../hooks/useDebounce'
|
|
3
|
-
import {
|
|
4
|
-
import React, {
|
|
3
|
+
import {PatchEvent, SanityClient, set, StringInputProps, unset, useClient} from 'sanity'
|
|
4
|
+
import React, {
|
|
5
|
+
ClipboardEvent,
|
|
6
|
+
forwardRef,
|
|
7
|
+
Ref,
|
|
8
|
+
SetStateAction,
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useState,
|
|
12
|
+
DragEvent,
|
|
13
|
+
} from 'react'
|
|
5
14
|
import {Card, useTheme} from '@sanity/ui'
|
|
6
15
|
import rehypeSanitize from 'rehype-sanitize'
|
|
7
16
|
|
|
@@ -18,7 +27,7 @@ export const MarkdownEditor = forwardRef(function MarkdownEditor(
|
|
|
18
27
|
const [editedValue, setEditedValue] = useState<string | undefined>(value)
|
|
19
28
|
const debouncedValue = useDebounce(editedValue, 100)
|
|
20
29
|
// const client = useClient({apiVersion: '2021-03-25'})
|
|
21
|
-
|
|
30
|
+
const client = useClient({apiVersion: '2022-01-01'})
|
|
22
31
|
useEffect(() => {
|
|
23
32
|
setEditedValue(value)
|
|
24
33
|
}, [value])
|
|
@@ -32,22 +41,25 @@ export const MarkdownEditor = forwardRef(function MarkdownEditor(
|
|
|
32
41
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
42
|
}, [debouncedValue, onChange])
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
success = false
|
|
43
|
-
return `Error: Could not upload file. Only images are supported.`
|
|
44
|
-
})
|
|
44
|
+
const {sanity: studioTheme} = useTheme()
|
|
45
|
+
const onPaste = useCallback(
|
|
46
|
+
async (event: ClipboardEvent<HTMLDivElement>) => {
|
|
47
|
+
await onImagePasted(event.clipboardData, setEditedValue, client)
|
|
48
|
+
},
|
|
49
|
+
[setEditedValue, client]
|
|
50
|
+
)
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
const onDrop = useCallback(
|
|
53
|
+
async (event: DragEvent<HTMLDivElement>) => {
|
|
54
|
+
event.preventDefault()
|
|
55
|
+
event.stopPropagation()
|
|
56
|
+
if (event.dataTransfer) {
|
|
57
|
+
await onImagePasted(event.dataTransfer, setEditedValue, client)
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[setEditedValue, client]
|
|
61
|
+
)
|
|
49
62
|
|
|
50
|
-
const {sanity: studioTheme} = useTheme()
|
|
51
63
|
return (
|
|
52
64
|
<div ref={ref} data-color-mode={studioTheme.color.dark ? 'dark' : 'light'}>
|
|
53
65
|
{readOnly ? (
|
|
@@ -64,8 +76,59 @@ export const MarkdownEditor = forwardRef(function MarkdownEditor(
|
|
|
64
76
|
rehypePlugins: [[rehypeSanitize]],
|
|
65
77
|
}}
|
|
66
78
|
preview="edit"
|
|
79
|
+
onPaste={onPaste}
|
|
80
|
+
onDrop={onDrop}
|
|
67
81
|
/>
|
|
68
82
|
)}
|
|
69
83
|
</div>
|
|
70
84
|
)
|
|
71
85
|
})
|
|
86
|
+
|
|
87
|
+
// https://github.com/uiwjs/react-md-editor/issues/83#issuecomment-1185471844
|
|
88
|
+
async function onImagePasted(
|
|
89
|
+
dataTransfer: DataTransfer,
|
|
90
|
+
setMarkdown: (value: SetStateAction<string | undefined>) => void,
|
|
91
|
+
client: SanityClient
|
|
92
|
+
) {
|
|
93
|
+
const files: File[] = []
|
|
94
|
+
for (let index = 0; index < dataTransfer.items.length; index += 1) {
|
|
95
|
+
const file = dataTransfer.files.item(index)
|
|
96
|
+
|
|
97
|
+
if (file) {
|
|
98
|
+
files.push(file)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await Promise.all(
|
|
103
|
+
files.map(async (file) => {
|
|
104
|
+
const url = await client.assets.upload('image', file).then((doc) => `${doc.url}?w=450`)
|
|
105
|
+
const insertedMarkdown = insertToTextArea(``)
|
|
106
|
+
if (!insertedMarkdown) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
setMarkdown(insertedMarkdown)
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function insertToTextArea(insertString: string) {
|
|
115
|
+
const textarea = document.querySelector('textarea')
|
|
116
|
+
if (!textarea) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let sentence = textarea.value
|
|
121
|
+
const len = sentence.length
|
|
122
|
+
const pos = textarea.selectionStart
|
|
123
|
+
const end = textarea.selectionEnd
|
|
124
|
+
|
|
125
|
+
const front = sentence.slice(0, pos)
|
|
126
|
+
const back = sentence.slice(pos, len)
|
|
127
|
+
|
|
128
|
+
sentence = front + insertString + back
|
|
129
|
+
|
|
130
|
+
textarea.value = sentence
|
|
131
|
+
textarea.selectionEnd = end + insertString.length
|
|
132
|
+
|
|
133
|
+
return sentence
|
|
134
|
+
}
|