@nethru/ui 2.1.37 → 2.1.39

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.
Files changed (2) hide show
  1. package/base/editor/Editor.js +166 -131
  2. package/package.json +1 -1
@@ -42,7 +42,77 @@ export default function Editor({
42
42
  const [view, setView] = useState();
43
43
  const [content, setContent] = useState('');
44
44
  const [focused, setFocused] = useState(false);
45
- const theme = useMemo(() => EditorView.theme({
45
+ const theme = useMemo(() => makeTheme(focused, error), [focused, error]);
46
+ const highlight = useMemo(() => makeHighlight(), []);
47
+ // eslint-disable-next-line
48
+ const styles = useMemo(() => makeStyle(restrictWidth, ref.current), [restrictWidth, ref.current]);
49
+ const eslintLinter = useMemo(() => makeEslinter(format, eslintRules, onEslintUpdate, customVariables), [format, eslintRules, onEslintUpdate, customVariables]);
50
+ const languageExtension = useMemo(() => getLanguageExtension(format), [format]);
51
+ const keymapExtension = useMemo(() => getKeymapExtension(keymap), [keymap]);
52
+ const extensionList = useMemo(() => makeExtensions(languageExtension, eslintLinter, highlight.extension, keymapExtension, extensions, onScroll), [languageExtension, eslintLinter, highlight.extension, keymapExtension, extensions, onScroll]);
53
+ const handleUpdate = useCallback(viewUpdate => {
54
+ setFocused(viewUpdate.view.hasFocus);
55
+ if (onUpdate) onUpdate(viewUpdate);
56
+ }, [onUpdate]);
57
+ const highlightKeyword = useCallback(() => {
58
+ if (!view || !keyword) return;
59
+ const {
60
+ effect,
61
+ decoration
62
+ } = highlight;
63
+ const cursor = isRegExp ? new RegExpCursor(view.state.doc, keyword) : new SearchCursor(view.state.doc, keyword, undefined, undefined, s => s.toLowerCase());
64
+ while (true) {
65
+ cursor.next();
66
+ if (cursor.done) break;
67
+ view.dispatch({
68
+ effects: effect.of([decoration.range(cursor.value.from, cursor.value.to)])
69
+ });
70
+ }
71
+ }, [view, highlight, keyword, isRegExp]);
72
+ const scrollToBottom = useCallback(() => {
73
+ if (!view || !autoScrollToBottom) return;
74
+ view.dispatch({
75
+ effects: EditorView.scrollIntoView(view.state.doc.length)
76
+ });
77
+ }, [view, autoScrollToBottom]);
78
+ useEffect(() => {
79
+ setContent(value);
80
+ }, [value]);
81
+ useEffect(() => {
82
+ highlightKeyword();
83
+ scrollToBottom();
84
+ }, [content, view, highlightKeyword, scrollToBottom]);
85
+ useEffect(() => {
86
+ if (focused && onFocus) onFocus();
87
+ if (!focused && onBlur) onBlur();
88
+ }, [focused, onFocus, onBlur]);
89
+ return /*#__PURE__*/_jsxs(Box, {
90
+ ref: ref,
91
+ children: [/*#__PURE__*/_jsx(CodeMirror, {
92
+ value: content,
93
+ editable: !readOnly,
94
+ extensions: extensionList,
95
+ basicSetup: {
96
+ ...basicSetup,
97
+ highlightActiveLineGutter: false,
98
+ highlightActiveLine: !readOnly
99
+ },
100
+ onCreateEditor: (view, state) => setView(view),
101
+ onUpdate: handleUpdate,
102
+ theme: theme,
103
+ style: {
104
+ ...style,
105
+ ...styles
106
+ },
107
+ ...props
108
+ }), helperText && /*#__PURE__*/_jsx(FormHelperText, {
109
+ error: error,
110
+ children: helperText
111
+ })]
112
+ });
113
+ }
114
+ function makeTheme(focused, error) {
115
+ return EditorView.theme({
46
116
  '&': {
47
117
  fontSize: '13px',
48
118
  outline: 'none !important'
@@ -95,59 +165,48 @@ export default function Editor({
95
165
  }
96
166
  }, {
97
167
  dark: false
98
- }), [focused, error]);
99
- const styles = useMemo(() => {
100
- return {
101
- maxWidth: restrictWidth && ref.current ? `${ref.current.offsetWidth}px` : 'unset'
102
- };
103
- // eslint-disable-next-line
104
- }, [restrictWidth, ref.current]);
105
- const highlight = useMemo(() => {
106
- const highlightEffect = StateEffect.define();
107
- const decoration = Decoration.mark({
108
- attributes: {
109
- style: `background-color: ${yellow[100]}`
110
- }
111
- });
112
- const extension = StateField.define({
113
- create() {
114
- return Decoration.none;
115
- },
116
- update(value, transaction) {
117
- value = value.map(transaction.changes);
118
- for (let effect of transaction.effects) {
119
- if (effect.is(highlightEffect)) {
120
- value = value.update({
121
- add: effect.value,
122
- sort: true
123
- });
124
- }
125
- }
126
- return value;
127
- },
128
- provide: field => EditorView.decorations.from(field)
129
- });
130
- return {
131
- effect: highlightEffect,
132
- decoration: decoration,
133
- extension: extension
134
- };
135
- }, []);
136
- const languageExtension = useMemo(() => {
137
- switch (format) {
138
- case 'json':
139
- return json();
140
- case 'nscript':
141
- return undefined;
142
- default:
143
- return javascript({
144
- jsx: true
145
- });
168
+ });
169
+ }
170
+ function makeStyle(restrictWidth, instance) {
171
+ return {
172
+ maxWidth: restrictWidth && instance ? `${instance.offsetWidth}px` : 'unset'
173
+ };
174
+ }
175
+ function makeHighlight() {
176
+ const highlightEffect = StateEffect.define();
177
+ const decoration = Decoration.mark({
178
+ attributes: {
179
+ style: `background-color: ${yellow[100]}`
146
180
  }
147
- }, [format]);
148
- const eslintLinter = useMemo(() => {
149
- const regex = /\{\{(\S+)\}\}/g;
150
- return format === 'javascript' ? linter(view => {
181
+ });
182
+ const extension = StateField.define({
183
+ create() {
184
+ return Decoration.none;
185
+ },
186
+ update(value, transaction) {
187
+ value = value.map(transaction.changes);
188
+ for (let effect of transaction.effects) {
189
+ if (effect.is(highlightEffect)) {
190
+ value = value.update({
191
+ add: effect.value,
192
+ sort: true
193
+ });
194
+ }
195
+ }
196
+ return value;
197
+ },
198
+ provide: field => EditorView.decorations.from(field)
199
+ });
200
+ return {
201
+ effect: highlightEffect,
202
+ decoration: decoration,
203
+ extension: extension
204
+ };
205
+ }
206
+ function makeEslinter(format, eslintRules, onEslintUpdate, customVariables) {
207
+ const regex = /\{\{(.+)\}\}/g;
208
+ if (format === 'javascript') {
209
+ return linter(view => {
151
210
  const text = view.state.doc.toString();
152
211
  const linter = new Linter();
153
212
  let replaced = text;
@@ -193,84 +252,60 @@ export default function Editor({
193
252
  }
194
253
  if (onEslintUpdate) onEslintUpdate(hints);
195
254
  return hints;
196
- }) : undefined;
197
- }, [format, eslintRules, onEslintUpdate, customVariables]);
198
- const keymapExtension = useMemo(() => {
199
- return [keyMapper.of(keymap), keyMapper.of(defaultKeymap)];
200
- }, [keymap]);
201
- const extensionList = useMemo(() => {
202
- const result = [EditorView.lineWrapping, highlight.extension, ...keymapExtension];
203
- if (languageExtension) result.push(languageExtension);
204
- if (eslintLinter) result.push(eslintLinter);
205
- if (onScroll) {
206
- result.push(EditorView.domEventHandlers({
207
- scroll(event, view) {
208
- if (!event.target?.className.includes('cm-scroller')) return;
209
- onScroll(event, view.scrollDOM);
255
+ });
256
+ } else if (format === 'json') {
257
+ return linter(view => {
258
+ if (!onEslintUpdate) return [];
259
+ const text = view.state.doc.toString();
260
+ const hints = [];
261
+ try {
262
+ JSON.parse(text);
263
+ } catch (e) {
264
+ if (e instanceof SyntaxError && 'message' in e) {
265
+ const match = e.message.match(/at position (\d+)/);
266
+ let pos = 0;
267
+ if (match) pos = parseInt(match[1], 10);
268
+ hints.push({
269
+ line: view.state.doc.lineAt(pos),
270
+ from: pos,
271
+ to: pos + 1,
272
+ severity: 'error',
273
+ message: e.message
274
+ });
210
275
  }
211
- }));
212
- }
213
- result.push(...extensions);
214
- return result;
215
- }, [languageExtension, eslintLinter, highlight.extension, keymapExtension, extensions, onScroll]);
216
- const handleUpdate = viewUpdate => {
217
- setFocused(viewUpdate.view.hasFocus);
218
- if (onUpdate) onUpdate(viewUpdate);
219
- };
220
- const highlightKeyword = useCallback(() => {
221
- if (!view || !keyword) return;
222
- const {
223
- effect,
224
- decoration
225
- } = highlight;
226
- const cursor = isRegExp ? new RegExpCursor(view.state.doc, keyword) : new SearchCursor(view.state.doc, keyword, undefined, undefined, s => s.toLowerCase());
227
- while (true) {
228
- cursor.next();
229
- if (cursor.done) break;
230
- view.dispatch({
231
- effects: effect.of([decoration.range(cursor.value.from, cursor.value.to)])
232
- });
233
- }
234
- }, [view, highlight, keyword, isRegExp]);
235
- const scrollToBottom = useCallback(() => {
236
- if (!view || !autoScrollToBottom) return;
237
- view.dispatch({
238
- effects: EditorView.scrollIntoView(view.state.doc.length)
276
+ }
277
+ onEslintUpdate(hints);
278
+ return hints;
239
279
  });
240
- }, [view, autoScrollToBottom]);
241
- useEffect(() => {
242
- setContent(value);
243
- }, [value]);
244
- useEffect(() => {
245
- highlightKeyword();
246
- scrollToBottom();
247
- }, [content, view, highlightKeyword, scrollToBottom]);
248
- useEffect(() => {
249
- if (focused && onFocus) onFocus();
250
- if (!focused && onBlur) onBlur();
251
- }, [focused, onFocus, onBlur]);
252
- return /*#__PURE__*/_jsxs(Box, {
253
- ref: ref,
254
- children: [/*#__PURE__*/_jsx(CodeMirror, {
255
- value: content,
256
- editable: !readOnly,
257
- extensions: extensionList,
258
- basicSetup: {
259
- ...basicSetup,
260
- highlightActiveLineGutter: false,
261
- highlightActiveLine: !readOnly
262
- },
263
- onCreateEditor: (view, state) => setView(view),
264
- onUpdate: handleUpdate,
265
- theme: theme,
266
- style: {
267
- ...style,
268
- ...styles
269
- },
270
- ...props
271
- }), helperText && /*#__PURE__*/_jsx(FormHelperText, {
272
- error: error,
273
- children: helperText
274
- })]
275
- });
280
+ }
281
+ }
282
+ function makeExtensions(languageExtension, eslintLinter, highlightExtension, keymapExtension, extensions, onScroll) {
283
+ const result = [EditorView.lineWrapping, highlightExtension, ...keymapExtension];
284
+ if (languageExtension) result.push(languageExtension);
285
+ if (eslintLinter) result.push(eslintLinter);
286
+ if (onScroll) {
287
+ result.push(EditorView.domEventHandlers({
288
+ scroll(event, view) {
289
+ if (!event.target?.className.includes('cm-scroller')) return;
290
+ onScroll(event, view.scrollDOM);
291
+ }
292
+ }));
293
+ }
294
+ result.push(...extensions);
295
+ return result;
296
+ }
297
+ function getLanguageExtension(format) {
298
+ switch (format) {
299
+ case 'json':
300
+ return json();
301
+ case 'nscript':
302
+ return undefined;
303
+ default:
304
+ return javascript({
305
+ jsx: true
306
+ });
307
+ }
308
+ }
309
+ function getKeymapExtension(keymap) {
310
+ return [keyMapper.of(keymap), keyMapper.of(defaultKeymap)];
276
311
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nethru/ui",
3
- "version": "2.1.37",
3
+ "version": "2.1.39",
4
4
  "main": "base/index.js",
5
5
  "files": [
6
6
  "/base"