@wallarm-org/design-system 1.32.0 → 1.33.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/dist/components/CodeEditor/CodeEditorRoot.d.ts +1 -1
- package/dist/components/CodeEditor/engine/SearchPanel.js +17 -3
- package/dist/components/CodeEditor/engine/completion.d.ts +3 -3
- package/dist/components/CodeEditor/engine/completion.js +142 -2
- package/dist/components/CodeEditor/engine/index.js +21 -4
- package/dist/metadata/components.json +9 -9
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ export interface CodeEditorRootProps extends CodeEditorRootNativeProps, Testable
|
|
|
23
23
|
* document would be seeded with the previous document's text.
|
|
24
24
|
*/
|
|
25
25
|
documentId?: string;
|
|
26
|
-
/**
|
|
26
|
+
/** Non-editable and unfocusable content; text remains selectable, searchable and copyable */
|
|
27
27
|
readOnly?: boolean;
|
|
28
28
|
/** Offsets gutter numbers; `lines`, `folds`, `diagnostics` use absolute numbers */
|
|
29
29
|
startingLineNumber?: number;
|
|
@@ -41,9 +41,20 @@ const SearchPanel = ({ view, query, matchCount, readOnly, testId })=>{
|
|
|
41
41
|
]
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
const blurReadOnlyContent = ()=>{
|
|
45
|
+
if (view.state.readOnly && view.hasFocus) view.contentDOM.blur();
|
|
46
|
+
};
|
|
47
|
+
const closeSearch = ()=>{
|
|
48
|
+
closeSearchPanel(view);
|
|
49
|
+
blurReadOnlyContent();
|
|
50
|
+
};
|
|
44
51
|
const handleKeyDown = (event, onEnter)=>{
|
|
45
52
|
if (event.nativeEvent.isComposing || 229 === event.keyCode) return;
|
|
46
|
-
if (runScopeHandlers(view, event.nativeEvent, 'search-panel'))
|
|
53
|
+
if (runScopeHandlers(view, event.nativeEvent, 'search-panel')) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
blurReadOnlyContent();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
47
58
|
if ('Enter' === event.key) {
|
|
48
59
|
event.preventDefault();
|
|
49
60
|
onEnter(event.shiftKey);
|
|
@@ -53,7 +64,10 @@ const SearchPanel = ({ view, query, matchCount, readOnly, testId })=>{
|
|
|
53
64
|
const handleReplaceKeyDown = (event)=>handleKeyDown(event, ()=>replaceNext(view));
|
|
54
65
|
const handlePanelKeyDown = (event)=>{
|
|
55
66
|
if (event.target instanceof HTMLInputElement) return;
|
|
56
|
-
if (runScopeHandlers(view, event.nativeEvent, 'search-panel'))
|
|
67
|
+
if (runScopeHandlers(view, event.nativeEvent, 'search-panel')) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
blurReadOnlyContent();
|
|
70
|
+
}
|
|
57
71
|
};
|
|
58
72
|
const canSearch = query.valid;
|
|
59
73
|
return /*#__PURE__*/ jsxs("div", {
|
|
@@ -154,7 +168,7 @@ const SearchPanel = ({ view, query, matchCount, readOnly, testId })=>{
|
|
|
154
168
|
className: "ml-auto",
|
|
155
169
|
"aria-label": "Close search",
|
|
156
170
|
"data-testid": slot('search-close'),
|
|
157
|
-
onClick:
|
|
171
|
+
onClick: closeSearch,
|
|
158
172
|
children: /*#__PURE__*/ jsx(X, {})
|
|
159
173
|
})
|
|
160
174
|
]
|
|
@@ -4,9 +4,9 @@ import type { CodeEditorCompletionContext, CodeEditorCompletionSource, CodeEdito
|
|
|
4
4
|
/** Public, engine-free view of a CodeMirror completion request (spec §4). */
|
|
5
5
|
export declare const buildCompletionContext: (ctx: CompletionContext, language: CodeEditorLanguage, startingLineNumber: number) => CodeEditorCompletionContext;
|
|
6
6
|
/**
|
|
7
|
-
* Autocomplete
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Autocomplete: HTTP and JSON Schema sources, consumer sources, then language keywords and
|
|
8
|
+
* document words, all through `override` (language-data sources are off).
|
|
9
|
+
* `completionKeymap` comes with `defaultKeymap: true` at `Prec.highest`, so Enter,
|
|
10
10
|
* arrows and Escape reach the open list before `indentWithTab` / fullscreen Escape.
|
|
11
11
|
*/
|
|
12
12
|
export declare const completionExtension: (config: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { autocompletion } from "@codemirror/autocomplete";
|
|
1
|
+
import { autocompletion, completeAnyWord, completeFromList } from "@codemirror/autocomplete";
|
|
2
2
|
import { EditorView } from "@codemirror/view";
|
|
3
3
|
import { cn } from "../../../utils/cn.js";
|
|
4
4
|
import { dropdownMenuItemVariants } from "../../DropdownMenu/classes.js";
|
|
@@ -106,13 +106,153 @@ const adaptSource = (source, language, startingLineNumber)=>(ctx)=>{
|
|
|
106
106
|
}
|
|
107
107
|
};
|
|
108
108
|
const reopensAfter = (completion)=>'string' == typeof completion.apply && completion.apply.endsWith(': ');
|
|
109
|
+
const LANGUAGE_KEYWORDS = {
|
|
110
|
+
json: [
|
|
111
|
+
'true',
|
|
112
|
+
'false',
|
|
113
|
+
'null'
|
|
114
|
+
],
|
|
115
|
+
yaml: [
|
|
116
|
+
'true',
|
|
117
|
+
'false',
|
|
118
|
+
'null'
|
|
119
|
+
],
|
|
120
|
+
bash: [
|
|
121
|
+
'echo',
|
|
122
|
+
'export',
|
|
123
|
+
'if',
|
|
124
|
+
'then',
|
|
125
|
+
'else',
|
|
126
|
+
'fi',
|
|
127
|
+
'for',
|
|
128
|
+
'in',
|
|
129
|
+
'do',
|
|
130
|
+
'done',
|
|
131
|
+
'function'
|
|
132
|
+
],
|
|
133
|
+
javascript: [
|
|
134
|
+
'const',
|
|
135
|
+
'let',
|
|
136
|
+
'var',
|
|
137
|
+
'function',
|
|
138
|
+
'return',
|
|
139
|
+
'if',
|
|
140
|
+
'else',
|
|
141
|
+
'for',
|
|
142
|
+
'while',
|
|
143
|
+
'class',
|
|
144
|
+
'import',
|
|
145
|
+
'export',
|
|
146
|
+
'from',
|
|
147
|
+
'async',
|
|
148
|
+
'await',
|
|
149
|
+
'true',
|
|
150
|
+
'false',
|
|
151
|
+
'null',
|
|
152
|
+
'undefined'
|
|
153
|
+
],
|
|
154
|
+
typescript: [
|
|
155
|
+
'const',
|
|
156
|
+
'let',
|
|
157
|
+
'function',
|
|
158
|
+
'return',
|
|
159
|
+
'if',
|
|
160
|
+
'else',
|
|
161
|
+
'class',
|
|
162
|
+
'import',
|
|
163
|
+
'export',
|
|
164
|
+
'from',
|
|
165
|
+
'async',
|
|
166
|
+
'await',
|
|
167
|
+
'interface',
|
|
168
|
+
'type',
|
|
169
|
+
'enum',
|
|
170
|
+
'extends',
|
|
171
|
+
'implements',
|
|
172
|
+
'public',
|
|
173
|
+
'private',
|
|
174
|
+
'readonly',
|
|
175
|
+
'true',
|
|
176
|
+
'false',
|
|
177
|
+
'null',
|
|
178
|
+
'undefined'
|
|
179
|
+
],
|
|
180
|
+
python: [
|
|
181
|
+
'def',
|
|
182
|
+
'class',
|
|
183
|
+
'return',
|
|
184
|
+
'if',
|
|
185
|
+
'elif',
|
|
186
|
+
'else',
|
|
187
|
+
'for',
|
|
188
|
+
'while',
|
|
189
|
+
'in',
|
|
190
|
+
'import',
|
|
191
|
+
'from',
|
|
192
|
+
'as',
|
|
193
|
+
'async',
|
|
194
|
+
'await',
|
|
195
|
+
'try',
|
|
196
|
+
'except',
|
|
197
|
+
'with',
|
|
198
|
+
'True',
|
|
199
|
+
'False',
|
|
200
|
+
'None'
|
|
201
|
+
],
|
|
202
|
+
lua: [
|
|
203
|
+
'function',
|
|
204
|
+
'local',
|
|
205
|
+
'return',
|
|
206
|
+
'if',
|
|
207
|
+
'then',
|
|
208
|
+
'elseif',
|
|
209
|
+
'else',
|
|
210
|
+
'end',
|
|
211
|
+
'for',
|
|
212
|
+
'while',
|
|
213
|
+
'do',
|
|
214
|
+
'repeat',
|
|
215
|
+
'until',
|
|
216
|
+
'true',
|
|
217
|
+
'false',
|
|
218
|
+
'nil'
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
const isInsideQuotedString = (text)=>{
|
|
222
|
+
let quoted = false;
|
|
223
|
+
for(let i = 0; i < text.length; i++)if ('\\' === text[i]) i++;
|
|
224
|
+
else if ('"' === text[i]) quoted = !quoted;
|
|
225
|
+
return quoted;
|
|
226
|
+
};
|
|
227
|
+
const builtInSources = (language)=>{
|
|
228
|
+
const keywords = LANGUAGE_KEYWORDS[language];
|
|
229
|
+
if (!keywords) return [
|
|
230
|
+
completeAnyWord
|
|
231
|
+
];
|
|
232
|
+
const completeKeywords = completeFromList(keywords.map((label)=>({
|
|
233
|
+
label,
|
|
234
|
+
type: 'keyword'
|
|
235
|
+
})));
|
|
236
|
+
if ('json' === language) return [
|
|
237
|
+
(ctx)=>{
|
|
238
|
+
const line = ctx.state.doc.lineAt(ctx.pos);
|
|
239
|
+
const before = line.text.slice(0, ctx.pos - line.from);
|
|
240
|
+
if (isInsideQuotedString(before)) return completeAnyWord(ctx);
|
|
241
|
+
return /(?:^|[:,\[])[\s\w]*$/.test(before) ? completeKeywords(ctx) : null;
|
|
242
|
+
}
|
|
243
|
+
];
|
|
244
|
+
return [
|
|
245
|
+
completeKeywords,
|
|
246
|
+
completeAnyWord
|
|
247
|
+
];
|
|
248
|
+
};
|
|
109
249
|
const completionExtension = (config)=>{
|
|
110
250
|
const { language, schema, sources, startingLineNumber } = config;
|
|
111
251
|
const override = [];
|
|
112
252
|
if ('http' === language) override.push(adaptSource(httpCompletions, language, startingLineNumber));
|
|
113
253
|
if (void 0 !== schema && isJsonLike(language)) override.push(schemaCompletionSource(()=>schema, (state)=>jsonRegion(state, language)));
|
|
114
254
|
for (const source of sources)override.push(adaptSource(source, language, startingLineNumber));
|
|
115
|
-
|
|
255
|
+
override.push(...builtInSources(language));
|
|
116
256
|
return [
|
|
117
257
|
autocompletion({
|
|
118
258
|
override,
|
|
@@ -79,7 +79,8 @@ const SLOT_DEPS = {
|
|
|
79
79
|
],
|
|
80
80
|
contentAttributes: [
|
|
81
81
|
'contentAttributes',
|
|
82
|
-
'testId'
|
|
82
|
+
'testId',
|
|
83
|
+
'readOnly'
|
|
83
84
|
],
|
|
84
85
|
cspNonce: [
|
|
85
86
|
'cspNonce'
|
|
@@ -184,6 +185,7 @@ const featureExtensions = (options, callbacks)=>({
|
|
|
184
185
|
});
|
|
185
186
|
const contentAttributesFor = (options)=>{
|
|
186
187
|
const attrs = sanitizeContentAttributes(options.contentAttributes);
|
|
188
|
+
if (options.readOnly) delete attrs.tabindex;
|
|
187
189
|
if (options.testId) attrs['data-testid'] = `${options.testId}--editor`;
|
|
188
190
|
return attrs;
|
|
189
191
|
};
|
|
@@ -225,7 +227,10 @@ const gutterAria = [
|
|
|
225
227
|
];
|
|
226
228
|
const slotExtensions = (options, callbacks)=>({
|
|
227
229
|
language: ()=>languageExtension(options.language),
|
|
228
|
-
readOnly: ()=>
|
|
230
|
+
readOnly: ()=>[
|
|
231
|
+
EditorState.readOnly.of(options.readOnly),
|
|
232
|
+
EditorView.editable.of(!options.readOnly)
|
|
233
|
+
],
|
|
229
234
|
wrap: ()=>options.wrapLines ? EditorView.lineWrapping : [],
|
|
230
235
|
maxHeight: ()=>maxHeightTheme(options.maxHeight),
|
|
231
236
|
contentAttributes: ()=>EditorView.contentAttributes.of(contentAttributesFor(options)),
|
|
@@ -298,6 +303,10 @@ const createEditor = (parent, initialOptions, callbacks)=>{
|
|
|
298
303
|
state: createState(initialOptions.value, initialOptions),
|
|
299
304
|
parent
|
|
300
305
|
});
|
|
306
|
+
const preventReadOnlyMouseFocus = (event)=>{
|
|
307
|
+
if (view.state.readOnly && event.target instanceof Node && view.contentDOM.contains(event.target)) event.stopPropagation();
|
|
308
|
+
};
|
|
309
|
+
view.dom.addEventListener('mousedown', preventReadOnlyMouseFocus, true);
|
|
301
310
|
const ensureLazyLanguage = (lang)=>{
|
|
302
311
|
if (!isLazyLanguage(lang)) return;
|
|
303
312
|
loadLanguageExtension(lang).then((extension)=>{
|
|
@@ -346,13 +355,20 @@ const createEditor = (parent, initialOptions, callbacks)=>{
|
|
|
346
355
|
const update = (next)=>{
|
|
347
356
|
const prev = options;
|
|
348
357
|
options = next;
|
|
349
|
-
if (next.documentId !== prev.documentId)
|
|
358
|
+
if (next.documentId !== prev.documentId) {
|
|
359
|
+
swapDocument(prev, next);
|
|
360
|
+
if (next.readOnly && view.hasFocus) view.contentDOM.blur();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
350
363
|
reconfigure(prev, next);
|
|
364
|
+
if (next.readOnly && view.hasFocus) view.contentDOM.blur();
|
|
351
365
|
if (next.language !== prev.language) ensureLazyLanguage(next.language);
|
|
352
366
|
syncValue(next.value);
|
|
353
367
|
};
|
|
354
368
|
const api = {
|
|
355
|
-
focus: ()=>
|
|
369
|
+
focus: ()=>{
|
|
370
|
+
if (!view.state.readOnly) view.focus();
|
|
371
|
+
},
|
|
356
372
|
getValue: ()=>view.state.doc.toString(),
|
|
357
373
|
insertText: (text)=>{
|
|
358
374
|
if (view.state.readOnly) return;
|
|
@@ -380,6 +396,7 @@ const createEditor = (parent, initialOptions, callbacks)=>{
|
|
|
380
396
|
destroy: ()=>{
|
|
381
397
|
destroyed = true;
|
|
382
398
|
cache.clear();
|
|
399
|
+
view.dom.removeEventListener('mousedown', preventReadOnlyMouseFocus, true);
|
|
383
400
|
view.destroy();
|
|
384
401
|
}
|
|
385
402
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"generatedAt": "2026-10-
|
|
2
|
+
"version": "1.32.0",
|
|
3
|
+
"generatedAt": "2026-10-02T12:06:48.075Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Accordion",
|
|
@@ -19208,7 +19208,7 @@
|
|
|
19208
19208
|
"name": "readOnly",
|
|
19209
19209
|
"type": "boolean | undefined",
|
|
19210
19210
|
"required": false,
|
|
19211
|
-
"description": "
|
|
19211
|
+
"description": "Non-editable and unfocusable content; text remains selectable, searchable and copyable"
|
|
19212
19212
|
},
|
|
19213
19213
|
{
|
|
19214
19214
|
"name": "startingLineNumber",
|
|
@@ -19882,7 +19882,7 @@
|
|
|
19882
19882
|
"name": "readOnly",
|
|
19883
19883
|
"type": "boolean | undefined",
|
|
19884
19884
|
"required": false,
|
|
19885
|
-
"description": "
|
|
19885
|
+
"description": "Non-editable and unfocusable content; text remains selectable, searchable and copyable",
|
|
19886
19886
|
"defaultValue": "false"
|
|
19887
19887
|
},
|
|
19888
19888
|
{
|
|
@@ -20301,7 +20301,7 @@
|
|
|
20301
20301
|
{
|
|
20302
20302
|
"name": "ReadOnly",
|
|
20303
20303
|
"code": "() => (\n <div style={{ width: '600px' }}>\n <CodeEditorRoot\n value={httpResponseCode}\n language='http'\n readOnly\n data-testid='code-editor-read-only'\n >\n <CodeSnippetHeader>\n <CodeSnippetTitle>Response</CodeSnippetTitle>\n <CodeSnippetActions>\n <CodeSnippetCopyButton />\n </CodeSnippetActions>\n </CodeSnippetHeader>\n <CodeEditorContent lineNumbers aria-label='HTTP response, read only' />\n </CodeEditorRoot>\n </div>\n)",
|
|
20304
|
-
"description": "`readOnly`
|
|
20304
|
+
"description": "`readOnly` lets you select, search and copy without focusing the code or showing a caret. Use it when the value\ncan change elsewhere but not here. If it never changes, use `CodeSnippet`."
|
|
20305
20305
|
},
|
|
20306
20306
|
{
|
|
20307
20307
|
"name": "Tabs",
|
|
@@ -20344,9 +20344,9 @@
|
|
|
20344
20344
|
"description": "Parity check for the three sizes: the same font size, line height and gutter padding per size."
|
|
20345
20345
|
},
|
|
20346
20346
|
{
|
|
20347
|
-
"name": "
|
|
20348
|
-
"code": "() => (\n <VStack gap={24}>\n <ParityPair testId='parity-
|
|
20349
|
-
"description": "Parity check for the shared
|
|
20347
|
+
"name": "ParityFrame",
|
|
20348
|
+
"code": "() => (\n <VStack gap={24}>\n <ParityPair testId='parity-frame-header' code={sampleCode} language='text'>\n {PARITY_HEADER_FRAME}\n </ParityPair>\n <ParityPair testId='parity-frame-floating' code={sampleCode} language='text'>\n {PARITY_FLOATING_FRAME}\n </ParityPair>\n </VStack>\n)",
|
|
20349
|
+
"description": "Parity check for the shared frame: a header with tabs and actions, and floating actions\nwithout a header, sit in the same place with the same spacing."
|
|
20350
20350
|
},
|
|
20351
20351
|
{
|
|
20352
20352
|
"name": "ParityShowMore",
|
|
@@ -20391,7 +20391,7 @@
|
|
|
20391
20391
|
{
|
|
20392
20392
|
"name": "LongDocument",
|
|
20393
20393
|
"code": "() => <LongDocumentDemo />",
|
|
20394
|
-
"description": "2 000 read-only lines clamped by `maxLines` — the top of the envelope the colour painter is\nbuilt for, where `readOnly` still lets the text be
|
|
20394
|
+
"description": "2 000 read-only lines clamped by `maxLines` — the top of the envelope the colour painter is\nbuilt for, where `readOnly` still lets the text be selected, searched and copied."
|
|
20395
20395
|
}
|
|
20396
20396
|
]
|
|
20397
20397
|
},
|