typing-fixes
This commit is contained in:
parent
d0e98bb525
commit
ce9057d637
|
@ -89,6 +89,7 @@
|
||||||
"markdown-it-mark": "^3.0.1",
|
"markdown-it-mark": "^3.0.1",
|
||||||
"markdown-it-replace-link": "^1.1.0",
|
"markdown-it-replace-link": "^1.1.0",
|
||||||
"nanostores": "^0.7.0",
|
"nanostores": "^0.7.0",
|
||||||
|
"orderedmap": "^2.1.0",
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.16",
|
||||||
"postcss-modules": "^5.0.0",
|
"postcss-modules": "^5.0.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
|
|
3
src/assets/handle.svg
Normal file
3
src/assets/handle.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg viewBox="0 0 10 10" height="14" width="14">
|
||||||
|
<path d="M3 2a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm4-8a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 245 B |
|
@ -20,7 +20,8 @@ export default () => {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ProseMirror
|
<ProseMirror
|
||||||
class={'editor'}
|
// eslint-disable-next-line solid/no-react-specific-props
|
||||||
|
className="editor"
|
||||||
style={style()}
|
style={style()}
|
||||||
editorView={store.editorView as EditorView}
|
editorView={store.editorView as EditorView}
|
||||||
text={store.text as ProseMirrorState}
|
text={store.text as ProseMirrorState}
|
||||||
|
|
|
@ -11,7 +11,8 @@ export const Editor = () => {
|
||||||
const onChange = (text: EditorState) => ctrl.setState({ text, lastModified: new Date() })
|
const onChange = (text: EditorState) => ctrl.setState({ text, lastModified: new Date() })
|
||||||
return (
|
return (
|
||||||
<ProseMirror
|
<ProseMirror
|
||||||
class="editor"
|
// eslint-disable-next-line solid/no-react-specific-props
|
||||||
|
className="editor"
|
||||||
style={store.markdown && `white-space: pre-wrap;`}
|
style={store.markdown && `white-space: pre-wrap;`}
|
||||||
editorView={store.editorView}
|
editorView={store.editorView}
|
||||||
text={store.text}
|
text={store.text}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { schema as markdownSchema } from 'prosemirror-markdown'
|
import { schema as markdownSchema } from 'prosemirror-markdown'
|
||||||
import { Schema } from 'prosemirror-model'
|
import type OrderedMap from 'orderedmap'
|
||||||
|
import { NodeSpec, Schema } from 'prosemirror-model'
|
||||||
import { baseKeymap } from 'prosemirror-commands'
|
import { baseKeymap } from 'prosemirror-commands'
|
||||||
import { sinkListItem, liftListItem } from 'prosemirror-schema-list'
|
import { sinkListItem, liftListItem } from 'prosemirror-schema-list'
|
||||||
import { history } from 'prosemirror-history'
|
import { history } from 'prosemirror-history'
|
||||||
|
@ -39,7 +40,10 @@ export default (plain = false): ProseMirrorExtension => ({
|
||||||
marks: plainSchema.spec.marks
|
marks: plainSchema.spec.marks
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
nodes: (markdownSchema.spec.nodes as any).update('blockquote', blockquoteSchema),
|
nodes: (markdownSchema.spec.nodes as OrderedMap<NodeSpec>).update(
|
||||||
|
'blockquote',
|
||||||
|
blockquoteSchema as unknown as NodeSpec
|
||||||
|
),
|
||||||
marks: markdownSchema.spec.marks
|
marks: markdownSchema.spec.marks
|
||||||
},
|
},
|
||||||
plugins: (prev, schema) => [
|
plugins: (prev, schema) => [
|
||||||
|
|
|
@ -2,7 +2,13 @@ import { ySyncPlugin, yCursorPlugin, yUndoPlugin } from 'y-prosemirror'
|
||||||
import type { YOptions } from '../../store'
|
import type { YOptions } from '../../store'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { ProseMirrorExtension } from '../helpers'
|
||||||
|
|
||||||
export const cursorBuilder = (user: any): HTMLElement => {
|
export interface EditingProps {
|
||||||
|
name: string
|
||||||
|
foreground: string
|
||||||
|
background: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cursorBuilder = (user: EditingProps): HTMLElement => {
|
||||||
const cursor = document.createElement('span')
|
const cursor = document.createElement('span')
|
||||||
cursor.classList.add('ProseMirror-yjs-cursor')
|
cursor.classList.add('ProseMirror-yjs-cursor')
|
||||||
cursor.setAttribute('style', `border-color: ${user.background}`)
|
cursor.setAttribute('style', `border-color: ${user.background}`)
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import { Plugin, NodeSelection } from 'prosemirror-state'
|
import { Plugin, NodeSelection } from 'prosemirror-state'
|
||||||
import { DecorationSet, Decoration } from 'prosemirror-view'
|
import { DecorationSet, Decoration } from 'prosemirror-view'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { ProseMirrorExtension } from '../helpers'
|
||||||
|
import handleIcon from '../../../../assets/handle.svg'
|
||||||
const handleIcon = `
|
|
||||||
<svg viewBox="0 0 10 10" height="14" width="14">
|
|
||||||
<path d="M3 2a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm4-8a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2zm0 4a1 1 0 110-2 1 1 0 010 2z"/>
|
|
||||||
</svg>`
|
|
||||||
|
|
||||||
const createDragHandle = () => {
|
const createDragHandle = () => {
|
||||||
const handle = document.createElement('span')
|
const handle = document.createElement('span')
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from 'prosemirror-state'
|
||||||
import type { Node, Schema } from 'prosemirror-model'
|
import type { Node, NodeSpec, Schema } from 'prosemirror-model'
|
||||||
import type { EditorView } from 'prosemirror-view'
|
import type { EditorView } from 'prosemirror-view'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { NodeViewFn, ProseMirrorExtension } from '../helpers'
|
||||||
|
import type OrderedMap from 'orderedmap'
|
||||||
|
|
||||||
const REGEX = /^!\[([^[\]]*?)]\((.+?)\)\s+/
|
const REGEX = /^!\[([^[\]]*?)]\((.+?)\)\s+/
|
||||||
const MAX_MATCH = 500
|
const MAX_MATCH = 500
|
||||||
|
@ -17,7 +18,7 @@ const isUrl = (str: string) => {
|
||||||
|
|
||||||
const isBlank = (text: string) => text === ' ' || text === '\u00A0'
|
const isBlank = (text: string) => text === ' ' || text === '\u00A0'
|
||||||
|
|
||||||
const imageInput = (schema: Schema, path?: string) =>
|
const imageInput = (schema: Schema, _path?: string) =>
|
||||||
new Plugin({
|
new Plugin({
|
||||||
props: {
|
props: {
|
||||||
handleTextInput(view, from, to, text) {
|
handleTextInput(view, from, to, text) {
|
||||||
|
@ -68,7 +69,7 @@ const imageSchema = {
|
||||||
src: dom.getAttribute('src'),
|
src: dom.getAttribute('src'),
|
||||||
title: dom.getAttribute('title'),
|
title: dom.getAttribute('title'),
|
||||||
alt: dom.getAttribute('alt'),
|
alt: dom.getAttribute('alt'),
|
||||||
path: (dom as any).dataset.path
|
path: (dom as NodeSpec).dataset.path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -101,12 +102,12 @@ class ImageView {
|
||||||
contentDOM: Element
|
contentDOM: Element
|
||||||
container: HTMLElement
|
container: HTMLElement
|
||||||
handle: HTMLElement
|
handle: HTMLElement
|
||||||
onResizeFn: any
|
onResizeFn: (e: Event) => void
|
||||||
onResizeEndFn: any
|
onResizeEndFn: (e: Event) => void
|
||||||
width: number
|
width: number
|
||||||
updating: number
|
updating: number
|
||||||
|
|
||||||
constructor(node: Node, view: EditorView, getPos: () => number, schema: Schema, path: string) {
|
constructor(node: Node, view: EditorView, getPos: () => number, schema: Schema, _path: string) {
|
||||||
this.node = node
|
this.node = node
|
||||||
this.view = view
|
this.view = view
|
||||||
this.getPos = getPos
|
this.getPos = getPos
|
||||||
|
@ -161,12 +162,12 @@ class ImageView {
|
||||||
export default (path?: string): ProseMirrorExtension => ({
|
export default (path?: string): ProseMirrorExtension => ({
|
||||||
schema: (prev) => ({
|
schema: (prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
nodes: (prev.nodes as any).update('image', imageSchema)
|
nodes: (prev.nodes as OrderedMap<NodeSpec>).update('image', imageSchema as unknown as NodeSpec)
|
||||||
}),
|
}),
|
||||||
plugins: (prev, schema) => [...prev, imageInput(schema, path)],
|
plugins: (prev, schema) => [...prev, imageInput(schema, path)],
|
||||||
nodeViews: {
|
nodeViews: {
|
||||||
image: (node, view, getPos) => {
|
image: (node, view, getPos) => {
|
||||||
return new ImageView(node, view, getPos, view.state.schema, path)
|
return new ImageView(node, view, getPos, view.state.schema, path)
|
||||||
}
|
}
|
||||||
} as any
|
} as unknown as { [key: string]: NodeViewFn }
|
||||||
})
|
})
|
||||||
|
|
|
@ -31,7 +31,6 @@ const markdownLinks = (schema: Schema) =>
|
||||||
if (action?.pos) {
|
if (action?.pos) {
|
||||||
(state as any).pos = action.pos
|
(state as any).pos = action.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,48 +16,34 @@ import {
|
||||||
import type { MenuItemSpec, MenuElement } from 'prosemirror-menu'
|
import type { MenuItemSpec, MenuElement } from 'prosemirror-menu'
|
||||||
|
|
||||||
import { wrapInList } from 'prosemirror-schema-list'
|
import { wrapInList } from 'prosemirror-schema-list'
|
||||||
import { NodeSelection } from 'prosemirror-state'
|
import { Command, EditorState, NodeSelection, Transaction } from 'prosemirror-state'
|
||||||
|
|
||||||
import { TextField, openPrompt } from './prompt'
|
import { TextField, openPrompt } from './prompt'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { ProseMirrorExtension } from '../helpers'
|
||||||
import type { Schema } from 'prosemirror-model'
|
import type { Attrs, MarkType, NodeType, Schema } from 'prosemirror-model'
|
||||||
|
import type { EditorView } from 'prosemirror-view'
|
||||||
|
|
||||||
// Helpers to create specific types of items
|
// Helpers to create specific types of items
|
||||||
|
|
||||||
function canInsert(state: { selection: { $from: any } }, nodeType: any) {
|
function canInsert(state: EditorState, nodeType: NodeType) {
|
||||||
const $from = state.selection.$from
|
const $from = state.selection.$from
|
||||||
|
|
||||||
for (let d = $from.depth; d >= 0; d--) {
|
for (let d = $from.depth; d >= 0; d--) {
|
||||||
const index = $from.index(d)
|
const index = $from.index(d)
|
||||||
|
|
||||||
if ($from.node(d).canReplaceWith(index, index, nodeType)) return true
|
if ($from.node(d).canReplaceWith(index, index, nodeType)) return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertImageItem(nodeType: { createAndFill: (arg0: any) => any }) {
|
function insertImageItem(nodeType: NodeType) {
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
icon: icons.image,
|
icon: icons.image,
|
||||||
label: 'image',
|
label: 'image',
|
||||||
enable(state: any) {
|
enable(state) {
|
||||||
return canInsert(state, nodeType)
|
return canInsert(state, nodeType)
|
||||||
},
|
},
|
||||||
run(
|
run(state: EditorState, _, view: EditorView) {
|
||||||
state: {
|
const { from, to, node } = state.selection as NodeSelection
|
||||||
selection: { node?: any; from?: any; to?: any }
|
|
||||||
doc: { textBetween: (arg0: any, arg1: any, arg2: string) => any }
|
|
||||||
},
|
|
||||||
_: any,
|
|
||||||
view: {
|
|
||||||
dispatch: (arg0: any) => void
|
|
||||||
state: { tr: { replaceSelectionWith: (arg0: any) => any } }
|
|
||||||
focus: () => void
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { from, to, node } = state.selection
|
|
||||||
let attrs = null
|
let attrs = null
|
||||||
|
|
||||||
if (state.selection instanceof NodeSelection && node.type === nodeType) {
|
if (state.selection instanceof NodeSelection && node.type === nodeType) {
|
||||||
attrs = node.attrs
|
attrs = node.attrs
|
||||||
}
|
}
|
||||||
|
@ -77,7 +63,7 @@ function insertImageItem(nodeType: { createAndFill: (arg0: any) => any }) {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
callback(attrs: any) {
|
callback(attrs: Attrs) {
|
||||||
view.dispatch(view.state.tr.replaceSelectionWith(nodeType.createAndFill(attrs)))
|
view.dispatch(view.state.tr.replaceSelectionWith(nodeType.createAndFill(attrs)))
|
||||||
view.focus()
|
view.focus()
|
||||||
}
|
}
|
||||||
|
@ -86,53 +72,31 @@ function insertImageItem(nodeType: { createAndFill: (arg0: any) => any }) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdItem(
|
function cmdItem(cmd: Command, options: MenuItemSpec) {
|
||||||
cmd: (arg0: any) => any,
|
const passedOptions = { label: options.title, run: cmd } as MenuItemSpec
|
||||||
options: { [x: string]: any; active?: (state: any) => any; enable?: any; title?: any; select?: any }
|
|
||||||
) {
|
|
||||||
const passedOptions = {
|
|
||||||
label: options.title,
|
|
||||||
run: cmd
|
|
||||||
} as { [key: string]: any }
|
|
||||||
|
|
||||||
Object.keys(options).forEach((prop) => (passedOptions[prop] = options[prop]))
|
Object.keys(options).forEach((prop) => (passedOptions[prop] = options[prop]))
|
||||||
|
// TODO: enable/disable items logix
|
||||||
if ((!options.enable || options.enable === true) && !options.select) {
|
passedOptions.select = (state) => cmd(state)
|
||||||
passedOptions[options.enable ? 'enable' : 'select'] = (state: any) => cmd(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MenuItem(passedOptions as MenuItemSpec)
|
return new MenuItem(passedOptions as MenuItemSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
function markActive(
|
function markActive(state: EditorState, type: MarkType) {
|
||||||
state: {
|
|
||||||
selection: { from: any; $from: any; to: any; empty: any }
|
|
||||||
storedMarks: any
|
|
||||||
doc: { rangeHasMark: (arg0: any, arg1: any, arg2: any) => any }
|
|
||||||
},
|
|
||||||
type: { isInSet: (arg0: any) => any }
|
|
||||||
) {
|
|
||||||
const { from, $from, to, empty } = state.selection
|
const { from, $from, to, empty } = state.selection
|
||||||
|
|
||||||
if (empty) return type.isInSet(state.storedMarks || $from.marks())
|
if (empty) return type.isInSet(state.storedMarks || $from.marks())
|
||||||
|
|
||||||
return state.doc.rangeHasMark(from, to, type)
|
return state.doc.rangeHasMark(from, to, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
function markItem(markType: any, options: { [x: string]: any; title?: string; icon?: any }) {
|
function markItem(markType: MarkType, options: MenuItemSpec) {
|
||||||
const passedOptions = {
|
const passedOptions = {
|
||||||
active(state: any) {
|
active(state) {
|
||||||
return markActive(state, markType)
|
return markActive(state, markType)
|
||||||
},
|
}
|
||||||
enable: true
|
} as MenuItemSpec
|
||||||
} as { [key: string]: any }
|
|
||||||
|
|
||||||
Object.keys(options).forEach((prop: string) => (passedOptions[prop] = options[prop]))
|
Object.keys(options).forEach((prop: string) => (passedOptions[prop] = options[prop]))
|
||||||
|
|
||||||
return cmdItem(toggleMark(markType), passedOptions)
|
return cmdItem(toggleMark(markType), passedOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkItem(markType: any) {
|
function linkItem(markType: MarkType) {
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
title: 'Add or remove link',
|
title: 'Add or remove link',
|
||||||
icon: {
|
icon: {
|
||||||
|
@ -140,19 +104,13 @@ function linkItem(markType: any) {
|
||||||
height: 18,
|
height: 18,
|
||||||
path: 'M3.27177 14.7277C2.06258 13.5186 2.06258 11.5527 3.27177 10.3435L6.10029 7.51502L4.75675 6.17148L1.92823 9C-0.0234511 10.9517 -0.0234511 14.1196 1.92823 16.0713C3.87991 18.023 7.04785 18.023 8.99952 16.0713L11.828 13.2428L10.4845 11.8992L7.65598 14.7277C6.44679 15.9369 4.48097 15.9369 3.27177 14.7277ZM6.87756 12.536L12.5346 6.87895L11.1203 5.46469L5.4633 11.1217L6.87756 12.536ZM6.17055 4.75768L8.99907 1.92916C10.9507 -0.0225206 14.1187 -0.0225201 16.0704 1.92916C18.022 3.88084 18.022 7.04878 16.0704 9.00046L13.2418 11.829L11.8983 10.4854L14.7268 7.65691C15.936 6.44772 15.936 4.4819 14.7268 3.27271C13.5176 2.06351 11.5518 2.06351 10.3426 3.2727L7.51409 6.10122L6.17055 4.75768Z'
|
path: 'M3.27177 14.7277C2.06258 13.5186 2.06258 11.5527 3.27177 10.3435L6.10029 7.51502L4.75675 6.17148L1.92823 9C-0.0234511 10.9517 -0.0234511 14.1196 1.92823 16.0713C3.87991 18.023 7.04785 18.023 8.99952 16.0713L11.828 13.2428L10.4845 11.8992L7.65598 14.7277C6.44679 15.9369 4.48097 15.9369 3.27177 14.7277ZM6.87756 12.536L12.5346 6.87895L11.1203 5.46469L5.4633 11.1217L6.87756 12.536ZM6.17055 4.75768L8.99907 1.92916C10.9507 -0.0225206 14.1187 -0.0225201 16.0704 1.92916C18.022 3.88084 18.022 7.04878 16.0704 9.00046L13.2418 11.829L11.8983 10.4854L14.7268 7.65691C15.936 6.44772 15.936 4.4819 14.7268 3.27271C13.5176 2.06351 11.5518 2.06351 10.3426 3.2727L7.51409 6.10122L6.17055 4.75768Z'
|
||||||
},
|
},
|
||||||
active(state: any) {
|
active: (state) => Boolean(markActive(state, markType)),
|
||||||
return markActive(state, markType)
|
enable: (state: EditorState) => !state.selection.empty,
|
||||||
},
|
run(state: EditorState, dispatch: (t: Transaction) => void, view: EditorView) {
|
||||||
enable(state: { selection: { empty: any } }) {
|
|
||||||
return !state.selection.empty
|
|
||||||
},
|
|
||||||
run(state: any, dispatch: any, view: { state: any; dispatch: any; focus: () => void }) {
|
|
||||||
if (markActive(state, markType)) {
|
if (markActive(state, markType)) {
|
||||||
toggleMark(markType)(state, dispatch)
|
toggleMark(markType)(state, dispatch)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
openPrompt({
|
openPrompt({
|
||||||
fields: {
|
fields: {
|
||||||
href: new TextField({
|
href: new TextField({
|
||||||
|
@ -160,7 +118,7 @@ function linkItem(markType: any) {
|
||||||
required: true
|
required: true
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
callback(attrs: any) {
|
callback(attrs: Attrs) {
|
||||||
toggleMark(markType, attrs)(view.state, view.dispatch)
|
toggleMark(markType, attrs)(view.state, view.dispatch)
|
||||||
view.focus()
|
view.focus()
|
||||||
}
|
}
|
||||||
|
@ -169,14 +127,8 @@ function linkItem(markType: any) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapListItem(
|
function wrapListItem(nodeType: NodeType, options: MenuItemSpec & { attrs: Attrs }) {
|
||||||
nodeType: any,
|
options.run = (_) => true
|
||||||
options: {
|
|
||||||
title?: string
|
|
||||||
icon?: { width: number; height: number; path: string } | { width: number; height: number; path: string }
|
|
||||||
attrs?: any
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
return cmdItem(wrapInList(nodeType, options.attrs), options)
|
return cmdItem(wrapInList(nodeType, options.attrs), options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +207,7 @@ type BuildSchema = {
|
||||||
*/
|
*/
|
||||||
export function buildMenuItems(schema: Schema) {
|
export function buildMenuItems(schema: Schema) {
|
||||||
const r: { [key: string]: MenuItem | MenuItem[] } = {}
|
const r: { [key: string]: MenuItem | MenuItem[] } = {}
|
||||||
let type: any
|
let type: NodeType | MarkType
|
||||||
|
|
||||||
if ((type = schema.marks.strong)) {
|
if ((type = schema.marks.strong)) {
|
||||||
r.toggleStrong = markItem(type, {
|
r.toggleStrong = markItem(type, {
|
||||||
|
@ -265,7 +217,7 @@ export function buildMenuItems(schema: Schema) {
|
||||||
height: 16,
|
height: 16,
|
||||||
path: 'M9.82857 7.76C10.9371 6.99429 11.7143 5.73714 11.7143 4.57143C11.7143 1.98857 9.71428 0 7.14286 0H0V16H8.04571C10.4343 16 12.2857 14.0571 12.2857 11.6686C12.2857 9.93143 11.3029 8.44571 9.82857 7.76ZM3.42799 2.85708H6.85656C7.80513 2.85708 8.57085 3.6228 8.57085 4.57137C8.57085 5.51994 7.80513 6.28565 6.85656 6.28565H3.42799V2.85708ZM3.42799 13.1429H7.42799C8.37656 13.1429 9.14228 12.3772 9.14228 11.4286C9.14228 10.4801 8.37656 9.71434 7.42799 9.71434H3.42799V13.1429Z'
|
path: 'M9.82857 7.76C10.9371 6.99429 11.7143 5.73714 11.7143 4.57143C11.7143 1.98857 9.71428 0 7.14286 0H0V16H8.04571C10.4343 16 12.2857 14.0571 12.2857 11.6686C12.2857 9.93143 11.3029 8.44571 9.82857 7.76ZM3.42799 2.85708H6.85656C7.80513 2.85708 8.57085 3.6228 8.57085 4.57137C8.57085 5.51994 7.80513 6.28565 6.85656 6.28565H3.42799V2.85708ZM3.42799 13.1429H7.42799C8.37656 13.1429 9.14228 12.3772 9.14228 11.4286C9.14228 10.4801 8.37656 9.71434 7.42799 9.71434H3.42799V13.1429Z'
|
||||||
}
|
}
|
||||||
})
|
} as MenuItemSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type = schema.marks.em)) {
|
if ((type = schema.marks.em)) {
|
||||||
|
@ -276,14 +228,14 @@ export function buildMenuItems(schema: Schema) {
|
||||||
height: 16,
|
height: 16,
|
||||||
path: 'M4.39216 0V3.42857H6.81882L3.06353 12.5714H0V16H8.78431V12.5714H6.35765L10.1129 3.42857H13.1765V0H4.39216Z'
|
path: 'M4.39216 0V3.42857H6.81882L3.06353 12.5714H0V16H8.78431V12.5714H6.35765L10.1129 3.42857H13.1765V0H4.39216Z'
|
||||||
}
|
}
|
||||||
})
|
} as MenuItemSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type = schema.marks.code)) {
|
if ((type = schema.marks.code)) {
|
||||||
r.toggleCode = markItem(type, {
|
r.toggleCode = markItem(type, {
|
||||||
title: 'Toggle code font',
|
title: 'Toggle code font',
|
||||||
icon: icons.code
|
icon: icons.code
|
||||||
})
|
} as MenuItemSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type = schema.marks.link)) r.toggleLink = linkItem(type)
|
if ((type = schema.marks.link)) r.toggleLink = linkItem(type)
|
||||||
|
@ -298,7 +250,7 @@ export function buildMenuItems(schema: Schema) {
|
||||||
height: 16,
|
height: 16,
|
||||||
path: 'M0.000114441 1.6C0.000114441 0.714665 0.71478 0 1.60011 0C2.48544 0 3.20011 0.714665 3.20011 1.6C3.20011 2.48533 2.48544 3.19999 1.60011 3.19999C0.71478 3.19999 0.000114441 2.48533 0.000114441 1.6ZM0 8.00013C0 7.1148 0.714665 6.40014 1.6 6.40014C2.48533 6.40014 3.19999 7.1148 3.19999 8.00013C3.19999 8.88547 2.48533 9.60013 1.6 9.60013C0.714665 9.60013 0 8.88547 0 8.00013ZM1.6 12.8C0.714665 12.8 0 13.5254 0 14.4C0 15.2747 0.725332 16 1.6 16C2.47466 16 3.19999 15.2747 3.19999 14.4C3.19999 13.5254 2.48533 12.8 1.6 12.8ZM19.7333 15.4662H4.79999V13.3329H19.7333V15.4662ZM4.79999 9.06677H19.7333V6.93344H4.79999V9.06677ZM4.79999 2.66664V0.533307H19.7333V2.66664H4.79999Z'
|
path: 'M0.000114441 1.6C0.000114441 0.714665 0.71478 0 1.60011 0C2.48544 0 3.20011 0.714665 3.20011 1.6C3.20011 2.48533 2.48544 3.19999 1.60011 3.19999C0.71478 3.19999 0.000114441 2.48533 0.000114441 1.6ZM0 8.00013C0 7.1148 0.714665 6.40014 1.6 6.40014C2.48533 6.40014 3.19999 7.1148 3.19999 8.00013C3.19999 8.88547 2.48533 9.60013 1.6 9.60013C0.714665 9.60013 0 8.88547 0 8.00013ZM1.6 12.8C0.714665 12.8 0 13.5254 0 14.4C0 15.2747 0.725332 16 1.6 16C2.47466 16 3.19999 15.2747 3.19999 14.4C3.19999 13.5254 2.48533 12.8 1.6 12.8ZM19.7333 15.4662H4.79999V13.3329H19.7333V15.4662ZM4.79999 9.06677H19.7333V6.93344H4.79999V9.06677ZM4.79999 2.66664V0.533307H19.7333V2.66664H4.79999Z'
|
||||||
}
|
}
|
||||||
})
|
} as MenuItemSpec & { attrs: Attrs })
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type = schema.nodes.ordered_list)) {
|
if ((type = schema.nodes.ordered_list)) {
|
||||||
|
@ -309,7 +261,7 @@ export function buildMenuItems(schema: Schema) {
|
||||||
height: 16,
|
height: 16,
|
||||||
path: 'M2.00002 4.00003H1.00001V1.00001H0V0H2.00002V4.00003ZM2.00002 13.5V13H0V12H3.00003V16H0V15H2.00002V14.5H1.00001V13.5H2.00002ZM0 6.99998H1.80002L0 9.1V10H3.00003V9H1.20001L3.00003 6.89998V5.99998H0V6.99998ZM4.9987 2.99967V0.999648H18.9988V2.99967H4.9987ZM4.9987 15.0001H18.9988V13.0001H4.9987V15.0001ZM18.9988 8.99987H4.9987V6.99986H18.9988V8.99987Z'
|
path: 'M2.00002 4.00003H1.00001V1.00001H0V0H2.00002V4.00003ZM2.00002 13.5V13H0V12H3.00003V16H0V15H2.00002V14.5H1.00001V13.5H2.00002ZM0 6.99998H1.80002L0 9.1V10H3.00003V9H1.20001L3.00003 6.89998V5.99998H0V6.99998ZM4.9987 2.99967V0.999648H18.9988V2.99967H4.9987ZM4.9987 15.0001H18.9988V13.0001H4.9987V15.0001ZM18.9988 8.99987H4.9987V6.99986H18.9988V8.99987Z'
|
||||||
}
|
}
|
||||||
})
|
} as MenuItemSpec & { attrs: Attrs })
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type = schema.nodes.blockquote)) {
|
if ((type = schema.nodes.blockquote)) {
|
||||||
|
@ -360,10 +312,8 @@ export function buildMenuItems(schema: Schema) {
|
||||||
r.insertHorizontalRule = new MenuItem({
|
r.insertHorizontalRule = new MenuItem({
|
||||||
label: '---',
|
label: '---',
|
||||||
icon: icons.horizontal_rule,
|
icon: icons.horizontal_rule,
|
||||||
enable(state: any) {
|
enable: (state) => canInsert(state, hr),
|
||||||
return canInsert(state, hr)
|
run(state: EditorState, dispatch: (tr: Transaction) => void) {
|
||||||
},
|
|
||||||
run(state: { tr: { replaceSelectionWith: (arg0: any) => any } }, dispatch: (arg0: any) => void) {
|
|
||||||
dispatch(state.tr.replaceSelectionWith(hr.create()))
|
dispatch(state.tr.replaceSelectionWith(hr.create()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -404,7 +354,7 @@ export default (): ProseMirrorExtension => ({
|
||||||
...prev,
|
...prev,
|
||||||
menuBar({
|
menuBar({
|
||||||
floating: true,
|
floating: true,
|
||||||
content: buildMenuItems(schema).fullMenu as any[]
|
content: buildMenuItems(schema).fullMenu as MenuItem | MenuItem[]
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -64,7 +64,7 @@ const pasteMarkdown = (schema: Schema) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
const paste = parser.parse(text)
|
const paste = parser.parse(text)
|
||||||
const slice = paste as any
|
const slice = paste as Node & { openStart: number; openEnd: number }
|
||||||
const fragment = shiftKey ? slice.content : transform(schema, slice.content)
|
const fragment = shiftKey ? slice.content : transform(schema, slice.content)
|
||||||
const tr = view.state.tr.replaceSelection(new Slice(fragment, slice.openStart, slice.openEnd))
|
const tr = view.state.tr.replaceSelection(new Slice(fragment, slice.openStart, slice.openEnd))
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
const prefix = 'ProseMirror-prompt'
|
const prefix = 'ProseMirror-prompt'
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
export function openPrompt(options: any) {
|
export function openPrompt(options) {
|
||||||
const wrapper = document.body.appendChild(document.createElement('div'))
|
const wrapper = document.body.appendChild(document.createElement('div'))
|
||||||
wrapper.className = prefix
|
wrapper.className = prefix
|
||||||
|
const mouseOutside = (e: MouseEvent) => {
|
||||||
const mouseOutside = (e: any) => {
|
if (!wrapper.contains(e.target as Node)) close()
|
||||||
if (!wrapper.contains(e.target)) close()
|
|
||||||
}
|
}
|
||||||
setTimeout(() => window.addEventListener('mousedown', mouseOutside), 50)
|
setTimeout(() => window.addEventListener('mousedown', mouseOutside), 50)
|
||||||
const close = () => {
|
const close = () => {
|
||||||
|
@ -14,7 +13,7 @@ export function openPrompt(options: any) {
|
||||||
if (wrapper.parentNode) wrapper.remove()
|
if (wrapper.parentNode) wrapper.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
const domFields: any = []
|
const domFields = []
|
||||||
options.fields.forEach((name) => {
|
options.fields.forEach((name) => {
|
||||||
domFields.push(options.fields[name].render())
|
domFields.push(options.fields[name].render())
|
||||||
})
|
})
|
||||||
|
@ -33,7 +32,7 @@ export function openPrompt(options: any) {
|
||||||
if (options.title) {
|
if (options.title) {
|
||||||
form.appendChild(document.createElement('h5')).textContent = options.title
|
form.appendChild(document.createElement('h5')).textContent = options.title
|
||||||
}
|
}
|
||||||
domFields.forEach((field: any) => {
|
domFields.forEach((field) => {
|
||||||
form.appendChild(document.createElement('div')).appendChild(field)
|
form.appendChild(document.createElement('div')).appendChild(field)
|
||||||
})
|
})
|
||||||
const buttons = form.appendChild(document.createElement('div'))
|
const buttons = form.appendChild(document.createElement('div'))
|
||||||
|
@ -74,11 +73,11 @@ export function openPrompt(options: any) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const input: any = form.elements[0]
|
const inpel = form.elements[0] as HTMLInputElement
|
||||||
if (input) input.focus()
|
if (inpel) inpel.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getValues(fields: any, domFields: any) {
|
function getValues(fields, domFields) {
|
||||||
const result = Object.create(null)
|
const result = Object.create(null)
|
||||||
let i = 0
|
let i = 0
|
||||||
fields.forEarch((name) => {
|
fields.forEarch((name) => {
|
||||||
|
@ -95,7 +94,7 @@ function getValues(fields: any, domFields: any) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportInvalid(dom: any, message: any) {
|
function reportInvalid(dom: HTMLElement, message: string) {
|
||||||
const parent = dom.parentNode
|
const parent = dom.parentNode
|
||||||
const msg = parent.appendChild(document.createElement('div'))
|
const msg = parent.appendChild(document.createElement('div'))
|
||||||
msg.style.left = dom.offsetLeft + dom.offsetWidth + 2 + 'px'
|
msg.style.left = dom.offsetLeft + dom.offsetWidth + 2 + 'px'
|
||||||
|
@ -106,13 +105,24 @@ function reportInvalid(dom: any, message: any) {
|
||||||
setTimeout(() => parent.removeChild(msg), 1500)
|
setTimeout(() => parent.removeChild(msg), 1500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FieldOptions {
|
||||||
|
options: { value: string; label: string }[]
|
||||||
|
required: boolean
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
validateType: (v) => boolean
|
||||||
|
validate: (v) => boolean
|
||||||
|
read: (v) => string
|
||||||
|
clean: (v) => boolean
|
||||||
|
}
|
||||||
|
|
||||||
export class Field {
|
export class Field {
|
||||||
options: any
|
options: FieldOptions
|
||||||
constructor(options: any) {
|
constructor(options) {
|
||||||
this.options = options
|
this.options = options
|
||||||
}
|
}
|
||||||
|
|
||||||
read(dom: any) {
|
read(dom) {
|
||||||
return dom.value
|
return dom.value
|
||||||
}
|
}
|
||||||
// :: (any) → ?string
|
// :: (any) → ?string
|
||||||
|
@ -121,13 +131,12 @@ export class Field {
|
||||||
return typeof _value === typeof ''
|
return typeof _value === typeof ''
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(value: any) {
|
validate(value) {
|
||||||
if (!value && this.options.required) return 'Required field'
|
if (!value && this.options.required) return 'Required field'
|
||||||
|
|
||||||
return this.validateType(value) || (this.options.validate && this.options.validate(value))
|
return this.validateType(value) || (this.options.validate && this.options.validate(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(value: any) {
|
clean(value) {
|
||||||
return this.options.clean ? this.options.clean(value) : value
|
return this.options.clean ? this.options.clean(value) : value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +156,7 @@ export class TextField extends Field {
|
||||||
export class SelectField extends Field {
|
export class SelectField extends Field {
|
||||||
render() {
|
render() {
|
||||||
const select = document.createElement('select')
|
const select = document.createElement('select')
|
||||||
this.options.options.forEach((o: { value: string; label: string }) => {
|
this.options.options.forEach((o) => {
|
||||||
const opt = select.appendChild(document.createElement('option'))
|
const opt = select.appendChild(document.createElement('option'))
|
||||||
opt.value = o.value
|
opt.value = o.value
|
||||||
opt.selected = o.value === this.options.value
|
opt.selected = o.value === this.options.value
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import { renderGrouped } from 'prosemirror-menu'
|
import { renderGrouped } from 'prosemirror-menu'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { EditorState, Plugin } from 'prosemirror-state'
|
||||||
|
import type { EditorView } from 'prosemirror-view'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { ProseMirrorExtension } from '../helpers'
|
||||||
import { buildMenuItems } from './menu'
|
import { buildMenuItems } from './menu'
|
||||||
|
|
||||||
export class SelectionTooltip {
|
export class SelectionTooltip {
|
||||||
tooltip: any
|
tooltip: HTMLElement
|
||||||
|
|
||||||
constructor(view: any, schema: any) {
|
constructor(view: EditorView, schema) {
|
||||||
this.tooltip = document.createElement('div')
|
this.tooltip = document.createElement('div')
|
||||||
this.tooltip.className = 'tooltip'
|
this.tooltip.className = 'tooltip'
|
||||||
view.dom.parentNode.appendChild(this.tooltip)
|
view.dom.parentNode.appendChild(this.tooltip)
|
||||||
const { dom } = renderGrouped(view, (buildMenuItems(schema) as any).fullMenu)
|
const { dom } = renderGrouped(view, buildMenuItems(schema).fullMenu as any)
|
||||||
this.tooltip.appendChild(dom)
|
this.tooltip.appendChild(dom)
|
||||||
this.update(view, null)
|
this.update(view, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
update(view: any, lastState: any) {
|
update(view: EditorView, lastState: EditorState) {
|
||||||
const state = view.state
|
const state = view.state
|
||||||
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
||||||
return
|
return
|
||||||
|
@ -41,9 +42,9 @@ export class SelectionTooltip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toolTip(schema: any) {
|
export function toolTip(schema) {
|
||||||
return new Plugin({
|
return new Plugin({
|
||||||
view(editorView: any) {
|
view(editorView: EditorView) {
|
||||||
return new SelectionTooltip(editorView, schema)
|
return new SelectionTooltip(editorView, schema)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { EditorState, Selection } from 'prosemirror-state'
|
import { EditorState, Selection } from 'prosemirror-state'
|
||||||
import type { Node, Schema, ResolvedPos } from 'prosemirror-model'
|
import type { Node, Schema, ResolvedPos, NodeSpec } from 'prosemirror-model'
|
||||||
import { InputRule, inputRules } from 'prosemirror-inputrules'
|
import { InputRule, inputRules } from 'prosemirror-inputrules'
|
||||||
import { keymap } from 'prosemirror-keymap'
|
import { keymap } from 'prosemirror-keymap'
|
||||||
import type { ProseMirrorExtension } from '../helpers'
|
import type { ProseMirrorExtension } from '../helpers'
|
||||||
|
import type OrderedMap from 'orderedmap'
|
||||||
|
|
||||||
export const tableInputRule = (schema: Schema) =>
|
export const tableInputRule = (schema: Schema) =>
|
||||||
new InputRule(
|
new InputRule(
|
||||||
|
@ -174,7 +175,7 @@ const getTextSize = (n: Node) => {
|
||||||
export default (): ProseMirrorExtension => ({
|
export default (): ProseMirrorExtension => ({
|
||||||
schema: (prev) => ({
|
schema: (prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
nodes: (prev.nodes as any).append(tableSchema)
|
nodes: (prev.nodes as OrderedMap<NodeSpec>).append(tableSchema as NodeSpec)
|
||||||
}),
|
}),
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
plugins: (prev, schema) => [
|
plugins: (prev, schema) => [
|
||||||
|
|
|
@ -1,80 +1,73 @@
|
||||||
import { keymap } from 'prosemirror-keymap'
|
|
||||||
import type { ProseMirrorExtension } from './helpers'
|
|
||||||
import { Schema } from 'prosemirror-model'
|
|
||||||
import base from './extension/base'
|
|
||||||
import markdown from './extension/markdown'
|
|
||||||
import link from './extension/link'
|
|
||||||
// import scroll from './prosemirror/extension/scroll'
|
|
||||||
import todoList from './extension/todo-list'
|
|
||||||
import code from './extension/code'
|
|
||||||
import strikethrough from './extension/strikethrough'
|
|
||||||
import placeholder from './extension/placeholder'
|
|
||||||
// import menu from './extension/menu'
|
// import menu from './extension/menu'
|
||||||
import image from './extension/image'
|
// import scroll from './prosemirror/extension/scroll'
|
||||||
import dragHandle from './extension/drag-handle'
|
import { keymap } from 'prosemirror-keymap'
|
||||||
import pasteMarkdown from './extension/paste-markdown'
|
import { Schema } from 'prosemirror-model'
|
||||||
import table from './extension/table'
|
import type { Command } from 'prosemirror-state'
|
||||||
|
import { t } from '../../../utils/intl'
|
||||||
|
import base from './extension/base'
|
||||||
|
import code from './extension/code'
|
||||||
import collab from './extension/collab'
|
import collab from './extension/collab'
|
||||||
import type { Config, YOptions } from '../store'
|
import dragHandle from './extension/drag-handle'
|
||||||
|
import image from './extension/image'
|
||||||
|
import link from './extension/link'
|
||||||
|
import markdown from './extension/markdown'
|
||||||
|
import pasteMarkdown from './extension/paste-markdown'
|
||||||
|
import placeholder from './extension/placeholder'
|
||||||
import selectionMenu from './extension/selection'
|
import selectionMenu from './extension/selection'
|
||||||
|
import strikethrough from './extension/strikethrough'
|
||||||
|
import table from './extension/table'
|
||||||
|
import todoList from './extension/todo-list'
|
||||||
|
import type { Config, YOptions } from '../store'
|
||||||
|
import type { ProseMirrorExtension } from './helpers'
|
||||||
|
|
||||||
interface Props {
|
interface ExtensionsProps {
|
||||||
data?: unknown
|
data?: unknown
|
||||||
keymap?: any
|
keymap?: { [key: string]: Command }
|
||||||
config: Config
|
config: Config
|
||||||
markdown: boolean
|
markdown: boolean
|
||||||
path?: string
|
path?: string
|
||||||
y?: YOptions
|
y?: YOptions
|
||||||
schema?: Schema
|
schema?: Schema
|
||||||
|
collab?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const customKeymap = (props: Props): ProseMirrorExtension => ({
|
const customKeymap = (props: ExtensionsProps): ProseMirrorExtension => ({
|
||||||
plugins: (prev) => (props.keymap ? [...prev, keymap(props.keymap)] : prev)
|
plugins: (prev) => (props.keymap ? [...prev, keymap(props.keymap)] : prev)
|
||||||
})
|
})
|
||||||
/*
|
|
||||||
const codeMirrorKeymap = (props: Props) => {
|
|
||||||
const keys = []
|
|
||||||
for (const key in props.keymap) {
|
|
||||||
keys.push({key: key, run: props.keymap[key]})
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmKeymap.of(keys)
|
export const createExtensions = (props: ExtensionsProps): ProseMirrorExtension[] => {
|
||||||
|
const eee = [
|
||||||
|
// scroll(props.config.typewriterMode),
|
||||||
|
placeholder(t('Just start typing...')),
|
||||||
|
customKeymap(props),
|
||||||
|
base(props.markdown),
|
||||||
|
selectionMenu()
|
||||||
|
]
|
||||||
|
if (props.markdown) {
|
||||||
|
eee.push(
|
||||||
|
markdown(),
|
||||||
|
todoList(),
|
||||||
|
dragHandle(),
|
||||||
|
code(),
|
||||||
|
strikethrough(),
|
||||||
|
link(),
|
||||||
|
table(),
|
||||||
|
image(props.path),
|
||||||
|
pasteMarkdown()
|
||||||
|
/*
|
||||||
|
codeBlock({
|
||||||
|
theme: codeTheme(props.config),
|
||||||
|
typewriterMode: props.config.typewriterMode,
|
||||||
|
fontSize: props.config.fontSize,
|
||||||
|
prettier: props.config.prettier,
|
||||||
|
extensions: () => [codeMirrorKeymap(props)],
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (props.collab) eee.push(collab(props.y))
|
||||||
|
return eee
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
export const createExtensions = (props: Props): ProseMirrorExtension[] =>
|
|
||||||
props.markdown
|
|
||||||
? [
|
|
||||||
placeholder('Просто начните...'),
|
|
||||||
customKeymap(props),
|
|
||||||
base(props.markdown),
|
|
||||||
collab(props.y),
|
|
||||||
selectionMenu()
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
selectionMenu(),
|
|
||||||
customKeymap(props),
|
|
||||||
base(props.markdown),
|
|
||||||
markdown(),
|
|
||||||
todoList(),
|
|
||||||
dragHandle(),
|
|
||||||
code(),
|
|
||||||
strikethrough(),
|
|
||||||
link(),
|
|
||||||
table(),
|
|
||||||
image(props.path),
|
|
||||||
pasteMarkdown(),
|
|
||||||
collab(props.y)
|
|
||||||
// scroll(props.config.typewriterMode),
|
|
||||||
/*
|
|
||||||
codeBlock({
|
|
||||||
theme: codeTheme(props.config),
|
|
||||||
typewriterMode: props.config.typewriterMode,
|
|
||||||
fontSize: props.config.fontSize,
|
|
||||||
prettier: props.config.prettier,
|
|
||||||
extensions: () => [codeMirrorKeymap(props)],
|
|
||||||
}),
|
|
||||||
*/
|
|
||||||
]
|
|
||||||
|
|
||||||
export const createEmptyText = () => ({
|
export const createEmptyText = () => ({
|
||||||
doc: {
|
doc: {
|
||||||
|
@ -88,7 +81,7 @@ export const createEmptyText = () => ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const createSchema = (props: Props) => {
|
export const createSchema = (props: ExtensionsProps) => {
|
||||||
const extensions = createExtensions({
|
const extensions = createExtensions({
|
||||||
config: props.config,
|
config: props.config,
|
||||||
markdown: props.markdown,
|
markdown: props.markdown,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Store, createStore, unwrap } from 'solid-js/store'
|
import { Store, createStore, unwrap } from 'solid-js/store'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import type { EditorState } from 'prosemirror-state'
|
import type { Command, EditorState } from 'prosemirror-state'
|
||||||
import { undo, redo } from 'prosemirror-history'
|
import { undo, redo } from 'prosemirror-history'
|
||||||
import { selectAll, deleteSelection } from 'prosemirror-commands'
|
import { selectAll, deleteSelection } from 'prosemirror-commands'
|
||||||
import * as Y from 'yjs'
|
import * as Y from 'yjs'
|
||||||
|
@ -23,7 +23,7 @@ const isState = (x) => typeof x.lastModified !== 'string' && Array.isArray(x.dra
|
||||||
const isDraft = (x): boolean => x && (x.text || x.path)
|
const isDraft = (x): boolean => x && (x.text || x.path)
|
||||||
const mod = 'Ctrl'
|
const mod = 'Ctrl'
|
||||||
|
|
||||||
export const createCtrl = (initial): [Store<State>, any] => {
|
export const createCtrl = (initial): [Store<State>, { [key: string]: any }] => {
|
||||||
const [store, setState] = createStore(initial)
|
const [store, setState] = createStore(initial)
|
||||||
|
|
||||||
const onNew = () => {
|
const onNew = () => {
|
||||||
|
@ -64,7 +64,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
[`Shift-${mod}-z`]: onRedo,
|
[`Shift-${mod}-z`]: onRedo,
|
||||||
[`${mod}-y`]: onRedo,
|
[`${mod}-y`]: onRedo,
|
||||||
[`${mod}-m`]: onToggleMarkdown
|
[`${mod}-m`]: onToggleMarkdown
|
||||||
}
|
} as { [key: string]: Command }
|
||||||
|
|
||||||
const createTextFromDraft = async (d: Draft): Promise<Draft> => {
|
const createTextFromDraft = async (d: Draft): Promise<Draft> => {
|
||||||
let draft = d
|
let draft = d
|
||||||
|
@ -83,7 +83,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
return {
|
return {
|
||||||
text: draft.text,
|
text: draft.text,
|
||||||
extensions,
|
extensions,
|
||||||
updatedAt: draft.updatedAt ? new Date(draft.updatedAt) : undefined,
|
lastModified: draft.lastModified ? new Date(draft.lastModified) : undefined,
|
||||||
path: draft.path,
|
path: draft.path,
|
||||||
markdown: draft.markdown
|
markdown: draft.markdown
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
...drafts,
|
...drafts,
|
||||||
{
|
{
|
||||||
body: text,
|
body: text,
|
||||||
updatedAt: prev.updatedAt as Date,
|
lastModified: prev.lastModified as Date,
|
||||||
path: prev.path,
|
path: prev.path,
|
||||||
markdown: prev.markdown
|
markdown: prev.markdown
|
||||||
} as Draft
|
} as Draft
|
||||||
|
@ -121,7 +121,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
next = {
|
next = {
|
||||||
text: createEmptyText(),
|
text: createEmptyText(),
|
||||||
extensions,
|
extensions,
|
||||||
updatedAt: new Date(),
|
lastModified: new Date(),
|
||||||
path: undefined,
|
path: undefined,
|
||||||
markdown: state.markdown
|
markdown: state.markdown
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
} else if (data.args.text) {
|
} else if (data.args.text) {
|
||||||
data = await doOpenDraft(data, {
|
data = await doOpenDraft(data, {
|
||||||
text: { ...JSON.parse(data.args.text) },
|
text: { ...JSON.parse(data.args.text) },
|
||||||
updatedAt: new Date()
|
lastModified: new Date()
|
||||||
})
|
})
|
||||||
} else if (data.args.draft) {
|
} else if (data.args.draft) {
|
||||||
const draft = await loadDraft(data.config, data.args.draft)
|
const draft = await loadDraft(data.config, data.args.draft)
|
||||||
|
@ -258,7 +258,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
const loadDraft = async (config: Config, path: string): Promise<Draft> => {
|
const loadDraft = async (config: Config, path: string): Promise<Draft> => {
|
||||||
const draftstore = useStore(draftsatom)
|
const draftstore = useStore(draftsatom)
|
||||||
const draft = createMemo(() => draftstore()[path])
|
const draft = createMemo(() => draftstore()[path])
|
||||||
const lastModified = draft().updatedAt
|
const lastModified = draft().lastModified
|
||||||
const draftContent = draft().body
|
const draftContent = draft().body
|
||||||
const schema = createSchema({
|
const schema = createSchema({
|
||||||
config,
|
config,
|
||||||
|
@ -280,7 +280,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
...draft(),
|
...draft(),
|
||||||
body: doc,
|
body: doc,
|
||||||
text,
|
text,
|
||||||
updatedAt: lastModified.toISOString(),
|
lastModified: lastModified.toISOString(),
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,9 +327,9 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
const item = index === -1 ? draft : state.drafts[index]
|
const item = index === -1 ? draft : state.drafts[index]
|
||||||
let drafts = state.drafts.filter((f) => f !== item)
|
let drafts = state.drafts.filter((f) => f !== item)
|
||||||
if (!isEmpty(state.text as EditorState) && state.lastModified) {
|
if (!isEmpty(state.text as EditorState) && state.lastModified) {
|
||||||
drafts = addToDrafts(drafts, { updatedAt: new Date(), text: state.text } as Draft)
|
drafts = addToDrafts(drafts, { lastModified: new Date(), text: state.text } as Draft)
|
||||||
}
|
}
|
||||||
draft.updatedAt = item.updatedAt
|
draft.lastModified = item.lastModified
|
||||||
const next = await createTextFromDraft(draft)
|
const next = await createTextFromDraft(draft)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -343,7 +343,8 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
|
|
||||||
const saveState = () =>
|
const saveState = () =>
|
||||||
debounce(async (state: State) => {
|
debounce(async (state: State) => {
|
||||||
const data: any = {
|
const data: State = {
|
||||||
|
loading: 'initialized',
|
||||||
lastModified: state.lastModified,
|
lastModified: state.lastModified,
|
||||||
drafts: state.drafts,
|
drafts: state.drafts,
|
||||||
config: state.config,
|
config: state.config,
|
||||||
|
@ -357,7 +358,8 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
if (isInitialized(state.text as EditorState)) {
|
if (isInitialized(state.text as EditorState)) {
|
||||||
if (state.path) {
|
if (state.path) {
|
||||||
const text = serialize(store.editorView.state)
|
const text = serialize(store.editorView.state)
|
||||||
// TODO: await remote.writeDraft(state.path, text)
|
// await remote.writeDraft(state.path, text)
|
||||||
|
draftsatom.setKey(state.path, text)
|
||||||
} else {
|
} else {
|
||||||
data.text = store.editorView.state.toJSON()
|
data.text = store.editorView.state.toJSON()
|
||||||
}
|
}
|
||||||
|
@ -418,7 +420,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
if ((backup && !isEmpty(state.text as EditorState)) || state.path) {
|
if ((backup && !isEmpty(state.text as EditorState)) || state.path) {
|
||||||
let drafts = state.drafts
|
let drafts = state.drafts
|
||||||
if (!state.error) {
|
if (!state.error) {
|
||||||
drafts = addToDrafts(drafts, { updatedAt: new Date(), text: state.text } as Draft)
|
drafts = addToDrafts(drafts, { lastModified: new Date(), text: state.text } as Draft)
|
||||||
}
|
}
|
||||||
|
|
||||||
newst = {
|
newst = {
|
||||||
|
@ -455,7 +457,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
const editorState = store.text as EditorState
|
const editorState = store.text as EditorState
|
||||||
const markdown = !state.markdown
|
const markdown = !state.markdown
|
||||||
const selection = { type: 'text', anchor: 1, head: 1 }
|
const selection = { type: 'text', anchor: 1, head: 1 }
|
||||||
let doc: any
|
let doc
|
||||||
|
|
||||||
if (markdown) {
|
if (markdown) {
|
||||||
const lines = serialize(editorState).split('\n')
|
const lines = serialize(editorState).split('\n')
|
||||||
|
@ -495,6 +497,7 @@ export const createCtrl = (initial): [Store<State>, any] => {
|
||||||
extensions,
|
extensions,
|
||||||
markdown
|
markdown
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateConfig = (config: Partial<Config>) => {
|
const updateConfig = (config: Partial<Config>) => {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import type { WebrtcProvider } from 'y-webrtc'
|
||||||
import type { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers'
|
import type { ProseMirrorExtension, ProseMirrorState } from '../prosemirror/helpers'
|
||||||
import type { EditorView } from 'prosemirror-view'
|
import type { EditorView } from 'prosemirror-view'
|
||||||
import { createEmptyText } from '../prosemirror/setup'
|
import { createEmptyText } from '../prosemirror/setup'
|
||||||
import type { Shout } from '../../../graphql/types.gen'
|
|
||||||
|
|
||||||
export interface Args {
|
export interface Args {
|
||||||
draft: string // path to draft
|
draft: string // path to draft
|
||||||
|
@ -70,7 +69,7 @@ export interface State {
|
||||||
|
|
||||||
export interface Draft {
|
export interface Draft {
|
||||||
extensions?: ProseMirrorExtension[]
|
extensions?: ProseMirrorExtension[]
|
||||||
updatedAt: Date
|
lastModified: Date
|
||||||
body?: string
|
body?: string
|
||||||
text?: { doc: any; selection: { type: string; anchor: number; head: number } }
|
text?: { doc: any; selection: { type: string; anchor: number; head: number } }
|
||||||
path?: string
|
path?: string
|
||||||
|
|
|
@ -2,48 +2,64 @@
|
||||||
@import './Sidebar';
|
@import './Sidebar';
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
flex: 1;
|
margin: 0.5em;
|
||||||
padding-top: 1em;
|
padding: 1em;
|
||||||
|
min-width: 50%;
|
||||||
|
min-height: fit-content;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px dotted rgb(0 0 0 / 80%);
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
a {
|
||||||
display: block;
|
color: rgb(0 100 200);
|
||||||
}
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
a:hover {
|
||||||
button,
|
text-decoration: underline;
|
||||||
select,
|
}
|
||||||
textarea {
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
-webkit-padding: 0.4em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
margin: 0 0 0.5em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
a:visited {
|
||||||
color: #ccc;
|
color: rgb(0 100 200 / 70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
label {
|
||||||
color: #333;
|
display: block;
|
||||||
background-color: #f4f4f4;
|
}
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:disabled {
|
input,
|
||||||
color: #999;
|
button,
|
||||||
}
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0.4em;
|
||||||
|
margin: 0 0 0.5em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
button:not(:disabled):active {
|
input:disabled {
|
||||||
background-color: #ddd;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus {
|
button {
|
||||||
border-color: #666;
|
color: #333;
|
||||||
}
|
background-color: #f4f4f4;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):active {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
border-color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ProseMirror {
|
.ProseMirror {
|
||||||
|
@ -104,17 +120,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 2px solid;
|
|
||||||
@include font-size(1.6rem);
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
|
border-left: 2px solid;
|
||||||
padding-left: 1.6em;
|
padding-left: 1.6em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ProseMirror-menuitem {
|
.ProseMirror-menuitem {
|
||||||
display: flex;
|
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
> * {
|
> * {
|
||||||
|
@ -175,7 +191,7 @@
|
||||||
content: '';
|
content: '';
|
||||||
border-left: 4px solid transparent;
|
border-left: 4px solid transparent;
|
||||||
border-right: 4px solid transparent;
|
border-right: 4px solid transparent;
|
||||||
border-top: 4px solid currentcolor;
|
border-top: 4px solid draftcurrentcolor;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 4px;
|
right: 4px;
|
||||||
|
@ -215,7 +231,7 @@
|
||||||
content: '';
|
content: '';
|
||||||
border-top: 4px solid transparent;
|
border-top: 4px solid transparent;
|
||||||
border-bottom: 4px solid transparent;
|
border-bottom: 4px solid transparent;
|
||||||
border-left: 4px solid currentcolor;
|
border-left: 4px solid draftcurrentcolor;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 4px;
|
right: 4px;
|
||||||
|
@ -270,7 +286,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ProseMirror-icon svg {
|
.ProseMirror-icon svg {
|
||||||
fill: currentcolor;
|
fill: draftcurrentcolor;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +349,7 @@ li.ProseMirror-selectednode::after {
|
||||||
|
|
||||||
.ProseMirror-prompt {
|
.ProseMirror-prompt {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 4px 10px rgb(0 0 0 / 25%);
|
box-shadow: 0 4px 10px rgba(0 0 0 / 25%);
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
@ -378,7 +394,7 @@ li.ProseMirror-selectednode::after {
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
box-shadow: 0 4px 10px rgb(0 0 0 / 25%);
|
box-shadow: 0 4px 10px rgba(0 0 0 / 25%);
|
||||||
color: #000;
|
color: #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -92,10 +92,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-container {
|
.sidebar-container {
|
||||||
color: rgb(255 255 255 / 50%);
|
|
||||||
font-family: Muller;
|
|
||||||
@include font-size(1.6rem);
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
|
color: rgb(255 255 255 / 50%);
|
||||||
|
font-family: Muller;
|
||||||
|
display: inline-flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { persistentAtom } from '@nanostores/persistent'
|
import { persistentAtom, persistentMap } from '@nanostores/persistent'
|
||||||
import type { Reaction } from '../graphql/types.gen'
|
import type { Reaction } from '../graphql/types.gen'
|
||||||
import { atom } from 'nanostores'
|
import { atom } from 'nanostores'
|
||||||
import { createSignal } from 'solid-js'
|
import { createSignal } from 'solid-js'
|
||||||
|
@ -18,7 +18,7 @@ interface Collab {
|
||||||
title?: string
|
title?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const drafts = persistentAtom<{ [key: string]: Draft }>(
|
export const drafts = persistentMap<{ [key: string]: Draft }>(
|
||||||
'drafts',
|
'drafts',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
|
|
@ -8553,7 +8553,7 @@ ora@^6.1.0:
|
||||||
strip-ansi "^7.0.1"
|
strip-ansi "^7.0.1"
|
||||||
wcwidth "^1.0.1"
|
wcwidth "^1.0.1"
|
||||||
|
|
||||||
orderedmap@^2.0.0:
|
orderedmap@^2.0.0, orderedmap@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.0.tgz#819457082fa3a06abd316d83a281a1ca467437cd"
|
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.0.tgz#819457082fa3a06abd316d83a281a1ca467437cd"
|
||||||
integrity sha512-/pIFexOm6S70EPdznemIz3BQZoJ4VTFrhqzu0ACBqBgeLsLxq8e6Jim63ImIfwW/zAD1AlXpRMlOv3aghmo4dA==
|
integrity sha512-/pIFexOm6S70EPdznemIz3BQZoJ4VTFrhqzu0ACBqBgeLsLxq8e6Jim63ImIfwW/zAD1AlXpRMlOv3aghmo4dA==
|
||||||
|
|
Loading…
Reference in New Issue
Block a user