@podlite/editor-react 0.0.18 → 0.0.20
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/CHANGELOG.md +43 -1
- package/README.md +38 -1
- package/lib/index.cjs +23858 -0
- package/lib/index.cjs.map +7 -0
- package/lib/index.css +311 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.esm.css +311 -0
- package/lib/index.esm.js +23853 -0
- package/lib/index.esm.js.map +7 -0
- package/package.json +32 -12
- package/src/index.tsx +290 -269
- package/esm/dict.d.ts +0 -7
- package/esm/helpers.d.ts +0 -15
- package/esm/index.css +0 -1
- package/esm/index.d.ts +0 -23
- package/esm/index.js +0 -1362
- package/esm/index.js.map +0 -7
- package/index.js +0 -2
- package/jest.config copy.js +0 -2
- package/jest.config.js +0 -2
- package/jest.tsconfig.json +0 -1
- package/lib/index.js +0 -1362
- package/lib/index.js.map +0 -7
- package/scripts/build.ts +0 -17
- package/scripts/build_esm.ts +0 -17
- package/src/Editor.css +0 -356
- package/src/dict.ts +0 -248
- package/src/helpers.ts +0 -74
- package/t/editor.test.tsx +0 -86
- package/tsconfig.json +0 -15
- package/tsconfig.tsbuildinfo +0 -1
package/src/index.tsx
CHANGED
|
@@ -1,73 +1,72 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Controlled as CodeMirrorControlled, UnControlled as CodeMirror} from 'react-codemirror2'
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Controlled as CodeMirrorControlled, UnControlled as CodeMirror } from 'react-codemirror2'
|
|
3
3
|
import CMirror from 'codemirror'
|
|
4
4
|
import dictionary from './dict'
|
|
5
|
-
import { useState, useEffect, useRef
|
|
6
|
-
import {
|
|
5
|
+
import { useState, useEffect, useRef } from 'react'
|
|
6
|
+
import { isElement } from 'react-is'
|
|
7
7
|
|
|
8
8
|
// TODO: use bundler to add into package
|
|
9
9
|
// import '../../../node_modules/codemirror/lib/codemirror.css';
|
|
10
|
-
import 'codemirror/mode/gfm/gfm'
|
|
11
|
-
import
|
|
12
|
-
import 'codemirror/addon/hint/show-hint.css'
|
|
13
|
-
import './Editor.css'
|
|
10
|
+
import 'codemirror/mode/gfm/gfm'
|
|
11
|
+
import 'codemirror/addon/hint/show-hint'
|
|
12
|
+
import 'codemirror/addon/hint/show-hint.css'
|
|
13
|
+
import './Editor.css'
|
|
14
14
|
import { addVMargin, getSuggestionContextForLine, templateGetSelectionPos } from './helpers'
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
//@ts-ignore
|
|
18
17
|
function useDebouncedEffect(fn, deps, time) {
|
|
19
|
-
const dependencies = [...deps, time]
|
|
18
|
+
const dependencies = [...deps, time]
|
|
20
19
|
useEffect(() => {
|
|
21
|
-
const timeout = setTimeout(fn, time)
|
|
20
|
+
const timeout = setTimeout(fn, time)
|
|
22
21
|
return () => {
|
|
23
|
-
clearTimeout(timeout)
|
|
22
|
+
clearTimeout(timeout)
|
|
24
23
|
}
|
|
25
|
-
}, dependencies)
|
|
24
|
+
}, dependencies)
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
/* set window title */
|
|
27
|
+
/* set window title */
|
|
29
28
|
// @ts-ignore
|
|
30
29
|
// const setWindowTitle = (title: string) => { vmd.setWindowTitle(title) }
|
|
31
|
-
export interface ConverterResult
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
export interface ConverterResult {
|
|
31
|
+
errors?: any
|
|
32
|
+
result: any
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
let instanceCM = null
|
|
37
|
-
type Props={
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
type Props = {
|
|
37
|
+
content: string
|
|
38
|
+
onChangeSource: Function
|
|
39
|
+
sourceType?: 'pod6' | 'md'
|
|
40
|
+
onConvertSource: (source: string) => ConverterResult
|
|
41
|
+
onSavePressed?: Function
|
|
42
|
+
isDarkTheme?: boolean
|
|
43
|
+
isLineNumbers?: boolean
|
|
44
|
+
isAutoComplete?: boolean
|
|
45
|
+
isPreviewModeEnabled?: boolean
|
|
46
|
+
isControlled?: boolean
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
export const Editor = ({
|
|
50
|
+
onChangeSource = () => {},
|
|
51
|
+
content,
|
|
52
|
+
isDarkTheme = false,
|
|
53
|
+
isLineNumbers = false,
|
|
54
|
+
isPreviewModeEnabled = false,
|
|
55
|
+
onConvertSource,
|
|
56
|
+
onSavePressed = () => {},
|
|
57
|
+
sourceType = 'pod6',
|
|
58
|
+
isControlled = false,
|
|
59
|
+
isAutoComplete = true,
|
|
60
|
+
}: Props) => {
|
|
62
61
|
const [text, updateText] = useState(content)
|
|
63
62
|
|
|
64
63
|
const [marks, updateMarks] = useState([])
|
|
65
64
|
const [, updateScrollMap] = useState([])
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
const [isPreviewMode, setPreviewMode] = useState(isPreviewModeEnabled)
|
|
68
67
|
|
|
69
|
-
const [isPreviewScroll, setPreviewScrolling] = useState(false)
|
|
70
|
-
const refValue = useRef(isPreviewScroll)
|
|
68
|
+
const [isPreviewScroll, setPreviewScrolling] = useState(false)
|
|
69
|
+
const refValue = useRef(isPreviewScroll)
|
|
71
70
|
const [showTree, setShowTree] = useState(false)
|
|
72
71
|
|
|
73
72
|
const [filePath, setFilePath] = useState('')
|
|
@@ -77,275 +76,297 @@ export default ({
|
|
|
77
76
|
|
|
78
77
|
const [fileLoading, setFileLoading] = useState(true)
|
|
79
78
|
|
|
80
|
-
useEffect(()=>{
|
|
81
|
-
updateText(content)
|
|
82
|
-
},[content])
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
updateText(content)
|
|
81
|
+
}, [content])
|
|
82
|
+
|
|
83
|
+
const [result, updateResult] = useState<ConverterResult>()
|
|
84
|
+
useDebouncedEffect(
|
|
85
|
+
() => {
|
|
86
|
+
updateResult(onConvertSource(text))
|
|
87
|
+
},
|
|
88
|
+
[text],
|
|
89
|
+
50,
|
|
90
|
+
)
|
|
83
91
|
|
|
84
|
-
const [result, updateResult] = useState<ConverterResult>()
|
|
85
|
-
useDebouncedEffect(() => {
|
|
86
|
-
updateResult(onConvertSource(text))
|
|
87
|
-
}, [text], 50)
|
|
88
|
-
|
|
89
92
|
const inputEl = useRef(null)
|
|
90
93
|
|
|
91
|
-
// hot keys
|
|
92
|
-
useEffect(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.warn(
|
|
94
|
+
// hot keys
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const saveFileAction = () => {
|
|
97
|
+
if (isChanged) {
|
|
98
|
+
console.warn('Save File')
|
|
96
99
|
onSavePressed(text)
|
|
97
|
-
|
|
100
|
+
}
|
|
98
101
|
}
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
refValue.current = isPreviewScroll;
|
|
105
|
-
});
|
|
106
|
-
var options: CMirror.EditorConfiguration = {
|
|
107
|
-
lineNumbers: isLineNumbers,
|
|
108
|
-
inputStyle: "contenteditable",
|
|
109
|
-
//@ts-ignore
|
|
110
|
-
spellcheck: true,
|
|
111
|
-
autofocus:true,
|
|
112
|
-
lineWrapping:true,
|
|
113
|
-
viewportMargin:Infinity,
|
|
114
|
-
mode: sourceType !== 'md' ? null :
|
|
115
|
-
{
|
|
116
|
-
name: "gfm",
|
|
117
|
-
tokenTypeOverrides: {
|
|
118
|
-
emoji: "emoji"
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
theme: isDarkTheme ? "duotone-dark" : "default"
|
|
122
|
-
};
|
|
102
|
+
})
|
|
123
103
|
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
refValue.current = isPreviewScroll
|
|
106
|
+
})
|
|
107
|
+
var options: CMirror.EditorConfiguration = {
|
|
108
|
+
lineNumbers: isLineNumbers,
|
|
109
|
+
inputStyle: 'contenteditable',
|
|
110
|
+
//@ts-ignore
|
|
111
|
+
spellcheck: true,
|
|
112
|
+
autofocus: true,
|
|
113
|
+
lineWrapping: true,
|
|
114
|
+
viewportMargin: Infinity,
|
|
115
|
+
mode:
|
|
116
|
+
sourceType !== 'md'
|
|
117
|
+
? null
|
|
118
|
+
: {
|
|
119
|
+
name: 'gfm',
|
|
120
|
+
tokenTypeOverrides: {
|
|
121
|
+
emoji: 'emoji',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
theme: isDarkTheme ? 'duotone-dark' : 'default',
|
|
125
|
+
}
|
|
124
126
|
|
|
125
|
-
const previewEl = useRef(null)
|
|
127
|
+
const previewEl = useRef(null)
|
|
126
128
|
|
|
127
|
-
useEffect(() => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
updateScrollMap(newScrollMap)
|
|
138
|
-
//@ts-ignore
|
|
139
|
-
const listener = (e) => {
|
|
140
|
-
if (!isPreviewScroll ) {return}
|
|
141
|
-
let element = e.target
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
//@ts-ignore
|
|
131
|
+
const newScrollMap = [...document.querySelectorAll('.line-src')].map(n => {
|
|
132
|
+
const line = parseInt(n.getAttribute('data-line'), 10)
|
|
133
|
+
//@ts-ignore
|
|
134
|
+
const offsetTop = n.offsetTop
|
|
135
|
+
return { line, offsetTop }
|
|
136
|
+
})
|
|
137
|
+
//@ts-ignore
|
|
138
|
+
updateScrollMap(newScrollMap)
|
|
142
139
|
//@ts-ignore
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
const listener = e => {
|
|
141
|
+
if (!isPreviewScroll) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
let element = e.target
|
|
145
|
+
//@ts-ignore
|
|
146
|
+
const getLine = offset => {
|
|
147
|
+
const c = newScrollMap.filter(i => i.offsetTop > offset)
|
|
148
|
+
const lineElement = c.shift() || newScrollMap[newScrollMap.length - 1]
|
|
149
|
+
if (!lineElement) {
|
|
147
150
|
console.warn(`[podlite-editor] can't get line for offset. Forget add .line-src ?`)
|
|
151
|
+
}
|
|
152
|
+
return lineElement.line
|
|
148
153
|
}
|
|
149
|
-
|
|
154
|
+
const line = getLine(element.scrollTop)
|
|
155
|
+
if (instanceCM) {
|
|
156
|
+
const t = element.scrollTop === 0 ? 0 : instanceCM.charCoords({ line: line, ch: 0 }, 'local').top
|
|
157
|
+
instanceCM.scrollTo(null, t)
|
|
150
158
|
}
|
|
151
|
-
|
|
152
|
-
if (instanceCM) {
|
|
153
|
-
const t = element.scrollTop === 0 ? 0 : instanceCM.charCoords({line: line, ch: 0}, "local").top;
|
|
154
|
-
instanceCM.scrollTo(null, t);
|
|
159
|
+
return true
|
|
155
160
|
}
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
if (previewEl && previewEl.current) {
|
|
161
|
+
if (previewEl && previewEl.current) {
|
|
159
162
|
//@ts-ignore
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
},[text,isPreviewScroll])
|
|
163
|
+
previewEl.current.addEventListener('scroll', listener)
|
|
164
|
+
}
|
|
165
|
+
return () => {
|
|
166
|
+
// @ts-ignore
|
|
167
|
+
previewEl && previewEl.current && previewEl && previewEl.current.removeEventListener('scroll', listener)
|
|
168
|
+
}
|
|
169
|
+
}, [text, isPreviewScroll])
|
|
167
170
|
|
|
168
|
-
useEffect(() => {
|
|
171
|
+
useEffect(() => {
|
|
169
172
|
//@ts-ignore
|
|
170
173
|
let cm = instanceCM
|
|
171
|
-
if (!cm) {
|
|
174
|
+
if (!cm) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
172
177
|
//@ts-ignore
|
|
173
178
|
marks.forEach(marker => marker.clear())
|
|
174
179
|
//@ts-ignore
|
|
175
|
-
let cmMrks:Array<never> = []
|
|
180
|
+
let cmMrks: Array<never> = []
|
|
176
181
|
//@ts-ignore
|
|
177
|
-
if (result && result.errors
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
let to = {line: loc.end.line-1, ch: loc.end.column-1};
|
|
182
|
+
if (result && result.errors) {
|
|
183
|
+
//@ts-ignore
|
|
184
|
+
result.errors.map((loc: any) => {
|
|
185
|
+
// @ts-ignore
|
|
186
|
+
let from = { line: loc.start.line - 1, ch: loc.start.column - 1 - (loc.start.offset === loc.end.offset) }
|
|
187
|
+
let to = { line: loc.end.line - 1, ch: loc.end.column - 1 }
|
|
184
188
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
)
|
|
198
|
-
})
|
|
189
|
+
cmMrks.push(
|
|
190
|
+
//@ts-ignore
|
|
191
|
+
cm.markText(from, to, {
|
|
192
|
+
className: 'syntax-error',
|
|
193
|
+
title: ';data.error.message',
|
|
194
|
+
css: 'color : red',
|
|
195
|
+
}),
|
|
196
|
+
)
|
|
197
|
+
})
|
|
199
198
|
}
|
|
200
199
|
updateMarks(cmMrks)
|
|
200
|
+
}, [text, result])
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
202
|
+
const previewHtml = (
|
|
203
|
+
<div
|
|
204
|
+
className={'Editorright ' + (isDarkTheme ? 'dark' : '')}
|
|
205
|
+
onMouseEnter={() => setPreviewScrolling(true)}
|
|
206
|
+
onMouseMove={() => setPreviewScrolling(true)}
|
|
207
|
+
ref={previewEl}
|
|
208
|
+
>
|
|
209
|
+
{result ? (
|
|
210
|
+
isElement(result.result) ? (
|
|
211
|
+
<div className="content">{result.result}</div>
|
|
212
|
+
) : (
|
|
213
|
+
<div dangerouslySetInnerHTML={{ __html: result.result }} className="content"></div>
|
|
214
|
+
)
|
|
215
|
+
) : (
|
|
216
|
+
''
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
219
|
+
)
|
|
220
|
+
//@ts-ignore
|
|
221
|
+
const scrollEditorHandler = editor => {
|
|
222
|
+
if (refValue.current) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
let scrollInfo = editor.getScrollInfo()
|
|
223
226
|
// get line number of the top line in the page
|
|
224
|
-
let lineNumber = editor.lineAtHeight(scrollInfo.top, 'local') + 1
|
|
227
|
+
let lineNumber = editor.lineAtHeight(scrollInfo.top, 'local') + 1
|
|
225
228
|
if (previewEl) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
229
|
+
const el = previewEl.current
|
|
230
|
+
const elementId = `#line-${lineNumber}`
|
|
231
|
+
const scrollToElement = document.querySelector(elementId)
|
|
232
|
+
if (scrollToElement) {
|
|
233
|
+
//@ts-ignore
|
|
234
|
+
const scrollTo = scrollToElement.offsetTop
|
|
235
|
+
//@ts-ignore
|
|
236
|
+
el.scrollTo({
|
|
237
|
+
top: scrollTo,
|
|
238
|
+
left: 0,
|
|
239
|
+
behavior: 'smooth',
|
|
240
|
+
})
|
|
241
|
+
}
|
|
239
242
|
}
|
|
240
|
-
}
|
|
241
|
-
const [instanceCMLocal, updateInstanceCM] = useState<any>()
|
|
243
|
+
}
|
|
244
|
+
const [instanceCMLocal, updateInstanceCM] = useState<any>()
|
|
242
245
|
|
|
243
|
-
useEffect(()=>{
|
|
246
|
+
useEffect(() => {
|
|
244
247
|
if (!instanceCMLocal) return
|
|
245
248
|
if (!isAutoComplete) return
|
|
246
|
-
var onChange = function(instance, object)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
instanceCMLocal.on('change', onChange)
|
|
257
|
-
|
|
258
|
-
CMirror.registerHelper('hint', 'dictionaryHint', function(editor) {
|
|
259
|
-
var cur = editor.getCursor();
|
|
260
|
-
var curLine = editor.getLine(cur.line);
|
|
261
|
-
var start = cur.ch;
|
|
262
|
-
var end = start;
|
|
263
|
-
while (end < curLine.length && /[\w$]/.test(curLine.charAt(end))) ++end;
|
|
264
|
-
while (start && /[^=]/.test(curLine.charAt(start - 1))) --start;
|
|
265
|
-
var curWord = start !== end && curLine.slice(start, end);
|
|
266
|
-
var regex = new RegExp('' + curWord, 'i');
|
|
267
|
-
// filter dict by regex and sort by mostly nearness
|
|
268
|
-
const filterDictByRegex = (arr, regex)=>{
|
|
269
|
-
const dict = arr.reduce((acc, item)=>{
|
|
270
|
-
if (item === null) { return acc; }
|
|
271
|
-
const result = (typeof item === 'object' && !Array.isArray(item)) ? item.displayText.match(regex): item.match(regex);
|
|
272
|
-
if ( result ) {
|
|
273
|
-
acc.push({item, index:result.index})
|
|
274
|
-
}
|
|
275
|
-
return acc;
|
|
276
|
-
}, []);
|
|
277
|
-
return dict.sort((a, b) => a.index - b.index).map(i=>i.item)
|
|
278
|
-
}
|
|
279
|
-
const langMode = sourceType === 'md' ? 'md' : getSuggestionContextForLine(editor.getValue() ,cur.line+1)
|
|
280
|
-
const langDict = dictionary.filter( ({lang='pod6'})=>lang === langMode)
|
|
281
|
-
// apply hint
|
|
282
|
-
const resultDict = (!curWord ? langDict : filterDictByRegex(langDict,regex )).map((item)=>{
|
|
283
|
-
return {...item, hint: function(cm,data, completion){
|
|
284
|
-
const from = completion.from || data.from
|
|
285
|
-
const to = completion.to || data.to
|
|
286
|
-
// add vMargin
|
|
287
|
-
const text = addVMargin(from.ch, typeof completion == "string" ? completion : completion.text)
|
|
288
|
-
const selFromTemplate = templateGetSelectionPos(text)
|
|
289
|
-
console.log({from ,to , text})
|
|
290
|
-
if (selFromTemplate) {
|
|
291
|
-
const {text,start, end} = selFromTemplate
|
|
292
|
-
cm.replaceRange(text, from, to, "complete");
|
|
293
|
-
cm.setSelection({line: start.line + from.line, ch: start.offset + from.ch}, {line: end.line + to.line, ch: end.offset + to.ch -1});
|
|
294
|
-
} else {
|
|
295
|
-
cm.replaceRange(text, from, to, "complete");
|
|
296
|
-
}
|
|
249
|
+
var onChange = function (instance, object) {
|
|
250
|
+
// Check if the last inserted character is `=`.
|
|
251
|
+
if (
|
|
252
|
+
object.text[0] === '=' &&
|
|
253
|
+
// start directive
|
|
254
|
+
instance.getRange({ ch: 0, line: object.to.line }, object.to).match(/^\s*$/)
|
|
255
|
+
) {
|
|
256
|
+
CMirror.showHint(instanceCMLocal, CMirror.hint.dictionaryHint)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
instanceCMLocal.on('change', onChange)
|
|
297
260
|
|
|
298
|
-
|
|
299
|
-
|
|
261
|
+
CMirror.registerHelper('hint', 'dictionaryHint', function (editor) {
|
|
262
|
+
var cur = editor.getCursor()
|
|
263
|
+
var curLine = editor.getLine(cur.line)
|
|
264
|
+
var start = cur.ch
|
|
265
|
+
var end = start
|
|
266
|
+
while (end < curLine.length && /[\w$]/.test(curLine.charAt(end))) ++end
|
|
267
|
+
while (start && /[^=]/.test(curLine.charAt(start - 1))) --start
|
|
268
|
+
var curWord = start !== end && curLine.slice(start, end)
|
|
269
|
+
var regex = new RegExp('' + curWord, 'i')
|
|
270
|
+
// filter dict by regex and sort by mostly nearness
|
|
271
|
+
const filterDictByRegex = (arr, regex) => {
|
|
272
|
+
const dict = arr.reduce((acc, item) => {
|
|
273
|
+
if (item === null) {
|
|
274
|
+
return acc
|
|
275
|
+
}
|
|
276
|
+
const result =
|
|
277
|
+
typeof item === 'object' && !Array.isArray(item) ? item.displayText.match(regex) : item.match(regex)
|
|
278
|
+
if (result) {
|
|
279
|
+
acc.push({ item, index: result.index })
|
|
280
|
+
}
|
|
281
|
+
return acc
|
|
282
|
+
}, [])
|
|
283
|
+
return dict.sort((a, b) => a.index - b.index).map(i => i.item)
|
|
284
|
+
}
|
|
285
|
+
const langMode = sourceType === 'md' ? 'md' : getSuggestionContextForLine(editor.getValue(), cur.line + 1)
|
|
286
|
+
const langDict = dictionary.filter(({ lang = 'pod6' }) => lang === langMode)
|
|
287
|
+
// apply hint
|
|
288
|
+
const resultDict = (!curWord ? langDict : filterDictByRegex(langDict, regex)).map(item => {
|
|
300
289
|
return {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
290
|
+
...item,
|
|
291
|
+
hint: function (cm, data, completion) {
|
|
292
|
+
const from = completion.from || data.from
|
|
293
|
+
const to = completion.to || data.to
|
|
294
|
+
// add vMargin
|
|
295
|
+
const text = addVMargin(from.ch, typeof completion == 'string' ? completion : completion.text)
|
|
296
|
+
const selFromTemplate = templateGetSelectionPos(text)
|
|
297
|
+
console.log({ from, to, text })
|
|
298
|
+
if (selFromTemplate) {
|
|
299
|
+
const { text, start, end } = selFromTemplate
|
|
300
|
+
cm.replaceRange(text, from, to, 'complete')
|
|
301
|
+
cm.setSelection(
|
|
302
|
+
{ line: start.line + from.line, ch: start.offset + from.ch },
|
|
303
|
+
{ line: end.line + to.line, ch: end.offset + to.ch - 1 },
|
|
304
|
+
)
|
|
305
|
+
} else {
|
|
306
|
+
cm.replaceRange(text, from, to, 'complete')
|
|
307
|
+
}
|
|
308
|
+
},
|
|
304
309
|
}
|
|
305
|
-
|
|
310
|
+
})
|
|
311
|
+
return {
|
|
312
|
+
list: resultDict,
|
|
313
|
+
from: CMirror.Pos(cur.line, start - 1),
|
|
314
|
+
to: CMirror.Pos(cur.line, end),
|
|
315
|
+
}
|
|
316
|
+
})
|
|
306
317
|
instanceCMLocal.refresh()
|
|
307
318
|
return () => {
|
|
308
|
-
|
|
309
|
-
|
|
319
|
+
//@ts-ignore
|
|
320
|
+
instanceCMLocal.off('change', onChange)
|
|
310
321
|
}
|
|
311
|
-
},[instanceCMLocal, isAutoComplete])
|
|
322
|
+
}, [instanceCMLocal, isAutoComplete])
|
|
312
323
|
|
|
313
|
-
return (
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
<div
|
|
317
|
-
|
|
324
|
+
return (
|
|
325
|
+
<div className="EditorApp">
|
|
326
|
+
<div className={isPreviewModeEnabled ? 'layoutPreview' : 'layout'}>
|
|
327
|
+
<div
|
|
328
|
+
className="Editorleft"
|
|
329
|
+
onMouseEnter={() => setPreviewScrolling(false)}
|
|
330
|
+
onMouseMove={() => setPreviewScrolling(false)}
|
|
318
331
|
>
|
|
319
|
-
|
|
332
|
+
{isControlled ? (
|
|
320
333
|
<CodeMirrorControlled
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
334
|
+
value={content}
|
|
335
|
+
editorDidMount={editor => {
|
|
336
|
+
instanceCM = editor
|
|
337
|
+
updateInstanceCM(editor)
|
|
338
|
+
}}
|
|
339
|
+
onBeforeChange={(editor, data, value) => {
|
|
340
|
+
setChanged(true)
|
|
325
341
|
// updateText(value);
|
|
326
342
|
onChangeSource(value)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
343
|
+
}}
|
|
344
|
+
onScroll={scrollEditorHandler}
|
|
345
|
+
options={options}
|
|
346
|
+
className="editorApp"
|
|
331
347
|
/>
|
|
332
|
-
|
|
333
|
-
<CodeMirror
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
348
|
+
) : (
|
|
349
|
+
<CodeMirror
|
|
350
|
+
value={content}
|
|
351
|
+
editorDidMount={editor => {
|
|
352
|
+
instanceCM = editor
|
|
353
|
+
updateInstanceCM(editor)
|
|
354
|
+
}}
|
|
355
|
+
onChange={(editor, data, value) => {
|
|
356
|
+
setChanged(true)
|
|
357
|
+
updateText(value)
|
|
358
|
+
onChangeSource(value)
|
|
359
|
+
}}
|
|
360
|
+
onScroll={scrollEditorHandler}
|
|
361
|
+
options={options}
|
|
362
|
+
className="editorApp"
|
|
344
363
|
/>
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
{previewHtml}
|
|
367
|
+
</div>
|
|
348
368
|
</div>
|
|
349
|
-
|
|
350
|
-
);
|
|
369
|
+
)
|
|
351
370
|
}
|
|
371
|
+
|
|
372
|
+
export default Editor
|
package/esm/dict.d.ts
DELETED
package/esm/helpers.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { PodliteDocument } from '@podlite/schema';
|
|
2
|
-
export declare const parse: (str: string) => PodliteDocument;
|
|
3
|
-
export declare const getSuggestionContextForLine: (pod: string, line: number) => 'pod6' | 'md';
|
|
4
|
-
interface Pos {
|
|
5
|
-
line: number;
|
|
6
|
-
offset: number;
|
|
7
|
-
}
|
|
8
|
-
interface Selection {
|
|
9
|
-
start: Pos;
|
|
10
|
-
end: Pos;
|
|
11
|
-
text?: string;
|
|
12
|
-
}
|
|
13
|
-
export declare const templateGetSelectionPos: (pod: string) => Selection | null;
|
|
14
|
-
export declare const addVMargin: (count: number, pod: string) => string;
|
|
15
|
-
export {};
|